...

Source file src/google.golang.org/protobuf/internal/encoding/json/decode_number.go

Documentation: google.golang.org/protobuf/internal/encoding/json

     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  

View as plain text