xhex.go raw

   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