1 // Copyright (c) 2020. Temple3x (temple3x@gmail.com)
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 15 // Package xhex implements hexadecimal encoding and decoding.
16 // xhex use AVX2 (if has) to accelerate encoding&decoding.
17 package xhex
18 19 import (
20 "errors"
21 "fmt"
22 )
23 24 const hextable = "0123456789abcdef"
25 26 // Encode encodes src into (2 * len(src)) bytes of dst.
27 //
28 // Warn:
29 // dst should have enough space(2 * len(src)),
30 // and len(src) must not be 0.
31 func Encode(dst, src []byte) {
32 encode(dst, src)
33 }
34 35 // Define encode as a variable for reducing branch (test has AVX2 or not),
36 // see xhex_amd64.go for details.
37 var encode = func(dst, src []byte) {
38 encodeBase(dst, src)
39 }
40 41 // encodeBase encodes src byte by byte.
42 func encodeBase(dst, src []byte) {
43 j := 0
44 for _, v := range src {
45 dst[j] = hextable[v>>4]
46 dst[j+1] = hextable[v&0x0f]
47 j += 2
48 }
49 }
50 51 // ErrLength reports an attempt to decode an odd-length input
52 // using Decode or DecodeString.
53 // The stream-based Decoder returns io.ErrUnexpectedEOF instead of ErrLength.
54 var ErrLength = errors.New("encoding/hex: odd length hex string")
55 56 // InvalidByteError values describe errors resulting from an invalid byte in a hex string.
57 type InvalidByteError byte
58 59 func (e InvalidByteError) Error() string {
60 return fmt.Sprintf("encoding/hex: invalid byte: %#U", rune(e))
61 }
62 63 // Decode decodes src into len(src)/2 bytes.
64 //
65 // Decode expects that src contains only hexadecimal
66 // characters and that src has even length.
67 func Decode(dst, src []byte) error {
68 return decode(dst, src)
69 }
70 71 var decode = func(dst, src []byte) error {
72 return decodeBase(dst, src)
73 }
74 75 func decodeBase(dst, src []byte) error {
76 i, j := 0, 1
77 for ; j < len(src); j += 2 {
78 a, ok := fromHexChar(src[j-1])
79 if !ok {
80 return InvalidByteError(src[j-1])
81 }
82 b, ok := fromHexChar(src[j])
83 if !ok {
84 return InvalidByteError(src[j])
85 }
86 dst[i] = (a << 4) | b
87 i++
88 }
89 if len(src)%2 == 1 {
90 // Check for invalid char before reporting bad length,
91 // since the invalid char (if present) is an earlier problem.
92 if _, ok := fromHexChar(src[j-1]); !ok {
93 return InvalidByteError(src[j-1])
94 }
95 return ErrLength
96 }
97 return nil
98 }
99 100 // fromHexChar converts a hex character into its value and a success flag.
101 func fromHexChar(c byte) (byte, bool) {
102 switch {
103 case '0' <= c && c <= '9':
104 return c - '0', true
105 case 'a' <= c && c <= 'f':
106 return c - 'a' + 10, true
107 case 'A' <= c && c <= 'F':
108 return c - 'A' + 10, true
109 }
110 111 return 0, false
112 }
113