// 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 ( ) 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 ( int) int { return * 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 (, []byte) int { := 0 for , := range { [] = hextable[>>4] [+1] = hextable[&0x0f] += 2 } return len() * 2 } // AppendEncode appends the hexadecimally encoded src to dst // and returns the extended buffer. func (, []byte) []byte { := EncodedLen(len()) = slices.Grow(, ) Encode([len():][:], ) return [:len()+] } // 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 ( InvalidByteError) () string { return fmt.Sprintf("encoding/hex: invalid byte: %#U", rune()) } // DecodedLen returns the length of a decoding of x source bytes. // Specifically, it returns x / 2. func ( int) int { return / 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 (, []byte) (int, error) { , := 0, 1 for ; < len(); += 2 { := [-1] := [] := reverseHexTable[] := reverseHexTable[] if > 0x0f { return , InvalidByteError() } if > 0x0f { return , InvalidByteError() } [] = ( << 4) | ++ } if len()%2 == 1 { // Check for invalid char before reporting bad length, // since the invalid char (if present) is an earlier problem. if reverseHexTable[[-1]] > 0x0f { return , InvalidByteError([-1]) } return , ErrLength } return , nil } // AppendDecode appends the hexadecimally decoded src to dst // and returns the extended buffer. // If the input is malformed, it returns the partially decoded src and an error. func (, []byte) ([]byte, error) { := DecodedLen(len()) = slices.Grow(, ) , := Decode([len():][:], ) return [:len()+], } // EncodeToString returns the hexadecimal encoding of src. func ( []byte) string { := make([]byte, EncodedLen(len())) Encode(, ) return string() } // 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 ( string) ([]byte, error) { := make([]byte, DecodedLen(len())) , := Decode(, []byte()) return [:], } // 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 ( []byte) string { if len() == 0 { return "" } var 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. .Grow((1 + ((len() - 1) / 16)) * 79) := Dumper(&) .Write() .Close() return .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 ( io.Writer) io.Writer { return &encoder{w: } } func ( *encoder) ( []byte) ( int, error) { for len() > 0 && .err == nil { := bufferSize / 2 if len() < { = len() } var int := Encode(.out[:], [:]) , .err = .w.Write(.out[:]) += / 2 = [:] } return , .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 ( io.Reader) io.Reader { return &decoder{r: } } func ( *decoder) ( []byte) ( int, error) { // Fill internal buffer with sufficient bytes to decode if len(.in) < 2 && .err == nil { var , int = copy(.arr[:], .in) // Copies either 0 or 1 bytes , .err = .r.Read(.arr[:]) .in = .arr[:+] if .err == io.EOF && len(.in)%2 != 0 { if := reverseHexTable[.in[len(.in)-1]]; > 0x0f { .err = InvalidByteError(.in[len(.in)-1]) } else { .err = io.ErrUnexpectedEOF } } } // Decode internal buffer into output buffer if := len(.in) / 2; len() > { = [:] } , := Decode(, .in[:len()*2]) .in = .in[2*:] if != nil { .in, .err = nil, // Decode error; discard input remainder } if len(.in) < 2 { return , .err // Only expose errors when buffer fully consumed } return , nil } // Dumper returns a [io.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 ( io.Writer) io.WriteCloser { return &dumper{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( byte) byte { if < 32 || > 126 { return '.' } return } func ( *dumper) ( []byte) ( int, error) { if .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 := range { if .used == 0 { // At the beginning of a line we print the current // offset in hex. .buf[0] = byte(.n >> 24) .buf[1] = byte(.n >> 16) .buf[2] = byte(.n >> 8) .buf[3] = byte(.n) Encode(.buf[4:], .buf[:4]) .buf[12] = ' ' .buf[13] = ' ' _, = .w.Write(.buf[4:]) if != nil { return } } Encode(.buf[:], [:+1]) .buf[2] = ' ' := 3 if .used == 7 { // There's an additional space after the 8th byte. .buf[3] = ' ' = 4 } else if .used == 15 { // At the end of the line there's an extra space and // the bar for the right column. .buf[3] = ' ' .buf[4] = '|' = 5 } _, = .w.Write(.buf[:]) if != nil { return } ++ .rightChars[.used] = toChar([]) .used++ .n++ if .used == 16 { .rightChars[16] = '|' .rightChars[17] = '\n' _, = .w.Write(.rightChars[:]) if != nil { return } .used = 0 } } return } func ( *dumper) () ( error) { // See the comments in Write() for the details of this format. if .closed { return } .closed = true if .used == 0 { return } .buf[0] = ' ' .buf[1] = ' ' .buf[2] = ' ' .buf[3] = ' ' .buf[4] = '|' := .used for .used < 16 { := 3 if .used == 7 { = 4 } else if .used == 15 { = 5 } _, = .w.Write(.buf[:]) if != nil { return } .used++ } .rightChars[] = '|' .rightChars[+1] = '\n' _, = .w.Write(.rightChars[:+2]) return }