blob: 2999d71332021a8e913c27d78544fd59820af30c [file] [log] [blame]
khenaidood948f772021-08-11 17:49:24 -04001// Copyright 2018 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
5package json
6
7import (
8 "bytes"
9 "strconv"
10)
11
12// parseNumber reads the given []byte for a valid JSON number. If it is valid,
13// it returns the number of bytes. Parsing logic follows the definition in
14// https://tools.ietf.org/html/rfc7159#section-6, and is based off
15// encoding/json.isValidNumber function.
16func parseNumber(input []byte) (int, bool) {
17 var n int
18
19 s := input
20 if len(s) == 0 {
21 return 0, false
22 }
23
24 // Optional -
25 if s[0] == '-' {
26 s = s[1:]
27 n++
28 if len(s) == 0 {
29 return 0, false
30 }
31 }
32
33 // Digits
34 switch {
35 case s[0] == '0':
36 s = s[1:]
37 n++
38
39 case '1' <= s[0] && s[0] <= '9':
40 s = s[1:]
41 n++
42 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
43 s = s[1:]
44 n++
45 }
46
47 default:
48 return 0, false
49 }
50
51 // . followed by 1 or more digits.
52 if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
53 s = s[2:]
54 n += 2
55 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
56 s = s[1:]
57 n++
58 }
59 }
60
61 // e or E followed by an optional - or + and
62 // 1 or more digits.
63 if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
64 s = s[1:]
65 n++
66 if s[0] == '+' || s[0] == '-' {
67 s = s[1:]
68 n++
69 if len(s) == 0 {
70 return 0, false
71 }
72 }
73 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
74 s = s[1:]
75 n++
76 }
77 }
78
79 // Check that next byte is a delimiter or it is at the end.
80 if n < len(input) && isNotDelim(input[n]) {
81 return 0, false
82 }
83
84 return n, true
85}
86
87// numberParts is the result of parsing out a valid JSON number. It contains
88// the parts of a number. The parts are used for integer conversion.
89type numberParts struct {
90 neg bool
91 intp []byte
92 frac []byte
93 exp []byte
94}
95
96// parseNumber constructs numberParts from given []byte. The logic here is
97// similar to consumeNumber above with the difference of having to construct
98// numberParts. The slice fields in numberParts are subslices of the input.
99func parseNumberParts(input []byte) (numberParts, bool) {
100 var neg bool
101 var intp []byte
102 var frac []byte
103 var exp []byte
104
105 s := input
106 if len(s) == 0 {
107 return numberParts{}, false
108 }
109
110 // Optional -
111 if s[0] == '-' {
112 neg = true
113 s = s[1:]
114 if len(s) == 0 {
115 return numberParts{}, false
116 }
117 }
118
119 // Digits
120 switch {
121 case s[0] == '0':
122 // Skip first 0 and no need to store.
123 s = s[1:]
124
125 case '1' <= s[0] && s[0] <= '9':
126 intp = s
127 n := 1
128 s = s[1:]
129 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
130 s = s[1:]
131 n++
132 }
133 intp = intp[:n]
134
135 default:
136 return numberParts{}, false
137 }
138
139 // . followed by 1 or more digits.
140 if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
141 frac = s[1:]
142 n := 1
143 s = s[2:]
144 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
145 s = s[1:]
146 n++
147 }
148 frac = frac[:n]
149 }
150
151 // e or E followed by an optional - or + and
152 // 1 or more digits.
153 if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
154 s = s[1:]
155 exp = s
156 n := 0
157 if s[0] == '+' || s[0] == '-' {
158 s = s[1:]
159 n++
160 if len(s) == 0 {
161 return numberParts{}, false
162 }
163 }
164 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
165 s = s[1:]
166 n++
167 }
168 exp = exp[:n]
169 }
170
171 return numberParts{
172 neg: neg,
173 intp: intp,
174 frac: bytes.TrimRight(frac, "0"), // Remove unnecessary 0s to the right.
175 exp: exp,
176 }, true
177}
178
179// normalizeToIntString returns an integer string in normal form without the
180// E-notation for given numberParts. It will return false if it is not an
181// integer or if the exponent exceeds than max/min int value.
182func normalizeToIntString(n numberParts) (string, bool) {
183 intpSize := len(n.intp)
184 fracSize := len(n.frac)
185
186 if intpSize == 0 && fracSize == 0 {
187 return "0", true
188 }
189
190 var exp int
191 if len(n.exp) > 0 {
192 i, err := strconv.ParseInt(string(n.exp), 10, 32)
193 if err != nil {
194 return "", false
195 }
196 exp = int(i)
197 }
198
199 var num []byte
200 if exp >= 0 {
201 // For positive E, shift fraction digits into integer part and also pad
202 // with zeroes as needed.
203
204 // If there are more digits in fraction than the E value, then the
205 // number is not an integer.
206 if fracSize > exp {
207 return "", false
208 }
209
210 // Make sure resulting digits are within max value limit to avoid
211 // unnecessarily constructing a large byte slice that may simply fail
212 // later on.
213 const maxDigits = 20 // Max uint64 value has 20 decimal digits.
214 if intpSize+exp > maxDigits {
215 return "", false
216 }
217
218 // Set cap to make a copy of integer part when appended.
219 num = n.intp[:len(n.intp):len(n.intp)]
220 num = append(num, n.frac...)
221 for i := 0; i < exp-fracSize; i++ {
222 num = append(num, '0')
223 }
224 } else {
225 // For negative E, shift digits in integer part out.
226
227 // If there are fractions, then the number is not an integer.
228 if fracSize > 0 {
229 return "", false
230 }
231
232 // index is where the decimal point will be after adjusting for negative
233 // exponent.
234 index := intpSize + exp
235 if index < 0 {
236 return "", false
237 }
238
239 num = n.intp
240 // If any of the digits being shifted to the right of the decimal point
241 // is non-zero, then the number is not an integer.
242 for i := index; i < intpSize; i++ {
243 if num[i] != '0' {
244 return "", false
245 }
246 }
247 num = num[:index]
248 }
249
250 if n.neg {
251 return "-" + string(num), true
252 }
253 return string(num), true
254}