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 }