forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/davecgh/go-spew/spew/dump.go

897 lines
14 KiB
Go
Raw Normal View History

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