package hexutility import ( "encoding/hex" "fmt" ) const ( badNibble = ^uint64(0) ) // UnmarshalFixedText decodes the input as a string with 0x prefix. The length of out // determines the required input length. This function is commonly used to implement the // UnmarshalText method for fixed-size types. func UnmarshalFixedText(typname string, input, out []byte) error { raw, err := checkText(input, true) if err != nil { return err } if len(raw)/2 != len(out) { return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname) } // Pre-verify syntax before modifying out. for _, b := range raw { if decodeNibble(b) == badNibble { return ErrSyntax } } _, err = hex.Decode(out, raw) return err } func checkText(input []byte, wantPrefix bool) ([]byte, error) { if len(input) == 0 { return nil, nil // empty strings are allowed } if bytesHave0xPrefix(input) { input = input[2:] } else if wantPrefix { return nil, ErrMissingPrefix } if len(input)%2 != 0 { return nil, ErrOddLength } return input, nil } func bytesHave0xPrefix(input []byte) bool { return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') } func decodeNibble(in byte) uint64 { switch { case in >= '0' && in <= '9': return uint64(in - '0') case in >= 'A' && in <= 'F': return uint64(in - 'A' + 10) case in >= 'a' && in <= 'f': return uint64(in - 'a' + 10) default: return badNibble } }