package spew
import (
"bytes"
"encoding/hex"
"fmt"
"io"
"os"
"reflect"
"regexp"
"strconv"
"strings"
)
var (
uint8Type = reflect .TypeOf (uint8 (0 ))
cCharRE = regexp .MustCompile (`^.*\._Ctype_char$` )
cUnsignedCharRE = regexp .MustCompile (`^.*\._Ctype_unsignedchar$` )
cUint8tCharRE = regexp .MustCompile (`^.*\._Ctype_uint8_t$` )
)
type dumpState struct {
w io .Writer
depth int
pointers map [uintptr ]int
ignoreNextType bool
ignoreNextIndent bool
cs *ConfigState
}
func (d *dumpState ) indent () {
if d .ignoreNextIndent {
d .ignoreNextIndent = false
return
}
d .w .Write (bytes .Repeat ([]byte (d .cs .Indent ), d .depth ))
}
func (d *dumpState ) unpackValue (v reflect .Value ) reflect .Value {
if v .Kind () == reflect .Interface && !v .IsNil () {
v = v .Elem ()
}
return v
}
func (d *dumpState ) dumpPtr (v reflect .Value ) {
for k , depth := range d .pointers {
if depth >= d .depth {
delete (d .pointers , k )
}
}
pointerChain := make ([]uintptr , 0 )
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 ()
}
}
d .w .Write (openParenBytes )
d .w .Write (bytes .Repeat (asteriskBytes , indirects ))
d .w .Write ([]byte (ve .Type ().String ()))
d .w .Write (closeParenBytes )
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 )
}
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 )
}
func (d *dumpState ) dumpSlice (v reflect .Value ) {
var buf []uint8
doConvert := false
doHexDump := false
numEntries := v .Len ()
if numEntries > 0 {
vt := v .Index (0 ).Type ()
vts := vt .String ()
switch {
case cCharRE .MatchString (vts ):
fallthrough
case cUnsignedCharRE .MatchString (vts ):
fallthrough
case cUint8tCharRE .MatchString (vts ):
doConvert = true
case vt .Kind () == reflect .Uint8 :
vs := v
if !vs .CanInterface () || !vs .CanAddr () {
vs = unsafeReflectValue (vs )
}
if !UnsafeDisabled {
vs = vs .Slice (0 , numEntries )
iface := vs .Interface ()
if slice , ok := iface .([]uint8 ); ok {
buf = slice
doHexDump = true
break
}
}
doConvert = true
}
if doConvert && vt .ConvertibleTo (uint8Type ) {
buf = make ([]uint8 , numEntries )
for i := 0 ; i < numEntries ; i ++ {
vv := v .Index (i )
buf [i ] = uint8 (vv .Convert (uint8Type ).Uint ())
}
doHexDump = true
}
}
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
}
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 )
}
}
}
func (d *dumpState ) dump (v reflect .Value ) {
kind := v .Kind ()
if kind == reflect .Invalid {
d .w .Write (invalidAngleBytes )
return
}
if kind == reflect .Ptr {
d .indent ()
d .dumpPtr (v )
return
}
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
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 )
}
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 :
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 :
if v .IsNil () {
d .w .Write (nilAngleBytes )
}
case reflect .Ptr :
case reflect .Map :
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 ())
default :
if v .CanInterface () {
fmt .Fprintf (d .w , "%v" , v .Interface ())
} else {
fmt .Fprintf (d .w , "%v" , v .String ())
}
}
}
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 )
}
}
func Fdump (w io .Writer , a ...interface {}) {
fdump (&Config , w , a ...)
}
func Sdump (a ...interface {}) string {
var buf bytes .Buffer
fdump (&Config , &buf , a ...)
return buf .String ()
}
func Dump (a ...interface {}) {
fdump (&Config , os .Stdout , a ...)
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .