12 s string // Original casing.
13 lower string // Lower casing, for case-insensitive token consumption.
14 o int // Offset in s/lower.
17type parseError struct{ err error }
19func (e parseError) Error() string {
23func (e parseError) Unwrap() error {
27// toLower lower cases bytes that are A-Z. strings.ToLower does too much. and
28// would replace invalid bytes with unicode replacement characters, which would
29// break our requirement that offsets into the original and upper case strings
30// point to the same character.
31func toLower(s string) string {
34 if c >= 'A' && c <= 'Z' {
41func newParser(buf []byte) *parser {
43 return &parser{s, toLower(s), 0}
46// Turn panics of parseError into a descriptive ErrInvalidEncoding. Called with
47// defer by functions that parse.
48func (p *parser) recover(rerr *error) {
58 if errors.As(err, &xerr) {
62 *rerr = fmt.Errorf("%w: %s", ErrInvalidEncoding, err)
65func (p *parser) xerrorf(format string, args ...any) {
66 panic(parseError{fmt.Errorf(format, args...)})
69func (p *parser) xcheckf(err error, format string, args ...any) {
71 panic(parseError{fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err)})
75func (p *parser) xempty() {
77 p.xerrorf("leftover data")
81func (p *parser) xnonempty() {
83 p.xerrorf("unexpected end")
87func (p *parser) xbyte() byte {
94func (p *parser) peek(s string) bool {
95 return strings.HasPrefix(p.lower[p.o:], s)
98func (p *parser) take(s string) bool {
106func (p *parser) xtake(s string) {
108 p.xerrorf("expected %q", s)
112func (p *parser) xauthzid() string {
117func (p *parser) xusername() string {
122func (p *parser) xnonce() string {
125 for ; o < len(p.s); o++ {
127 if c <= ' ' || c >= 0x7f || c == ',' {
132 p.xerrorf("empty nonce")
139func (p *parser) xattrval() {
141 if !(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') {
142 p.xerrorf("expected alpha for attr-val")
148func (p *parser) xvalue() string {
149 for o, c := range p.s[p.o:] {
150 if c == 0 || c == ',' {
152 p.xerrorf("invalid empty value")
154 r := p.s[p.o : p.o+o]
165func (p *parser) xbase64() []byte {
167 for ; o < len(p.s); o++ {
169 if !(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '/' || c == '+' || c == '=') {
173 buf, err := base64.StdEncoding.DecodeString(p.s[p.o:o])
174 p.xcheckf(err, "decoding base64")
179func (p *parser) xsaslname() string {
183 for o, c := range p.s[p.o:] {
184 if c == 0 || c == ',' {
186 p.xerrorf("saslname unexpected end")
189 p.xerrorf("saslname cannot be empty")
205 p.xerrorf("bad escape %q in saslanem", esc)
217 p.xerrorf("saslname unexpected end")
220 p.xerrorf("saslname cannot be empty")
227func (p *parser) xcbname() string {
229 for ; o < len(p.s); o++ {
231 if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '.' || c == '-' {
237 p.xerrorf("empty channel binding name")
244func (p *parser) xchannelBinding() []byte {
249func (p *parser) xproof() []byte {
254func (p *parser) xsalt() []byte {
259func (p *parser) xtakefn1(fn func(rune, int) bool) string {
260 for o, c := range p.s[p.o:] {
263 p.xerrorf("non-empty match required")
265 r := p.s[p.o : p.o+o]
276func (p *parser) xiterations() int {
278 digits := p.xtakefn1(func(c rune, i int) bool {
279 return c >= '1' && c <= '9' || i > 0 && c == '0'
281 v, err := strconv.ParseInt(digits, 10, 32)
282 p.xcheckf(err, "parsing int")