summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Collins <[email protected]>2013-03-08 22:09:12 -0600
committerDave Collins <[email protected]>2013-03-08 22:15:12 -0600
commit70d81533ec31581eba20e3342aba5d52702a609f (patch)
tree02d8d81d2b6a6b4672d3a1db9c9af2db9f4f4c28
parent471552e81e198e81193f3b19abb61f901a27710a (diff)
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.
-rw-r--r--spew/common.go2
-rw-r--r--spew/dump.go56
2 files changed, 48 insertions, 10 deletions
diff --git a/spew/common.go b/spew/common.go
index e452260..11b6d56 100644
--- a/spew/common.go
+++ b/spew/common.go
@@ -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() {
diff --git a/spew/dump.go b/spew/dump.go
index 779bace..b47b5db 100644
--- a/spew/dump.go
+++ b/spew/dump.go
@@ -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()