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