Skip to content

Commit 02916f6

Browse files
authored
Add message and field name to binary serialization errors (#984)
1 parent 42e834b commit 02916f6

File tree

6 files changed

+285
-231
lines changed

6 files changed

+285
-231
lines changed

packages/bundle-size/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ usually do. We repeat this for an increasing number of files.
1616

1717
| code generator | files | bundle size | minified | compressed |
1818
| ------------------- | ----: | ----------: | --------: | ---------: |
19-
| Protobuf-ES | 1 | 126,701 b | 65,774 b | 15,261 b |
20-
| Protobuf-ES | 4 | 128,890 b | 67,282 b | 15,950 b |
21-
| Protobuf-ES | 8 | 131,652 b | 69,053 b | 16,483 b |
22-
| Protobuf-ES | 16 | 142,102 b | 77,034 b | 18,801 b |
23-
| Protobuf-ES | 32 | 169,893 b | 99,052 b | 24,273 b |
19+
| Protobuf-ES | 1 | 127,197 b | 66,017 b | 15,359 b |
20+
| Protobuf-ES | 4 | 129,386 b | 67,525 b | 16,031 b |
21+
| Protobuf-ES | 8 | 132,148 b | 69,296 b | 16,564 b |
22+
| Protobuf-ES | 16 | 142,598 b | 77,277 b | 18,875 b |
23+
| Protobuf-ES | 32 | 170,389 b | 99,295 b | 24,350 b |
2424
| protobuf-javascript | 1 | 104,048 b | 70,318 b | 15,474 b |
2525
| protobuf-javascript | 4 | 130,537 b | 85,670 b | 16,986 b |
2626
| protobuf-javascript | 8 | 152,429 b | 98,042 b | 18,111 b |

packages/bundle-size/chart.svg

Lines changed: 6 additions & 6 deletions
Loading

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,16 @@ describe(`binary serialization`, () => {
182182
).toEqual("foo");
183183
});
184184
});
185+
test("error for invalid data", () => {
186+
const msg = create(ScalarValuesMessageSchema, {
187+
uint32Field: -1, // -1 is invalid for a uint
188+
});
189+
expect(() => toBinary(ScalarValuesMessageSchema, msg)).toThrow(
190+
new Error(
191+
"cannot encode field spec.ScalarValuesMessage.uint32_field to binary: invalid uint32: -1",
192+
),
193+
);
194+
});
185195
});
186196

