github.com/v2pro/plz@v0.0.0-20221028024117-e5f9aec5b631/test/go-spew/spew/dump.go (about)

     1  /*
     2   * Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
     3   *
     4   * Permission to use, copy, modify, and distribute this software for any
     5   * purpose with or without fee is hereby granted, provided that the above
     6   * copyright notice and this permission notice appear in all copies.
     7   *
     8   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     9   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    10   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    11   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    12   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    13   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    14   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    15   */
    16  
    17  package spew
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/hex"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  	"reflect"
    26  	"regexp"
    27  	"strconv"
    28  	"strings"
    29  )
    30  
    31  var (
    32  	// uint8Type is a reflect.Type representing a uint8.  It is used to
    33  	// convert cgo types to uint8 slices for hexdumping.
    34  	uint8Type = reflect.TypeOf(uint8(0))
    35  
    36  	// cCharRE is a regular expression that matches a cgo char.
    37  	// It is used to detect character arrays to hexdump them.
    38  	cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
    39  
    40  	// cUnsignedCharRE is a regular expression that matches a cgo unsigned
    41  	// char.  It is used to detect unsigned character arrays to hexdump
    42  	// them.
    43  	cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
    44  
    45  	// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
    46  	// It is used to detect uint8_t arrays to hexdump them.
    47  	cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
    48  )
    49  
    50  // dumpState contains information about the state of a dump operation.
    51  type dumpState struct {
    52  	w                io.Writer
    53  	depth            int
    54  	pointers         map[uintptr]int
    55  	ignoreNextType   bool
    56  	ignoreNextIndent bool
    57  	cs               *ConfigState
    58  }
    59  
    60  // indent performs indentation according to the depth level and cs.Indent
    61  // option.
    62  func (d *dumpState) indent() {
    63  	if d.ignoreNextIndent {
    64  		d.ignoreNextIndent = false
    65  		return
    66  	}
    67  	d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
    68  }
    69  
    70  // unpackValue returns values inside of non-nil interfaces when possible.
    71  // This is useful for data types like structs, arrays, slices, and maps which
    72  // can contain varying types packed inside an interface.
    73  func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
    74  	if v.Kind() == reflect.Interface && !v.IsNil() {
    75  		v = v.Elem()
    76  	}
    77  	return v
    78  }
    79  
    80  // dumpPtr handles formatting of pointers by indirecting them as necessary.
    81  func (d *dumpState) dumpPtr(v reflect.Value) {
    82  	// Remove pointers at or below the current depth from map used to detect
    83  	// circular refs.
    84  	for k, depth := range d.pointers {
    85  		if depth >= d.depth {
    86  			delete(d.pointers, k)
    87  		}
    88  	}
    89  
    90  	// Keep list of all dereferenced pointers to show later.
    91  	pointerChain := make([]uintptr, 0)
    92  
    93  	// Figure out how many levels of indirection there are by dereferencing
    94  	// pointers and unpacking interfaces down the chain while detecting circular
    95  	// references.
    96  	nilFound := false
    97  	cycleFound := false
    98  	indirects := 0
    99  	ve := v
   100  	for ve.Kind() == reflect.Ptr {
   101  		if ve.IsNil() {
   102  			nilFound = true
   103  			break
   104  		}
   105  		indirects++
   106  		addr := ve.Pointer()
   107  		pointerChain = append(pointerChain, addr)
   108  		if pd, ok := d.pointers[addr]; ok && pd < d.depth {
   109  			cycleFound = true
   110  			indirects--
   111  			break
   112  		}
   113  		d.pointers[addr] = d.depth
   114  
   115  		ve = ve.Elem()
   116  		if ve.Kind() == reflect.Interface {
   117  			if ve.IsNil() {
   118  				nilFound = true
   119  				break
   120  			}
   121  			ve = ve.Elem()
   122  		}
   123  	}
   124  
   125  	// Display type information.
   126  	d.w.Write(openParenBytes)
   127  	d.w.Write(bytes.Repeat(asteriskBytes, indirects))
   128  	d.w.Write([]byte(ve.Type().String()))
   129  	d.w.Write(closeParenBytes)
   130  
   131  	// Display pointer information.
   132  	if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
   133  		d.w.Write(openParenBytes)
   134  		for i, addr := range pointerChain {
   135  			if i > 0 {
   136  				d.w.Write(pointerChainBytes)
   137  			}
   138  			printHexPtr(d.w, addr)
   139  		}
   140  		d.w.Write(closeParenBytes)
   141  	}
   142  
   143  	// Display dereferenced value.
   144  	d.w.Write(openParenBytes)
   145  	switch {
   146  	case nilFound == true:
   147  		d.w.Write(nilAngleBytes)
   148  
   149  	case cycleFound == true:
   150  		d.w.Write(circularBytes)
   151  
   152  	default:
   153  		d.ignoreNextType = true
   154  		d.dump(ve)
   155  	}
   156  	d.w.Write(closeParenBytes)
   157  }
   158  
   159  // dumpSlice handles formatting of arrays and slices.  Byte (uint8 under
   160  // reflection) arrays and slices are dumped in hexdump -C fashion.
   161  func (d *dumpState) dumpSlice(v reflect.Value) {
   162  	// Determine whether this type should be hex dumped or not.  Also,
   163  	// for types which should be hexdumped, try to use the underlying data
   164  	// first, then fall back to trying to convert them to a uint8 slice.
   165  	var buf []uint8
   166  	doConvert := false
   167  	doHexDump := false
   168  	numEntries := v.Len()
   169  	if numEntries > 0 {
   170  		vt := v.Index(0).Type()
   171  		vts := vt.String()
   172  		switch {
   173  		// C types that need to be converted.
   174  		case cCharRE.MatchString(vts):
   175  			fallthrough
   176  		case cUnsignedCharRE.MatchString(vts):
   177  			fallthrough
   178  		case cUint8tCharRE.MatchString(vts):
   179  			doConvert = true
   180  
   181  		// Try to use existing uint8 slices and fall back to converting
   182  		// and copying if that fails.
   183  		case vt.Kind() == reflect.Uint8:
   184  			// We need an addressable interface to convert the type
   185  			// to a byte slice.  However, the reflect package won't
   186  			// give us an interface on certain things like
   187  			// unexported struct fields in order to enforce
   188  			// visibility rules.  We use unsafe, when available, to
   189  			// bypass these restrictions since this package does not
   190  			// mutate the values.
   191  			vs := v
   192  			if !vs.CanInterface() || !vs.CanAddr() {
   193  				vs = unsafeReflectValue(vs)
   194  			}
   195  			if !UnsafeDisabled {
   196  				vs = vs.Slice(0, numEntries)
   197  
   198  				// Use the existing uint8 slice if it can be
   199  				// type asserted.
   200  				iface := vs.Interface()
   201  				if slice, ok := iface.([]uint8); ok {
   202  					buf = slice
   203  					doHexDump = true
   204  					break
   205  				}
   206  			}
   207  
   208  			// The underlying data needs to be converted if it can't
   209  			// be type asserted to a uint8 slice.
   210  			doConvert = true
   211  		}
   212  
   213  		// Copy and convert the underlying type if needed.
   214  		if doConvert && vt.ConvertibleTo(uint8Type) {
   215  			// Convert and copy each element into a uint8 byte
   216  			// slice.
   217  			buf = make([]uint8, numEntries)
   218  			for i := 0; i < numEntries; i++ {
   219  				vv := v.Index(i)
   220  				buf[i] = uint8(vv.Convert(uint8Type).Uint())
   221  			}
   222  			doHexDump = true
   223  		}
   224  	}
   225  
   226  	// Hexdump the entire slice as needed.
   227  	if doHexDump {
   228  		indent := strings.Repeat(d.cs.Indent, d.depth)
   229  		str := indent + hex.Dump(buf)
   230  		str = strings.Replace(str, "\n", "\n"+indent, -1)
   231  		str = strings.TrimRight(str, d.cs.Indent)
   232  		d.w.Write([]byte(str))
   233  		return
   234  	}
   235  
   236  	// Recursively call dump for each item.
   237  	for i := 0; i < numEntries; i++ {
   238  		d.dump(d.unpackValue(v.Index(i)))
   239  		if i < (numEntries - 1) {
   240  			d.w.Write(commaNewlineBytes)
   241  		} else {
   242  			d.w.Write(newlineBytes)
   243  		}
   244  	}
   245  }
   246  
   247  // dump is the main workhorse for dumping a value.  It uses the passed reflect
   248  // value to figure out what kind of object we are dealing with and formats it
   249  // appropriately.  It is a recursive function, however circular data structures
   250  // are detected and handled properly.
   251  func (d *dumpState) dump(v reflect.Value) {
   252  	// Handle invalid reflect values immediately.
   253  	kind := v.Kind()
   254  	if kind == reflect.Invalid {
   255  		d.w.Write(invalidAngleBytes)
   256  		return
   257  	}
   258  
   259  	// Handle pointers specially.
   260  	if kind == reflect.Ptr {
   261  		d.indent()
   262  		d.dumpPtr(v)
   263  		return
   264  	}
   265  
   266  	// Print type information unless already handled elsewhere.
   267  	if !d.ignoreNextType {
   268  		d.indent()
   269  		d.w.Write(openParenBytes)
   270  		d.w.Write([]byte(v.Type().String()))
   271  		d.w.Write(closeParenBytes)
   272  		d.w.Write(spaceBytes)
   273  	}
   274  	d.ignoreNextType = false
   275  
   276  	// Display length and capacity if the built-in len and cap functions
   277  	// work with the value's kind and the len/cap itself is non-zero.
   278  	valueLen, valueCap := 0, 0
   279  	switch v.Kind() {
   280  	case reflect.Array, reflect.Slice, reflect.Chan:
   281  		valueLen, valueCap = v.Len(), v.Cap()
   282  	case reflect.Map, reflect.String:
   283  		valueLen = v.Len()
   284  	}
   285  	if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
   286  		d.w.Write(openParenBytes)
   287  		if valueLen != 0 {
   288  			d.w.Write(lenEqualsBytes)
   289  			printInt(d.w, int64(valueLen), 10)
   290  		}
   291  		if !d.cs.DisableCapacities && valueCap != 0 {
   292  			if valueLen != 0 {
   293  				d.w.Write(spaceBytes)
   294  			}
   295  			d.w.Write(capEqualsBytes)
   296  			printInt(d.w, int64(valueCap), 10)
   297  		}
   298  		d.w.Write(closeParenBytes)
   299  		d.w.Write(spaceBytes)
   300  	}
   301  
   302  	// Call Stringer/error interfaces if they exist and the handle methods flag
   303  	// is enabled
   304  	if !d.cs.DisableMethods {
   305  		if (kind != reflect.Invalid) && (kind != reflect.Interface) {
   306  			if handled := handleMethods(d.cs, d.w, v); handled {
   307  				return
   308  			}
   309  		}
   310  	}
   311  
   312  	switch kind {
   313  	case reflect.Invalid:
   314  		// Do nothing.  We should never get here since invalid has already
   315  		// been handled above.
   316  
   317  	case reflect.Bool:
   318  		printBool(d.w, v.Bool())
   319  
   320  	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
   321  		printInt(d.w, v.Int(), 10)
   322  
   323  	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
   324  		printUint(d.w, v.Uint(), 10)
   325  
   326  	case reflect.Float32:
   327  		printFloat(d.w, v.Float(), 32)
   328  
   329  	case reflect.Float64:
   330  		printFloat(d.w, v.Float(), 64)
   331  
   332  	case reflect.Complex64:
   333  		printComplex(d.w, v.Complex(), 32)
   334  
   335  	case reflect.Complex128:
   336  		printComplex(d.w, v.Complex(), 64)
   337  
   338  	case reflect.Slice:
   339  		if v.IsNil() {
   340  			d.w.Write(nilAngleBytes)
   341  			break
   342  		}
   343  		fallthrough
   344  
   345  	case reflect.Array:
   346  		d.w.Write(openBraceNewlineBytes)
   347  		d.depth++
   348  		if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
   349  			d.indent()
   350  			d.w.Write(maxNewlineBytes)
   351  		} else {
   352  			d.dumpSlice(v)
   353  		}
   354  		d.depth--
   355  		d.indent()
   356  		d.w.Write(closeBraceBytes)
   357  
   358  	case reflect.String:
   359  		d.w.Write([]byte(strconv.Quote(v.String())))
   360  
   361  	case reflect.Interface:
   362  		// The only time we should get here is for nil interfaces due to
   363  		// unpackValue calls.
   364  		if v.IsNil() {
   365  			d.w.Write(nilAngleBytes)
   366  		}
   367  
   368  	case reflect.Ptr:
   369  		// Do nothing.  We should never get here since pointers have already
   370  		// been handled above.
   371  
   372  	case reflect.Map:
   373  		// nil maps should be indicated as different than empty maps
   374  		if v.IsNil() {
   375  			d.w.Write(nilAngleBytes)
   376  			break
   377  		}
   378  
   379  		d.w.Write(openBraceNewlineBytes)
   380  		d.depth++
   381  		if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
   382  			d.indent()
   383  			d.w.Write(maxNewlineBytes)
   384  		} else {
   385  			numEntries := v.Len()
   386  			keys := v.MapKeys()
   387  			if d.cs.SortKeys {
   388  				sortValues(keys, d.cs)
   389  			}
   390  			for i, key := range keys {
   391  				d.dump(d.unpackValue(key))
   392  				d.w.Write(colonSpaceBytes)
   393  				d.ignoreNextIndent = true
   394  				d.dump(d.unpackValue(v.MapIndex(key)))
   395  				if i < (numEntries - 1) {
   396  					d.w.Write(commaNewlineBytes)
   397  				} else {
   398  					d.w.Write(newlineBytes)
   399  				}
   400  			}
   401  		}
   402  		d.depth--
   403  		d.indent()
   404  		d.w.Write(closeBraceBytes)
   405  
   406  	case reflect.Struct:
   407  		d.w.Write(openBraceNewlineBytes)
   408  		d.depth++
   409  		if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
   410  			d.indent()
   411  			d.w.Write(maxNewlineBytes)
   412  		} else {
   413  			vt := v.Type()
   414  			numFields := v.NumField()
   415  			for i := 0; i < numFields; i++ {
   416  				d.indent()
   417  				vtf := vt.Field(i)
   418  				d.w.Write([]byte(vtf.Name))
   419  				d.w.Write(colonSpaceBytes)
   420  				d.ignoreNextIndent = true
   421  				d.dump(d.unpackValue(v.Field(i)))
   422  				if i < (numFields - 1) {
   423  					d.w.Write(commaNewlineBytes)
   424  				} else {
   425  					d.w.Write(newlineBytes)
   426  				}
   427  			}
   428  		}
   429  		d.depth--
   430  		d.indent()
   431  		d.w.Write(closeBraceBytes)
   432  
   433  	case reflect.Uintptr:
   434  		printHexPtr(d.w, uintptr(v.Uint()))
   435  
   436  	case reflect.UnsafePointer, reflect.Chan, reflect.Func:
   437  		printHexPtr(d.w, v.Pointer())
   438  
   439  	// There were not any other types at the time this code was written, but
   440  	// fall back to letting the default fmt package handle it in case any new
   441  	// types are added.
   442  	default:
   443  		if v.CanInterface() {
   444  			fmt.Fprintf(d.w, "%v", v.Interface())
   445  		} else {
   446  			fmt.Fprintf(d.w, "%v", v.String())
   447  		}
   448  	}
   449  }
   450  
   451  // fdump is a helper function to consolidate the logic from the various public
   452  // methods which take varying writers and config states.
   453  func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
   454  	for _, arg := range a {
   455  		if arg == nil {
   456  			w.Write(interfaceBytes)
   457  			w.Write(spaceBytes)
   458  			w.Write(nilAngleBytes)
   459  			w.Write(newlineBytes)
   460  			continue
   461  		}
   462  
   463  		d := dumpState{w: w, cs: cs}
   464  		d.pointers = make(map[uintptr]int)
   465  		d.dump(reflect.ValueOf(arg))
   466  		d.w.Write(newlineBytes)
   467  	}
   468  }
   469  
   470  // Fdump formats and displays the passed arguments to io.Writer w.  It formats
   471  // exactly the same as Dump.
   472  func Fdump(w io.Writer, a ...interface{}) {
   473  	fdump(&Config, w, a...)
   474  }
   475  
   476  // Sdump returns a string with the passed arguments formatted exactly the same
   477  // as Dump.
   478  func Sdump(a ...interface{}) string {
   479  	var buf bytes.Buffer
   480  	fdump(&Config, &buf, a...)
   481  	return buf.String()
   482  }
   483  
   484  /*
   485  Dump displays the passed parameters to standard out with newlines, customizable
   486  indentation, and additional debug information such as complete types and all
   487  pointer addresses used to indirect to the final value.  It provides the
   488  following features over the built-in printing facilities provided by the fmt
   489  package:
   490  
   491  	* Pointers are dereferenced and followed
   492  	* Circular data structures are detected and handled properly
   493  	* Custom Stringer/error interfaces are optionally invoked, including
   494  	  on unexported types
   495  	* Custom types which only implement the Stringer/error interfaces via
   496  	  a pointer receiver are optionally invoked when passing non-pointer
   497  	  variables
   498  	* Byte arrays and slices are dumped like the hexdump -C command which
   499  	  includes offsets, byte values in hex, and ASCII output
   500  
   501  The configuration options are controlled by an exported package global,
   502  spew.Config.  See ConfigState for options documentation.
   503  
   504  See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
   505  get the formatted result as a string.
   506  */
   507  func Dump(a ...interface{}) {
   508  	fdump(&Config, os.Stdout, a...)
   509  }