gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

dump.go (13794B)


      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:
    147 		d.w.Write(nilAngleBytes)
    148 
    149 	case cycleFound:
    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 }