diff options
Diffstat (limited to 'vendor/github.com/davecgh/go-spew/spew/dump.go')
| -rw-r--r-- | vendor/github.com/davecgh/go-spew/spew/dump.go | 509 | 
1 files changed, 509 insertions, 0 deletions
diff --git a/vendor/github.com/davecgh/go-spew/spew/dump.go b/vendor/github.com/davecgh/go-spew/spew/dump.go new file mode 100644 index 0000000..f78d89f --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/dump.go @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2013-2016 Dave Collins <[email protected]> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( +	"bytes" +	"encoding/hex" +	"fmt" +	"io" +	"os" +	"reflect" +	"regexp" +	"strconv" +	"strings" +) + +var ( +	// uint8Type is a reflect.Type representing a uint8.  It is used to +	// convert cgo types to uint8 slices for hexdumping. +	uint8Type = reflect.TypeOf(uint8(0)) + +	// cCharRE is a regular expression that matches a cgo char. +	// It is used to detect character arrays to hexdump them. +	cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`) + +	// cUnsignedCharRE is a regular expression that matches a cgo unsigned +	// char.  It is used to detect unsigned character arrays to hexdump +	// them. +	cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`) + +	// cUint8tCharRE is a regular expression that matches a cgo uint8_t. +	// It is used to detect uint8_t arrays to hexdump them. +	cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`) +) + +// dumpState contains information about the state of a dump operation. +type dumpState struct { +	w                io.Writer +	depth            int +	pointers         map[uintptr]int +	ignoreNextType   bool +	ignoreNextIndent bool +	cs               *ConfigState +} + +// indent performs indentation according to the depth level and cs.Indent +// option. +func (d *dumpState) indent() { +	if d.ignoreNextIndent { +		d.ignoreNextIndent = false +		return +	} +	d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth)) +} + +// unpackValue returns values inside of non-nil interfaces when possible. +// This is useful for data types like structs, arrays, slices, and maps which +// can contain varying types packed inside an interface. +func (d *dumpState) unpackValue(v reflect.Value) reflect.Value { +	if v.Kind() == reflect.Interface && !v.IsNil() { +		v = v.Elem() +	} +	return v +} + +// dumpPtr handles formatting of pointers by indirecting them as necessary. +func (d *dumpState) dumpPtr(v reflect.Value) { +	// Remove pointers at or below the current depth from map used to detect +	// circular refs. +	for k, depth := range d.pointers { +		if depth >= d.depth { +			delete(d.pointers, k) +		} +	} + +	// Keep list of all dereferenced pointers to show later. +	pointerChain := make([]uintptr, 0) + +	// Figure out how many levels of indirection there are by dereferencing +	// pointers and unpacking interfaces down the chain while detecting circular +	// references. +	nilFound := false +	cycleFound := false +	indirects := 0 +	ve := v +	for ve.Kind() == reflect.Ptr { +		if ve.IsNil() { +			nilFound = true +			break +		} +		indirects++ +		addr := ve.Pointer() +		pointerChain = append(pointerChain, addr) +		if pd, ok := d.pointers[addr]; ok && pd < d.depth { +			cycleFound = true +			indirects-- +			break +		} +		d.pointers[addr] = d.depth + +		ve = ve.Elem() +		if ve.Kind() == reflect.Interface { +			if ve.IsNil() { +				nilFound = true +				break +			} +			ve = ve.Elem() +		} +	} + +	// Display type information. +	d.w.Write(openParenBytes) +	d.w.Write(bytes.Repeat(asteriskBytes, indirects)) +	d.w.Write([]byte(ve.Type().String())) +	d.w.Write(closeParenBytes) + +	// Display pointer information. +	if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 { +		d.w.Write(openParenBytes) +		for i, addr := range pointerChain { +			if i > 0 { +				d.w.Write(pointerChainBytes) +			} +			printHexPtr(d.w, addr) +		} +		d.w.Write(closeParenBytes) +	} + +	// Display dereferenced value. +	d.w.Write(openParenBytes) +	switch { +	case nilFound: +		d.w.Write(nilAngleBytes) + +	case cycleFound: +		d.w.Write(circularBytes) + +	default: +		d.ignoreNextType = true +		d.dump(ve) +	} +	d.w.Write(closeParenBytes) +} + +// dumpSlice handles formatting of arrays and slices.  Byte (uint8 under +// reflection) arrays and slices are dumped in hexdump -C fashion. +func (d *dumpState) dumpSlice(v reflect.Value) { +	// Determine whether this type should be hex dumped or not.  Also, +	// for types which should be hexdumped, try to use the underlying data +	// first, then fall back to trying to convert them to a uint8 slice. +	var buf []uint8 +	doConvert := false +	doHexDump := false +	numEntries := v.Len() +	if numEntries > 0 { +		vt := v.Index(0).Type() +		vts := vt.String() +		switch { +		// C types that need to be converted. +		case cCharRE.MatchString(vts): +			fallthrough +		case cUnsignedCharRE.MatchString(vts): +			fallthrough +		case cUint8tCharRE.MatchString(vts): +			doConvert = true + +		// Try to use existing uint8 slices and fall back to converting +		// and copying if that fails. +		case vt.Kind() == reflect.Uint8: +			// We need an addressable interface to convert the type +			// to a byte slice.  However, the reflect package won't +			// give us an interface on certain things like +			// unexported struct fields in order to enforce +			// visibility rules.  We use unsafe, when available, to +			// bypass these restrictions since this package does not +			// mutate the values. +			vs := v +			if !vs.CanInterface() || !vs.CanAddr() { +				vs = unsafeReflectValue(vs) +			} +			if !UnsafeDisabled { +				vs = vs.Slice(0, numEntries) + +				// Use the existing uint8 slice if it can be +				// type asserted. +				iface := vs.Interface() +				if slice, ok := iface.([]uint8); ok { +					buf = slice +					doHexDump = true +					break +				} +			} + +			// The underlying data needs to be converted if it can't +			// be type asserted to a uint8 slice. +			doConvert = true +		} + +		// Copy and convert the underlying type if needed. +		if doConvert && vt.ConvertibleTo(uint8Type) { +			// Convert and copy each element into a uint8 byte +			// slice. +			buf = make([]uint8, numEntries) +			for i := 0; i < numEntries; i++ { +				vv := v.Index(i) +				buf[i] = uint8(vv.Convert(uint8Type).Uint()) +			} +			doHexDump = true +		} +	} + +	// Hexdump the entire slice as needed. +	if doHexDump { +		indent := strings.Repeat(d.cs.Indent, d.depth) +		str := indent + hex.Dump(buf) +		str = strings.Replace(str, "\n", "\n"+indent, -1) +		str = strings.TrimRight(str, d.cs.Indent) +		d.w.Write([]byte(str)) +		return +	} + +	// Recursively call dump for each item. +	for i := 0; i < numEntries; i++ { +		d.dump(d.unpackValue(v.Index(i))) +		if i < (numEntries - 1) { +			d.w.Write(commaNewlineBytes) +		} else { +			d.w.Write(newlineBytes) +		} +	} +} + +// dump is the main workhorse for dumping a value.  It uses the passed reflect +// value to figure out what kind of object we are dealing with and formats it +// appropriately.  It is a recursive function, however circular data structures +// are detected and handled properly. +func (d *dumpState) dump(v reflect.Value) { +	// Handle invalid reflect values immediately. +	kind := v.Kind() +	if kind == reflect.Invalid { +		d.w.Write(invalidAngleBytes) +		return +	} + +	// Handle pointers specially. +	if kind == reflect.Ptr { +		d.indent() +		d.dumpPtr(v) +		return +	} + +	// Print type information unless already handled elsewhere. +	if !d.ignoreNextType { +		d.indent() +		d.w.Write(openParenBytes) +		d.w.Write([]byte(v.Type().String())) +		d.w.Write(closeParenBytes) +		d.w.Write(spaceBytes) +	} +	d.ignoreNextType = false + +	// Display length and capacity if the built-in len and cap functions +	// work with the value's kind and the len/cap itself is non-zero. +	valueLen, valueCap := 0, 0 +	switch v.Kind() { +	case reflect.Array, reflect.Slice, reflect.Chan: +		valueLen, valueCap = v.Len(), v.Cap() +	case reflect.Map, reflect.String: +		valueLen = v.Len() +	} +	if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 { +		d.w.Write(openParenBytes) +		if valueLen != 0 { +			d.w.Write(lenEqualsBytes) +			printInt(d.w, int64(valueLen), 10) +		} +		if !d.cs.DisableCapacities && valueCap != 0 { +			if valueLen != 0 { +				d.w.Write(spaceBytes) +			} +			d.w.Write(capEqualsBytes) +			printInt(d.w, int64(valueCap), 10) +		} +		d.w.Write(closeParenBytes) +		d.w.Write(spaceBytes) +	} + +	// Call Stringer/error interfaces if they exist and the handle methods flag +	// is enabled +	if !d.cs.DisableMethods { +		if (kind != reflect.Invalid) && (kind != reflect.Interface) { +			if handled := handleMethods(d.cs, d.w, v); handled { +				return +			} +		} +	} + +	switch kind { +	case reflect.Invalid: +		// Do nothing.  We should never get here since invalid has already +		// been handled above. + +	case reflect.Bool: +		printBool(d.w, v.Bool()) + +	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: +		printInt(d.w, v.Int(), 10) + +	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: +		printUint(d.w, v.Uint(), 10) + +	case reflect.Float32: +		printFloat(d.w, v.Float(), 32) + +	case reflect.Float64: +		printFloat(d.w, v.Float(), 64) + +	case reflect.Complex64: +		printComplex(d.w, v.Complex(), 32) + +	case reflect.Complex128: +		printComplex(d.w, v.Complex(), 64) + +	case reflect.Slice: +		if v.IsNil() { +			d.w.Write(nilAngleBytes) +			break +		} +		fallthrough + +	case reflect.Array: +		d.w.Write(openBraceNewlineBytes) +		d.depth++ +		if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { +			d.indent() +			d.w.Write(maxNewlineBytes) +		} else { +			d.dumpSlice(v) +		} +		d.depth-- +		d.indent() +		d.w.Write(closeBraceBytes) + +	case reflect.String: +		d.w.Write([]byte(strconv.Quote(v.String()))) + +	case reflect.Interface: +		// The only time we should get here is for nil interfaces due to +		// unpackValue calls. +		if v.IsNil() { +			d.w.Write(nilAngleBytes) +		} + +	case reflect.Ptr: +		// Do nothing.  We should never get here since pointers have already +		// been handled above. + +	case reflect.Map: +		// nil maps should be indicated as different than empty maps +		if v.IsNil() { +			d.w.Write(nilAngleBytes) +			break +		} + +		d.w.Write(openBraceNewlineBytes) +		d.depth++ +		if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { +			d.indent() +			d.w.Write(maxNewlineBytes) +		} else { +			numEntries := v.Len() +			keys := v.MapKeys() +			if d.cs.SortKeys { +				sortValues(keys, d.cs) +			} +			for i, key := range keys { +				d.dump(d.unpackValue(key)) +				d.w.Write(colonSpaceBytes) +				d.ignoreNextIndent = true +				d.dump(d.unpackValue(v.MapIndex(key))) +				if i < (numEntries - 1) { +					d.w.Write(commaNewlineBytes) +				} else { +					d.w.Write(newlineBytes) +				} +			} +		} +		d.depth-- +		d.indent() +		d.w.Write(closeBraceBytes) + +	case reflect.Struct: +		d.w.Write(openBraceNewlineBytes) +		d.depth++ +		if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { +			d.indent() +			d.w.Write(maxNewlineBytes) +		} else { +			vt := v.Type() +			numFields := v.NumField() +			for i := 0; i < numFields; i++ { +				d.indent() +				vtf := vt.Field(i) +				d.w.Write([]byte(vtf.Name)) +				d.w.Write(colonSpaceBytes) +				d.ignoreNextIndent = true +				d.dump(d.unpackValue(v.Field(i))) +				if i < (numFields - 1) { +					d.w.Write(commaNewlineBytes) +				} else { +					d.w.Write(newlineBytes) +				} +			} +		} +		d.depth-- +		d.indent() +		d.w.Write(closeBraceBytes) + +	case reflect.Uintptr: +		printHexPtr(d.w, uintptr(v.Uint())) + +	case reflect.UnsafePointer, reflect.Chan, reflect.Func: +		printHexPtr(d.w, v.Pointer()) + +	// There were not any other types at the time this code was written, but +	// fall back to letting the default fmt package handle it in case any new +	// types are added. +	default: +		if v.CanInterface() { +			fmt.Fprintf(d.w, "%v", v.Interface()) +		} else { +			fmt.Fprintf(d.w, "%v", v.String()) +		} +	} +} + +// fdump is a helper function to consolidate the logic from the various public +// methods which take varying writers and config states. +func fdump(cs *ConfigState, w io.Writer, a ...interface{}) { +	for _, arg := range a { +		if arg == nil { +			w.Write(interfaceBytes) +			w.Write(spaceBytes) +			w.Write(nilAngleBytes) +			w.Write(newlineBytes) +			continue +		} + +		d := dumpState{w: w, cs: cs} +		d.pointers = make(map[uintptr]int) +		d.dump(reflect.ValueOf(arg)) +		d.w.Write(newlineBytes) +	} +} + +// Fdump formats and displays the passed arguments to io.Writer w.  It formats +// exactly the same as Dump. +func Fdump(w io.Writer, a ...interface{}) { +	fdump(&Config, w, a...) +} + +// Sdump returns a string with the passed arguments formatted exactly the same +// as Dump. +func Sdump(a ...interface{}) string { +	var buf bytes.Buffer +	fdump(&Config, &buf, a...) +	return buf.String() +} + +/* +Dump displays the passed parameters to standard out with newlines, customizable +indentation, and additional debug information such as complete types and all +pointer addresses used to indirect to the final value.  It provides the +following features over the built-in printing facilities provided by the fmt +package: + +	* Pointers are dereferenced and followed +	* Circular data structures are detected and handled properly +	* Custom Stringer/error interfaces are optionally invoked, including +	  on unexported types +	* Custom types which only implement the Stringer/error interfaces via +	  a pointer receiver are optionally invoked when passing non-pointer +	  variables +	* Byte arrays and slices are dumped like the hexdump -C command which +	  includes offsets, byte values in hex, and ASCII output + +The configuration options are controlled by an exported package global, +spew.Config.  See ConfigState for options documentation. + +See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to +get the formatted result as a string. +*/ +func Dump(a ...interface{}) { +	fdump(&Config, os.Stdout, a...) +}  | 
