Selaa lähdekoodia

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.
Dave Collins 12 vuotta sitten
vanhempi
commit
70d81533ec
2 muutettua tiedostoa jossa 48 lisäystä ja 10 poistoa
  1. 1 1
      spew/common.go
  2. 47 9
      spew/dump.go

+ 1 - 1
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() {

+ 47 - 9
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()