// Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package json import ( "bytes" "strconv" ) // parseNumber reads the given []byte for a valid JSON number. If it is valid, // it returns the number of bytes. Parsing logic follows the definition in // https://tools.ietf.org/html/rfc7159#section-6, and is based off // encoding/json.isValidNumber function. func parseNumber(input []byte) (int, bool) { var n int s := input if len(s) == 0 { return 0, false } // Optional - if s[0] == '-' { s = s[1:] n++ if len(s) == 0 { return 0, false } } // Digits switch { case s[0] == '0': s = s[1:] n++ case '1' <= s[0] && s[0] <= '9': s = s[1:] n++ for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { s = s[1:] n++ } default: return 0, false } // . followed by 1 or more digits. if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' { s = s[2:] n += 2 for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { s = s[1:] n++ } } // e or E followed by an optional - or + and // 1 or more digits. if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') { s = s[1:] n++ if s[0] == '+' || s[0] == '-' { s = s[1:] n++ if len(s) == 0 { return 0, false } } for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { s = s[1:] n++ } } // Check that next byte is a delimiter or it is at the end. if n < len(input) && isNotDelim(input[n]) { return 0, false } return n, true } // numberParts is the result of parsing out a valid JSON number. It contains // the parts of a number. The parts are used for integer conversion. type numberParts struct { neg bool intp []byte frac []byte exp []byte } // parseNumber constructs numberParts from given []byte. The logic here is // similar to consumeNumber above with the difference of having to construct // numberParts. The slice fields in numberParts are subslices of the input. func parseNumberParts(input []byte) (numberParts, bool) { var neg bool var intp []byte var frac []byte var exp []byte s := input if len(s) == 0 { return numberParts{}, false } // Optional - if s[0] == '-' { neg = true s = s[1:] if len(s) == 0 { return numberParts{}, false } } // Digits switch { case s[0] == '0': // Skip first 0 and no need to store. s = s[1:] case '1' <= s[0] && s[0] <= '9': intp = s n := 1 s = s[1:] for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { s = s[1:] n++ } intp = intp[:n] default: return numberParts{}, false } // . followed by 1 or more digits. if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' { frac = s[1:] n := 1 s = s[2:] for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { s = s[1:] n++ } frac = frac[:n] } // e or E followed by an optional - or + and // 1 or more digits. if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') { s = s[1:] exp = s n := 0 if s[0] == '+' || s[0] == '-' { s = s[1:] n++ if len(s) == 0 { return numberParts{}, false } } for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { s = s[1:] n++ } exp = exp[:n] } return numberParts{ neg: neg, intp: intp, frac: bytes.TrimRight(frac, "0"), // Remove unnecessary 0s to the right. exp: exp, }, true } // normalizeToIntString returns an integer string in normal form without the // E-notation for given numberParts. It will return false if it is not an // integer or if the exponent exceeds than max/min int value. func normalizeToIntString(n numberParts) (string, bool) { intpSize := len(n.intp) fracSize := len(n.frac) if intpSize == 0 && fracSize == 0 { return "0", true } var exp int if len(n.exp) > 0 { i, err := strconv.ParseInt(string(n.exp), 10, 32) if err != nil { return "", false } exp = int(i) } var num []byte if exp >= 0 { // For positive E, shift fraction digits into integer part and also pad // with zeroes as needed. // If there are more digits in fraction than the E value, then the // number is not an integer. if fracSize > exp { return "", false } // Make sure resulting digits are within max value limit to avoid // unnecessarily constructing a large byte slice that may simply fail // later on. const maxDigits = 20 // Max uint64 value has 20 decimal digits. if intpSize+exp > maxDigits { return "", false } // Set cap to make a copy of integer part when appended. num = n.intp[:len(n.intp):len(n.intp)] num = append(num, n.frac...) for i := 0; i < exp-fracSize; i++ { num = append(num, '0') } } else { // For negative E, shift digits in integer part out. // If there are fractions, then the number is not an integer. if fracSize > 0 { return "", false } // index is where the decimal point will be after adjusting for negative // exponent. index := intpSize + exp if index < 0 { return "", false } num = n.intp // If any of the digits being shifted to the right of the decimal point // is non-zero, then the number is not an integer. for i := index; i < intpSize; i++ { if num[i] != '0' { return "", false } } num = num[:index] } if n.neg { return "-" + string(num), true } return string(num), true }