blob: a695689291611f2c87016e3f0617a8b8ad0f7df1 [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 "strconv"
Joe Tsai879b18d2018-08-03 17:22:24 -070010)
11
Herbie Ongd2ece132020-01-07 16:45:24 -080012// parseNumber reads the given []byte for a valid JSON number. If it is valid,
Herbie Onga3421952019-03-21 18:12:26 -070013// it returns the number of bytes. Parsing logic follows the definition in
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080014// https://tools.ietf.org/html/rfc7159#section-6, and is based off
15// encoding/json.isValidNumber function.
Herbie Ongd2ece132020-01-07 16:45:24 -080016func parseNumber(input []byte) (int, bool) {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080017 var n int
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080018
19 s := input
20 if len(s) == 0 {
Herbie Onga3421952019-03-21 18:12:26 -070021 return 0, false
Joe Tsai879b18d2018-08-03 17:22:24 -070022 }
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080023
24 // Optional -
25 if s[0] == '-' {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080026 s = s[1:]
27 n++
28 if len(s) == 0 {
Herbie Onga3421952019-03-21 18:12:26 -070029 return 0, false
Joe Tsai879b18d2018-08-03 17:22:24 -070030 }
Joe Tsai879b18d2018-08-03 17:22:24 -070031 }
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080032
33 // Digits
34 switch {
35 case s[0] == '0':
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080036 s = s[1:]
37 n++
38
39 case '1' <= s[0] && s[0] <= '9':
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080040 s = s[1:]
41 n++
42 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080043 s = s[1:]
44 n++
45 }
46
47 default:
Herbie Onga3421952019-03-21 18:12:26 -070048 return 0, false
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080049 }
50
51 // . followed by 1 or more digits.
52 if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080053 s = s[2:]
54 n += 2
55 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080056 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] == '-' {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080067 s = s[1:]
68 n++
69 if len(s) == 0 {
Herbie Onga3421952019-03-21 18:12:26 -070070 return 0, false
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080071 }
72 }
73 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080074 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]) {
Herbie Onga3421952019-03-21 18:12:26 -070081 return 0, false
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080082 }
83
Herbie Onga3421952019-03-21 18:12:26 -070084 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
Herbie Ong8fa64d92019-04-05 20:45:07 -070098// numberParts. The slice fields in numberParts are subslices of the input.
Herbie Ongd2ece132020-01-07 16:45:24 -080099func parseNumberParts(input []byte) (numberParts, bool) {
Herbie Onga3421952019-03-21 18:12:26 -0700100 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':
Herbie Ong8fa64d92019-04-05 20:45:07 -0700126 intp = s
127 n := 1
Herbie Onga3421952019-03-21 18:12:26 -0700128 s = s[1:]
129 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
Herbie Onga3421952019-03-21 18:12:26 -0700130 s = s[1:]
Herbie Ong8fa64d92019-04-05 20:45:07 -0700131 n++
Herbie Onga3421952019-03-21 18:12:26 -0700132 }
Herbie Ong8fa64d92019-04-05 20:45:07 -0700133 intp = intp[:n]
Herbie Onga3421952019-03-21 18:12:26 -0700134
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' {
Herbie Ong8fa64d92019-04-05 20:45:07 -0700141 frac = s[1:]
142 n := 1
Herbie Onga3421952019-03-21 18:12:26 -0700143 s = s[2:]
144 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
Herbie Onga3421952019-03-21 18:12:26 -0700145 s = s[1:]
Herbie Ong8fa64d92019-04-05 20:45:07 -0700146 n++
Herbie Onga3421952019-03-21 18:12:26 -0700147 }
Herbie Ong8fa64d92019-04-05 20:45:07 -0700148 frac = frac[:n]
Herbie Onga3421952019-03-21 18:12:26 -0700149 }
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:]
Herbie Ong8fa64d92019-04-05 20:45:07 -0700155 exp = s
156 n := 0
Herbie Onga3421952019-03-21 18:12:26 -0700157 if s[0] == '+' || s[0] == '-' {
Herbie Onga3421952019-03-21 18:12:26 -0700158 s = s[1:]
Herbie Ong8fa64d92019-04-05 20:45:07 -0700159 n++
Herbie Onga3421952019-03-21 18:12:26 -0700160 if len(s) == 0 {
161 return numberParts{}, false
162 }
163 }
164 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
Herbie Onga3421952019-03-21 18:12:26 -0700165 s = s[1:]
Herbie Ong8fa64d92019-04-05 20:45:07 -0700166 n++
Herbie Onga3421952019-03-21 18:12:26 -0700167 }
Herbie Ong8fa64d92019-04-05 20:45:07 -0700168 exp = exp[:n]
Herbie Onga3421952019-03-21 18:12:26 -0700169 }
170
171 return numberParts{
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800172 neg: neg,
173 intp: intp,
174 frac: bytes.TrimRight(frac, "0"), // Remove unnecessary 0s to the right.
175 exp: exp,
Herbie Onga3421952019-03-21 18:12:26 -0700176 }, true
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800177}
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.
Herbie Onga3421952019-03-21 18:12:26 -0700182func normalizeToIntString(n numberParts) (string, bool) {
Herbie Ong8fa64d92019-04-05 20:45:07 -0700183 intpSize := len(n.intp)
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800184 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
Herbie Ong8fa64d92019-04-05 20:45:07 -0700199 var num []byte
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800200 if exp >= 0 {
201 // For positive E, shift fraction digits into integer part and also pad
202 // with zeroes as needed.
203
Herbie Ong8fa64d92019-04-05 20:45:07 -0700204 // If there are more digits in fraction than the E value, then the
205 // number is not an integer.
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800206 if fracSize > exp {
207 return "", false
208 }
209
Herbie Ongb02b6d12020-01-07 17:26:43 -0800210 // 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
Herbie Ong8fa64d92019-04-05 20:45:07 -0700218 // Set cap to make a copy of integer part when appended.
219 num = n.intp[:len(n.intp):len(n.intp)]
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800220 num = append(num, n.frac...)
221 for i := 0; i < exp-fracSize; i++ {
222 num = append(num, '0')
223 }
224
225 } else {
226 // For negative E, shift digits in integer part out.
227
Herbie Ong8fa64d92019-04-05 20:45:07 -0700228 // If there are fractions, then the number is not an integer.
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800229 if fracSize > 0 {
230 return "", false
231 }
232
Herbie Ong8fa64d92019-04-05 20:45:07 -0700233 // index is where the decimal point will be after adjusting for negative
234 // exponent.
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800235 index := intpSize + exp
236 if index < 0 {
237 return "", false
238 }
Herbie Ong8fa64d92019-04-05 20:45:07 -0700239
240 num = n.intp
241 // If any of the digits being shifted to the right of the decimal point
242 // is non-zero, then the number is not an integer.
Herbie Ongd3f8f2d2019-03-06 00:28:23 -0800243 for i := index; i < intpSize; i++ {
244 if num[i] != '0' {
245 return "", false
246 }
247 }
248 num = num[:index]
249 }
250
251 if n.neg {
252 return "-" + string(num), true
253 }
254 return string(num), true
Joe Tsai879b18d2018-08-03 17:22:24 -0700255}