// 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 len := 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[len] = modhexTable[v>>4] len++ // convert the lower nibble to modhex // and store it into dst dst[len] = modhexTable[v&0x0f] len++ } // return the number of bytes written return len } // 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) { i, j := 0, 1 for ; j < len(src); j += 2 { a, ok := modhexMap[src[j-1]] if !ok { return i, InvalidByteError(src[j-1]) } b, ok := modhexMap[src[j]] if !ok { return i, InvalidByteError(src[j]) } dst[i] = (a << 4) | b i++ } if len(src)%2 == 1 { if _, ok := modhexMap[src[j-1]]; !ok { return i, InvalidByteError(src[j-1]) } return i, ErrLength } return i, 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 }