decode_number.go raw

   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  
   5  package json
   6  
   7  import (
   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.
  16  func 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.
  89  type 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.
  99  func 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.
 182  func 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