187197
function testBinary<Desc extends DescMessage>(

packages/protobuf-test/src/perf.ts

Lines changed: 159 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@
1313
// limitations under the License.
1414

1515
import Benchmark from "benchmark";
16-
import { create, fromBinary, toBinary, protoInt64 } from "@bufbuild/protobuf";
16+
import {
17+
create,
18+
fromBinary,
19+
toBinary,
20+
protoInt64,
21+
toJsonString,
22+
fromJsonString,
23+
} from "@bufbuild/protobuf";
1724
import { readFileSync } from "fs";
1825
import { UserSchema } from "./gen/ts/extra/example_pb.js";
1926
import {
@@ -95,173 +102,163 @@ interface Test {
95102
}
96103

97104
function setupTests(): Test[] {
98-
const tests: Test[] = [];
99-
{
100-
const bytes = readFileSync(
101-
new URL("perf-payload.bin", import.meta.url).pathname,
102-
);
103-
tests.push({
104-
name: `fromBinary perf-payload.bin`,
105-
fn: () => {
106-
fromBinary(PerfMessageSchema, bytes);
107-
},
108-
});
109-
}
110-
{
111-
const desc = UserSchema;
112-
const tinyUser = create(desc, {
113-
active: false,
114-
manager: { active: true },
115-
});
116-
const data = toBinary(desc, tinyUser);
117-
tests.push({
118-
name: `fromBinary tiny example.User (${data.byteLength} bytes)`,
119-
fn: () => {
120-
fromBinary(desc, data);
121-
},
122-
});
123-
}
124-
{
125-
const desc = UserSchema;
126-
const normalUser = create(desc, {
127-
firstName: "Jane",
128-
lastName: "Doe",
129-
active: true,
130-
manager: { firstName: "Jane", lastName: "Doe", active: false },
131-
locations: ["Seattle", "New York", "Tokyo"],
132-
projects: { foo: "project foo", bar: "project bar" },
133-
});
134-
const data = toBinary(desc, normalUser);
135-
tests.push({
136-
name: `fromBinary normal example.User (${data.byteLength} bytes)`,
137-
fn: () => {
138-
fromBinary(desc, data);
139-
},
140-
});
141-
}
142-
{
143-
const desc = ScalarValuesMessageSchema;
144-
const message = create(ScalarValuesMessageSchema, {
145-
doubleField: 0.75,
146-
floatField: -0.75,
147-
int64Field: protoInt64.parse(-1),
148-
uint64Field: protoInt64.uParse(1),
149-
int32Field: -123,
150-
fixed64Field: protoInt64.uParse(1),
151-
fixed32Field: 123,
152-
boolField: true,
153-
stringField: "hello world",
154-
bytesField: new Uint8Array([
155-
104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100,
156-
]),
157-
uint32Field: 123,
158-
sfixed32Field: -123,
159-
sfixed64Field: protoInt64.parse(-1),
160-
sint32Field: -1,
161-
sint64Field: protoInt64.parse(-1),
162-
});
163-
const data = toBinary(desc, message);
164-
tests.push({
165-
name: `fromBinary scalar values (${data.byteLength} bytes)`,
166-
fn: () => {
167-
fromBinary(desc, data);
168-
},
169-
});
170-
}
171-
{
172-
const desc = RepeatedScalarValuesMessageSchema;
173-
const message = create(desc, {
174-
doubleField: [0.75, 0, 1],
175-
floatField: [0.75, -0.75],
176-
int64Field: [protoInt64.parse(-1), protoInt64.parse(-2)],
177-
uint64Field: [protoInt64.uParse(1), protoInt64.uParse(2)],
178-
int32Field: [-123, 500],
179-
fixed64Field: [protoInt64.uParse(1), protoInt64.uParse(99)],
180-
fixed32Field: [123, 999],
181-
boolField: [true, false, true],
182-
stringField: ["hello", "world"],
183-
bytesField: [
184-
new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]),
185-
],
186-
uint32Field: [123, 123],
187-
sfixed32Field: [-123, -123, -123],
188-
sfixed64Field: [
189-
protoInt64.parse(-1),
190-
protoInt64.parse(-2),
191-
protoInt64.parse(100),
192-
],
193-
sint32Field: [-1, -2, 999],
194-
sint64Field: [
195-
protoInt64.parse(-1),
196-
protoInt64.parse(-99),
197-
protoInt64.parse(99),
198-
],
199-
});
200-
const data = toBinary(desc, message);
201-
tests.push({
202-
name: `fromBinary repeated scalar fields (${data.byteLength} bytes)`,
203-
fn: () => {
204-
fromBinary(desc, data);
205-
},
206-
});
207-
}
208-
{
209-
const desc = MapsMessageSchema;
210-
const message = create(desc, {
211-
strStrField: { a: "str", b: "xx" },
212-
strInt32Field: { a: 123, b: 455 },
213-
strInt64Field: { a: protoInt64.parse(123) },
214-
strBoolField: { a: true, b: false },
215-
strBytesField: {
216-
a: new Uint8Array([
105+
const tests: Test[] = [
106+
{
107+
name: "perf-payload.bin",
108+
desc: PerfMessageSchema,
109+
msg: fromBinary(
110+
PerfMessageSchema,
111+
readFileSync(new URL("perf-payload.bin", import.meta.url).pathname),
112+
),
113+
},
114+
{
115+
name: "tiny example.User",
116+
desc: UserSchema,
117+
msg: create(UserSchema, { active: false, manager: { active: true } }),
118+
},
119+
{
120+
name: "normal example.User",
121+
desc: UserSchema,
122+
msg: create(UserSchema, {
123+
firstName: "Jane",
124+
lastName: "Doe",
125+
active: true,
126+
manager: { firstName: "Jane", lastName: "Doe", active: false },
127+
locations: ["Seattle", "New York", "Tokyo"],
128+
projects: { foo: "project foo", bar: "project bar" },
129+
}),
130+
},
131+
{
132+
name: "scalar values",
133+
desc: ScalarValuesMessageSchema,
134+
msg: create(ScalarValuesMessageSchema, {
135+
doubleField: 0.75,
136+
floatField: -0.75,
137+
int64Field: protoInt64.parse(-1),
138+
uint64Field: protoInt64.uParse(1),
139+
int32Field: -123,
140+
fixed64Field: protoInt64.uParse(1),
141+
fixed32Field: 123,
142+
boolField: true,
143+
stringField: "hello world",
144+
bytesField: new Uint8Array([
217145
104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100,
218146
]),
147+
uint32Field: 123,
148+
sfixed32Field: -123,
149+
sfixed64Field: protoInt64.parse(-1),
150+
sint32Field: -1,
151+
sint64Field: protoInt64.parse(-1),
152+
}),
153+
},
154+
{
155+
name: "repeated scalar values",
156+
desc: RepeatedScalarValuesMessageSchema,
157+
msg: create(RepeatedScalarValuesMessageSchema, {
158+
doubleField: [0.75, 0, 1],
159+
floatField: [0.75, -0.75],
160+
int64Field: [protoInt64.parse(-1), protoInt64.parse(-2)],
161+
uint64Field: [protoInt64.uParse(1), protoInt64.uParse(2)],
162+
int32Field: [-123, 500],
163+
fixed64Field: [protoInt64.uParse(1), protoInt64.uParse(99)],
164+
fixed32Field: [123, 999],
165+
boolField: [true, false, true],
166+
stringField: ["hello", "world"],
167+
bytesField: [
168+
new Uint8Array([
169+
104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100,
170+
]),
171+
],
172+
uint32Field: [123, 123],
173+
sfixed32Field: [-123, -123, -123],
174+
sfixed64Field: [
175+
protoInt64.parse(-1),
176+
protoInt64.parse(-2),
177+
protoInt64.parse(100),
178+
],
179+
sint32Field: [-1, -2, 999],
180+
sint64Field: [
181+
protoInt64.parse(-1),
182+
protoInt64.parse(-99),
183+
protoInt64.parse(99),
184+
],
185+
}),
186+
},
187+
{
188+
name: "map with scalar keys and values",
189+
desc: MapsMessageSchema,
190+
msg: create(MapsMessageSchema, {
191+
strStrField: { a: "str", b: "xx" },
192+
strInt32Field: { a: 123, b: 455 },
193+
strInt64Field: { a: protoInt64.parse(123) },
194+
strBoolField: { a: true, b: false },
195+
strBytesField: {
196+
a: new Uint8Array([
197+
104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100,
198+
]),
199+
},
200+
int32StrField: { 123: "hello" },
201+
int64StrField: { "9223372036854775807": "hello" },
202+
boolStrField: { true: "yes", false: "no" },
203+
strEnuField: { a: 0, b: 1, c: 2 },
204+
int32EnuField: { 1: 0, 2: 1, 0: 2 },
205+
int64EnuField: { "-1": 0, "2": 1, "0": 2 },
206+
}),
207+
},
208+
{
209+
name: "repeated field with 1000 messages",
210+
desc: MessageFieldMessageSchema,
211+
msg: (() => {
212+
const message = create(MessageFieldMessageSchema);
213+
for (let i = 0; i < 1000; i++) {
214+
message.repeatedMessageField.push(
215+
create(MessageFieldMessage_TestMessageSchema),
216+
);
217+
}
218+
return message;
219+
})(),
220+
},
221+
{
222+
name: "map field with 1000 messages",
223+
desc: MapsMessageSchema,
224+
msg: (() => {
225+
const message = create(MapsMessageSchema);
226+
for (let i = 0; i < 1000; i++) {
227+
message.strMsgField[i.toString()] = create(MapsMessageSchema);
228+
}
229+
return message;
230+
})(),
231+
},
232+
].flatMap(({ name, msg, desc }) => {
233+
const bytes = toBinary(desc, msg);
234+
const jsonString = toJsonString(desc, msg);
235+
return [
236+
{
237+
name: `fromBinary ${name}`,
238+
fn: () => {
239+
fromBinary(desc, bytes);
240+
},
219241
},
220-
int32StrField: { 123: "hello" },
221-
int64StrField: { "9223372036854775807": "hello" },
222-
boolStrField: { true: "yes", false: "no" },
223-
strEnuField: { a: 0, b: 1, c: 2 },
224-
int32EnuField: { 1: 0, 2: 1, 0: 2 },
225-
int64EnuField: { "-1": 0, "2": 1, "0": 2 },
226-
});
227-
const data = toBinary(desc, message);
228-
tests.push({
229-
name: `fromBinary map with scalar keys and values (${data.byteLength} bytes)`,
230-
fn: () => {
231-
fromBinary(desc, data);
242+
{
243+
name: `fromJson ${name}`,
244+
fn: () => {
245+
fromJsonString(desc, jsonString);
246+
},
232247
},
233-
});
234-
}
235-
{
236-
const desc = MessageFieldMessageSchema;
237-
const message = create(desc);
238-
for (let i = 0; i < 1000; i++) {
239-
message.repeatedMessageField.push(
240-
create(MessageFieldMessage_TestMessageSchema),
241-
);
242-
}
243-
const data = toBinary(desc, message);
244-
tests.push({
245-
name: `fromBinary repeated field with 1000 messages (${data.byteLength} bytes)`,
246-
fn: () => {
247-
fromBinary(desc, data);
248+
{
249+
name: `toBinary ${name}`,
250+
fn: () => {
251+
toBinary(desc, msg);
252+
},
248253
},
249-
});
250-
}
251-
{
252-
const desc = MapsMessageSchema;
253-
const message = create(desc);
254-
for (let i = 0; i < 1000; i++) {
255-
message.strMsgField[i.toString()] = create(desc);
256-
}
257-
const data = toBinary(desc, message);
258-
tests.push({
259-
name: `fromBinary map field with 1000 messages (${data.byteLength} bytes)`,
260-
fn: () => {
261-
fromBinary(desc, data);
254+
{
255+
name: `toJson ${name}`,
256+
fn: () => {
257+
toJsonString(desc, msg);
258+
},
262259
},
263-
});
264-
}
260+
];
261+
});
265262
return tests;
266263
}
267264

0 commit comments

Comments
 (0)