|
|
@ -1,3 +1,4 @@ |
|
|
|
// Package modhex implements modhex encoding and decoding.
|
|
|
|
package modhex |
|
|
|
|
|
|
|
import ( |
|
|
@ -5,62 +6,83 @@ import ( |
|
|
|
"fmt" |
|
|
|
) |
|
|
|
|
|
|
|
const hexChars = "0123456789abcdef" |
|
|
|
const modhexChars = "cbdefghijklnrtuv" |
|
|
|
const modhexTable = "cbdefghijklnrtuv" |
|
|
|
|
|
|
|
var modhex2HexMap map[byte]byte |
|
|
|
var hex2ModhexMap map[byte]byte |
|
|
|
var modhexMap map[byte]byte |
|
|
|
|
|
|
|
func init() { |
|
|
|
hex2ModhexMap = make(map[byte]byte) |
|
|
|
for i, h := range []byte(hexChars) { |
|
|
|
hex2ModhexMap[h] = modhexChars[i] |
|
|
|
} |
|
|
|
|
|
|
|
modhex2HexMap = make(map[byte]byte) |
|
|
|
for i, m := range []byte(modhexChars) { |
|
|
|
modhex2HexMap[m] = hexChars[i] |
|
|
|
modhexMap = make(map[byte]byte) |
|
|
|
for i, v := range []byte(modhexTable) { |
|
|
|
modhexMap[v] = byte(i) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// EncodeHex encodes a hex string into a modhex string
|
|
|
|
func EncodeHex(hex string) (string, error) { |
|
|
|
size := len([]byte(hex)) |
|
|
|
// EncodedLen returns the length of an encoding of n source bytes.
|
|
|
|
// Specifically, it returns n * 2.
|
|
|
|
func EncodedLen(n int) int { |
|
|
|
return n * 2 |
|
|
|
} |
|
|
|
|
|
|
|
if size%2 == 1 { |
|
|
|
return "", errors.New("size of input hex input not even") |
|
|
|
// Encode encodes src into EncodedLen(len(src))
|
|
|
|
// bytes of dst. It returns the number of bytes
|
|
|
|
// written to dst, but this is always EncodedLen(len(src)).
|
|
|
|
// Encode implements modhex encoding.
|
|
|
|
func Encode(dst, src []byte) int { |
|
|
|
j := 0 |
|
|
|
for _, v := range src { |
|
|
|
dst[j] = modhexTable[v>>4] |
|
|
|
dst[j+1] = modhexTable[v&0x0f] |
|
|
|
j += 2 |
|
|
|
} |
|
|
|
return len(src) * 2 |
|
|
|
} |
|
|
|
|
|
|
|
var modhex = make([]byte, size) |
|
|
|
|
|
|
|
for i, h := range []byte(hex) { |
|
|
|
if m, ok := hex2ModhexMap[h]; ok { |
|
|
|
modhex[i] = m |
|
|
|
} else { |
|
|
|
return "", fmt.Errorf("input not hex encoded; position %d", i) |
|
|
|
} |
|
|
|
} |
|
|
|
// EncodeToString returns the modhex encoding of src.
|
|
|
|
func EncodeToString(src []byte) string { |
|
|
|
dst := make([]byte, EncodedLen(len(src))) |
|
|
|
Encode(dst, src) |
|
|
|
return string(dst) |
|
|
|
} |
|
|
|
|
|
|
|
return string(modhex), nil |
|
|
|
// DecodedLen returns the length of a decoding of n source bytes.
|
|
|
|
// Specifically, it returns n / 2.
|
|
|
|
func DecodedLen(n int) int { |
|
|
|
return n / 2 |
|
|
|
} |
|
|
|
|
|
|
|
// DecodeHex decodes a modhex string into a hex string
|
|
|
|
func DecodeHex(modhex string) (string, error) { |
|
|
|
size := len([]byte(modhex)) |
|
|
|
// Decode decodes src into DecodedLen(len(src)) bytes,
|
|
|
|
// returning the actual number of bytes written to dst.
|
|
|
|
func Decode(dst, src []byte) (int, error) { |
|
|
|
i, j := 0, 1 |
|
|
|
for ; j < len(src); j += 2 { |
|
|
|
a, ok := modhexMap[src[j-1]] |
|
|
|
if !ok { |
|
|
|
return i, fmt.Errorf("modhex: invalid byte: %#U", rune(src[j-1])) |
|
|
|
} |
|
|
|
|
|
|
|
if size%2 == 1 { |
|
|
|
return "", errors.New("size of modhex input not even") |
|
|
|
} |
|
|
|
b, ok := modhexMap[src[j]] |
|
|
|
if !ok { |
|
|
|
return i, fmt.Errorf("modhex: invalid byte: %#U", rune(src[j])) |
|
|
|
} |
|
|
|
|
|
|
|
var hex = make([]byte, size) |
|
|
|
dst[i] = (a << 4) | b |
|
|
|
i++ |
|
|
|
} |
|
|
|
|
|
|
|
for i, m := range []byte(modhex) { |
|
|
|
if h, ok := modhex2HexMap[m]; ok { |
|
|
|
hex[i] = h |
|
|
|
} else { |
|
|
|
return "", fmt.Errorf("input not modhex encoded; position %d", i) |
|
|
|
if len(src)%2 == 1 { |
|
|
|
if _, ok := modhexMap[src[j-1]]; !ok { |
|
|
|
return i, fmt.Errorf("modhex: invalid byte: %#U", rune(src[j-1])) |
|
|
|
} |
|
|
|
|
|
|
|
return i, errors.New("modhex: odd length modhex string") |
|
|
|
} |
|
|
|
|
|
|
|
return string(hex), nil |
|
|
|
return i, nil |
|
|
|
} |
|
|
|
|
|
|
|
// DecodeString returns the bytes represesented by the modhex string s.
|
|
|
|
func DecodeString(s string) ([]byte, error) { |
|
|
|
src := []byte(s) |
|
|
|
n, err := Decode(src, src) |
|
|
|
return src[:n], err |
|
|
|
} |