From 5dad14dde3e2cc5891a9f22fe973eb1383c8aa03 Mon Sep 17 00:00:00 2001 From: blinkthethings Date: Thu, 24 Dec 2020 12:52:03 -0500 Subject: [PATCH] Make modhex more like encoding/hex package --- modhex.go | 102 ++++++++++++++++++++++++++++++------------------- modhex_test.go | 24 ++++++------ 2 files changed, 74 insertions(+), 52 deletions(-) diff --git a/modhex.go b/modhex.go index fc0cf37..9247210 100644 --- a/modhex.go +++ b/modhex.go @@ -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 } diff --git a/modhex_test.go b/modhex_test.go index ed1e839..dde1aaa 100644 --- a/modhex_test.go +++ b/modhex_test.go @@ -1,6 +1,9 @@ package modhex -import "testing" +import ( + "encoding/hex" + "testing" +) type test struct { hex string @@ -26,24 +29,21 @@ func TestEncodeHexDecodeHex(t *testing.T) { } for _, tc := range tests { - modhex, err := EncodeHex(tc.hex) - - if err != nil { - t.Errorf("EncodeHex Error: %s\n", err) - } + src, _ := hex.DecodeString(tc.hex) + modhex := EncodeToString(src) if modhex != tc.modhex { - t.Errorf("EncodeHex Incorrect: Actual: %s; Expected: %s\n", modhex, tc.modhex) + t.Errorf("Encode Incorrect: Actual: %s; Expected: %s\n", modhex, tc.modhex) } - hex, err := DecodeHex(tc.modhex) - + hexBytes, err := DecodeString(tc.modhex) if err != nil { - t.Errorf("DecodeHex Error: %s\n", err) + t.Error(err) } - if hex != tc.hex { - t.Errorf("DecodeHex Incorrect: Actual: %s; Expected: %s\n", hex, tc.hex) + hexStr := hex.EncodeToString(hexBytes) + if hexStr != tc.hex { + t.Errorf("Decode Incorrect: Actual: %s; Expected: %s\n", hexStr, tc.hex) } } }