blob: 27faa6994bdc2abfcc12cade7c7b7398db31ea63 [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
Herbie Ong8fa64d92019-04-05 20:45:07 -0700130// numberParts. The slice fields in numberParts are subslices of the input.
Herbie Onga3421952019-03-21 18:12:26 -0700131func 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':
Herbie Ong8fa64d92019-04-05 20:45:07 -0700158 intp = s
159 n := 1
Herbie Onga3421952019-03-21 18:12:26 -0700160 s = s[1:]
161 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
Herbie Onga3421952019-03-21 18:12:26 -0700162 s = s[1:]
Herbie Ong8fa64d92019-04-05 20:45:07 -0700163 n++
Herbie Onga3421952019-03-21 18:12:26 -0700164 }
Herbie Ong8fa64d92019-04-05 20:45:07 -0700165 intp = intp[:n]
Herbie Onga3421952019-03-21 18:12:26 -0700166
167 default:
168 return numberParts{}, false
169 }
170
171 // . followed by 1 or more digits.
172 if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
Herbie Ong8fa64d92019-04-05 20:45:07 -0700173 frac = s[1:]
174 n := 1
Herbie Onga3421952019-03-21 18:12:26 -0700175 s = s[2:]
176 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
Herbie Onga3421952019-03-21 18:12:26 -0700177 s = s[1:]
Herbie Ong8fa64d92019-04-05 20:45:07 -0700178 n++
Herbie Onga3421952019-03-21 18:12:26 -0700179 }
Herbie Ong8fa64d92019-04-05 20:45:07 -0700180 frac = frac[:n]
Herbie Onga3421952019-03-21 18:12:26 -0700181 }
182
183 // e or E followed by an optional - or + and
184 // 1 or more digits.
185 if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
186 s = s[1:]
Herbie Ong8fa64d92019-04-05 20:45:07 -0700187 exp = s
188 n := 0
Herbie Onga3421952019-03-21 18:12:26 -0700189 if s[0] == '+' || s[0] == '-' {
Herbie Onga3421952019-03-21 18:12:26 -0700190 s = s[1:]
Herbie Ong8fa64d92019-04-05 20:45:07 -0700191 n++
Herbie Onga3421952019-03-21 18:12:26 -0700192 if len(s) == 0 {
193 return numberParts{}, false
194 }
195 }
196 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
Herbie Onga3421952019-03-21 18:12:26 -0700197 s = s[1:]
Herbie Ong8fa64d92019-04-05 20:45:07 -0700198 n++
Herbie Onga3421952019-03-21 18:12:26 -0700199 }
Herbie Ong8fa64d92019-04-05 20:45:07 -0700200 exp = exp[:n]
Herbie Onga3421952019-03-21 18:12:26 -0700201 }
202
203 return numberParts{
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800204 neg: neg,
205 intp: intp,
206 frac: bytes.TrimRight(frac, "0"), // Remove unnecessary 0s to the right.
207 exp: exp,
Herbie Onga3421952019-03-21 18:12:26 -0700208 }, true
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800209}
210
211// normalizeToIntString returns an integer string in normal form without the
212// E-notation for given numberParts. It will return false if it is not an
213// integer or if the exponent exceeds than max/min int value.
Herbie Onga3421952019-03-21 18:12:26 -0700214func normalizeToIntString(n numberParts) (string, bool) {
Herbie Ong8fa64d92019-04-05 20:45:07 -0700215 intpSize := len(n.intp)
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800216 fracSize := len(n.frac)
217
218 if intpSize == 0 && fracSize == 0 {
219 return "0", true
220 }
221
222 var exp int
223 if len(n.exp) > 0 {
224 i, err := strconv.ParseInt(string(n.exp), 10, 32)
225 if err != nil {
226 return "", false
227 }
228 exp = int(i)
229 }
230
Herbie Ong8fa64d92019-04-05 20:45:07 -0700231 var num []byte
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800232 if exp >= 0 {
233 // For positive E, shift fraction digits into integer part and also pad
234 // with zeroes as needed.
235
Herbie Ong8fa64d92019-04-05 20:45:07 -0700236 // If there are more digits in fraction than the E value, then the
237 // number is not an integer.
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800238 if fracSize > exp {
239 return "", false
240 }
241
Herbie Ong8fa64d92019-04-05 20:45:07 -0700242 // Set cap to make a copy of integer part when appended.
243 num = n.intp[:len(n.intp):len(n.intp)]
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800244 num = append(num, n.frac...)
245 for i := 0; i < exp-fracSize; i++ {
246 num = append(num, '0')
247 }
248
249 } else {
250 // For negative E, shift digits in integer part out.
251
Herbie Ong8fa64d92019-04-05 20:45:07 -0700252 // If there are fractions, then the number is not an integer.
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800253 if fracSize > 0 {
254 return "", false
255 }
256
Herbie Ong8fa64d92019-04-05 20:45:07 -0700257 // index is where the decimal point will be after adjusting for negative
258 // exponent.
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800259 index := intpSize + exp
260 if index < 0 {
261 return "", false
262 }
Herbie Ong8fa64d92019-04-05 20:45:07 -0700263
264 num = n.intp
265 // If any of the digits being shifted to the right of the decimal point
266 // is non-zero, then the number is not an integer.
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800267 for i := index; i < intpSize; i++ {
268 if num[i] != '0' {
269 return "", false
270 }
271 }
272 num = num[:index]
273 }
274
275 if n.neg {
276 return "-" + string(num), true
277 }
278 return string(num), true
Joe Tsai879b18d2018-08-03 17:22:24 -0700279}