| | |
| | |
| | |
| |
|
| | |
| | package hex |
| |
|
| | import ( |
| | "errors" |
| | "fmt" |
| | "io" |
| | "slices" |
| | "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" |
| | ) |
| |
|
| | |
| | |
| | func EncodedLen(n int) int { return n * 2 } |
| |
|
| | |
| | |
| | |
| | |
| | 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 |
| | } |
| |
|
| | |
| | |
| | func AppendEncode(dst, src []byte) []byte { |
| | n := EncodedLen(len(src)) |
| | dst = slices.Grow(dst, n) |
| | Encode(dst[len(dst):][:n], src) |
| | return dst[:len(dst)+n] |
| | } |
| |
|
| | |
| | |
| | |
| | var ErrLength = errors.New("encoding/hex: odd length hex string") |
| |
|
| | |
| | type InvalidByteError byte |
| |
|
| | func (e InvalidByteError) Error() string { |
| | return fmt.Sprintf("encoding/hex: invalid byte: %#U", rune(e)) |
| | } |
| |
|
| | |
| | |
| | func DecodedLen(x int) int { return x / 2 } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 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 { |
| | |
| | |
| | if reverseHexTable[src[j-1]] > 0x0f { |
| | return i, InvalidByteError(src[j-1]) |
| | } |
| | return i, ErrLength |
| | } |
| | return i, nil |
| | } |
| |
|
| | |
| | |
| | |
| | func AppendDecode(dst, src []byte) ([]byte, error) { |
| | n := DecodedLen(len(src)) |
| | dst = slices.Grow(dst, n) |
| | n, err := Decode(dst[len(dst):][:n], src) |
| | return dst[:len(dst)+n], err |
| | } |
| |
|
| | |
| | func EncodeToString(src []byte) string { |
| | dst := make([]byte, EncodedLen(len(src))) |
| | Encode(dst, src) |
| | return string(dst) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | func DecodeString(s string) ([]byte, error) { |
| | dst := make([]byte, DecodedLen(len(s))) |
| | n, err := Decode(dst, []byte(s)) |
| | return dst[:n], err |
| | } |
| |
|
| | |
| | |
| | func Dump(data []byte) string { |
| | if len(data) == 0 { |
| | return "" |
| | } |
| |
|
| | var buf strings.Builder |
| | |
| | |
| | |
| | buf.Grow((1 + ((len(data) - 1) / 16)) * 79) |
| |
|
| | dumper := Dumper(&buf) |
| | dumper.Write(data) |
| | dumper.Close() |
| | return buf.String() |
| | } |
| |
|
| | |
| | const bufferSize = 1024 |
| |
|
| | type encoder struct { |
| | w io.Writer |
| | err error |
| | out [bufferSize]byte |
| | } |
| |
|
| | |
| | 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 |
| | arr [bufferSize]byte |
| | } |
| |
|
| | |
| | |
| | func NewDecoder(r io.Reader) io.Reader { |
| | return &decoder{r: r} |
| | } |
| |
|
| | func (d *decoder) Read(p []byte) (n int, err error) { |
| | |
| | if len(d.in) < 2 && d.err == nil { |
| | var numCopy, numRead int |
| | numCopy = copy(d.arr[:], d.in) |
| | 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 |
| | } |
| | } |
| | } |
| |
|
| | |
| | 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 |
| | } |
| |
|
| | if len(d.in) < 2 { |
| | return numDec, d.err |
| | } |
| | return numDec, nil |
| | } |
| |
|
| | |
| | |
| | |
| | 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 |
| | n uint |
| | 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") |
| | } |
| |
|
| | |
| | |
| | |
| | for i := range data { |
| | if h.used == 0 { |
| | |
| | |
| | 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 { |
| | |
| | h.buf[3] = ' ' |
| | l = 4 |
| | } else if h.used == 15 { |
| | |
| | |
| | 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) { |
| | |
| | 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 |
| | } |
| |
|