// Package hexutil decodes hex strings in the loose formats people paste. package hexutil import ( "encoding/hex" "fmt" "strings" ) // Decode decodes a hex string, tolerating common formatting: // whitespace and newlines, ':' '-' ',' '.' separators, and "0x" / "\x" byte prefixes. // So "45 00 00 3c", "45:00:00:3c", "0x45,0x00", "\x45\x00" and "4500003c" all work. func Decode(s string) ([]byte, error) { s = strings.NewReplacer(`\x`, " ", `\X`, " ", "0x", " ", "0X", " ").Replace(s) digits := make([]byte, 0, len(s)) for i := 0; i < len(s); i++ { c := s[i] switch { case isHexDigit(c): digits = append(digits, c) case c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ':' || c == '-' || c == ',' || c == '.': // separator default: return nil, fmt.Errorf("invalid character %q in hex input", c) } } if len(digits)%2 != 0 { return nil, fmt.Errorf("odd number of hex digits (%d)", len(digits)) } out := make([]byte, len(digits)/2) if _, err := hex.Decode(out, digits); err != nil { return nil, err } return out, nil } func isHexDigit(c byte) bool { return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' }