// 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" "errors" "fmt" "io" ) const hextable = "0123456789abcdef" // 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 { for i, v := range src { dst[i*2] = hextable[v>>4] dst[i*2+1] = hextable[v&0x0f] } 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 contain only hexadecimal // characters and that src should have an even length. // If the input is malformed, Decode returns the number // of bytes decoded before the error. func Decode(dst, src []byte) (int, error) { var i int for i = 0; i < len(src)/2; i++ { a, ok := fromHexChar(src[i*2]) if !ok { return i, InvalidByteError(src[i*2]) } b, ok := fromHexChar(src[i*2+1]) if !ok { return i, InvalidByteError(src[i*2+1]) } dst[i] = (a << 4) | b } if len(src)%2 == 1 { // Check for invalid char before reporting bad length, // since the invalid char (if present) is an earlier problem. if _, ok := fromHexChar(src[i*2]); !ok { return i, InvalidByteError(src[i*2]) } return i, ErrLength } return i, 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. // // DecodeString expects that src contain only hexadecimal // characters and that src should have an even length. // If the input is malformed, DecodeString returns a string // containing 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 { var buf bytes.Buffer 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 _, ok := fromHexChar(d.in[len(d.in)-1]); !ok { 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 } func toChar(b byte) byte { if b < 32 || b > 126 { return '.' } return b } func (h *dumper) Write(data []byte) (n int, err 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:]) 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.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 }