summaryrefslogtreecommitdiffstats
path: root/src/encoding/hex
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
commitccd992355df7192993c666236047820244914598 (patch)
treef00fea65147227b7743083c6148396f74cd66935 /src/encoding/hex
parentInitial commit. (diff)
downloadgolang-1.21-ccd992355df7192993c666236047820244914598.tar.xz
golang-1.21-ccd992355df7192993c666236047820244914598.zip
Adding upstream version 1.21.8.upstream/1.21.8
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/encoding/hex')
-rw-r--r--src/encoding/hex/example_test.go98
-rw-r--r--src/encoding/hex/hex.go335
-rw-r--r--src/encoding/hex/hex_test.go277
3 files changed, 710 insertions, 0 deletions
diff --git a/src/encoding/hex/example_test.go b/src/encoding/hex/example_test.go
new file mode 100644
index 0000000..3580757
--- /dev/null
+++ b/src/encoding/hex/example_test.go
@@ -0,0 +1,98 @@
+// Copyright 2016 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_test
+
+import (
+ "encoding/hex"
+ "fmt"
+ "log"
+ "os"
+)
+
+func ExampleEncode() {
+ src := []byte("Hello Gopher!")
+
+ dst := make([]byte, hex.EncodedLen(len(src)))
+ hex.Encode(dst, src)
+
+ fmt.Printf("%s\n", dst)
+
+ // Output:
+ // 48656c6c6f20476f7068657221
+}
+
+func ExampleDecode() {
+ src := []byte("48656c6c6f20476f7068657221")
+
+ dst := make([]byte, hex.DecodedLen(len(src)))
+ n, err := hex.Decode(dst, src)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ fmt.Printf("%s\n", dst[:n])
+
+ // Output:
+ // Hello Gopher!
+}
+
+func ExampleDecodeString() {
+ const s = "48656c6c6f20476f7068657221"
+ decoded, err := hex.DecodeString(s)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ fmt.Printf("%s\n", decoded)
+
+ // Output:
+ // Hello Gopher!
+}
+
+func ExampleDump() {
+ content := []byte("Go is an open source programming language.")
+
+ fmt.Printf("%s", hex.Dump(content))
+
+ // Output:
+ // 00000000 47 6f 20 69 73 20 61 6e 20 6f 70 65 6e 20 73 6f |Go is an open so|
+ // 00000010 75 72 63 65 20 70 72 6f 67 72 61 6d 6d 69 6e 67 |urce programming|
+ // 00000020 20 6c 61 6e 67 75 61 67 65 2e | language.|
+}
+
+func ExampleDumper() {
+ lines := []string{
+ "Go is an open source programming language.",
+ "\n",
+ "We encourage all Go users to subscribe to golang-announce.",
+ }
+
+ stdoutDumper := hex.Dumper(os.Stdout)
+
+ defer stdoutDumper.Close()
+
+ for _, line := range lines {
+ stdoutDumper.Write([]byte(line))
+ }
+
+ // Output:
+ // 00000000 47 6f 20 69 73 20 61 6e 20 6f 70 65 6e 20 73 6f |Go is an open so|
+ // 00000010 75 72 63 65 20 70 72 6f 67 72 61 6d 6d 69 6e 67 |urce programming|
+ // 00000020 20 6c 61 6e 67 75 61 67 65 2e 0a 57 65 20 65 6e | language..We en|
+ // 00000030 63 6f 75 72 61 67 65 20 61 6c 6c 20 47 6f 20 75 |courage all Go u|
+ // 00000040 73 65 72 73 20 74 6f 20 73 75 62 73 63 72 69 62 |sers to subscrib|
+ // 00000050 65 20 74 6f 20 67 6f 6c 61 6e 67 2d 61 6e 6e 6f |e to golang-anno|
+ // 00000060 75 6e 63 65 2e |unce.|
+}
+
+func ExampleEncodeToString() {
+ src := []byte("Hello")
+ encodedStr := hex.EncodeToString(src)
+
+ fmt.Printf("%s\n", encodedStr)
+
+ // Output:
+ // 48656c6c6f
+}
diff --git a/src/encoding/hex/hex.go b/src/encoding/hex/hex.go
new file mode 100644
index 0000000..375f583
--- /dev/null
+++ b/src/encoding/hex/hex.go
@@ -0,0 +1,335 @@
+// 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 (
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+)
+
+const (
+ hextable = "0123456789abcdef"
+ reverseHexTable = "" +
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\xff\xff\xff\xff\xff\xff" +
+ "\xff\x0a\x0b\x0c\x0d\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
+ "\xff\x0a\x0b\x0c\x0d\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+)
+
+// EncodedLen returns the length of an encoding of n source bytes.
+// Specifically, it returns n * 2.
+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 {
+ j := 0
+ for _, v := range src {
+ dst[j] = hextable[v>>4]
+ dst[j+1] = hextable[v&0x0f]
+ j += 2
+ }
+ return len(src) * 2
+}
+
+// ErrLength reports an attempt to decode an odd-length input
+// using Decode or DecodeString.
+// The stream-based Decoder returns io.ErrUnexpectedEOF instead of ErrLength.
+var ErrLength = errors.New("encoding/hex: odd length hex string")
+
+// InvalidByteError values describe errors resulting from an invalid byte in a hex string.
+type InvalidByteError byte
+
+func (e InvalidByteError) Error() string {
+ return fmt.Sprintf("encoding/hex: invalid byte: %#U", rune(e))
+}
+
+// DecodedLen returns the length of a decoding of x source bytes.
+// Specifically, it returns x / 2.
+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.
+//
+// Decode expects that src contains only hexadecimal
+// characters and that src has even length.
+// If the input is malformed, Decode returns the number
+// of bytes decoded before the error.
+func Decode(dst, src []byte) (int, error) {
+ i, j := 0, 1
+ for ; j < len(src); j += 2 {
+ p := src[j-1]
+ q := src[j]
+
+ a := reverseHexTable[p]
+ b := reverseHexTable[q]
+ if a > 0x0f {
+ return i, InvalidByteError(p)
+ }
+ if b > 0x0f {
+ return i, InvalidByteError(q)
+ }
+ dst[i] = (a << 4) | b
+ i++
+ }
+ if len(src)%2 == 1 {
+ // Check for invalid char before reporting bad length,
+ // since the invalid char (if present) is an earlier problem.
+ if reverseHexTable[src[j-1]] > 0x0f {
+ return i, InvalidByteError(src[j-1])
+ }
+ return i, ErrLength
+ }
+ return i, nil
+}
+
+// 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.
+//
+// DecodeString expects that src contains only hexadecimal
+// characters and that src has even length.
+// If the input is malformed, DecodeString returns
+// the bytes decoded before the error.
+func DecodeString(s string) ([]byte, error) {
+ src := []byte(s)
+ // We can use the source slice itself as the destination
+ // because the decode loop increments by one and then the 'seen' byte is not used anymore.
+ n, err := Decode(src, src)
+ return src[:n], err
+}
+
+// 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 {
+ if len(data) == 0 {
+ return ""
+ }
+
+ var buf strings.Builder
+ // Dumper will write 79 bytes per complete 16 byte chunk, and at least
+ // 64 bytes for whatever remains. Round the allocation up, since only a
+ // maximum of 15 bytes will be wasted.
+ buf.Grow((1 + ((len(data) - 1) / 16)) * 79)
+
+ dumper := Dumper(&buf)
+ dumper.Write(data)
+ dumper.Close()
+ return buf.String()
+}
+
+// bufferSize is the number of hexadecimal characters to buffer in encoder and decoder.
+const bufferSize = 1024
+
+type encoder struct {
+ w io.Writer
+ err error
+ out [bufferSize]byte // output buffer
+}
+
+// NewEncoder returns an io.Writer that writes lowercase hexadecimal characters to w.
+func NewEncoder(w io.Writer) io.Writer {
+ return &encoder{w: w}
+}
+
+func (e *encoder) Write(p []byte) (n int, err error) {
+ for len(p) > 0 && e.err == nil {
+ chunkSize := bufferSize / 2
+ if len(p) < chunkSize {
+ chunkSize = len(p)
+ }
+
+ var written int
+ encoded := Encode(e.out[:], p[:chunkSize])
+ written, e.err = e.w.Write(e.out[:encoded])
+ n += written / 2
+ p = p[chunkSize:]
+ }
+ return n, e.err
+}
+
+type decoder struct {
+ r io.Reader
+ err error
+ in []byte // input buffer (encoded form)
+ arr [bufferSize]byte // backing array for in
+}
+
+// NewDecoder returns an io.Reader that decodes hexadecimal characters from r.
+// NewDecoder expects that r contain only an even number of hexadecimal characters.
+func NewDecoder(r io.Reader) io.Reader {
+ return &decoder{r: r}
+}
+
+func (d *decoder) Read(p []byte) (n int, err error) {
+ // Fill internal buffer with sufficient bytes to decode
+ if len(d.in) < 2 && d.err == nil {
+ var numCopy, numRead int
+ numCopy = copy(d.arr[:], d.in) // Copies either 0 or 1 bytes
+ numRead, d.err = d.r.Read(d.arr[numCopy:])
+ d.in = d.arr[:numCopy+numRead]
+ if d.err == io.EOF && len(d.in)%2 != 0 {
+
+ if a := reverseHexTable[d.in[len(d.in)-1]]; a > 0x0f {
+ d.err = InvalidByteError(d.in[len(d.in)-1])
+ } else {
+ d.err = io.ErrUnexpectedEOF
+ }
+ }
+ }
+
+ // Decode internal buffer into output buffer
+ if numAvail := len(d.in) / 2; len(p) > numAvail {
+ p = p[:numAvail]
+ }
+ numDec, err := Decode(p, d.in[:len(p)*2])
+ d.in = d.in[2*numDec:]
+ if err != nil {
+ d.in, d.err = nil, err // Decode error; discard input remainder
+ }
+
+ if len(d.in) < 2 {
+ return numDec, d.err // Only expose errors when buffer fully consumed
+ }
+ return numDec, nil
+}
+
+// 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
+ closed bool
+}
+
+func toChar(b byte) byte {
+ if b < 32 || b > 126 {
+ return '.'
+ }
+ return b
+}
+
+func (h *dumper) Write(data []byte) (n int, err error) {
+ if h.closed {
+ return 0, errors.New("encoding/hex: dumper closed")
+ }
+
+ // 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:])
+ if err != nil {
+ return
+ }
+ }
+ 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 error) {
+ // See the comments in Write() for the details of this format.
+ if h.closed {
+ return
+ }
+ h.closed = true
+ 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/encoding/hex/hex_test.go b/src/encoding/hex/hex_test.go
new file mode 100644
index 0000000..a820fe7
--- /dev/null
+++ b/src/encoding/hex/hex_test.go
@@ -0,0 +1,277 @@
+// 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"
+ "fmt"
+ "io"
+ "strings"
+ "testing"
+)
+
+type encDecTest struct {
+ enc string
+ dec []byte
+}
+
+var encDecTests = []encDecTest{
+ {"", []byte{}},
+ {"0001020304050607", []byte{0, 1, 2, 3, 4, 5, 6, 7}},
+ {"08090a0b0c0d0e0f", []byte{8, 9, 10, 11, 12, 13, 14, 15}},
+ {"f0f1f2f3f4f5f6f7", []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7}},
+ {"f8f9fafbfcfdfeff", []byte{0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}},
+ {"67", []byte{'g'}},
+ {"e3a1", []byte{0xe3, 0xa1}},
+}
+
+func TestEncode(t *testing.T) {
+ for i, test := range encDecTests {
+ dst := make([]byte, EncodedLen(len(test.dec)))
+ n := Encode(dst, test.dec)
+ if n != len(dst) {
+ t.Errorf("#%d: bad return value: got: %d want: %d", i, n, len(dst))
+ }
+ if string(dst) != test.enc {
+ t.Errorf("#%d: got: %#v want: %#v", i, dst, test.enc)
+ }
+ }
+}
+
+func TestDecode(t *testing.T) {
+ // Case for decoding uppercase hex characters, since
+ // Encode always uses lowercase.
+ decTests := append(encDecTests, encDecTest{"F8F9FAFBFCFDFEFF", []byte{0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}})
+ for i, test := range decTests {
+ dst := make([]byte, DecodedLen(len(test.enc)))
+ n, err := Decode(dst, []byte(test.enc))
+ if err != nil {
+ t.Errorf("#%d: bad return value: got:%d want:%d", i, n, len(dst))
+ } else if !bytes.Equal(dst, test.dec) {
+ t.Errorf("#%d: got: %#v want: %#v", i, dst, test.dec)
+ }
+ }
+}
+
+func TestEncodeToString(t *testing.T) {
+ for i, test := range encDecTests {
+ s := EncodeToString(test.dec)
+ if s != test.enc {
+ t.Errorf("#%d got:%s want:%s", i, s, test.enc)
+ }
+ }
+}
+
+func TestDecodeString(t *testing.T) {
+ for i, test := range encDecTests {
+ dst, err := DecodeString(test.enc)
+ if err != nil {
+ t.Errorf("#%d: unexpected err value: %s", i, err)
+ continue
+ }
+ if !bytes.Equal(dst, test.dec) {
+ t.Errorf("#%d: got: %#v want: #%v", i, dst, test.dec)
+ }
+ }
+}
+
+var errTests = []struct {
+ in string
+ out string
+ err error
+}{
+ {"", "", nil},
+ {"0", "", ErrLength},
+ {"zd4aa", "", InvalidByteError('z')},
+ {"d4aaz", "\xd4\xaa", InvalidByteError('z')},
+ {"30313", "01", ErrLength},
+ {"0g", "", InvalidByteError('g')},
+ {"00gg", "\x00", InvalidByteError('g')},
+ {"0\x01", "", InvalidByteError('\x01')},
+ {"ffeed", "\xff\xee", ErrLength},
+}
+
+func TestDecodeErr(t *testing.T) {
+ for _, tt := range errTests {
+ out := make([]byte, len(tt.in)+10)
+ n, err := Decode(out, []byte(tt.in))
+ if string(out[:n]) != tt.out || err != tt.err {
+ t.Errorf("Decode(%q) = %q, %v, want %q, %v", tt.in, string(out[:n]), err, tt.out, tt.err)
+ }
+ }
+}
+
+func TestDecodeStringErr(t *testing.T) {
+ for _, tt := range errTests {
+ out, err := DecodeString(tt.in)
+ if string(out) != tt.out || err != tt.err {
+ t.Errorf("DecodeString(%q) = %q, %v, want %q, %v", tt.in, out, err, tt.out, tt.err)
+ }
+ }
+}
+
+func TestEncoderDecoder(t *testing.T) {
+ for _, multiplier := range []int{1, 128, 192} {
+ for _, test := range encDecTests {
+ input := bytes.Repeat(test.dec, multiplier)
+ output := strings.Repeat(test.enc, multiplier)
+
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+ r := struct{ io.Reader }{bytes.NewReader(input)} // io.Reader only; not io.WriterTo
+ if n, err := io.CopyBuffer(enc, r, make([]byte, 7)); n != int64(len(input)) || err != nil {
+ t.Errorf("encoder.Write(%q*%d) = (%d, %v), want (%d, nil)", test.dec, multiplier, n, err, len(input))
+ continue
+ }
+
+ if encDst := buf.String(); encDst != output {
+ t.Errorf("buf(%q*%d) = %v, want %v", test.dec, multiplier, encDst, output)
+ continue
+ }
+
+ dec := NewDecoder(&buf)
+ var decBuf bytes.Buffer
+ w := struct{ io.Writer }{&decBuf} // io.Writer only; not io.ReaderFrom
+ if _, err := io.CopyBuffer(w, dec, make([]byte, 7)); err != nil || decBuf.Len() != len(input) {
+ t.Errorf("decoder.Read(%q*%d) = (%d, %v), want (%d, nil)", test.enc, multiplier, decBuf.Len(), err, len(input))
+ }
+
+ if !bytes.Equal(decBuf.Bytes(), input) {
+ t.Errorf("decBuf(%q*%d) = %v, want %v", test.dec, multiplier, decBuf.Bytes(), input)
+ continue
+ }
+ }
+ }
+}
+
+func TestDecoderErr(t *testing.T) {
+ for _, tt := range errTests {
+ dec := NewDecoder(strings.NewReader(tt.in))
+ out, err := io.ReadAll(dec)
+ wantErr := tt.err
+ // Decoder is reading from stream, so it reports io.ErrUnexpectedEOF instead of ErrLength.
+ if wantErr == ErrLength {
+ wantErr = io.ErrUnexpectedEOF
+ }
+ if string(out) != tt.out || err != wantErr {
+ t.Errorf("NewDecoder(%q) = %q, %v, want %q, %v", tt.in, out, err, tt.out, wantErr)
+ }
+ }
+}
+
+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++ {
+ var out bytes.Buffer
+ 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 TestDumper_doubleclose(t *testing.T) {
+ var out strings.Builder
+ dumper := Dumper(&out)
+
+ dumper.Write([]byte(`gopher`))
+ dumper.Close()
+ dumper.Close()
+ dumper.Write([]byte(`gopher`))
+ dumper.Close()
+
+ expected := "00000000 67 6f 70 68 65 72 |gopher|\n"
+ if out.String() != expected {
+ t.Fatalf("got:\n%#v\nwant:\n%#v", out.String(), expected)
+ }
+}
+
+func TestDumper_earlyclose(t *testing.T) {
+ var out strings.Builder
+ dumper := Dumper(&out)
+
+ dumper.Close()
+ dumper.Write([]byte(`gopher`))
+
+ expected := ""
+ if out.String() != expected {
+ t.Fatalf("got:\n%#v\nwant:\n%#v", out.String(), expected)
+ }
+}
+
+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|
+`)
+
+var sink []byte
+
+func BenchmarkEncode(b *testing.B) {
+ for _, size := range []int{256, 1024, 4096, 16384} {
+ src := bytes.Repeat([]byte{2, 3, 5, 7, 9, 11, 13, 17}, size/8)
+ sink = make([]byte, 2*size)
+
+ b.Run(fmt.Sprintf("%v", size), func(b *testing.B) {
+ b.SetBytes(int64(size))
+ for i := 0; i < b.N; i++ {
+ Encode(sink, src)
+ }
+ })
+ }
+}
+
+func BenchmarkDecode(b *testing.B) {
+ for _, size := range []int{256, 1024, 4096, 16384} {
+ src := bytes.Repeat([]byte{'2', 'b', '7', '4', '4', 'f', 'a', 'a'}, size/8)
+ sink = make([]byte, size/2)
+
+ b.Run(fmt.Sprintf("%v", size), func(b *testing.B) {
+ b.SetBytes(int64(size))
+ for i := 0; i < b.N; i++ {
+ Decode(sink, src)
+ }
+ })
+ }
+}
+
+func BenchmarkDump(b *testing.B) {
+ for _, size := range []int{256, 1024, 4096, 16384} {
+ src := bytes.Repeat([]byte{2, 3, 5, 7, 9, 11, 13, 17}, size/8)
+
+ b.Run(fmt.Sprintf("%v", size), func(b *testing.B) {
+ b.SetBytes(int64(size))
+ for i := 0; i < b.N; i++ {
+ Dump(src)
+ }
+ })
+ }
+}