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