mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-01-07 12:56:47 +00:00
edaf511a7a
Fix a panic when parsing the incomplete negative number "- ". Fixes golang/protobuf#1530 Change-Id: Iba5e8ee68d5f7255c28f1a74f31beee36c9ed847 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/475995 Reviewed-by: Lasse Folger <lassefolger@google.com> Run-TryBot: Damien Neil <dneil@google.com>
212 lines
4.4 KiB
Go
212 lines
4.4 KiB
Go
// 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 text
|
|
|
|
// parseNumberValue parses a number from the input and returns a Token object.
|
|
func (d *Decoder) parseNumberValue() (Token, bool) {
|
|
in := d.in
|
|
num := parseNumber(in)
|
|
if num.size == 0 {
|
|
return Token{}, false
|
|
}
|
|
numAttrs := num.kind
|
|
if num.neg {
|
|
numAttrs |= isNegative
|
|
}
|
|
tok := Token{
|
|
kind: Scalar,
|
|
attrs: numberValue,
|
|
pos: len(d.orig) - len(d.in),
|
|
raw: d.in[:num.size],
|
|
str: num.string(d.in),
|
|
numAttrs: numAttrs,
|
|
}
|
|
d.consume(num.size)
|
|
return tok, true
|
|
}
|
|
|
|
const (
|
|
numDec uint8 = (1 << iota) / 2
|
|
numHex
|
|
numOct
|
|
numFloat
|
|
)
|
|
|
|
// number is the result of parsing out a valid number from parseNumber. It
|
|
// contains data for doing float or integer conversion via the strconv package
|
|
// in conjunction with the input bytes.
|
|
type number struct {
|
|
kind uint8
|
|
neg bool
|
|
size int
|
|
// if neg, this is the length of whitespace and comments between
|
|
// the minus sign and the rest fo the number literal
|
|
sep int
|
|
}
|
|
|
|
func (num number) string(data []byte) string {
|
|
strSize := num.size
|
|
last := num.size - 1
|
|
if num.kind == numFloat && (data[last] == 'f' || data[last] == 'F') {
|
|
strSize = last
|
|
}
|
|
if num.neg && num.sep > 0 {
|
|
// strip whitespace/comments between negative sign and the rest
|
|
strLen := strSize - num.sep
|
|
str := make([]byte, strLen)
|
|
str[0] = data[0]
|
|
copy(str[1:], data[num.sep+1:strSize])
|
|
return string(str)
|
|
}
|
|
return string(data[:strSize])
|
|
|
|
}
|
|
|
|
// parseNumber constructs a number object from given input. It allows for the
|
|
// following patterns:
|
|
//
|
|
// integer: ^-?([1-9][0-9]*|0[xX][0-9a-fA-F]+|0[0-7]*)
|
|
// float: ^-?((0|[1-9][0-9]*)?([.][0-9]*)?([eE][+-]?[0-9]+)?[fF]?)
|
|
//
|
|
// It also returns the number of parsed bytes for the given number, 0 if it is
|
|
// not a number.
|
|
func parseNumber(input []byte) number {
|
|
kind := numDec
|
|
var size int
|
|
var neg bool
|
|
|
|
s := input
|
|
if len(s) == 0 {
|
|
return number{}
|
|
}
|
|
|
|
// Optional -
|
|
var sep int
|
|
if s[0] == '-' {
|
|
neg = true
|
|
s = s[1:]
|
|
size++
|
|
// Consume any whitespace or comments between the
|
|
// negative sign and the rest of the number
|
|
lenBefore := len(s)
|
|
s = consume(s, 0)
|
|
sep = lenBefore - len(s)
|
|
size += sep
|
|
if len(s) == 0 {
|
|
return number{}
|
|
}
|
|
}
|
|
|
|
switch {
|
|
case s[0] == '0':
|
|
if len(s) > 1 {
|
|
switch {
|
|
case s[1] == 'x' || s[1] == 'X':
|
|
// Parse as hex number.
|
|
kind = numHex
|
|
n := 2
|
|
s = s[2:]
|
|
for len(s) > 0 && (('0' <= s[0] && s[0] <= '9') ||
|
|
('a' <= s[0] && s[0] <= 'f') ||
|
|
('A' <= s[0] && s[0] <= 'F')) {
|
|
s = s[1:]
|
|
n++
|
|
}
|
|
if n == 2 {
|
|
return number{}
|
|
}
|
|
size += n
|
|
|
|
case '0' <= s[1] && s[1] <= '7':
|
|
// Parse as octal number.
|
|
kind = numOct
|
|
n := 2
|
|
s = s[2:]
|
|
for len(s) > 0 && '0' <= s[0] && s[0] <= '7' {
|
|
s = s[1:]
|
|
n++
|
|
}
|
|
size += n
|
|
}
|
|
|
|
if kind&(numHex|numOct) > 0 {
|
|
if len(s) > 0 && !isDelim(s[0]) {
|
|
return number{}
|
|
}
|
|
return number{kind: kind, neg: neg, size: size, sep: sep}
|
|
}
|
|
}
|
|
s = s[1:]
|
|
size++
|
|
|
|
case '1' <= s[0] && s[0] <= '9':
|
|
n := 1
|
|
s = s[1:]
|
|
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
|
s = s[1:]
|
|
n++
|
|
}
|
|
size += n
|
|
|
|
case s[0] == '.':
|
|
// Set kind to numFloat to signify the intent to parse as float. And
|
|
// that it needs to have other digits after '.'.
|
|
kind = numFloat
|
|
|
|
default:
|
|
return number{}
|
|
}
|
|
|
|
// . followed by 0 or more digits.
|
|
if len(s) > 0 && s[0] == '.' {
|
|
n := 1
|
|
s = s[1:]
|
|
// If decimal point was before any digits, it should be followed by
|
|
// other digits.
|
|
if len(s) == 0 && kind == numFloat {
|
|
return number{}
|
|
}
|
|
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
|
s = s[1:]
|
|
n++
|
|
}
|
|
size += n
|
|
kind = numFloat
|
|
}
|
|
|
|
// e or E followed by an optional - or + and 1 or more digits.
|
|
if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
|
|
kind = numFloat
|
|
s = s[1:]
|
|
n := 1
|
|
if s[0] == '+' || s[0] == '-' {
|
|
s = s[1:]
|
|
n++
|
|
if len(s) == 0 {
|
|
return number{}
|
|
}
|
|
}
|
|
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
|
s = s[1:]
|
|
n++
|
|
}
|
|
size += n
|
|
}
|
|
|
|
// Optional suffix f or F for floats.
|
|
if len(s) > 0 && (s[0] == 'f' || s[0] == 'F') {
|
|
kind = numFloat
|
|
s = s[1:]
|
|
size++
|
|
}
|
|
|
|
// Check that next byte is a delimiter or it is at the end.
|
|
if len(s) > 0 && !isDelim(s[0]) {
|
|
return number{}
|
|
}
|
|
|
|
return number{kind: kind, neg: neg, size: size, sep: sep}
|
|
}
|