Source file src/pkg/encoding/hex/hex.go
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 }
150 Encode(h.buf[:], data[i:i+1])
151 h.buf[2] = ' '
152 l := 3
153 if h.used == 7 {
154 // There's an additional space after the 8th byte.
155 h.buf[3] = ' '
156 l = 4
157 } else if h.used == 15 {
158 // At the end of the line there's an extra space and
159 // the bar for the right column.
160 h.buf[3] = ' '
161 h.buf[4] = '|'
162 l = 5
163 }
164 _, err = h.w.Write(h.buf[:l])
165 if err != nil {
166 return
167 }
168 n++
169 h.rightChars[h.used] = toChar(data[i])
170 h.used++
171 h.n++
172 if h.used == 16 {
173 h.rightChars[16] = '|'
174 h.rightChars[17] = '\n'
175 _, err = h.w.Write(h.rightChars[:])
176 if err != nil {
177 return
178 }
179 h.used = 0
180 }
181 }
182 return
183 }
184
185 func (h *dumper) Close() (err error) {
186 // See the comments in Write() for the details of this format.
187 if h.used == 0 {
188 return
189 }
190 h.buf[0] = ' '
191 h.buf[1] = ' '
192 h.buf[2] = ' '
193 h.buf[3] = ' '
194 h.buf[4] = '|'
195 nBytes := h.used
196 for h.used < 16 {
197 l := 3
198 if h.used == 7 {
199 l = 4
200 } else if h.used == 15 {
201 l = 5
202 }
203 _, err = h.w.Write(h.buf[:l])
204 if err != nil {
205 return
206 }
207 h.used++
208 }
209 h.rightChars[nBytes] = '|'
210 h.rightChars[nBytes+1] = '\n'
211 _, err = h.w.Write(h.rightChars[:nBytes+2])
212 return
213 }