blob: 6b3ed86a3299423589c60ac065a972bea1c4ec3f [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 Ongd3f8f2d2019-03-06 00:28:23 -080044// numberParts is the result of parsing out a valid JSON number. It contains
45// the parts of a number. The parts are used for integer conversion.
46type numberParts struct {
47 neg bool
48 intp []byte
49 frac []byte
50 exp []byte
Joe Tsai879b18d2018-08-03 17:22:24 -070051}
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080052
53// parseNumber returns a numberParts instance if it is able to read a JSON
54// number from the given []byte. It also returns the number of bytes read.
55// Parsing logic follows the definition in
56// https://tools.ietf.org/html/rfc7159#section-6, and is based off
57// encoding/json.isValidNumber function.
58func parseNumber(input []byte) (*numberParts, int) {
59 var n int
60 var neg bool
61 var intp []byte
62 var frac []byte
63 var exp []byte
64
65 s := input
66 if len(s) == 0 {
67 return nil, 0
Joe Tsai879b18d2018-08-03 17:22:24 -070068 }
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080069
70 // Optional -
71 if s[0] == '-' {
72 neg = true
73 s = s[1:]
74 n++
75 if len(s) == 0 {
76 return nil, 0
Joe Tsai879b18d2018-08-03 17:22:24 -070077 }
Joe Tsai879b18d2018-08-03 17:22:24 -070078 }
Herbie Ongd3f8f2d2019-03-06 00:28:23 -080079
80 // Digits
81 switch {
82 case s[0] == '0':
83 // Skip first 0 and no need to store.
84 s = s[1:]
85 n++
86
87 case '1' <= s[0] && s[0] <= '9':
88 intp = append(intp, s[0])
89 s = s[1:]
90 n++
91 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
92 intp = append(intp, s[0])
93 s = s[1:]
94 n++
95 }
96
97 default:
98 return nil, 0
99 }
100
101 // . followed by 1 or more digits.
102 if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
103 frac = append(frac, s[1])
104 s = s[2:]
105 n += 2
106 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
107 frac = append(frac, s[0])
108 s = s[1:]
109 n++
110 }
111 }
112
113 // e or E followed by an optional - or + and
114 // 1 or more digits.
115 if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
116 s = s[1:]
117 n++
118 if s[0] == '+' || s[0] == '-' {
119 exp = append(exp, s[0])
120 s = s[1:]
121 n++
122 if len(s) == 0 {
123 return nil, 0
124 }
125 }
126 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
127 exp = append(exp, s[0])
128 s = s[1:]
129 n++
130 }
131 }
132
133 // Check that next byte is a delimiter or it is at the end.
134 if n < len(input) && isNotDelim(input[n]) {
135 return nil, 0
136 }
137
138 return &numberParts{
139 neg: neg,
140 intp: intp,
141 frac: bytes.TrimRight(frac, "0"), // Remove unnecessary 0s to the right.
142 exp: exp,
143 }, n
144}
145
146// normalizeToIntString returns an integer string in normal form without the
147// E-notation for given numberParts. It will return false if it is not an
148// integer or if the exponent exceeds than max/min int value.
149func normalizeToIntString(n *numberParts) (string, bool) {
150 num := n.intp
151 intpSize := len(num)
152 fracSize := len(n.frac)
153
154 if intpSize == 0 && fracSize == 0 {
155 return "0", true
156 }
157
158 var exp int
159 if len(n.exp) > 0 {
160 i, err := strconv.ParseInt(string(n.exp), 10, 32)
161 if err != nil {
162 return "", false
163 }
164 exp = int(i)
165 }
166
167 if exp >= 0 {
168 // For positive E, shift fraction digits into integer part and also pad
169 // with zeroes as needed.
170
171 // If there are more digits in fraction than the E value, then number is
172 // not an integer.
173 if fracSize > exp {
174 return "", false
175 }
176
177 num = append(num, n.frac...)
178 for i := 0; i < exp-fracSize; i++ {
179 num = append(num, '0')
180 }
181
182 } else {
183 // For negative E, shift digits in integer part out.
184
185 // If there are any fractions to begin with, then number is not an
186 // integer.
187 if fracSize > 0 {
188 return "", false
189 }
190
191 index := intpSize + exp
192 if index < 0 {
193 return "", false
194 }
195 // If any of the digits being shifted out is non-zero, then number is
196 // not an integer.
197 for i := index; i < intpSize; i++ {
198 if num[i] != '0' {
199 return "", false
200 }
201 }
202 num = num[:index]
203 }
204
205 if n.neg {
206 return "-" + string(num), true
207 }
208 return string(num), true
Joe Tsai879b18d2018-08-03 17:22:24 -0700209}