All checks were successful
Create and publish a Docker image 🚀 / build-and-push-image (push) Successful in 1m49s
121 lines
2.2 KiB
Go
121 lines
2.2 KiB
Go
package httpsfv
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"strconv"
|
|
)
|
|
|
|
const maxDigit = 12
|
|
|
|
// ErrNotDigit is returned when a character should be a digit but isn't.
|
|
var ErrNotDigit = errors.New("character is not a digit")
|
|
|
|
// ErrNumberOutOfRange is returned when the number is too large according to the specification.
|
|
var ErrNumberOutOfRange = errors.New("integer or decimal out of range")
|
|
|
|
// ErrInvalidDecimalFormat is returned when the decimal format is invalid.
|
|
var ErrInvalidDecimalFormat = errors.New("invalid decimal format")
|
|
|
|
const (
|
|
typeInteger = iota
|
|
typeDecimal
|
|
)
|
|
|
|
// marshalInteger serializes as defined in
|
|
// https://httpwg.org/specs/rfc9651.html#integer.
|
|
func marshalInteger(b io.StringWriter, i int64) error {
|
|
if i < -999999999999999 || i > 999999999999999 {
|
|
return ErrNumberOutOfRange
|
|
}
|
|
|
|
_, err := b.WriteString(strconv.FormatInt(i, 10))
|
|
|
|
return err
|
|
}
|
|
|
|
// parseNumber parses as defined in
|
|
// https://httpwg.org/specs/rfc9651.html#parse-number.
|
|
func parseNumber(s *scanner) (interface{}, error) {
|
|
neg := isNeg(s)
|
|
if neg && s.eof() {
|
|
return 0, &UnmarshalError{s.off, ErrUnexpectedEndOfString}
|
|
}
|
|
|
|
if !isDigit(s.data[s.off]) {
|
|
return 0, &UnmarshalError{s.off, ErrNotDigit}
|
|
}
|
|
|
|
start := s.off
|
|
s.off++
|
|
|
|
var (
|
|
decSepOff int
|
|
t = typeInteger
|
|
)
|
|
|
|
for s.off < len(s.data) {
|
|
size := s.off - start
|
|
if (t == typeInteger && (size >= 15)) || size >= 16 {
|
|
return 0, &UnmarshalError{s.off, ErrNumberOutOfRange}
|
|
}
|
|
|
|
c := s.data[s.off]
|
|
if isDigit(c) {
|
|
s.off++
|
|
|
|
continue
|
|
}
|
|
|
|
if t == typeInteger && c == '.' {
|
|
if size > maxDigit {
|
|
return 0, &UnmarshalError{s.off, ErrNumberOutOfRange}
|
|
}
|
|
|
|
t = typeDecimal
|
|
decSepOff = s.off
|
|
s.off++
|
|
|
|
continue
|
|
}
|
|
|
|
break
|
|
}
|
|
|
|
str := s.data[start:s.off]
|
|
|
|
if t == typeInteger {
|
|
return parseInteger(str, neg, s.off)
|
|
}
|
|
|
|
return parseDecimal(s, decSepOff, str, neg)
|
|
}
|
|
|
|
func isNeg(s *scanner) bool {
|
|
if s.data[s.off] == '-' {
|
|
s.off++
|
|
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func parseInteger(str string, neg bool, off int) (int64, error) {
|
|
i, err := strconv.ParseInt(str, 10, 64)
|
|
if err != nil {
|
|
// Should never happen
|
|
return 0, &UnmarshalError{off, err}
|
|
}
|
|
|
|
if neg {
|
|
i = -i
|
|
}
|
|
|
|
if i < -999999999999999 || i > 999999999999999 {
|
|
return 0, &UnmarshalError{off, ErrNumberOutOfRange}
|
|
}
|
|
|
|
return i, err
|
|
}
|