blob: 75f6a3a60c8a43689fa07c08490d5fb684dfd026 [file] [log] [blame]
Joe Tsai879b18d2018-08-03 17:22:24 -07001// 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 (
Herbie Ongd3f8f2d2019-03-06 00:28:23 -08008 "bytes"
Joe Tsai879b18d2018-08-03 17:22:24 -07009 "math"
Joe Tsai879b18d2018-08-03 17:22:24 -070010 "strconv"
Joe Tsai879b18d2018-08-03 17:22:24 -070011)
12
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080013// appendFloat formats given float in bitSize, and appends to the given []byte.
14func appendFloat(out []byte, n float64, bitSize int) []byte {
15 switch {
16 case math.IsNaN(n):
17 return append(out, `"NaN"`...)
18 case math.IsInf(n, +1):
19 return append(out, `"Infinity"`...)
20 case math.IsInf(n, -1):
21 return append(out, `"-Infinity"`...)
Joe Tsai879b18d2018-08-03 17:22:24 -070022 }
23
24 // JSON number formatting logic based on encoding/json.
25 // See floatEncoder.encode for reference.
Joe Tsai879b18d2018-08-03 17:22:24 -070026 fmt := byte('f')
27 if abs := math.Abs(n); abs != 0 {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080028 if bitSize == 64 && (abs < 1e-6 || abs >= 1e21) ||
29 bitSize == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
Joe Tsai879b18d2018-08-03 17:22:24 -070030 fmt = 'e'
31 }
32 }
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080033 out = strconv.AppendFloat(out, n, fmt, -1, bitSize)
Joe Tsai879b18d2018-08-03 17:22:24 -070034 if fmt == 'e' {
35 n := len(out)
36 if n >= 4 && out[n-4] == 'e' && out[n-3] == '-' && out[n-2] == '0' {
37 out[n-2] = out[n-1]
38 out = out[:n-1]
39 }
40 }
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080041 return out
Joe Tsai879b18d2018-08-03 17:22:24 -070042}
43
Herbie Onga3421952019-03-21 18:12:26 -070044// consumeNumber reads the given []byte for a valid JSON number. If it is valid,
45// it returns the number of bytes. Parsing logic follows the definition in
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080046// https://tools.ietf.org/html/rfc7159#section-6, and is based off
47// encoding/json.isValidNumber function.
Herbie Onga3421952019-03-21 18:12:26 -070048func consumeNumber(input []byte) (int, bool) {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080049 var n int
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080050
51 s := input
52 if len(s) == 0 {
Herbie Onga3421952019-03-21 18:12:26 -070053 return 0, false
Joe Tsai879b18d2018-08-03 17:22:24 -070054 }
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080055
56 // Optional -
57 if s[0] == '-' {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080058 s = s[1:]
59 n++
60 if len(s) == 0 {
Herbie Onga3421952019-03-21 18:12:26 -070061 return 0, false
Joe Tsai879b18d2018-08-03 17:22:24 -070062 }
Joe Tsai879b18d2018-08-03 17:22:24 -070063 }
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080064
65 // Digits
66 switch {
67 case s[0] == '0':
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080068 s = s[1:]
69 n++
70
71 case '1' <= s[0] && s[0] <= '9':
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080072 s = s[1:]
73 n++
74 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080075 s = s[1:]
76 n++
77 }
78
79 default:
Herbie Onga3421952019-03-21 18:12:26 -070080 return 0, false
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080081 }
82
83 // . followed by 1 or more digits.
84 if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080085 s = s[2:]
86 n += 2
87 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080088 s = s[1:]
89 n++
90 }
91 }
92
93 // e or E followed by an optional - or + and
94 // 1 or more digits.
95 if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
96 s = s[1:]
97 n++
98 if s[0] == '+' || s[0] == '-' {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080099 s = s[1:]
100 n++
101 if len(s) == 0 {
Herbie Onga3421952019-03-21 18:12:26 -0700102 return 0, false
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800103 }
104 }
105 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800106 s = s[1:]
107 n++
108 }
109 }
110
111 // Check that next byte is a delimiter or it is at the end.
112 if n < len(input) && isNotDelim(input[n]) {
Herbie Onga3421952019-03-21 18:12:26 -0700113 return 0, false
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800114 }
115
Herbie Onga3421952019-03-21 18:12:26 -0700116 return n, true
117}
118
119// numberParts is the result of parsing out a valid JSON number. It contains
120// the parts of a number. The parts are used for integer conversion.
121type numberParts struct {
122 neg bool
123 intp []byte
124 frac []byte
125 exp []byte
126}
127
128// parseNumber constructs numberParts from given []byte. The logic here is
129// similar to consumeNumber above with the difference of having to construct
130// numberParts.
131func parseNumber(input []byte) (numberParts, bool) {
132 var neg bool
133 var intp []byte
134 var frac []byte
135 var exp []byte
136
137 s := input
138 if len(s) == 0 {
139 return numberParts{}, false
140 }
141
142 // Optional -
143 if s[0] == '-' {
144 neg = true
145 s = s[1:]
146 if len(s) == 0 {
147 return numberParts{}, false
148 }
149 }
150
151 // Digits
152 switch {
153 case s[0] == '0':
154 // Skip first 0 and no need to store.
155 s = s[1:]
156
157 case '1' <= s[0] && s[0] <= '9':
158 intp = append(intp, s[0])
159 s = s[1:]
160 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
161 intp = append(intp, s[0])
162 s = s[1:]
163 }
164
165 default:
166 return numberParts{}, false
167 }
168
169 // . followed by 1 or more digits.
170 if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
171 frac = append(frac, s[1])
172 s = s[2:]
173 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
174 frac = append(frac, s[0])
175 s = s[1:]
176 }
177 }
178
179 // e or E followed by an optional - or + and
180 // 1 or more digits.
181 if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
182 s = s[1:]
183 if s[0] == '+' || s[0] == '-' {
184 exp = append(exp, s[0])
185 s = s[1:]
186 if len(s) == 0 {
187 return numberParts{}, false
188 }
189 }
190 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
191 exp = append(exp, s[0])
192 s = s[1:]
193 }
194 }
195
196 return numberParts{
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800197 neg: neg,
198 intp: intp,
199 frac: bytes.TrimRight(frac, "0"), // Remove unnecessary 0s to the right.
200 exp: exp,
Herbie Onga3421952019-03-21 18:12:26 -0700201 }, true
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800202}
203
204// normalizeToIntString returns an integer string in normal form without the
205// E-notation for given numberParts. It will return false if it is not an
206// integer or if the exponent exceeds than max/min int value.
Herbie Onga3421952019-03-21 18:12:26 -0700207func normalizeToIntString(n numberParts) (string, bool) {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800208 num := n.intp
209 intpSize := len(num)
210 fracSize := len(n.frac)
211
212 if intpSize == 0 && fracSize == 0 {
213 return "0", true
214 }
215
216 var exp int
217 if len(n.exp) > 0 {
218 i, err := strconv.ParseInt(string(n.exp), 10, 32)
219 if err != nil {
220 return "", false
221 }
222 exp = int(i)
223 }
224
225 if exp >= 0 {
226 // For positive E, shift fraction digits into integer part and also pad
227 // with zeroes as needed.
228
229 // If there are more digits in fraction than the E value, then number is
230 // not an integer.
231 if fracSize > exp {
232 return "", false
233 }
234
235 num = append(num, n.frac...)
236 for i := 0; i < exp-fracSize; i++ {
237 num = append(num, '0')
238 }
239
240 } else {
241 // For negative E, shift digits in integer part out.
242
243 // If there are any fractions to begin with, then number is not an
244 // integer.
245 if fracSize > 0 {
246 return "", false
247 }
248
249 index := intpSize + exp
250 if index < 0 {
251 return "", false
252 }
253 // If any of the digits being shifted out is non-zero, then number is
254 // not an integer.
255 for i := index; i < intpSize; i++ {
256 if num[i] != '0' {
257 return "", false
258 }
259 }
260 num = num[:index]
261 }
262
263 if n.neg {
264 return "-" + string(num), true
265 }
266 return string(num), true
Joe Tsai879b18d2018-08-03 17:22:24 -0700267}