]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/hex: add hex dumping.
authorAdam Langley <agl@golang.org>
Tue, 31 May 2011 16:58:09 +0000 (12:58 -0400)
committerAdam Langley <agl@golang.org>
Tue, 31 May 2011 16:58:09 +0000 (12:58 -0400)
I found this useful, esp with an io.MultiWriter. But I fear that
it may be bloat in such a low-level package so please feel free to
decline if you feel likewise.

R=rsc, ality
CC=golang-dev
https://golang.org/cl/4530088

src/pkg/encoding/hex/hex.go
src/pkg/encoding/hex/hex_test.go

index 891de1861077d17f342568144a320ede00dd3e45..227829ae5c1daa441ce037f6230d64d380e4708a 100644 (file)
@@ -6,6 +6,8 @@
 package hex
 
 import (
+       "bytes"
+       "io"
        "os"
        "strconv"
 )
@@ -99,3 +101,117 @@ func DecodeString(s string) ([]byte, os.Error) {
        }
        return dst, nil
 }
+
+// Dump returns a string that contains a hex dump of the given data. The format
+// of the hex dump matches the output of `hexdump -C` on the command line.
+func Dump(data []byte) string {
+       buf := bytes.NewBuffer(nil)
+       dumper := Dumper(buf)
+       dumper.Write(data)
+       dumper.Close()
+       return string(buf.Bytes())
+}
+
+// Dumper returns a WriteCloser that writes a hex dump of all written data to
+// w. The format of the dump matches the output of `hexdump -C` on the command
+// line.
+func Dumper(w io.Writer) io.WriteCloser {
+       return &dumper{w: w}
+}
+
+type dumper struct {
+       w          io.Writer
+       rightChars [18]byte
+       buf        [14]byte
+       used       int  // number of bytes in the current line
+       n          uint // number of bytes, total
+}
+
+func toChar(b byte) byte {
+       if b < 32 || b > 127 {
+               return '.'
+       }
+       return b
+}
+
+func (h *dumper) Write(data []byte) (n int, err os.Error) {
+       // Output lines look like:
+       // 00000010  2e 2f 30 31 32 33 34 35  36 37 38 39 3a 3b 3c 3d  |./0123456789:;<=|
+       // ^ offset                          ^ extra space              ^ ASCII of line.
+       for i := range data {
+               if h.used == 0 {
+                       // At the beginning of a line we print the current
+                       // offset in hex.
+                       h.buf[0] = byte(h.n >> 24)
+                       h.buf[1] = byte(h.n >> 16)
+                       h.buf[2] = byte(h.n >> 8)
+                       h.buf[3] = byte(h.n)
+                       Encode(h.buf[4:], h.buf[:4])
+                       h.buf[12] = ' '
+                       h.buf[13] = ' '
+                       _, err = h.w.Write(h.buf[4:])
+               }
+               Encode(h.buf[:], data[i:i+1])
+               h.buf[2] = ' '
+               l := 3
+               if h.used == 7 {
+                       // There's an additional space after the 8th byte.
+                       h.buf[3] = ' '
+                       l = 4
+               } else if h.used == 15 {
+                       // At the end of the line there's an extra space and
+                       // the bar for the right column.
+                       h.buf[3] = ' '
+                       h.buf[4] = '|'
+                       l = 5
+               }
+               _, err = h.w.Write(h.buf[:l])
+               if err != nil {
+                       return
+               }
+               n++
+               h.rightChars[h.used] = toChar(data[i])
+               h.used++
+               h.n++
+               if h.used == 16 {
+                       h.rightChars[16] = '|'
+                       h.rightChars[17] = '\n'
+                       _, err = h.w.Write(h.rightChars[:])
+                       if err != nil {
+                               return
+                       }
+                       h.used = 0
+               }
+       }
+       return
+}
+
+func (h *dumper) Close() (err os.Error) {
+       // See the comments in Write() for the details of this format.
+       if h.used == 0 {
+               return
+       }
+       h.buf[0] = ' '
+       h.buf[1] = ' '
+       h.buf[2] = ' '
+       h.buf[3] = ' '
+       h.buf[4] = '|'
+       nBytes := h.used
+       for h.used < 16 {
+               l := 3
+               if h.used == 7 {
+                       l = 4
+               } else if h.used == 15 {
+                       l = 5
+               }
+               _, err = h.w.Write(h.buf[:l])
+               if err != nil {
+                       return
+               }
+               h.used++
+       }
+       h.rightChars[nBytes] = '|'
+       h.rightChars[nBytes+1] = '\n'
+       _, err = h.w.Write(h.rightChars[:nBytes+2])
+       return
+}
index a14c9d4f4f7bf9f9418aa38eee4a60e8ef0bb9d9..8e1838e51e6f4c2b2562f74b2e462ec75f47462a 100644 (file)
@@ -147,3 +147,46 @@ func TestDecodeString(t *testing.T) {
                }
        }
 }
+
+func TestDumper(t *testing.T) {
+       var in [40]byte
+       for i := range in {
+               in[i] = byte(i + 30)
+       }
+
+       for stride := 1; stride < len(in); stride++ {
+               out := bytes.NewBuffer(nil)
+               dumper := Dumper(out)
+               done := 0
+               for done < len(in) {
+                       todo := done + stride
+                       if todo > len(in) {
+                               todo = len(in)
+                       }
+                       dumper.Write(in[done:todo])
+                       done = todo
+               }
+
+               dumper.Close()
+               if !bytes.Equal(out.Bytes(), expectedHexDump) {
+                       t.Errorf("stride: %d failed. got:\n%s\nwant:\n%s", stride, out.Bytes(), expectedHexDump)
+               }
+       }
+}
+
+func TestDump(t *testing.T) {
+       var in [40]byte
+       for i := range in {
+               in[i] = byte(i + 30)
+       }
+
+       out := []byte(Dump(in[:]))
+       if !bytes.Equal(out, expectedHexDump) {
+               t.Errorf("got:\n%s\nwant:\n%s", out, expectedHexDump)
+       }
+}
+
+var expectedHexDump = []byte(`00000000  1e 1f 20 21 22 23 24 25  26 27 28 29 2a 2b 2c 2d  |.. !"#$%&'()*+,-|
+00000010  2e 2f 30 31 32 33 34 35  36 37 38 39 3a 3b 3c 3d  |./0123456789:;<=|
+00000020  3e 3f 40 41 42 43 44 45                           |>?@ABCDE|
+`)