mirror of
https://github.com/golang/go
synced 2024-11-22 07:54:40 -07:00
encoding/hex: add hex dumping.
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
This commit is contained in:
parent
219805066e
commit
de15f6165e
@ -6,6 +6,8 @@
|
|||||||
package hex
|
package hex
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
@ -99,3 +101,117 @@ func DecodeString(s string) ([]byte, os.Error) {
|
|||||||
}
|
}
|
||||||
return dst, nil
|
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
|
||||||
|
}
|
||||||
|
@ -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|
|
||||||
|
`)
|
||||||
|
Loading…
Reference in New Issue
Block a user