Dump byte arrays and slices like hexdump -C.

This commit modifies the Dump family functions to output byte arrays and
slices like hexdump -C as specified in issue #9.
This commit is contained in:
Dave Collins 2013-03-08 22:09:12 -06:00
parent 471552e81e
commit 70d81533ec
2 changed files with 48 additions and 10 deletions

View File

@ -120,7 +120,7 @@ func catchPanic(w io.Writer, v reflect.Value) {
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
// We need an interface to check if the type implements the error or
// Stringer interface. However, the reflect package won't give us an
// an interface on certain things like unexported struct fields in order
// interface on certain things like unexported struct fields in order
// to enforce visibility rules. We use unsafe to bypass these restrictions
// since this package does not mutate the values.
if !v.CanInterface() {

View File

@ -18,11 +18,13 @@ package spew
import (
"bytes"
"encoding/hex"
"fmt"
"io"
"os"
"reflect"
"strconv"
"strings"
)
// dumpState contains information about the state of a dump operation.
@ -134,6 +136,50 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
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) {
// Handle byte (uint8 under reflection) arrays and slices uniquely.
numEntries := v.Len()
if (numEntries > 0) && (v.Index(0).Kind() == reflect.Uint8) {
// We need an addressable interface to convert the type back into 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 to bypass these restrictions since
// this package does not mutate the values.
vs := v
if !vs.CanInterface() || !vs.CanAddr() {
vs = unsafeReflectValue(vs)
}
vs = vs.Slice(0, numEntries)
// Type assert a uint8 slice and hexdump it. Also fix indentation
// based on the depth.
iface := vs.Interface()
if buf, ok := iface.([]uint8); ok {
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
}
// We shouldn't ever get here, but the return is intentionally in the
// above if statement to ensure we fall through to normal behavior if
// the type assertion fails for some reason.
}
// 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
@ -206,15 +252,7 @@ func (d *dumpState) dump(v reflect.Value) {
d.indent()
d.w.Write(maxNewlineBytes)
} else {
numEntries := v.Len()
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)
}
}
d.dumpSlice(v)
}
d.depth--
d.indent()