// Package modhex implements modhex encoding and decoding.
|
|
package modhex
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const modhexTable = "cbdefghijklnrtuv"
|
|
|
|
var modhexMap map[byte]byte
|
|
|
|
func init() {
|
|
modhexMap = make(map[byte]byte)
|
|
for i, v := range []byte(modhexTable) {
|
|
modhexMap[v] = byte(i)
|
|
}
|
|
}
|
|
|
|
// EncodedLen returns the length of an encoding of n source bytes.
|
|
// Specifically, it returns n * 2.
|
|
func EncodedLen(n int) int {
|
|
return n * 2
|
|
}
|
|
|
|
// Encode encodes src into EncodedLen(len(src)) bytes of dst.
|
|
//
|
|
// Encode returns the number of bytes written to dst, but this is always
|
|
// EncodedLen(len(src)).
|
|
func Encode(dst, src []byte) int {
|
|
// keep track of the number of bytes written to dst
|
|
n := 0
|
|
|
|
// convert each byte of src and store in dst
|
|
for _, v := range src {
|
|
// convert the upper nibble to modhex
|
|
// and store it into dst
|
|
dst[n] = modhexTable[v>>4]
|
|
n++
|
|
|
|
// convert the lower nibble to modhex
|
|
// and store it into dst
|
|
dst[n] = modhexTable[v&0x0f]
|
|
n++
|
|
}
|
|
|
|
// return the number of bytes written
|
|
return n
|
|
}
|
|
|
|
// EncodeToString returns the modhex encoding of src as a string.
|
|
func EncodeToString(src []byte) string {
|
|
// make a new slice to hold the encoded bytes
|
|
dst := make([]byte, EncodedLen(len(src)))
|
|
|
|
// encode it!
|
|
Encode(dst, src)
|
|
|
|
// return the encoded bytes as a string
|
|
return string(dst)
|
|
}
|
|
|
|
// DecodedLen returns the length of a decoding of n source bytes.
|
|
// Specifically, it returns n / 2.
|
|
func DecodedLen(n int) int {
|
|
return n / 2
|
|
}
|
|
|
|
// ErrLength reports an attempt to decode an odd-length input using Decode
|
|
// or DecodeString.
|
|
var ErrLength = errors.New("modhex: odd length modhex string")
|
|
|
|
// InvalidByteError values describe errors resulting from an invalid byte in a
|
|
// hex string.
|
|
type InvalidByteError byte
|
|
|
|
func (e InvalidByteError) Error() string {
|
|
return fmt.Sprintf("modhex: invalid byte: %#U", rune(e))
|
|
}
|
|
|
|
// Decode decodes src into DecodedLen(len(src)) bytes, returning
|
|
// the actual number of bytes written to dst.
|
|
//
|
|
// Decode expects that src contains only modhex characters and that src has an
|
|
// even length. If the input is malformed, Decode returns the number of bytes
|
|
// decoded before the error.
|
|
func Decode(dst, src []byte) (int, error) {
|
|
// decode the src bytes two at a time
|
|
n, i := 0, 1
|
|
for ; i < len(src); i += 2 {
|
|
|
|
// try to decode the upper nibble
|
|
a, ok := modhexMap[src[i-1]]
|
|
if !ok {
|
|
return n, InvalidByteError(src[i-1])
|
|
}
|
|
|
|
// try to decode the lower nibble
|
|
b, ok := modhexMap[src[i]]
|
|
if !ok {
|
|
return n, InvalidByteError(src[i])
|
|
}
|
|
|
|
// store the decoded value into dst
|
|
dst[n] = (a << 4) | b
|
|
n++
|
|
}
|
|
|
|
// check for an odd length input
|
|
if len(src)%2 == 1 {
|
|
// first check for an invalid byte
|
|
if _, ok := modhexMap[src[i-1]]; !ok {
|
|
return n, InvalidByteError(src[i-1])
|
|
}
|
|
|
|
// report the number of bytes written and the length error
|
|
return n, ErrLength
|
|
}
|
|
|
|
// return the number of bytes written to dst
|
|
return n, nil
|
|
}
|
|
|
|
// DecodeString returns the bytes represented by the modhex string s.
|
|
//
|
|
// DecodeString expects that s contains only modhex characters and that s
|
|
// has an even length. If the input is malformed, DecodeString returns the
|
|
// number of bytes decoded before the error.
|
|
func DecodeString(s string) ([]byte, error) {
|
|
// convert the string to lower case since the map uses lower case
|
|
s = strings.ToLower(s)
|
|
|
|
// convert the input string to an array of bytes
|
|
src := []byte(s)
|
|
|
|
// make a new slice to hold the decoded bytes
|
|
dst := make([]byte, DecodedLen(len(src)))
|
|
|
|
// decode it!
|
|
n, err := Decode(dst, src)
|
|
|
|
// return the decoded bytes along with any errors that may have occured
|
|
return dst[:n], err
|
|
}
|