From 94f1dba30832de8ba5c6d8d5411a476399582dc3 Mon Sep 17 00:00:00 2001 From: Dan Kortschak Date: Mon, 19 Feb 2024 14:18:03 +1030 Subject: [PATCH] utter: add byte index keys to byte slice dumps --- common.go | 10 +++++++++- config.go | 30 +++++++++++++++++------------- dump.go | 10 +++++----- spew_test.go | 29 +++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 19 deletions(-) diff --git a/common.go b/common.go index 2065f8e..fac40ad 100644 --- a/common.go +++ b/common.go @@ -22,6 +22,7 @@ import ( "fmt" "io" "math" + "math/bits" "reflect" "sort" "strconv" @@ -229,15 +230,22 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) { // hexDump is a modified 'hexdump -C'-like that returns a commented Go syntax // byte slice or array. -func hexDump(w io.Writer, data []byte, indent string, width int, comment bool) { +func hexDump(w io.Writer, data []byte, indent string, width int, comment, addr bool) { var commentBytes []byte if comment { commentBytes = make([]byte, width) } + var addrFmt string + if addr { + addrFmt = fmt.Sprintf("%%#0%dx: ", (bits.Len(uint(len(data)))+3)/4) + } for i, v := range data { if i%width == 0 { fmt.Fprint(w, indent) + if addr { + fmt.Fprintf(w, addrFmt, i) + } } else { w.Write(spaceBytes) } diff --git a/config.go b/config.go index 25d03bd..a4982d6 100644 --- a/config.go +++ b/config.go @@ -61,6 +61,10 @@ type ConfigState struct { // comment annotations. CommentBytes bool + // AddressBytes specifies whether byte slice or array dumps have index + // annotations. + AddressBytes bool + // CommentPointers specifies whether pointer information will be added // as comments. CommentPointers bool @@ -133,10 +137,10 @@ pointer addresses used to indirect to the final value. It provides the following features over the built-in printing facilities provided by the fmt package: - * Pointers are dereferenced and followed - * Circular data structures are detected and handled properly - * Byte arrays and slices are dumped in a way similar to the hexdump -C command - which includes byte values in hex, and ASCII output + - Pointers are dereferenced and followed + - Circular data structures are detected and handled properly + - Byte arrays and slices are dumped in a way similar to the hexdump -C command + which includes byte values in hex, and ASCII output The configuration options are controlled by modifying the public members of c. See ConfigState for options documentation. @@ -158,15 +162,15 @@ func (c *ConfigState) Sdump(a interface{}) string { // NewDefaultConfig returns a ConfigState with the following default settings. // -// Indent: " " -// NumericWidth: 1, -// StringWidth: 1, -// BytesWidth: 16 -// CommentBytes: true -// CommentPointers: false -// IgnoreUnexported: false -// ElideType: false -// SortKeys: false +// Indent: " " +// NumericWidth: 1, +// StringWidth: 1, +// BytesWidth: 16 +// CommentBytes: true +// CommentPointers: false +// IgnoreUnexported: false +// ElideType: false +// SortKeys: false func NewDefaultConfig() *ConfigState { return &ConfigState{ Indent: " ", diff --git a/dump.go b/dump.go index dd65947..0cdc59e 100644 --- a/dump.go +++ b/dump.go @@ -300,7 +300,7 @@ func (d *dumpState) dumpSlice(v reflect.Value, canElideCompound bool) { // Hexdump the entire slice as needed. if doHexDump { indent := strings.Repeat(d.cs.Indent, d.depth) - hexDump(d.w, buf, indent, d.cs.BytesWidth, d.cs.CommentBytes) + hexDump(d.w, buf, indent, d.cs.BytesWidth, d.cs.CommentBytes, d.cs.AddressBytes) return } @@ -785,10 +785,10 @@ pointer addresses used to indirect to the final value. It provides the following features over the built-in printing facilities provided by the fmt package: - * Pointers are dereferenced and followed - * Circular data structures are detected and annotated - * Byte arrays and slices are dumped in a way similar to the hexdump -C command, - which includes byte values in hex, and ASCII output + - Pointers are dereferenced and followed + - Circular data structures are detected and annotated + - Byte arrays and slices are dumped in a way similar to the hexdump -C command, + which includes byte values in hex, and ASCII output The configuration options are controlled by an exported package global, utter.Config. See ConfigState for options documentation. diff --git a/spew_test.go b/spew_test.go index f25e477..2602200 100644 --- a/spew_test.go +++ b/spew_test.go @@ -82,6 +82,11 @@ func initSpewTests() { bs8Default := utter.NewDefaultConfig() bs8Default.BytesWidth = 8 + // Byte slice with 8 columns and an address. + bsa8Default := utter.NewDefaultConfig() + bsa8Default.BytesWidth = 8 + bsa8Default.AddressBytes = true + // Numeric slice with 4 columns. num4elideDefault := utter.NewDefaultConfig() num4elideDefault.ElideType = true @@ -205,6 +210,30 @@ func initSpewTests() { {bs8Default, fCSFdump, []byte{1, 2, 3, 4, 5, 0, 1}, "[]uint8{\n" + " 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x01, // |.......|\n}\n", }, + {bsa8Default, fCSFdump, []byte{1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3}, "[]uint8{\n" + + " 0x0: 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x01, 0x02, // |........|\n" + + " 0x8: 0x03, 0x04, 0x05, 0x00, 0x01, 0x02, 0x03, /* */ // |.......|\n}\n", + }, + {bsa8Default, fCSFdump, []byte{1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2}, "[]uint8{\n" + + " 0x0: 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x01, 0x02, // |........|\n" + + " 0x8: 0x03, 0x04, 0x05, 0x00, 0x01, 0x02, /* */ // |......|\n}\n", + }, + {bsa8Default, fCSFdump, []byte{1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1}, "[]uint8{\n" + + " 0x0: 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x01, 0x02, // |........|\n" + + " 0x8: 0x03, 0x04, 0x05, 0x00, 0x01, /* */ // |.....|\n}\n", + }, + {bsa8Default, fCSFdump, []byte{1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0}, "[]uint8{\n" + + " 0x0: 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x01, 0x02, // |........|\n" + + " 0x8: 0x03, 0x04, 0x05, 0x00, /* */ // |....|\n}\n", + }, + {bsa8Default, fCSFdump, []byte{1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0}, "[]uint8{\n" + + " 0x00: 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x01, 0x02, // |........|\n" + + " 0x08: 0x03, 0x04, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04, // |........|\n" + + " 0x10: 0x05, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, // |........|\n}\n", + }, + {bsa8Default, fCSFdump, []byte{1, 2, 3, 4, 5, 0, 1}, "[]uint8{\n" + + " 0x0: 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x01, // |.......|\n}\n", + }, {ignUnexDefault, fCSFdump, Foo{Bar{flag: 1}, map[interface{}]interface{}{"one": true}}, "utter_test.Foo{\n ExportedField: map[interface{}]interface{}{\n string(\"one\"): bool(true),\n },\n}\n", },