1
2
3
4
5 package json_test
6
7 import (
8 "regexp"
9 "testing"
10
11 "github.com/goccy/go-json"
12 )
13
14 func TestNumberIsValid(t *testing.T) {
15
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
90
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
102
103
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
123 func isValidNumber(s string) bool {
124
125
126
127
128 if s == "" {
129 return false
130 }
131
132
133 if s[0] == '-' {
134 s = s[1:]
135 if s == "" {
136 return false
137 }
138 }
139
140
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
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
164
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
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 }
196
View as plain text