github.com/grbit/go-json@v0.11.0/number_test.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package json_test 6 7 import ( 8 "regexp" 9 "testing" 10 11 "github.com/grbit/go-json" 12 ) 13 14 func TestNumberIsValid(t *testing.T) { 15 // From: https://stackoverflow.com/a/13340826 16 var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`) 17 18 validTests := []string{ 19 "0", 20 "-0", 21 "1", 22 "-1", 23 "0.1", 24 "-0.1", 25 "1234", 26 "-1234", 27 "12.34", 28 "-12.34", 29 "12E0", 30 "12E1", 31 "12e34", 32 "12E-0", 33 "12e+1", 34 "12e-34", 35 "-12E0", 36 "-12E1", 37 "-12e34", 38 "-12E-0", 39 "-12e+1", 40 "-12e-34", 41 "1.2E0", 42 "1.2E1", 43 "1.2e34", 44 "1.2E-0", 45 "1.2e+1", 46 "1.2e-34", 47 "-1.2E0", 48 "-1.2E1", 49 "-1.2e34", 50 "-1.2E-0", 51 "-1.2e+1", 52 "-1.2e-34", 53 "0E0", 54 "0E1", 55 "0e34", 56 "0E-0", 57 "0e+1", 58 "0e-34", 59 "-0E0", 60 "-0E1", 61 "-0e34", 62 "-0E-0", 63 "-0e+1", 64 "-0e-34", 65 } 66 67 for i, test := range validTests { 68 if !isValidNumber(test) { 69 t.Errorf("%d: %s should be valid", i, test) 70 } 71 72 var f float64 73 if err := json.Unmarshal([]byte(test), &f); err != nil { 74 t.Errorf("%d: %s should be valid but Unmarshal failed: %v", i, test, err) 75 } 76 77 if !jsonNumberRegexp.MatchString(test) { 78 t.Errorf("%d: %s should be valid but regexp does not match", i, test) 79 } 80 } 81 82 invalidTests := []string{ 83 "", 84 "invalid", 85 "1.0.1", 86 "1..1", 87 "-1-2", 88 "012a42", 89 //"01.2", 90 //"012", 91 "12E12.12", 92 "1e2e3", 93 "1e+-2", 94 "1e--23", 95 "1e", 96 "e1", 97 "1e+", 98 "1ea", 99 "1a", 100 "1.a", 101 //"1.", 102 //"01", 103 //"1.e1", 104 } 105 106 for i, test := range invalidTests { 107 if isValidNumber(test) { 108 t.Errorf("%d: %s should be invalid", i, test) 109 } 110 111 var f float64 112 if err := json.Unmarshal([]byte(test), &f); err == nil { 113 t.Errorf("%d: %s should be invalid but unmarshal wrote %v", i, test, f) 114 } 115 116 if jsonNumberRegexp.MatchString(test) { 117 t.Errorf("%d: %s should be invalid but matches regexp", i, test) 118 } 119 } 120 } 121 122 // isValidNumber reports whether s is a valid JSON number literal. 123 func isValidNumber(s string) bool { 124 // This function implements the JSON numbers grammar. 125 // See https://tools.ietf.org/html/rfc7159#section-6 126 // and https://www.json.org/img/number.png 127 128 if s == "" { 129 return false 130 } 131 132 // Optional - 133 if s[0] == '-' { 134 s = s[1:] 135 if s == "" { 136 return false 137 } 138 } 139 140 // Digits 141 switch { 142 default: 143 return false 144 145 case s[0] == '0': 146 s = s[1:] 147 148 case '1' <= s[0] && s[0] <= '9': 149 s = s[1:] 150 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { 151 s = s[1:] 152 } 153 } 154 155 // . followed by 1 or more digits. 156 if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' { 157 s = s[2:] 158 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { 159 s = s[1:] 160 } 161 } 162 163 // e or E followed by an optional - or + and 164 // 1 or more digits. 165 if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') { 166 s = s[1:] 167 if s[0] == '+' || s[0] == '-' { 168 s = s[1:] 169 if s == "" { 170 return false 171 } 172 } 173 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { 174 s = s[1:] 175 } 176 } 177 178 // Make sure we are at the end. 179 return s == "" 180 } 181 182 func BenchmarkNumberIsValid(b *testing.B) { 183 s := "-61657.61667E+61673" 184 for i := 0; i < b.N; i++ { 185 isValidNumber(s) 186 } 187 } 188 189 func BenchmarkNumberIsValidRegexp(b *testing.B) { 190 var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`) 191 s := "-61657.61667E+61673" 192 for i := 0; i < b.N; i++ { 193 jsonNumberRegexp.MatchString(s) 194 } 195 }