Skip to content

Commit 9d2cbe1

Browse files
authored
V2: Check for matching field numbers in StartGroup / EndGroup tags (#810)
1 parent 1485e83 commit 9d2cbe1

5 files changed

Lines changed: 95 additions & 13 deletions

File tree

packages/protobuf-bench/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ server would usually do.
1010

1111
| code generator | bundle size | minified | compressed |
1212
|---------------------|------------------------:|-----------------------:|-------------------:|
13-
| protobuf-es | 126,666 b | 65,334 b | 15,946 b |
13+
| protobuf-es | 126,944 b | 65,418 b | 15,948 b |
1414
| protobuf-javascript | 394,384 b | 288,654 b | 45,122 b |

packages/protobuf-test/src/wire/binary-encoding.test.ts

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
// limitations under the License.
1414

1515
import { describe, expect, it } from "@jest/globals";
16-
import { BinaryWriter, WireType } from "@bufbuild/protobuf/wire";
17-
import { UserDesc } from "../gen/ts/extra/example_pb.js";
1816
import { fromBinary } from "@bufbuild/protobuf";
17+
import { BinaryWriter, BinaryReader, WireType } from "@bufbuild/protobuf/wire";
18+
import { UserDesc } from "../gen/ts/extra/example_pb.js";
1919

2020
describe("BinaryWriter example", () => {
2121
it("should work as expected", () => {
@@ -32,3 +32,78 @@ describe("BinaryWriter example", () => {
3232
expect(user.active).toBe(true);
3333
});
3434
});
35+
36+
describe("BinaryReader", () => {
37+
describe("skip", () => {
38+
it("should skip group", () => {
39+
const reader = new BinaryReader(
40+
new BinaryWriter()
41+
.tag(1, WireType.StartGroup)
42+
.tag(33, WireType.Varint)
43+
.bool(true)
44+
.tag(1, WireType.EndGroup)
45+
.finish(),
46+
);
47+
const [fieldNo, wireType] = reader.tag();
48+
expect(fieldNo).toBe(1);
49+
expect(wireType).toBe(WireType.StartGroup);
50+
reader.skip(WireType.StartGroup, 1);
51+
expect(reader.pos).toBe(reader.len);
52+
});
53+
it("should skip nested group", () => {
54+
const reader = new BinaryReader(
55+
new BinaryWriter()
56+
.tag(1, WireType.StartGroup)
57+
.tag(1, WireType.StartGroup)
58+
.tag(1, WireType.EndGroup)
59+
.tag(1, WireType.EndGroup)
60+
.finish(),
61+
);
62+
const [fieldNo, wireType] = reader.tag();
63+
expect(fieldNo).toBe(1);
64+
expect(wireType).toBe(WireType.StartGroup);
65+
reader.skip(WireType.StartGroup, 1);
66+
expect(reader.pos).toBe(reader.len);
67+
});
68+
it("should error on unexpected end group field number", () => {
69+
const reader = new BinaryReader(
70+
new BinaryWriter()
71+
.tag(1, WireType.StartGroup)
72+
.tag(2, WireType.EndGroup)
73+
.finish(),
74+
);
75+
const [fieldNo, wireType] = reader.tag();
76+
expect(fieldNo).toBe(1);
77+
expect(wireType).toBe(WireType.StartGroup);
78+
expect(() => {
79+
reader.skip(WireType.StartGroup, 1);
80+
}).toThrow(/^invalid end group tag$/);
81+
});
82+
it("should return skipped group data", () => {
83+
const reader = new BinaryReader(
84+
new BinaryWriter()
85+
.tag(1, WireType.StartGroup)
86+
.tag(33, WireType.Varint)
87+
.bool(true)
88+
.tag(1, WireType.EndGroup)
89+
.finish(),
90+
);
91+
reader.tag();
92+
const skipped = reader.skip(WireType.StartGroup, 1);
93+
const sr = new BinaryReader(skipped);
94+
{
95+
const [fieldNo, wireType] = sr.tag();
96+
expect(fieldNo).toBe(33);
97+
expect(wireType).toBe(WireType.Varint);
98+
const bool = sr.bool();
99+
expect(bool).toBe(true);
100+
}
101+
{
102+
const [fieldNo, wireType] = sr.tag();
103+
expect(fieldNo).toBe(1);
104+
expect(wireType).toBe(WireType.EndGroup);
105+
expect(sr.pos).toBe(sr.len);
106+
}
107+
});
108+
});
109+
});

packages/protobuf/src/extensions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export function setExtension<Desc extends DescExtension>(
8080
const reader = new BinaryReader(writer.finish());
8181
while (reader.pos < reader.len) {
8282
const [no, wireType] = reader.tag();
83-
const data = reader.skip(wireType);
83+
const data = reader.skip(wireType, no);
8484
ufs.push({ no, wireType, data });
8585
}
8686
message.$unknown = ufs;

packages/protobuf/src/from-binary.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,12 @@ function readMessage(
108108
const unknownFields = message.getUnknown() ?? [];
109109
while (reader.pos < end) {
110110
[fieldNo, wireType] = reader.tag();
111-
if (wireType == WireType.EndGroup) {
111+
if (delimited && wireType == WireType.EndGroup) {
112112
break;
113113
}
114114
const field = message.findNumber(fieldNo);
115115
if (!field) {
116-
const data = reader.skip(wireType);
116+
const data = reader.skip(wireType, fieldNo);
117117
if (options.readUnknownFields) {
118118
unknownFields.push({ no: fieldNo, wireType, data });
119119
}

packages/protobuf/src/wire/binary-encoding.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -374,10 +374,12 @@ export class BinaryReader {
374374
}
375375

376376
/**
377-
* Skip one element on the wire and return the skipped data.
378-
* Supports WireType.StartGroup since v2.0.0-alpha.23.
377+
* Skip one element and return the skipped data.
378+
*
379+
* When skipping StartGroup, provide the tags field number to check for
380+
* matching field number in the EndGroup tag.
379381
*/
380-
skip(wireType: WireType): Uint8Array {
382+
skip(wireType: WireType, fieldNo?: number): Uint8Array {
381383
let start = this.pos;
382384
switch (wireType) {
383385
case WireType.Varint:
@@ -398,10 +400,15 @@ export class BinaryReader {
398400
this.pos += len;
399401
break;
400402
case WireType.StartGroup:
401-
// TODO check for matching field numbers in StartGroup / EndGroup tags
402-
let t: WireType;
403-
while ((t = this.tag()[1]) !== WireType.EndGroup) {
404-
this.skip(t);
403+
for (;;) {
404+
const [fn, wt] = this.tag();
405+
if (wt === WireType.EndGroup) {
406+
if (fieldNo !== undefined && fn !== fieldNo) {
407+
throw new Error("invalid end group tag");
408+
}
409+
break;
410+
}
411+
this.skip(wt, fn);
405412
}
406413
break;
407414
default:

0 commit comments

Comments
 (0)