Add better support for dumping cgo char arrays.
This commit adds support for dumping a few cgo types like hexdump -C as requested in issue #11. In particular, it now handles char [#], unsigned char [#], and uint8_t [#].
This commit is contained in:
parent
800a0dd978
commit
a83f71796b
111
spew/dump.go
111
spew/dump.go
|
@ -23,10 +23,30 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// uint8Type is a reflect.Type representing a uint8. It is used to
|
||||||
|
// convert cgo types to uint8 slices for hexdumping.
|
||||||
|
uint8Type = reflect.TypeOf(uint8(0))
|
||||||
|
|
||||||
|
// cCharRE is a regular expression that matches a cgo char.
|
||||||
|
// It is used to detect character arrays to hexdump them.
|
||||||
|
cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
|
||||||
|
|
||||||
|
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
|
||||||
|
// char. It is used to detect unsigned character arrays to hexdump
|
||||||
|
// them.
|
||||||
|
cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
|
||||||
|
|
||||||
|
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
|
||||||
|
// It is used to detect uint8_t arrays to hexdump them.
|
||||||
|
cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
|
||||||
|
)
|
||||||
|
|
||||||
// dumpState contains information about the state of a dump operation.
|
// dumpState contains information about the state of a dump operation.
|
||||||
type dumpState struct {
|
type dumpState struct {
|
||||||
w io.Writer
|
w io.Writer
|
||||||
|
@ -139,34 +159,75 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
|
||||||
// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
|
// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
|
||||||
// reflection) arrays and slices are dumped in hexdump -C fashion.
|
// reflection) arrays and slices are dumped in hexdump -C fashion.
|
||||||
func (d *dumpState) dumpSlice(v reflect.Value) {
|
func (d *dumpState) dumpSlice(v reflect.Value) {
|
||||||
// Handle byte (uint8 under reflection) arrays and slices uniquely.
|
// Determine whether this type should be hex dumped or not. Also,
|
||||||
|
// for types which should be hexdumped, try to use the underlying data
|
||||||
|
// first, then fall back to trying to convert them to a uint8 slice.
|
||||||
|
var buf []uint8
|
||||||
|
doConvert := false
|
||||||
|
doHexDump := false
|
||||||
numEntries := v.Len()
|
numEntries := v.Len()
|
||||||
if (numEntries > 0) && (v.Index(0).Kind() == reflect.Uint8) {
|
if numEntries > 0 {
|
||||||
// We need an addressable interface to convert the type back into a byte
|
vt := v.Index(0).Type()
|
||||||
// slice. However, the reflect package won't give us an interface on
|
vts := vt.String()
|
||||||
// certain things like unexported struct fields in order to enforce
|
switch {
|
||||||
// visibility rules. We use unsafe to bypass these restrictions since
|
// C types that need to be converted.
|
||||||
// this package does not mutate the values.
|
case cCharRE.MatchString(vts):
|
||||||
vs := v
|
fallthrough
|
||||||
if !vs.CanInterface() || !vs.CanAddr() {
|
case cUnsignedCharRE.MatchString(vts):
|
||||||
vs = unsafeReflectValue(vs)
|
fallthrough
|
||||||
}
|
case cUint8tCharRE.MatchString(vts):
|
||||||
vs = vs.Slice(0, numEntries)
|
doConvert = true
|
||||||
|
|
||||||
// Type assert a uint8 slice and hexdump it. Also fix indentation
|
// Try to use existing uint8 slices and fall back to converting
|
||||||
// based on the depth.
|
// and copying if that fails.
|
||||||
iface := vs.Interface()
|
case vt.Kind() == reflect.Uint8:
|
||||||
if buf, ok := iface.([]uint8); ok {
|
// We need an addressable interface to convert the type back
|
||||||
indent := strings.Repeat(d.cs.Indent, d.depth)
|
// into a byte slice. However, the reflect package won't give
|
||||||
str := indent + hex.Dump(buf)
|
// us an interface on certain things like unexported struct
|
||||||
str = strings.Replace(str, "\n", "\n"+indent, -1)
|
// fields in order to enforce visibility rules. We use unsafe
|
||||||
str = strings.TrimRight(str, d.cs.Indent)
|
// to bypass these restrictions since this package does not
|
||||||
d.w.Write([]byte(str))
|
// mutate the values.
|
||||||
return
|
vs := v
|
||||||
|
if !vs.CanInterface() || !vs.CanAddr() {
|
||||||
|
vs = unsafeReflectValue(vs)
|
||||||
|
}
|
||||||
|
vs = vs.Slice(0, numEntries)
|
||||||
|
|
||||||
|
// Use the existing uint8 slice if it can be type
|
||||||
|
// asserted.
|
||||||
|
iface := vs.Interface()
|
||||||
|
if slice, ok := iface.([]uint8); ok {
|
||||||
|
buf = slice
|
||||||
|
doHexDump = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// The underlying data needs to be converted if it can't
|
||||||
|
// be type asserted to a uint8 slice.
|
||||||
|
doConvert = true
|
||||||
}
|
}
|
||||||
// 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
|
// Copy and convert the underlying type if needed.
|
||||||
// the type assertion fails for some reason.
|
if doConvert && vt.ConvertibleTo(uint8Type) {
|
||||||
|
// Convert and copy each element into a uint8 byte
|
||||||
|
// slice.
|
||||||
|
buf = make([]uint8, numEntries)
|
||||||
|
for i := 0; i < numEntries; i++ {
|
||||||
|
vv := v.Index(i)
|
||||||
|
buf[i] = uint8(vv.Convert(uint8Type).Uint())
|
||||||
|
}
|
||||||
|
doHexDump = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hexdump the entire slice as needed.
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively call dump for each item.
|
// Recursively call dump for each item.
|
||||||
|
|
Loading…
Reference in New Issue