github.com/kortschak/utter@v1.5.0/dump.go (about)

     1  /*
     2   * Copyright (c) 2013 Dave Collins <dave@davec.name>
     3   * Copyright (c) 2015 Dan Kortschak <dan.kortschak@adelaide.edu.au>
     4   *
     5   * Permission to use, copy, modify, and distribute this software for any
     6   * purpose with or without fee is hereby granted, provided that the above
     7   * copyright notice and this permission notice appear in all copies.
     8   *
     9   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    10   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    11   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    12   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    13   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    14   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    15   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    16   */
    17  
    18  package utter
    19  
    20  import (
    21  	"bytes"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  	"reflect"
    26  	"regexp"
    27  	"strconv"
    28  	"strings"
    29  	"unicode"
    30  	"unicode/utf8"
    31  )
    32  
    33  var (
    34  	// uint8Type is a reflect.Type representing a uint8.  It is used to
    35  	// convert cgo types to uint8 slices for hexdumping.
    36  	uint8Type = reflect.TypeOf(uint8(0))
    37  
    38  	// cCharRE is a regular expression that matches a cgo char.
    39  	// It is used to detect character arrays to hexdump them.
    40  	cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
    41  
    42  	// cUnsignedCharRE is a regular expression that matches a cgo unsigned
    43  	// char.  It is used to detect unsigned character arrays to hexdump
    44  	// them.
    45  	cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
    46  
    47  	// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
    48  	// It is used to detect uint8_t arrays to hexdump them.
    49  	cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
    50  )
    51  
    52  type addrType struct {
    53  	addr uintptr
    54  	typ  reflect.Type
    55  }
    56  
    57  // dumpState contains information about the state of a dump operation.
    58  type dumpState struct {
    59  	w                io.Writer
    60  	depth            int
    61  	pointers         map[uintptr]int
    62  	nodes            map[addrType]struct{}
    63  	displayed        map[addrType]struct{}
    64  	ignoreNextType   bool
    65  	ignoreNextIndent bool
    66  	cs               *ConfigState
    67  }
    68  
    69  // indent performs indentation according to the depth level and cs.Indent
    70  // option.
    71  func (d *dumpState) indent() {
    72  	if d.ignoreNextIndent {
    73  		d.ignoreNextIndent = false
    74  		return
    75  	}
    76  	d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
    77  }
    78  
    79  // unpackValue returns values inside of non-nil interfaces when possible.
    80  // This is useful for data types like structs, arrays, slices, and maps which
    81  // can contain varying types packed inside an interface.
    82  func (d *dumpState) unpackValue(v reflect.Value) (val reflect.Value, wasPtr, static, canElideStruct bool, addr uintptr) {
    83  	if v.CanAddr() {
    84  		addr = v.Addr().Pointer()
    85  	}
    86  	if v.Kind() == reflect.Interface && !v.IsNil() {
    87  		return v.Elem(), v.Kind() == reflect.Ptr, false, false, addr
    88  	}
    89  	return v, v.Kind() == reflect.Ptr, true, false, addr
    90  }
    91  
    92  // dumpPtr handles formatting of pointers by indirecting them as necessary.
    93  func (d *dumpState) dumpPtr(v reflect.Value) {
    94  	// Remove pointers below the current depth from map used to detect
    95  	// circular refs.
    96  	for k, depth := range d.pointers {
    97  		if depth > d.depth {
    98  			delete(d.pointers, k)
    99  		}
   100  	}
   101  
   102  	// Keep list of all dereferenced pointers to show later.
   103  	var pointerChain []uintptr
   104  
   105  	// Record the value's address.
   106  	value := addrType{addr: v.Pointer()}
   107  
   108  	// Keep the original value in case we have already displayed it.
   109  	orig := v
   110  
   111  	// Figure out how many levels of indirection there are by dereferencing
   112  	// pointers and unpacking interfaces down the chain while detecting circular
   113  	// references.
   114  	var nilFound, cycleFound bool
   115  	indirects := 0
   116  	for v.Kind() == reflect.Ptr {
   117  		if v.IsNil() {
   118  			nilFound = true
   119  			break
   120  		}
   121  		indirects++
   122  		addr := v.Pointer()
   123  		if d.cs.CommentPointers {
   124  			pointerChain = append(pointerChain, addr)
   125  		}
   126  		if pd, ok := d.pointers[addr]; ok && pd < d.depth {
   127  			cycleFound = true
   128  			indirects--
   129  			break
   130  		}
   131  		d.pointers[addr] = d.depth
   132  
   133  		v = v.Elem()
   134  		if v.Kind() == reflect.Interface {
   135  			if v.IsNil() {
   136  				nilFound = true
   137  				break
   138  			}
   139  			v = v.Elem()
   140  		}
   141  	}
   142  
   143  	// Record the value's element type and check whether it has been displayed
   144  	value.typ = v.Type()
   145  	_, displayed := d.displayed[value]
   146  
   147  	// Display type information.
   148  	var typeBytes []byte
   149  	if displayed {
   150  		d.w.Write(openParenBytes)
   151  		typeBytes = []byte(typeString(orig.Type(), d.cs.LocalPackage))
   152  	} else {
   153  		d.w.Write(bytes.Repeat(ampersandBytes, indirects))
   154  		typeBytes = []byte(typeString(v.Type(), d.cs.LocalPackage))
   155  	}
   156  	kind := v.Kind()
   157  	bufferedChan := kind == reflect.Chan && v.Cap() != 0
   158  	if kind == reflect.Ptr || bufferedChan {
   159  		d.w.Write(openParenBytes)
   160  	}
   161  	d.w.Write(bytes.ReplaceAll(typeBytes, interfaceTypeBytes, interfaceBytes))
   162  	if displayed {
   163  		d.w.Write(closeParenBytes)
   164  	}
   165  	switch {
   166  	case bufferedChan:
   167  		switch len := v.Len(); len {
   168  		case 0:
   169  			fmt.Fprintf(d.w, ", %d", v.Cap())
   170  		case 1:
   171  			fmt.Fprintf(d.w, ", %d /* %d element */", v.Cap(), len)
   172  		default:
   173  			fmt.Fprintf(d.w, ", %d /* %d elements */", v.Cap(), len)
   174  		}
   175  		fallthrough
   176  	case kind == reflect.Ptr:
   177  		d.w.Write(closeParenBytes)
   178  	}
   179  
   180  	// Display pointer information.
   181  	if len(pointerChain) > 0 {
   182  		d.w.Write(openCommentBytes)
   183  		for i, addr := range pointerChain {
   184  			if i > 0 {
   185  				d.w.Write(pointerChainBytes)
   186  			}
   187  			printHexPtr(d.w, addr, true)
   188  		}
   189  		d.w.Write(closeCommentBytes)
   190  	}
   191  
   192  	// Display dereferenced value.
   193  	switch {
   194  	case nilFound:
   195  		d.w.Write(openParenBytes)
   196  		d.w.Write(nilBytes)
   197  		d.w.Write(closeParenBytes)
   198  
   199  	case cycleFound, displayed:
   200  		d.w.Write(circularBytes)
   201  
   202  	default:
   203  		d.ignoreNextType = true
   204  		var addr uintptr
   205  		if v.CanAddr() {
   206  			addr = v.Addr().Pointer()
   207  		}
   208  		// Mark the value as having been displayed.
   209  		d.displayed[value] = struct{}{}
   210  		d.dump(v, true, false, false, addr)
   211  	}
   212  }
   213  
   214  // dumpSlice handles formatting of arrays and slices.  Byte (uint8 under
   215  // reflection) arrays and slices are dumped in hexdump -C fashion.
   216  func (d *dumpState) dumpSlice(v reflect.Value, canElideCompound bool) {
   217  	// Determine whether this type should be hex dumped or not.  Also,
   218  	// for types which should be hexdumped, try to use the underlying data
   219  	// first, then fall back to trying to convert them to a uint8 slice.
   220  	var buf []uint8
   221  	doConvert := false
   222  	doHexDump := false
   223  	nPeriod := 1
   224  	numEntries := v.Len()
   225  	vt := v.Type().Elem()
   226  	if numEntries > 0 {
   227  		vts := vt.String()
   228  		switch kind := vt.Kind(); {
   229  		// C types that need to be converted.
   230  		case cCharRE.MatchString(vts):
   231  			fallthrough
   232  		case cUnsignedCharRE.MatchString(vts):
   233  			fallthrough
   234  		case cUint8tCharRE.MatchString(vts):
   235  			doConvert = true
   236  
   237  		// Try to use existing uint8 slices and fall back to converting
   238  		// and copying if that fails.
   239  		case kind == reflect.Uint8:
   240  			// We need an addressable interface to convert the type back
   241  			// into a byte slice.  However, the reflect package won't give
   242  			// us an interface on certain things like unexported struct
   243  			// fields in order to enforce visibility rules.  We use unsafe
   244  			// to bypass these restrictions since this package does not
   245  			// mutate the values.
   246  			vs := v
   247  			if !vs.CanInterface() || !vs.CanAddr() {
   248  				vs = unsafeReflectValue(vs)
   249  			}
   250  			vs = vs.Slice(0, numEntries)
   251  
   252  			// Use the existing uint8 slice if it can be type
   253  			// asserted.
   254  			iface := vs.Interface()
   255  			if slice, ok := iface.([]uint8); ok {
   256  				buf = slice
   257  				doHexDump = true
   258  				break
   259  			}
   260  
   261  			// The underlying data needs to be converted if it can't
   262  			// be type asserted to a uint8 slice.
   263  			doConvert = true
   264  
   265  		case isNumeric(kind):
   266  			nPeriod = d.cs.NumericWidth
   267  
   268  		case kind == reflect.String:
   269  			nPeriod = d.cs.StringWidth
   270  		}
   271  
   272  		// Copy and convert the underlying type if needed.
   273  		if doConvert && vt.ConvertibleTo(uint8Type) {
   274  			// Convert and copy each element into a uint8 byte
   275  			// slice.
   276  			buf = make([]uint8, numEntries)
   277  			for i := 0; i < numEntries; i++ {
   278  				vv := v.Index(i)
   279  				buf[i] = uint8(vv.Convert(uint8Type).Uint())
   280  			}
   281  			doHexDump = true
   282  		}
   283  	}
   284  
   285  	// Prepare indenting for slice.
   286  	if nPeriod == 0 {
   287  		d.w.Write(openBraceBytes)
   288  	} else {
   289  		d.w.Write(openBraceNewlineBytes)
   290  	}
   291  	d.depth++
   292  	defer func() {
   293  		d.depth--
   294  		if nPeriod != 0 {
   295  			d.indent()
   296  		}
   297  		d.w.Write(closeBraceBytes)
   298  	}()
   299  
   300  	// Hexdump the entire slice as needed.
   301  	if doHexDump {
   302  		indent := strings.Repeat(d.cs.Indent, d.depth)
   303  		hexDump(d.w, buf, indent, d.cs.BytesWidth, d.cs.CommentBytes)
   304  		return
   305  	}
   306  
   307  	// Recursively call dump for each item.
   308  	for i := 0; i < numEntries; i++ {
   309  		vi := v.Index(i)
   310  		if nPeriod == 0 || i%nPeriod != 0 {
   311  			d.ignoreNextIndent = true
   312  		}
   313  		val, wasPtr, static, _, addr := d.unpackValue(vi)
   314  		d.dump(val, wasPtr, static, canElideCompound, addr)
   315  		if nPeriod == 0 || (i%nPeriod != nPeriod-1 && i != numEntries-1) {
   316  			if i < numEntries-1 {
   317  				d.w.Write(commaSpaceBytes)
   318  				continue
   319  			}
   320  			break
   321  		}
   322  		d.w.Write(commaNewlineBytes)
   323  	}
   324  }
   325  
   326  // isNumeric returns true for all numeric and boolean kinds.
   327  func isNumeric(k reflect.Kind) bool {
   328  	switch k {
   329  	case reflect.Int, reflect.Uint,
   330  		reflect.Int8, reflect.Bool,
   331  		reflect.Int16, reflect.Uint16,
   332  		reflect.Int32, reflect.Uint32,
   333  		reflect.Int64, reflect.Uint64,
   334  		reflect.Float32, reflect.Float64,
   335  		reflect.Complex64, reflect.Complex128,
   336  		reflect.Uintptr, reflect.UnsafePointer:
   337  		return true
   338  	default:
   339  		return false
   340  	}
   341  }
   342  
   343  // dump is the main workhorse for dumping a value.  It uses the passed reflect
   344  // value to figure out what kind of object we are dealing with and formats it
   345  // appropriately.  It is a recursive function, however circular data structures
   346  // are detected and annotated.
   347  func (d *dumpState) dump(v reflect.Value, wasPtr, static, canElideCompound bool, addr uintptr) {
   348  	// Handle invalid reflect values immediately.
   349  	kind := v.Kind()
   350  	if kind == reflect.Invalid {
   351  		d.w.Write(invalidAngleBytes)
   352  		return
   353  	}
   354  
   355  	// Handle pointers specially.
   356  	if kind == reflect.Ptr {
   357  		d.indent()
   358  		d.dumpPtr(v)
   359  		return
   360  	}
   361  
   362  	typ := v.Type()
   363  	wantType := true
   364  	interfaceContext := kind == reflect.Interface
   365  	if d.cs.ElideType {
   366  		defType := !wasPtr && isDefault(typ)
   367  		wantType = !static && !defType && (!interfaceContext || !v.IsNil())
   368  		if !canElideCompound {
   369  			wantType = wantType || isCompound(kind)
   370  		}
   371  	}
   372  
   373  	// Print type information unless already handled elsewhere.
   374  	if !d.ignoreNextType {
   375  		d.indent()
   376  		if wantType {
   377  			bufferedChan := v.Kind() == reflect.Chan && v.Cap() != 0
   378  			if bufferedChan {
   379  				d.w.Write(openParenBytes)
   380  			}
   381  			typeBytes := []byte(typeString(v.Type(), d.cs.LocalPackage))
   382  			d.w.Write(bytes.ReplaceAll(typeBytes, interfaceTypeBytes, interfaceBytes))
   383  			if bufferedChan {
   384  				switch len := v.Len(); len {
   385  				case 0:
   386  					fmt.Fprintf(d.w, ", %d)", v.Cap())
   387  				case 1:
   388  					fmt.Fprintf(d.w, ", %d /* %d element */)", v.Cap(), len)
   389  				default:
   390  					fmt.Fprintf(d.w, ", %d /* %d elements */)", v.Cap(), len)
   391  				}
   392  			}
   393  		}
   394  	}
   395  	d.ignoreNextType = false
   396  
   397  	if wantType {
   398  		switch kind {
   399  		case reflect.Invalid, reflect.Struct, reflect.Slice, reflect.Array, reflect.Map:
   400  		default:
   401  			d.w.Write(openParenBytes)
   402  		}
   403  	}
   404  
   405  	if _, referenced := d.nodes[addrType{addr, typ}]; !wasPtr && referenced {
   406  		d.w.Write(openCommentBytes)
   407  		printHexPtr(d.w, addr, true)
   408  		d.w.Write(closeCommentBytes)
   409  	}
   410  	switch kind {
   411  	case reflect.Invalid:
   412  		// We should never get here since invalid has already been handled above.
   413  		panic("cannot reach")
   414  
   415  	case reflect.Bool:
   416  		printBool(d.w, v.Bool())
   417  
   418  	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
   419  		printInt(d.w, v.Int(), 10)
   420  
   421  	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
   422  		d.w.Write(hexZeroBytes)
   423  		printUint(d.w, v.Uint(), 16)
   424  
   425  	case reflect.Float32:
   426  		printFloat(d.w, v.Float(), 32, !wantType)
   427  
   428  	case reflect.Float64:
   429  		printFloat(d.w, v.Float(), 64, !wantType)
   430  
   431  	case reflect.Complex64:
   432  		printComplex(d.w, v.Complex(), 32)
   433  
   434  	case reflect.Complex128:
   435  		printComplex(d.w, v.Complex(), 64)
   436  
   437  	case reflect.Slice:
   438  		if v.IsNil() {
   439  			d.w.Write(openParenBytes)
   440  			d.w.Write(nilBytes)
   441  			d.w.Write(closeParenBytes)
   442  			break
   443  		}
   444  		if v.Len() == 0 {
   445  			d.dumpSlice(v, !interfaceContext)
   446  			break
   447  		}
   448  		// Remove pointers below the current depth from map used to detect
   449  		// circular refs.
   450  		for k, depth := range d.pointers {
   451  			if depth > d.depth {
   452  				delete(d.pointers, k)
   453  			}
   454  		}
   455  		addr = v.Index(0).Addr().Pointer()
   456  		if pd, ok := d.pointers[addr]; ok && pd < d.depth {
   457  			d.w.Write(circularBytes)
   458  			break
   459  		}
   460  		d.pointers[addr] = d.depth
   461  
   462  		fallthrough
   463  
   464  	case reflect.Array:
   465  		d.dumpSlice(v, !interfaceContext)
   466  
   467  	case reflect.String:
   468  		d.writeQuoted(v.String())
   469  
   470  	case reflect.Interface:
   471  		// The only time we should get here is for nil interfaces due to
   472  		// unpackValue calls.
   473  		if v.IsNil() {
   474  			d.w.Write(nilBytes)
   475  		}
   476  
   477  	case reflect.Ptr:
   478  		// We should never get here since pointers have already been handled above.
   479  		panic("cannot reach")
   480  
   481  	case reflect.Map:
   482  		// nil maps should be indicated as different than empty maps
   483  		if v.IsNil() {
   484  			d.w.Write(openParenBytes)
   485  			d.w.Write(nilBytes)
   486  			d.w.Write(closeParenBytes)
   487  			break
   488  		}
   489  
   490  		// Remove pointers below the current depth from map used to detect
   491  		// circular refs.
   492  		for k, depth := range d.pointers {
   493  			if depth > d.depth {
   494  				delete(d.pointers, k)
   495  			}
   496  		}
   497  		addr := v.Pointer()
   498  		if pd, ok := d.pointers[addr]; ok && pd < d.depth {
   499  			d.w.Write(circularBytes)
   500  			break
   501  		}
   502  		d.pointers[addr] = d.depth
   503  
   504  		d.w.Write(openBraceNewlineBytes)
   505  		d.depth++
   506  		if d.cs.SortKeys {
   507  			iter := v.MapRange()
   508  			keys := make([]reflect.Value, 0, v.Len())
   509  			vals := make([]reflect.Value, 0, v.Len())
   510  			for iter.Next() {
   511  				keys = append(keys, iter.Key())
   512  				vals = append(vals, iter.Value())
   513  			}
   514  			sortMapByKeyVals(keys, vals)
   515  			for i, key := range keys {
   516  				val, wasPtr, static, _, addr := d.unpackValue(key)
   517  				d.dump(val, wasPtr, static, !interfaceContext, addr)
   518  				d.w.Write(colonSpaceBytes)
   519  				d.ignoreNextIndent = true
   520  				val, wasPtr, static, _, addr = d.unpackValue(vals[i])
   521  				d.dump(val, wasPtr, static, !interfaceContext, addr)
   522  				d.w.Write(commaNewlineBytes)
   523  			}
   524  		} else {
   525  			iter := v.MapRange()
   526  			for iter.Next() {
   527  				val, wasPtr, static, _, addr := d.unpackValue(iter.Key())
   528  				d.dump(val, wasPtr, static, !interfaceContext, addr)
   529  				d.w.Write(colonSpaceBytes)
   530  				d.ignoreNextIndent = true
   531  				val, wasPtr, static, _, addr = d.unpackValue(iter.Value())
   532  				d.dump(val, wasPtr, static, !interfaceContext, addr)
   533  				d.w.Write(commaNewlineBytes)
   534  			}
   535  		}
   536  		d.depth--
   537  		d.indent()
   538  		d.w.Write(closeBraceBytes)
   539  
   540  	case reflect.Struct:
   541  		d.w.Write(openBraceNewlineBytes)
   542  		d.depth++
   543  		vt := v.Type()
   544  		numFields := v.NumField()
   545  		for i := 0; i < numFields; i++ {
   546  			vtf := vt.Field(i)
   547  			if d.cs.IgnoreUnexported && vtf.PkgPath != "" {
   548  				continue
   549  			}
   550  			unpacked, wasPtr, static, _, addr := d.unpackValue(v.Field(i))
   551  			if d.cs.OmitZero && isZero(unpacked) {
   552  				continue
   553  			}
   554  			d.indent()
   555  			d.w.Write([]byte(vtf.Name))
   556  			d.w.Write(colonSpaceBytes)
   557  			d.ignoreNextIndent = true
   558  			d.dump(unpacked, wasPtr, static, false, addr)
   559  			d.w.Write(commaNewlineBytes)
   560  		}
   561  		d.depth--
   562  		d.indent()
   563  		d.w.Write(closeBraceBytes)
   564  
   565  	case reflect.Uintptr:
   566  		printHexPtr(d.w, uintptr(v.Uint()), false)
   567  
   568  	case reflect.UnsafePointer, reflect.Chan, reflect.Func:
   569  		printHexPtr(d.w, v.Pointer(), true)
   570  
   571  	// There were not any other types at the time this code was written, but
   572  	// fall back to letting the default fmt package handle it in case any new
   573  	// types are added.
   574  	default:
   575  		if v.CanInterface() {
   576  			fmt.Fprintf(d.w, "%v", v.Interface())
   577  		} else {
   578  			fmt.Fprintf(d.w, "%v", v.String())
   579  		}
   580  	}
   581  	if wantType {
   582  		switch kind {
   583  		case reflect.Invalid, reflect.Struct, reflect.Slice, reflect.Array, reflect.Map:
   584  		default:
   585  			d.w.Write(closeParenBytes)
   586  		}
   587  	}
   588  }
   589  
   590  // writeQuoted writes the string s quoted according to the quoting strategy.
   591  func (d *dumpState) writeQuoted(s string) {
   592  	switch d.cs.Quoting {
   593  	default:
   594  		fallthrough
   595  	case DoubleQuote:
   596  		d.w.Write([]byte(strconv.Quote(s)))
   597  
   598  	case AvoidEscapes:
   599  		if !needsEscape(s) || !canBackquoteString(s) {
   600  			d.w.Write([]byte(strconv.Quote(s)))
   601  			return
   602  		}
   603  		d.backQuote(s)
   604  
   605  	case AvoidEscapes | Force:
   606  		if !needsEscape(s) {
   607  			d.w.Write([]byte(strconv.Quote(s)))
   608  			return
   609  		}
   610  
   611  		fallthrough
   612  	case Backquote, Backquote | Force:
   613  		if canBackquoteString(s) {
   614  			d.backQuote(s)
   615  			return
   616  		}
   617  
   618  		var last int
   619  		inBackquote := true
   620  		for i, r := range s {
   621  			if canBackquote(r) != inBackquote {
   622  				if last != 0 {
   623  					d.w.Write(plusBytes)
   624  				}
   625  				if inBackquote {
   626  					if i != last {
   627  						d.backQuote(s[last:i])
   628  					}
   629  				} else {
   630  					d.w.Write([]byte(strconv.Quote(s[last:i])))
   631  				}
   632  				last = i
   633  				inBackquote = !inBackquote
   634  			}
   635  		}
   636  		if last != len(s) {
   637  			if last != 0 {
   638  				d.w.Write(plusBytes)
   639  			}
   640  			if !inBackquote {
   641  				d.w.Write([]byte(strconv.Quote(s[last:])))
   642  				return
   643  			}
   644  			d.backQuote(s[last:])
   645  		}
   646  	}
   647  }
   648  
   649  // backQuote writes s backquoted.
   650  func (d *dumpState) backQuote(s string) {
   651  	d.w.Write(backQuoteBytes)
   652  	d.w.Write([]byte(s))
   653  	d.w.Write(backQuoteBytes)
   654  }
   655  
   656  // needsEscape returns whether the string s needs any escape sequence to be
   657  // double quote printed.
   658  func needsEscape(s string) bool {
   659  	for _, r := range s {
   660  		if r == '"' || r == '\\' {
   661  			return true
   662  		}
   663  		if !strconv.IsPrint(r) && !strconv.IsGraphic(r) {
   664  			return true
   665  		}
   666  	}
   667  	return false
   668  }
   669  
   670  // canBackquoteString returns whether the string s can be represented
   671  // unchanged as a backquoted string without non-space control characters.
   672  func canBackquoteString(s string) bool {
   673  	for _, r := range s {
   674  		if !canBackquote(r) {
   675  			return false
   676  		}
   677  	}
   678  	return true
   679  }
   680  
   681  // canBackquote returns whether the rune r can be represented unchanged as a
   682  // backquoted string without non-space control characters.
   683  func canBackquote(r rune) bool {
   684  	if r == utf8.RuneError {
   685  		return false
   686  	}
   687  	if utf8.RuneLen(r) > 1 {
   688  		return r != '\ufeff'
   689  	}
   690  	return (unicode.IsSpace(r) || ' ' < r) && r != '`' && r != '\u007f'
   691  }
   692  
   693  // typeString returns the string representation of the reflect.Type with the local
   694  // package selector removed.
   695  func typeString(typ reflect.Type, local string) string {
   696  	if typ.PkgPath() != "" {
   697  		return strings.TrimPrefix(strings.TrimPrefix(typ.String(), local), ".")
   698  	}
   699  	switch typ.Kind() {
   700  	case reflect.Ptr:
   701  		return "*" + strings.TrimPrefix(strings.TrimPrefix(strings.TrimPrefix(typ.String(), "*"), local), ".")
   702  	case reflect.Array:
   703  		return fmt.Sprintf("[%d]%s", typ.Len(), typeString(typ.Elem(), local))
   704  	case reflect.Chan:
   705  		return fmt.Sprintf("%s %s", typ.ChanDir(), typeString(typ.Elem(), local))
   706  	case reflect.Map:
   707  		return fmt.Sprintf("map[%s]%s", typeString(typ.Key(), local), typeString(typ.Elem(), local))
   708  	case reflect.Slice:
   709  		return fmt.Sprintf("[]%s", typeString(typ.Elem(), local))
   710  	default:
   711  		return strings.TrimPrefix(strings.TrimPrefix(typ.String(), local), ".")
   712  	}
   713  }
   714  
   715  // isDefault returns whether the type is a default type absent of context.
   716  func isDefault(typ reflect.Type) bool {
   717  	if typ.PkgPath() != "" || typ.Name() == "" {
   718  		return false
   719  	}
   720  	kind := typ.Kind()
   721  	return kind == reflect.Int || kind == reflect.Float64 || kind == reflect.String || kind == reflect.Bool
   722  }
   723  
   724  // isCompound returns whether the kind is a compound data type.
   725  func isCompound(kind reflect.Kind) bool {
   726  	return kind == reflect.Struct || kind == reflect.Slice || kind == reflect.Array || kind == reflect.Map
   727  }
   728  
   729  // isZero returns whether v is the zero value of its type safely for all types.
   730  // If v is not a kind recognised by reflect it is not zero. See TestAddedReflectValue.
   731  // TODO(kortschak): Handle all cases.
   732  func isZero(v reflect.Value) bool {
   733  	if kind := v.Kind(); kind == reflect.Invalid || kind > reflect.UnsafePointer {
   734  		return false
   735  	}
   736  	return v.IsZero()
   737  }
   738  
   739  // fdump is a helper function to consolidate the logic from the various public
   740  // methods which take varying writers and config states.
   741  func fdump(cs *ConfigState, w io.Writer, a interface{}) {
   742  	if a == nil {
   743  		w.Write(interfaceBytes)
   744  		w.Write(openParenBytes)
   745  		w.Write(nilBytes)
   746  		w.Write(closeParenBytes)
   747  		w.Write(newlineBytes)
   748  		return
   749  	}
   750  
   751  	d := dumpState{w: w, cs: cs}
   752  	d.pointers = make(map[uintptr]int)
   753  	v := reflect.ValueOf(a)
   754  	var addr uintptr
   755  	if v.CanAddr() {
   756  		addr = v.Addr().Pointer()
   757  	}
   758  	d.displayed = make(map[addrType]struct{})
   759  	if cs.CommentPointers {
   760  		d.nodes = make(map[addrType]struct{})
   761  		d.walk(v, false, false, false, addr)
   762  	}
   763  	d.dump(v, false, false, false, addr)
   764  	d.w.Write(newlineBytes)
   765  }
   766  
   767  // Fdump formats and displays the passed arguments to io.Writer w.  It formats
   768  // exactly the same as Dump.
   769  func Fdump(w io.Writer, a interface{}) {
   770  	fdump(&Config, w, a)
   771  }
   772  
   773  // Sdump returns a string with the passed arguments formatted exactly the same
   774  // as Dump.
   775  func Sdump(a interface{}) string {
   776  	var buf bytes.Buffer
   777  	fdump(&Config, &buf, a)
   778  	return buf.String()
   779  }
   780  
   781  /*
   782  Dump displays the passed parameters to standard out with newlines, customizable
   783  indentation, and additional debug information such as complete types and all
   784  pointer addresses used to indirect to the final value.  It provides the
   785  following features over the built-in printing facilities provided by the fmt
   786  package:
   787  
   788  	* Pointers are dereferenced and followed
   789  	* Circular data structures are detected and annotated
   790  	* Byte arrays and slices are dumped in a way similar to the hexdump -C command,
   791  	  which includes byte values in hex, and ASCII output
   792  
   793  The configuration options are controlled by an exported package global,
   794  utter.Config.  See ConfigState for options documentation.
   795  
   796  See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
   797  get the formatted result as a string.
   798  */
   799  func Dump(a interface{}) {
   800  	fdump(&Config, os.Stdout, a)
   801  }