diff options
| author | Tim Cooper <tim.cooper@layeh.com> | 2017-10-11 22:05:03 -0300 |
|---|---|---|
| committer | Joe Tsai <thebrokentoaster@gmail.com> | 2017-10-20 23:47:07 +0000 |
| commit | 6db4950dc57deb899bf5550411c01ce32f16bdd0 (patch) | |
| tree | 11a16d2d68e4b82612cb7ffd6d78bf2d9a2c6718 /src/encoding/hex/hex.go | |
| parent | d05f82a11af68a65b118de14bb230d640722d55c (diff) | |
| download | go-6db4950dc57deb899bf5550411c01ce32f16bdd0.tar.xz | |
encoding/hex: add NewEncoder, NewDecoder
NewEncoder returns an io.Writer that writes all incoming bytes as
hexadecimal characters to the underlying io.Writer. NewDecoder returns an
io.Reader that does the inverse.
Fixes #21590
Change-Id: Iebe0813faf365b42598f19a9aa41768f571dc0a8
Reviewed-on: https://go-review.googlesource.com/70210
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/encoding/hex/hex.go')
| -rw-r--r-- | src/encoding/hex/hex.go | 75 |
1 files changed, 73 insertions, 2 deletions
diff --git a/src/encoding/hex/hex.go b/src/encoding/hex/hex.go index 18e0c09ef3..f47b7fa34e 100644 --- a/src/encoding/hex/hex.go +++ b/src/encoding/hex/hex.go @@ -58,11 +58,11 @@ func Decode(dst, src []byte) (int, error) { for i := 0; i < len(src)/2; i++ { a, ok := fromHexChar(src[i*2]) if !ok { - return 0, InvalidByteError(src[i*2]) + return i, InvalidByteError(src[i*2]) } b, ok := fromHexChar(src[i*2+1]) if !ok { - return 0, InvalidByteError(src[i*2+1]) + return i, InvalidByteError(src[i*2+1]) } dst[i] = (a << 4) | b } @@ -113,6 +113,77 @@ func Dump(data []byte) string { 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 { + 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. |
