Skip to content

Commit fcf5f6c

Browse files
jhumplfolger
authored andcommitted
encoding/prototext: allow whitespace and comments between minus sign and number in negative numeric literal
The text format specification[1] indicates that whitespace and comments may appear after a minus sign and before the subsequent numeric component in negative number literals. But the Go implementation does not allow this. This brings the Go implementation info conformance with this aspect. Fixes golang/protobuf#1526 [1] https://protobuf.dev/reference/protobuf/textformat-spec/#parsing Change-Id: I3996c89ee9d37cf2b7502fc6736d6e2ed6dbcf43 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/473015 Reviewed-by: Lasse Folger <[email protected]> Reviewed-by: Damien Neil <[email protected]>
1 parent bc1253a commit fcf5f6c

File tree

4 files changed

+46
-17
lines changed

4 files changed

+46
-17
lines changed

encoding/prototext/decode_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,9 @@ opt_int64: 3735928559
127127
opt_uint32: 0xff
128128
opt_uint64: 0xdeadbeef
129129
opt_sint32: -1001
130-
opt_sint64: -0xffff
130+
opt_sint64: - 0xffff
131131
opt_fixed64: 64
132-
opt_sfixed32: -32
132+
opt_sfixed32: - 32
133133
opt_float: 1.234
134134
opt_double: 1.23e+100
135135
opt_bytes: "\xe8\xb0\xb7\xe6\xad\x8c"
@@ -164,7 +164,8 @@ s_int64: 3735928559
164164
s_uint32: 0xff
165165
s_uint64: 0xdeadbeef
166166
s_sint32: -1001
167-
s_sint64: -0xffff
167+
s_sint64: - #
168+
0xffff
168169
s_fixed64: 64
169170
s_sfixed32: -32
170171
s_float: 1.234

internal/encoding/text/decode.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -412,12 +412,13 @@ func (d *Decoder) parseFieldName() (tok Token, err error) {
412412
// Field number. Identify if input is a valid number that is not negative
413413
// and is decimal integer within 32-bit range.
414414
if num := parseNumber(d.in); num.size > 0 {
415+
str := num.string(d.in)
415416
if !num.neg && num.kind == numDec {
416-
if _, err := strconv.ParseInt(string(d.in[:num.size]), 10, 32); err == nil {
417+
if _, err := strconv.ParseInt(str, 10, 32); err == nil {
417418
return d.consumeToken(Name, num.size, uint8(FieldNumber)), nil
418419
}
419420
}
420-
return Token{}, d.newSyntaxError("invalid field number: %s", d.in[:num.size])
421+
return Token{}, d.newSyntaxError("invalid field number: %s", str)
421422
}
422423

423424
return Token{}, d.newSyntaxError("invalid field name: %s", errId(d.in))

internal/encoding/text/decode_number.go

+31-12
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,12 @@ func (d *Decoder) parseNumberValue() (Token, bool) {
1515
if num.neg {
1616
numAttrs |= isNegative
1717
}
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-
}
2318
tok := Token{
2419
kind: Scalar,
2520
attrs: numberValue,
2621
pos: len(d.orig) - len(d.in),
2722
raw: d.in[:num.size],
28-
str: string(d.in[:strSize]),
23+
str: num.string(d.in),
2924
numAttrs: numAttrs,
3025
}
3126
d.consume(num.size)
@@ -46,6 +41,27 @@ type number struct {
4641
kind uint8
4742
neg bool
4843
size int
44+
// if neg, this is the length of whitespace and comments between
45+
// the minus sign and the rest fo the number literal
46+
sep int
47+
}
48+
49+
func (num number) string(data []byte) string {
50+
strSize := num.size
51+
last := num.size - 1
52+
if num.kind == numFloat && (data[last] == 'f' || data[last] == 'F') {
53+
strSize = last
54+
}
55+
if num.neg && num.sep > 0 {
56+
// strip whitespace/comments between negative sign and the rest
57+
strLen := strSize - num.sep
58+
str := make([]byte, strLen)
59+
str[0] = data[0]
60+
copy(str[1:], data[num.sep+1:strSize])
61+
return string(str)
62+
}
63+
return string(data[:strSize])
64+
4965
}
5066

5167
// parseNumber constructs a number object from given input. It allows for the
@@ -67,19 +83,22 @@ func parseNumber(input []byte) number {
6783
}
6884

6985
// Optional -
86+
var sep int
7087
if s[0] == '-' {
7188
neg = true
7289
s = s[1:]
7390
size++
7491
if len(s) == 0 {
7592
return number{}
7693
}
94+
// Consume any whitespace or comments between the
95+
// negative sign and the rest of the number
96+
lenBefore := len(s)
97+
s = consume(s, 0)
98+
sep = lenBefore - len(s)
99+
size += sep
77100
}
78101

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-
83102
switch {
84103
case s[0] == '0':
85104
if len(s) > 1 {
@@ -116,7 +135,7 @@ func parseNumber(input []byte) number {
116135
if len(s) > 0 && !isDelim(s[0]) {
117136
return number{}
118137
}
119-
return number{kind: kind, neg: neg, size: size}
138+
return number{kind: kind, neg: neg, size: size, sep: sep}
120139
}
121140
}
122141
s = s[1:]
@@ -188,5 +207,5 @@ func parseNumber(input []byte) number {
188207
return number{}
189208
}
190209

191-
return number{kind: kind, neg: neg, size: size}
210+
return number{kind: kind, neg: neg, size: size, sep: sep}
192211
}

internal/encoding/text/decode_test.go

+8
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,14 @@ func TestDecoder(t *testing.T) {
555555
in: "-123",
556556
want: []R{{E: "invalid field number: -123"}},
557557
},
558+
{
559+
in: "- \t 123.321e6",
560+
want: []R{{E: "invalid field number: -123.321e6"}},
561+
},
562+
{
563+
in: "- # negative\n 123",
564+
want: []R{{E: "invalid field number: -123"}},
565+
},
558566
{
559567
// Field number > math.MaxInt32.
560568
in: "2147483648:",

0 commit comments

Comments
 (0)