parser: improve floats validation (#636)
This commit is contained in:
@@ -219,7 +219,7 @@ func parseFloat(b []byte) (float64, error) {
|
|||||||
return math.NaN(), nil
|
return math.NaN(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cleaned, err := checkAndRemoveUnderscores(b)
|
cleaned, err := checkAndRemoveUnderscoresFloats(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -232,6 +232,30 @@ func parseFloat(b []byte) (float64, error) {
|
|||||||
return 0, newDecodeError(b, "float cannot end with a dot")
|
return 0, newDecodeError(b, "float cannot end with a dot")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dotAlreadySeen := false
|
||||||
|
for i, c := range cleaned {
|
||||||
|
if c == '.' {
|
||||||
|
if dotAlreadySeen {
|
||||||
|
return 0, newDecodeError(b[i:i+1], "float can have at most one decimal point")
|
||||||
|
}
|
||||||
|
if !isDigit(cleaned[i-1]) {
|
||||||
|
return 0, newDecodeError(b[i-1:i+1], "float decimal point must be preceded by a digit")
|
||||||
|
}
|
||||||
|
if !isDigit(cleaned[i+1]) {
|
||||||
|
return 0, newDecodeError(b[i:i+2], "float decimal point must be followed by a digit")
|
||||||
|
}
|
||||||
|
dotAlreadySeen = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start := 0
|
||||||
|
if b[0] == '+' || b[0] == '-' {
|
||||||
|
start = 1
|
||||||
|
}
|
||||||
|
if b[start] == '0' && isDigit(b[start+1]) {
|
||||||
|
return 0, newDecodeError(b, "float integer part cannot have leading zeroes")
|
||||||
|
}
|
||||||
|
|
||||||
f, err := strconv.ParseFloat(string(cleaned), 64)
|
f, err := strconv.ParseFloat(string(cleaned), 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, newDecodeError(b, "unable to parse float: %w", err)
|
return 0, newDecodeError(b, "unable to parse float: %w", err)
|
||||||
@@ -241,7 +265,7 @@ func parseFloat(b []byte) (float64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseIntHex(b []byte) (int64, error) {
|
func parseIntHex(b []byte) (int64, error) {
|
||||||
cleaned, err := checkAndRemoveUnderscores(b[2:])
|
cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -255,7 +279,7 @@ func parseIntHex(b []byte) (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseIntOct(b []byte) (int64, error) {
|
func parseIntOct(b []byte) (int64, error) {
|
||||||
cleaned, err := checkAndRemoveUnderscores(b[2:])
|
cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -269,7 +293,7 @@ func parseIntOct(b []byte) (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseIntBin(b []byte) (int64, error) {
|
func parseIntBin(b []byte) (int64, error) {
|
||||||
cleaned, err := checkAndRemoveUnderscores(b[2:])
|
cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -283,7 +307,7 @@ func parseIntBin(b []byte) (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseIntDec(b []byte) (int64, error) {
|
func parseIntDec(b []byte) (int64, error) {
|
||||||
cleaned, err := checkAndRemoveUnderscores(b)
|
cleaned, err := checkAndRemoveUnderscoresIntegers(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -296,7 +320,7 @@ func parseIntDec(b []byte) (int64, error) {
|
|||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkAndRemoveUnderscores(b []byte) ([]byte, error) {
|
func checkAndRemoveUnderscoresIntegers(b []byte) ([]byte, error) {
|
||||||
if b[0] == '_' {
|
if b[0] == '_' {
|
||||||
return nil, newDecodeError(b[0:1], "number cannot start with underscore")
|
return nil, newDecodeError(b[0:1], "number cannot start with underscore")
|
||||||
}
|
}
|
||||||
@@ -336,6 +360,60 @@ func checkAndRemoveUnderscores(b []byte) ([]byte, error) {
|
|||||||
return cleaned, nil
|
return cleaned, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkAndRemoveUnderscoresFloats(b []byte) ([]byte, error) {
|
||||||
|
if b[0] == '_' {
|
||||||
|
return nil, newDecodeError(b[0:1], "number cannot start with underscore")
|
||||||
|
}
|
||||||
|
|
||||||
|
if b[len(b)-1] == '_' {
|
||||||
|
return nil, newDecodeError(b[len(b)-1:], "number cannot end with underscore")
|
||||||
|
}
|
||||||
|
|
||||||
|
// fast path
|
||||||
|
i := 0
|
||||||
|
for ; i < len(b); i++ {
|
||||||
|
if b[i] == '_' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i == len(b) {
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
before := false
|
||||||
|
cleaned := make([]byte, 0, len(b))
|
||||||
|
|
||||||
|
for i := 0; i < len(b); i++ {
|
||||||
|
c := b[i]
|
||||||
|
|
||||||
|
switch c {
|
||||||
|
case '_':
|
||||||
|
if !before {
|
||||||
|
return nil, newDecodeError(b[i-1:i+1], "number must have at least one digit between underscores")
|
||||||
|
}
|
||||||
|
before = false
|
||||||
|
case 'e', 'E':
|
||||||
|
if i < len(b)-1 && b[i+1] == '_' {
|
||||||
|
return nil, newDecodeError(b[i+1:i+2], "cannot have underscore after exponent")
|
||||||
|
}
|
||||||
|
cleaned = append(cleaned, c)
|
||||||
|
case '.':
|
||||||
|
if i < len(b)-1 && b[i+1] == '_' {
|
||||||
|
return nil, newDecodeError(b[i+1:i+2], "cannot have underscore after decimal point")
|
||||||
|
}
|
||||||
|
if i > 0 && b[i-1] == '_' {
|
||||||
|
return nil, newDecodeError(b[i-1:i], "cannot have underscore before decimal point")
|
||||||
|
}
|
||||||
|
cleaned = append(cleaned, c)
|
||||||
|
default:
|
||||||
|
before = true
|
||||||
|
cleaned = append(cleaned, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned, nil
|
||||||
|
}
|
||||||
|
|
||||||
// isValidDate checks if a provided date is a date that exists.
|
// isValidDate checks if a provided date is a date that exists.
|
||||||
func isValidDate(year int, month int, day int) bool {
|
func isValidDate(year int, month int, day int) bool {
|
||||||
return day <= daysIn(month, year)
|
return day <= daysIn(month, year)
|
||||||
|
|||||||
@@ -266,7 +266,6 @@ func TestTOMLTest_Invalid_Float_ExpDoubleUs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTOMLTest_Invalid_Float_ExpLeadingUs(t *testing.T) {
|
func TestTOMLTest_Invalid_Float_ExpLeadingUs(t *testing.T) {
|
||||||
t.Skip("FIXME")
|
|
||||||
input := "exp-leading-us = 1e_23\n"
|
input := "exp-leading-us = 1e_23\n"
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
@@ -277,7 +276,6 @@ func TestTOMLTest_Invalid_Float_ExpPoint1(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTOMLTest_Invalid_Float_ExpPoint2(t *testing.T) {
|
func TestTOMLTest_Invalid_Float_ExpPoint2(t *testing.T) {
|
||||||
t.Skip("FIXME")
|
|
||||||
input := "exp-point-2 = 1.e2\n"
|
input := "exp-point-2 = 1.e2\n"
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
@@ -308,13 +306,11 @@ func TestTOMLTest_Invalid_Float_Inf_underscore(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTOMLTest_Invalid_Float_LeadingPointNeg(t *testing.T) {
|
func TestTOMLTest_Invalid_Float_LeadingPointNeg(t *testing.T) {
|
||||||
t.Skip("FIXME")
|
|
||||||
input := "leading-point-neg = -.12345\n"
|
input := "leading-point-neg = -.12345\n"
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTOMLTest_Invalid_Float_LeadingPointPlus(t *testing.T) {
|
func TestTOMLTest_Invalid_Float_LeadingPointPlus(t *testing.T) {
|
||||||
t.Skip("FIXME")
|
|
||||||
input := "leading-point-plus = +.12345\n"
|
input := "leading-point-plus = +.12345\n"
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
@@ -330,13 +326,11 @@ func TestTOMLTest_Invalid_Float_LeadingUs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTOMLTest_Invalid_Float_LeadingZeroNeg(t *testing.T) {
|
func TestTOMLTest_Invalid_Float_LeadingZeroNeg(t *testing.T) {
|
||||||
t.Skip("FIXME")
|
|
||||||
input := "leading-zero-neg = -03.14\n"
|
input := "leading-zero-neg = -03.14\n"
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTOMLTest_Invalid_Float_LeadingZeroPlus(t *testing.T) {
|
func TestTOMLTest_Invalid_Float_LeadingZeroPlus(t *testing.T) {
|
||||||
t.Skip("FIXME")
|
|
||||||
input := "leading-zero-plus = +03.14\n"
|
input := "leading-zero-plus = +03.14\n"
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
@@ -387,13 +381,11 @@ func TestTOMLTest_Invalid_Float_TrailingUs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTOMLTest_Invalid_Float_UsAfterPoint(t *testing.T) {
|
func TestTOMLTest_Invalid_Float_UsAfterPoint(t *testing.T) {
|
||||||
t.Skip("FIXME")
|
|
||||||
input := "us-after-point = 1._2\n"
|
input := "us-after-point = 1._2\n"
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTOMLTest_Invalid_Float_UsBeforePoint(t *testing.T) {
|
func TestTOMLTest_Invalid_Float_UsBeforePoint(t *testing.T) {
|
||||||
t.Skip("FIXME")
|
|
||||||
input := "us-before-point = 1_.2\n"
|
input := "us-before-point = 1_.2\n"
|
||||||
testgenInvalid(t, input)
|
testgenInvalid(t, input)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user