github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/encoding/hex/hex.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package hex implements hexadecimal encoding and decoding. 6 package hex 7 8 import ( 9 "bytes" 10 "errors" 11 "fmt" 12 "io" 13 ) 14 15 const hextable = "0123456789abcdef" 16 17 // EncodedLen returns the length of an encoding of n source bytes. 18 func EncodedLen(n int) int { return n * 2 } 19 20 // Encode encodes src into EncodedLen(len(src)) 21 // bytes of dst. As a convenience, it returns the number 22 // of bytes written to dst, but this value is always EncodedLen(len(src)). 23 // Encode implements hexadecimal encoding. 24 func Encode(dst, src []byte) int { 25 for i, v := range src { 26 dst[i*2] = hextable[v>>4] 27 dst[i*2+1] = hextable[v&0x0f] 28 } 29 30 return len(src) * 2 31 } 32 33 // ErrLength results from decoding an odd length slice. 34 var ErrLength = errors.New("encoding/hex: odd length hex string") 35 36 // InvalidByteError values describe errors resulting from an invalid byte in a hex string. 37 type InvalidByteError byte 38 39 func (e InvalidByteError) Error() string { 40 return fmt.Sprintf("encoding/hex: invalid byte: %#U", rune(e)) 41 } 42 43 func DecodedLen(x int) int { return x / 2 } 44 45 // Decode decodes src into DecodedLen(len(src)) bytes, returning the actual 46 // number of bytes written to dst. 47 // 48 // If Decode encounters invalid input, it returns an error describing the failure. 49 func Decode(dst, src []byte) (int, error) { 50 if len(src)%2 == 1 { 51 return 0, ErrLength 52 } 53 54 for i := 0; i < len(src)/2; i++ { 55 a, ok := fromHexChar(src[i*2]) 56 if !ok { 57 return 0, InvalidByteError(src[i*2]) 58 } 59 b, ok := fromHexChar(src[i*2+1]) 60 if !ok { 61 return 0, InvalidByteError(src[i*2+1]) 62 } 63 dst[i] = (a << 4) | b 64 } 65 66 return len(src) / 2, nil 67 } 68 69 // fromHexChar converts a hex character into its value and a success flag. 70 func fromHexChar(c byte) (byte, bool) { 71 switch { 72 case '0' <= c && c <= '9': 73 return c - '0', true 74 case 'a' <= c && c <= 'f': 75 return c - 'a' + 10, true 76 case 'A' <= c && c <= 'F': 77 return c - 'A' + 10, true 78 } 79 80 return 0, false 81 } 82 83 // EncodeToString returns the hexadecimal encoding of src. 84 func EncodeToString(src []byte) string { 85 dst := make([]byte, EncodedLen(len(src))) 86 Encode(dst, src) 87 return string(dst) 88 } 89 90 // DecodeString returns the bytes represented by the hexadecimal string s. 91 func DecodeString(s string) ([]byte, error) { 92 src := []byte(s) 93 dst := make([]byte, DecodedLen(len(src))) 94 _, err := Decode(dst, src) 95 if err != nil { 96 return nil, err 97 } 98 return dst, nil 99 } 100 101 // Dump returns a string that contains a hex dump of the given data. The format 102 // of the hex dump matches the output of `hexdump -C` on the command line. 103 func Dump(data []byte) string { 104 var buf bytes.Buffer 105 dumper := Dumper(&buf) 106 dumper.Write(data) 107 dumper.Close() 108 return string(buf.Bytes()) 109 } 110 111 // Dumper returns a WriteCloser that writes a hex dump of all written data to 112 // w. The format of the dump matches the output of `hexdump -C` on the command 113 // line. 114 func Dumper(w io.Writer) io.WriteCloser { 115 return &dumper{w: w} 116 } 117 118 type dumper struct { 119 w io.Writer 120 rightChars [18]byte 121 buf [14]byte 122 used int // number of bytes in the current line 123 n uint // number of bytes, total 124 } 125 126 func toChar(b byte) byte { 127 if b < 32 || b > 126 { 128 return '.' 129 } 130 return b 131 } 132 133 func (h *dumper) Write(data []byte) (n int, err error) { 134 // Output lines look like: 135 // 00000010 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d |./0123456789:;<=| 136 // ^ offset ^ extra space ^ ASCII of line. 137 for i := range data { 138 if h.used == 0 { 139 // At the beginning of a line we print the current 140 // offset in hex. 141 h.buf[0] = byte(h.n >> 24) 142 h.buf[1] = byte(h.n >> 16) 143 h.buf[2] = byte(h.n >> 8) 144 h.buf[3] = byte(h.n) 145 Encode(h.buf[4:], h.buf[:4]) 146 h.buf[12] = ' ' 147 h.buf[13] = ' ' 148 _, err = h.w.Write(h.buf[4:]) 149 if err != nil { 150 return 151 } 152 } 153 Encode(h.buf[:], data[i:i+1]) 154 h.buf[2] = ' ' 155 l := 3 156 if h.used == 7 { 157 // There's an additional space after the 8th byte. 158 h.buf[3] = ' ' 159 l = 4 160 } else if h.used == 15 { 161 // At the end of the line there's an extra space and 162 // the bar for the right column. 163 h.buf[3] = ' ' 164 h.buf[4] = '|' 165 l = 5 166 } 167 _, err = h.w.Write(h.buf[:l]) 168 if err != nil { 169 return 170 } 171 n++ 172 h.rightChars[h.used] = toChar(data[i]) 173 h.used++ 174 h.n++ 175 if h.used == 16 { 176 h.rightChars[16] = '|' 177 h.rightChars[17] = '\n' 178 _, err = h.w.Write(h.rightChars[:]) 179 if err != nil { 180 return 181 } 182 h.used = 0 183 } 184 } 185 return 186 } 187 188 func (h *dumper) Close() (err error) { 189 // See the comments in Write() for the details of this format. 190 if h.used == 0 { 191 return 192 } 193 h.buf[0] = ' ' 194 h.buf[1] = ' ' 195 h.buf[2] = ' ' 196 h.buf[3] = ' ' 197 h.buf[4] = '|' 198 nBytes := h.used 199 for h.used < 16 { 200 l := 3 201 if h.used == 7 { 202 l = 4 203 } else if h.used == 15 { 204 l = 5 205 } 206 _, err = h.w.Write(h.buf[:l]) 207 if err != nil { 208 return 209 } 210 h.used++ 211 } 212 h.rightChars[nBytes] = '|' 213 h.rightChars[nBytes+1] = '\n' 214 _, err = h.w.Write(h.rightChars[:nBytes+2]) 215 return 216 }