// Package modhex implements modhex encoding and decoding. package modhex import ( "errors" "fmt" "strings" ) // modhex character table for encoding const modhexTable = "cbdefghijklnrtuv" // modhex map for decoding var modhexMap map[byte]byte func init() { // create a map from byte to modhex for decoding 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 }