summaryrefslogtreecommitdiff
path: root/src/pkg/encoding/hex
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/encoding/hex')
-rw-r--r--src/pkg/encoding/hex/Makefile11
-rw-r--r--src/pkg/encoding/hex/hex.go216
-rw-r--r--src/pkg/encoding/hex/hex_test.go192
3 files changed, 419 insertions, 0 deletions
diff --git a/src/pkg/encoding/hex/Makefile b/src/pkg/encoding/hex/Makefile
new file mode 100644
index 000000000..22049f43b
--- /dev/null
+++ b/src/pkg/encoding/hex/Makefile
@@ -0,0 +1,11 @@
+# Copyright 2009 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include ../../../Make.inc
+
+TARG=encoding/hex
+GOFILES=\
+ hex.go\
+
+include ../../../Make.pkg
diff --git a/src/pkg/encoding/hex/hex.go b/src/pkg/encoding/hex/hex.go
new file mode 100644
index 000000000..e7ea8b0f2
--- /dev/null
+++ b/src/pkg/encoding/hex/hex.go
@@ -0,0 +1,216 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package hex implements hexadecimal encoding and decoding.
+package hex
+
+import (
+ "bytes"
+ "io"
+ "os"
+ "strconv"
+)
+
+const hextable = "0123456789abcdef"
+
+// EncodedLen returns the length of an encoding of n source bytes.
+func EncodedLen(n int) int { return n * 2 }
+
+// Encode encodes src into EncodedLen(len(src))
+// bytes of dst. As a convenience, it returns the number
+// of bytes written to dst, but this value is always EncodedLen(len(src)).
+// Encode implements hexadecimal encoding.
+func Encode(dst, src []byte) int {
+ for i, v := range src {
+ dst[i*2] = hextable[v>>4]
+ dst[i*2+1] = hextable[v&0x0f]
+ }
+
+ return len(src) * 2
+}
+
+// OddLengthInputError results from decoding an odd length slice.
+type OddLengthInputError struct{}
+
+func (OddLengthInputError) String() string { return "odd length hex string" }
+
+// InvalidHexCharError results from finding an invalid character in a hex string.
+type InvalidHexCharError byte
+
+func (e InvalidHexCharError) String() string {
+ return "invalid hex char: " + strconv.Itoa(int(e))
+}
+
+func DecodedLen(x int) int { return x / 2 }
+
+// Decode decodes src into DecodedLen(len(src)) bytes, returning the actual
+// number of bytes written to dst.
+//
+// If Decode encounters invalid input, it returns an OddLengthInputError or an
+// InvalidHexCharError.
+func Decode(dst, src []byte) (int, os.Error) {
+ if len(src)%2 == 1 {
+ return 0, OddLengthInputError{}
+ }
+
+ for i := 0; i < len(src)/2; i++ {
+ a, ok := fromHexChar(src[i*2])
+ if !ok {
+ return 0, InvalidHexCharError(src[i*2])
+ }
+ b, ok := fromHexChar(src[i*2+1])
+ if !ok {
+ return 0, InvalidHexCharError(src[i*2+1])
+ }
+ dst[i] = (a << 4) | b
+ }
+
+ return len(src) / 2, nil
+}
+
+// fromHexChar converts a hex character into its value and a success flag.
+func fromHexChar(c byte) (byte, bool) {
+ switch {
+ case '0' <= c && c <= '9':
+ return c - '0', true
+ case 'a' <= c && c <= 'f':
+ return c - 'a' + 10, true
+ case 'A' <= c && c <= 'F':
+ return c - 'A' + 10, true
+ }
+
+ return 0, false
+}
+
+// EncodeToString returns the hexadecimal encoding of src.
+func EncodeToString(src []byte) string {
+ dst := make([]byte, EncodedLen(len(src)))
+ Encode(dst, src)
+ return string(dst)
+}
+
+// DecodeString returns the bytes represented by the hexadecimal string s.
+func DecodeString(s string) ([]byte, os.Error) {
+ src := []byte(s)
+ dst := make([]byte, DecodedLen(len(src)))
+ _, err := Decode(dst, src)
+ if err != nil {
+ return nil, err
+ }
+ 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 > 126 {
+ 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
+}
diff --git a/src/pkg/encoding/hex/hex_test.go b/src/pkg/encoding/hex/hex_test.go
new file mode 100644
index 000000000..8e1838e51
--- /dev/null
+++ b/src/pkg/encoding/hex/hex_test.go
@@ -0,0 +1,192 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package hex
+
+import (
+ "bytes"
+ "testing"
+)
+
+type encodeTest struct {
+ in, out []byte
+}
+
+var encodeTests = []encodeTest{
+ {[]byte{}, []byte{}},
+ {[]byte{0x01}, []byte{'0', '1'}},
+ {[]byte{0xff}, []byte{'f', 'f'}},
+ {[]byte{0xff, 00}, []byte{'f', 'f', '0', '0'}},
+ {[]byte{0}, []byte{'0', '0'}},
+ {[]byte{1}, []byte{'0', '1'}},
+ {[]byte{2}, []byte{'0', '2'}},
+ {[]byte{3}, []byte{'0', '3'}},
+ {[]byte{4}, []byte{'0', '4'}},
+ {[]byte{5}, []byte{'0', '5'}},
+ {[]byte{6}, []byte{'0', '6'}},
+ {[]byte{7}, []byte{'0', '7'}},
+ {[]byte{8}, []byte{'0', '8'}},
+ {[]byte{9}, []byte{'0', '9'}},
+ {[]byte{10}, []byte{'0', 'a'}},
+ {[]byte{11}, []byte{'0', 'b'}},
+ {[]byte{12}, []byte{'0', 'c'}},
+ {[]byte{13}, []byte{'0', 'd'}},
+ {[]byte{14}, []byte{'0', 'e'}},
+ {[]byte{15}, []byte{'0', 'f'}},
+}
+
+func TestEncode(t *testing.T) {
+ for i, test := range encodeTests {
+ dst := make([]byte, EncodedLen(len(test.in)))
+ n := Encode(dst, test.in)
+ if n != len(dst) {
+ t.Errorf("#%d: bad return value: got: %d want: %d", i, n, len(dst))
+ }
+ if bytes.Compare(dst, test.out) != 0 {
+ t.Errorf("#%d: got: %#v want: %#v", i, dst, test.out)
+ }
+ }
+}
+
+type decodeTest struct {
+ in, out []byte
+ ok bool
+}
+
+var decodeTests = []decodeTest{
+ {[]byte{}, []byte{}, true},
+ {[]byte{'0'}, []byte{}, false},
+ {[]byte{'0', 'g'}, []byte{}, false},
+ {[]byte{'0', '\x01'}, []byte{}, false},
+ {[]byte{'0', '0'}, []byte{0}, true},
+ {[]byte{'0', '1'}, []byte{1}, true},
+ {[]byte{'0', '2'}, []byte{2}, true},
+ {[]byte{'0', '3'}, []byte{3}, true},
+ {[]byte{'0', '4'}, []byte{4}, true},
+ {[]byte{'0', '5'}, []byte{5}, true},
+ {[]byte{'0', '6'}, []byte{6}, true},
+ {[]byte{'0', '7'}, []byte{7}, true},
+ {[]byte{'0', '8'}, []byte{8}, true},
+ {[]byte{'0', '9'}, []byte{9}, true},
+ {[]byte{'0', 'a'}, []byte{10}, true},
+ {[]byte{'0', 'b'}, []byte{11}, true},
+ {[]byte{'0', 'c'}, []byte{12}, true},
+ {[]byte{'0', 'd'}, []byte{13}, true},
+ {[]byte{'0', 'e'}, []byte{14}, true},
+ {[]byte{'0', 'f'}, []byte{15}, true},
+ {[]byte{'0', 'A'}, []byte{10}, true},
+ {[]byte{'0', 'B'}, []byte{11}, true},
+ {[]byte{'0', 'C'}, []byte{12}, true},
+ {[]byte{'0', 'D'}, []byte{13}, true},
+ {[]byte{'0', 'E'}, []byte{14}, true},
+ {[]byte{'0', 'F'}, []byte{15}, true},
+}
+
+func TestDecode(t *testing.T) {
+ for i, test := range decodeTests {
+ dst := make([]byte, DecodedLen(len(test.in)))
+ n, err := Decode(dst, test.in)
+ if err == nil && n != len(dst) {
+ t.Errorf("#%d: bad return value: got:%d want:%d", i, n, len(dst))
+ }
+ if test.ok != (err == nil) {
+ t.Errorf("#%d: unexpected err value: %s", i, err)
+ }
+ if err == nil && bytes.Compare(dst, test.out) != 0 {
+ t.Errorf("#%d: got: %#v want: %#v", i, dst, test.out)
+ }
+ }
+}
+
+type encodeStringTest struct {
+ in []byte
+ out string
+}
+
+var encodeStringTests = []encodeStringTest{
+ {[]byte{}, ""},
+ {[]byte{0}, "00"},
+ {[]byte{0, 1}, "0001"},
+ {[]byte{0, 1, 255}, "0001ff"},
+}
+
+func TestEncodeToString(t *testing.T) {
+ for i, test := range encodeStringTests {
+ s := EncodeToString(test.in)
+ if s != test.out {
+ t.Errorf("#%d got:%s want:%s", i, s, test.out)
+ }
+ }
+}
+
+type decodeStringTest struct {
+ in string
+ out []byte
+ ok bool
+}
+
+var decodeStringTests = []decodeStringTest{
+ {"", []byte{}, true},
+ {"0", []byte{}, false},
+ {"00", []byte{0}, true},
+ {"0\x01", []byte{}, false},
+ {"0g", []byte{}, false},
+ {"00ff00", []byte{0, 255, 0}, true},
+ {"0000ff", []byte{0, 0, 255}, true},
+}
+
+func TestDecodeString(t *testing.T) {
+ for i, test := range decodeStringTests {
+ dst, err := DecodeString(test.in)
+ if test.ok != (err == nil) {
+ t.Errorf("#%d: unexpected err value: %s", i, err)
+ }
+ if err == nil && bytes.Compare(dst, test.out) != 0 {
+ t.Errorf("#%d: got: %#v want: #%v", i, dst, test.out)
+ }
+ }
+}
+
+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|
+`)