github.com/whiteCcinn/protobuf-go@v1.0.9/internal/encoding/text/decode_number.go (about)

     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 text
     6  
     7  // parseNumberValue parses a number from the input and returns a Token object.
     8  func (d *Decoder) parseNumberValue() (Token, bool) {
     9  	in := d.in
    10  	num := parseNumber(in)
    11  	if num.size == 0 {
    12  		return Token{}, false
    13  	}
    14  	numAttrs := num.kind
    15  	if num.neg {
    16  		numAttrs |= isNegative
    17  	}
    18  	strSize := num.size
    19  	last := num.size - 1
    20  	if num.kind == numFloat && (d.in[last] == 'f' || d.in[last] == 'F') {
    21  		strSize = last
    22  	}
    23  	tok := Token{
    24  		kind:     Scalar,
    25  		attrs:    numberValue,
    26  		pos:      len(d.orig) - len(d.in),
    27  		raw:      d.in[:num.size],
    28  		str:      string(d.in[:strSize]),
    29  		numAttrs: numAttrs,
    30  	}
    31  	d.consume(num.size)
    32  	return tok, true
    33  }
    34  
    35  const (
    36  	numDec uint8 = (1 << iota) / 2
    37  	numHex
    38  	numOct
    39  	numFloat
    40  )
    41  
    42  // number is the result of parsing out a valid number from parseNumber. It
    43  // contains data for doing float or integer conversion via the strconv package
    44  // in conjunction with the input bytes.
    45  type number struct {
    46  	kind uint8
    47  	neg  bool
    48  	size int
    49  }
    50  
    51  // parseNumber constructs a number object from given input. It allows for the
    52  // following patterns:
    53  //
    54  //	integer: ^-?([1-9][0-9]*|0[xX][0-9a-fA-F]+|0[0-7]*)
    55  //	float: ^-?((0|[1-9][0-9]*)?([.][0-9]*)?([eE][+-]?[0-9]+)?[fF]?)
    56  //
    57  // It also returns the number of parsed bytes for the given number, 0 if it is
    58  // not a number.
    59  func parseNumber(input []byte) number {
    60  	kind := numDec
    61  	var size int
    62  	var neg bool
    63  
    64  	s := input
    65  	if len(s) == 0 {
    66  		return number{}
    67  	}
    68  
    69  	// Optional -
    70  	if s[0] == '-' {
    71  		neg = true
    72  		s = s[1:]
    73  		size++
    74  		if len(s) == 0 {
    75  			return number{}
    76  		}
    77  	}
    78  
    79  	// C++ allows for whitespace and comments in between the negative sign and
    80  	// the rest of the number. This logic currently does not but is consistent
    81  	// with v1.
    82  
    83  	switch {
    84  	case s[0] == '0':
    85  		if len(s) > 1 {
    86  			switch {
    87  			case s[1] == 'x' || s[1] == 'X':
    88  				// Parse as hex number.
    89  				kind = numHex
    90  				n := 2
    91  				s = s[2:]
    92  				for len(s) > 0 && (('0' <= s[0] && s[0] <= '9') ||
    93  					('a' <= s[0] && s[0] <= 'f') ||
    94  					('A' <= s[0] && s[0] <= 'F')) {
    95  					s = s[1:]
    96  					n++
    97  				}
    98  				if n == 2 {
    99  					return number{}
   100  				}
   101  				size += n
   102  
   103  			case '0' <= s[1] && s[1] <= '7':
   104  				// Parse as octal number.
   105  				kind = numOct
   106  				n := 2
   107  				s = s[2:]
   108  				for len(s) > 0 && '0' <= s[0] && s[0] <= '7' {
   109  					s = s[1:]
   110  					n++
   111  				}
   112  				size += n
   113  			}
   114  
   115  			if kind&(numHex|numOct) > 0 {
   116  				if len(s) > 0 && !isDelim(s[0]) {
   117  					return number{}
   118  				}
   119  				return number{kind: kind, neg: neg, size: size}
   120  			}
   121  		}
   122  		s = s[1:]
   123  		size++
   124  
   125  	case '1' <= s[0] && s[0] <= '9':
   126  		n := 1
   127  		s = s[1:]
   128  		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
   129  			s = s[1:]
   130  			n++
   131  		}
   132  		size += n
   133  
   134  	case s[0] == '.':
   135  		// Set kind to numFloat to signify the intent to parse as float. And
   136  		// that it needs to have other digits after '.'.
   137  		kind = numFloat
   138  
   139  	default:
   140  		return number{}
   141  	}
   142  
   143  	// . followed by 0 or more digits.
   144  	if len(s) > 0 && s[0] == '.' {
   145  		n := 1
   146  		s = s[1:]
   147  		// If decimal point was before any digits, it should be followed by
   148  		// other digits.
   149  		if len(s) == 0 && kind == numFloat {
   150  			return number{}
   151  		}
   152  		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
   153  			s = s[1:]
   154  			n++
   155  		}
   156  		size += n
   157  		kind = numFloat
   158  	}
   159  
   160  	// e or E followed by an optional - or + and 1 or more digits.
   161  	if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
   162  		kind = numFloat
   163  		s = s[1:]
   164  		n := 1
   165  		if s[0] == '+' || s[0] == '-' {
   166  			s = s[1:]
   167  			n++
   168  			if len(s) == 0 {
   169  				return number{}
   170  			}
   171  		}
   172  		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
   173  			s = s[1:]
   174  			n++
   175  		}
   176  		size += n
   177  	}
   178  
   179  	// Optional suffix f or F for floats.
   180  	if len(s) > 0 && (s[0] == 'f' || s[0] == 'F') {
   181  		kind = numFloat
   182  		s = s[1:]
   183  		size++
   184  	}
   185  
   186  	// Check that next byte is a delimiter or it is at the end.
   187  	if len(s) > 0 && !isDelim(s[0]) {
   188  		return number{}
   189  	}
   190  
   191  	return number{kind: kind, neg: neg, size: size}
   192  }