1// 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}
255