codekingpro/portable-devtools
114k
1// Copyright 2009 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.4 5// Package hex implements hexadecimal encoding and decoding.6package hex7 8import (9 "errors"10 "fmt"11 "io"12 "slices"13 "strings"14)15 16const (17 hextable = "0123456789abcdef"18 reverseHexTable = "" +19 "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +20 "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +21 "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +22 "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\xff\xff\xff\xff\xff\xff" +23 "\xff\x0a\x0b\x0c\x0d\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff" +24 "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +25 "\xff\x0a\x0b\x0c\x0d\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff" +26 "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +27 "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +28 "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +29 "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +30 "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +31 "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +32 "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +33 "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +34 "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"35)36 37// EncodedLen returns the length of an encoding of n source bytes.38// Specifically, it returns n * 2.39func EncodedLen(n int) int { return n * 2 }40 41// Encode encodes src into [EncodedLen](len(src))42// bytes of dst. As a convenience, it returns the number43// of bytes written to dst, but this value is always [EncodedLen](len(src)).44// Encode implements hexadecimal encoding.45func Encode(dst, src []byte) int {46 j := 047 for _, v := range src {48 dst[j] = hextable[v>>4]49 dst[j+1] = hextable[v&0x0f]50 j += 251 }52 return len(src) * 253}54 55// AppendEncode appends the hexadecimally encoded src to dst56// and returns the extended buffer.57func AppendEncode(dst, src []byte) []byte {58 n := EncodedLen(len(src))59 dst = slices.Grow(dst, n)60 Encode(dst[len(dst):][:n], src)61 return dst[:len(dst)+n]62}63 64// ErrLength reports an attempt to decode an odd-length input65// using [Decode] or [DecodeString].66// The stream-based Decoder returns [io.ErrUnexpectedEOF] instead of ErrLength.67var ErrLength = errors.New("encoding/hex: odd length hex string")68 69// InvalidByteError values describe errors resulting from an invalid byte in a hex string.70type InvalidByteError byte71 72func (e InvalidByteError) Error() string {73 return fmt.Sprintf("encoding/hex: invalid byte: %#U", rune(e))74}75 76// DecodedLen returns the length of a decoding of x source bytes.77// Specifically, it returns x / 2.78func DecodedLen(x int) int { return x / 2 }79 80// Decode decodes src into [DecodedLen](len(src)) bytes,81// returning the actual number of bytes written to dst.82//83// Decode expects that src contains only hexadecimal84// characters and that src has even length.85// If the input is malformed, Decode returns the number86// of bytes decoded before the error.87func Decode(dst, src []byte) (int, error) {88 i, j := 0, 189 for ; j < len(src); j += 2 {90 p := src[j-1]91 q := src[j]92 93 a := reverseHexTable[p]94 b := reverseHexTable[q]95 if a > 0x0f {96 return i, InvalidByteError(p)97 }98 if b > 0x0f {99 return i, InvalidByteError(q)100 }101 dst[i] = (a << 4) | b102 i++103 }104 if len(src)%2 == 1 {105 // Check for invalid char before reporting bad length,106 // since the invalid char (if present) is an earlier problem.107 if reverseHexTable[src[j-1]] > 0x0f {108 return i, InvalidByteError(src[j-1])109 }110 return i, ErrLength111 }112 return i, nil113}114 115// AppendDecode appends the hexadecimally decoded src to dst116// and returns the extended buffer.117// If the input is malformed, it returns the partially decoded src and an error.118func AppendDecode(dst, src []byte) ([]byte, error) {119 n := DecodedLen(len(src))120 dst = slices.Grow(dst, n)121 n, err := Decode(dst[len(dst):][:n], src)122 return dst[:len(dst)+n], err123}124 125// EncodeToString returns the hexadecimal encoding of src.126func EncodeToString(src []byte) string {127 dst := make([]byte, EncodedLen(len(src)))128 Encode(dst, src)129 return string(dst)130}131 132// DecodeString returns the bytes represented by the hexadecimal string s.133//134// DecodeString expects that src contains only hexadecimal135// characters and that src has even length.136// If the input is malformed, DecodeString returns137// the bytes decoded before the error.138func DecodeString(s string) ([]byte, error) {139 dst := make([]byte, DecodedLen(len(s)))140 n, err := Decode(dst, []byte(s))141 return dst[:n], err142}143 144// Dump returns a string that contains a hex dump of the given data. The format145// of the hex dump matches the output of `hexdump -C` on the command line.146func Dump(data []byte) string {147 if len(data) == 0 {148 return ""149 }150 151 var buf strings.Builder152 // Dumper will write 79 bytes per complete 16 byte chunk, and at least153 // 64 bytes for whatever remains. Round the allocation up, since only a154 // maximum of 15 bytes will be wasted.155 buf.Grow((1 + ((len(data) - 1) / 16)) * 79)156 157 dumper := Dumper(&buf)158 dumper.Write(data)159 dumper.Close()160 return buf.String()161}162 163// bufferSize is the number of hexadecimal characters to buffer in encoder and decoder.164const bufferSize = 1024165 166type encoder struct {167 w io.Writer168 err error169 out [bufferSize]byte // output buffer170}171 172// NewEncoder returns an [io.Writer] that writes lowercase hexadecimal characters to w.173func NewEncoder(w io.Writer) io.Writer {174 return &encoder{w: w}175}176 177func (e *encoder) Write(p []byte) (n int, err error) {178 for len(p) > 0 && e.err == nil {179 chunkSize := bufferSize / 2180 if len(p) < chunkSize {181 chunkSize = len(p)182 }183 184 var written int185 encoded := Encode(e.out[:], p[:chunkSize])186 written, e.err = e.w.Write(e.out[:encoded])187 n += written / 2188 p = p[chunkSize:]189 }190 return n, e.err191}192 193type decoder struct {194 r io.Reader195 err error196 in []byte // input buffer (encoded form)197 arr [bufferSize]byte // backing array for in198}199 200// NewDecoder returns an [io.Reader] that decodes hexadecimal characters from r.201// NewDecoder expects that r contain only an even number of hexadecimal characters.202func NewDecoder(r io.Reader) io.Reader {203 return &decoder{r: r}204}205 206func (d *decoder) Read(p []byte) (n int, err error) {207 // Fill internal buffer with sufficient bytes to decode208 if len(d.in) < 2 && d.err == nil {209 var numCopy, numRead int210 numCopy = copy(d.arr[:], d.in) // Copies either 0 or 1 bytes211 numRead, d.err = d.r.Read(d.arr[numCopy:])212 d.in = d.arr[:numCopy+numRead]213 if d.err == io.EOF && len(d.in)%2 != 0 {214 215 if a := reverseHexTable[d.in[len(d.in)-1]]; a > 0x0f {216 d.err = InvalidByteError(d.in[len(d.in)-1])217 } else {218 d.err = io.ErrUnexpectedEOF219 }220 }221 }222 223 // Decode internal buffer into output buffer224 if numAvail := len(d.in) / 2; len(p) > numAvail {225 p = p[:numAvail]226 }227 numDec, err := Decode(p, d.in[:len(p)*2])228 d.in = d.in[2*numDec:]229 if err != nil {230 d.in, d.err = nil, err // Decode error; discard input remainder231 }232 233 if len(d.in) < 2 {234 return numDec, d.err // Only expose errors when buffer fully consumed235 }236 return numDec, nil237}238 239// Dumper returns a [io.WriteCloser] that writes a hex dump of all written data to240// w. The format of the dump matches the output of `hexdump -C` on the command241// line.242func Dumper(w io.Writer) io.WriteCloser {243 return &dumper{w: w}244}245 246type dumper struct {247 w io.Writer248 rightChars [18]byte249 buf [14]byte250 used int // number of bytes in the current line251 n uint // number of bytes, total252 closed bool253}254 255func toChar(b byte) byte {256 if b < 32 || b > 126 {257 return '.'258 }259 return b260}261 262func (h *dumper) Write(data []byte) (n int, err error) {263 if h.closed {264 return 0, errors.New("encoding/hex: dumper closed")265 }266 267 // Output lines look like:268 // 00000010 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d |./0123456789:;<=|269 // ^ offset ^ extra space ^ ASCII of line.270 for i := range data {271 if h.used == 0 {272 // At the beginning of a line we print the current273 // offset in hex.274 h.buf[0] = byte(h.n >> 24)275 h.buf[1] = byte(h.n >> 16)276 h.buf[2] = byte(h.n >> 8)277 h.buf[3] = byte(h.n)278 Encode(h.buf[4:], h.buf[:4])279 h.buf[12] = ' '280 h.buf[13] = ' '281 _, err = h.w.Write(h.buf[4:])282 if err != nil {283 return284 }285 }286 Encode(h.buf[:], data[i:i+1])287 h.buf[2] = ' '288 l := 3289 if h.used == 7 {290 // There's an additional space after the 8th byte.291 h.buf[3] = ' '292 l = 4293 } else if h.used == 15 {294 // At the end of the line there's an extra space and295 // the bar for the right column.296 h.buf[3] = ' '297 h.buf[4] = '|'298 l = 5299 }300 _, err = h.w.Write(h.buf[:l])301 if err != nil {302 return303 }304 n++305 h.rightChars[h.used] = toChar(data[i])306 h.used++307 h.n++308 if h.used == 16 {309 h.rightChars[16] = '|'310 h.rightChars[17] = '\n'311 _, err = h.w.Write(h.rightChars[:])312 if err != nil {313 return314 }315 h.used = 0316 }317 }318 return319}320 321func (h *dumper) Close() (err error) {322 // See the comments in Write() for the details of this format.323 if h.closed {324 return325 }326 h.closed = true327 if h.used == 0 {328 return329 }330 h.buf[0] = ' '331 h.buf[1] = ' '332 h.buf[2] = ' '333 h.buf[3] = ' '334 h.buf[4] = '|'335 nBytes := h.used336 for h.used < 16 {337 l := 3338 if h.used == 7 {339 l = 4340 } else if h.used == 15 {341 l = 5342 }343 _, err = h.w.Write(h.buf[:l])344 if err != nil {345 return346 }347 h.used++348 }349 h.rightChars[nBytes] = '|'350 h.rightChars[nBytes+1] = '\n'351 _, err = h.w.Write(h.rightChars[:nBytes+2])352 return353}354 