Skip to content

Commit 4a77a4c

Browse files
authored
V2: Clarify map behavior with message_encoding = DELIMITED (#829)
1 parent bd48dcd commit 4a77a4c

6 files changed

+114
-23
lines changed

packages/protobuf-test/extra/edition2023-map-encoding.proto

+5-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ option features.message_encoding = DELIMITED;
1919

2020
// Map fields are syntactic sugar for a repeated message field with field 1 for
2121
// key and field 2 for value. Despite that, the file feature message_encoding =
22-
// DELIMITED should NOT apply to this "synthetic" message.
22+
// DELIMITED should NOT apply to this "synthetic" message, and it should also
23+
// not apply to map message values.
2324
message Edition2023MapEncodingMessage {
24-
map<int32, bool> map_field = 77;
25+
map<int32, string> string_map = 77;
26+
map<int32, Child> message_map = 88;
27+
message Child {}
2528
}

packages/protobuf-test/src/edition2023-serialize.test.ts

+55-12
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ import {
2424
import * as edition2023_ts from "./gen/ts/extra/edition2023_pb.js";
2525
import * as edition2023_proto2_ts from "./gen/ts/extra/edition2023-proto2_pb.js";
2626
import * as edition2023_proto3_ts from "./gen/ts/extra/edition2023-proto3_pb.js";
27-
import { Edition2023MapEncodingMessageDesc } from "./gen/ts/extra/edition2023-map-encoding_pb.js";
27+
import {
28+
Edition2023MapEncodingMessage_ChildDesc,
29+
Edition2023MapEncodingMessageDesc,
30+
} from "./gen/ts/extra/edition2023-map-encoding_pb.js";
2831
import { BinaryReader, BinaryWriter, WireType } from "@bufbuild/protobuf/wire";
2932

3033
describe("edition2023 serialization", () => {
@@ -213,26 +216,40 @@ describe("edition2023 serialization", () => {
213216
describe("message_encoding DELIMITED with maps", () => {
214217
test("should round-trip", () => {
215218
const a = create(Edition2023MapEncodingMessageDesc);
216-
a.mapField[123] = true;
219+
a.stringMap[123] = "abc";
217220
const bytes = toBinary(Edition2023MapEncodingMessageDesc, a);
218221
const b = fromBinary(Edition2023MapEncodingMessageDesc, bytes);
219222
expect(b).toStrictEqual(a);
220223
});
221-
test("should expect LENGTH_PREFIXED", () => {
224+
test("should expect LENGTH_PREFIXED map entry", () => {
222225
const w = new BinaryWriter();
223226
w.tag(77, WireType.LengthDelimited);
224-
w.uint32(4);
227+
w.fork();
228+
w.tag(1, WireType.Varint).int32(123);
229+
w.tag(2, WireType.Varint).string("abc");
230+
w.join();
231+
const bytes = w.finish();
232+
const msg = fromBinary(Edition2023MapEncodingMessageDesc, bytes);
233+
expect(msg.stringMap).toStrictEqual({
234+
123: "abc",
235+
});
236+
});
237+
test("should expect LENGTH_PREFIXED map value message", () => {
238+
const w = new BinaryWriter();
239+
w.tag(88, WireType.LengthDelimited);
240+
w.fork();
225241
w.tag(1, WireType.Varint).int32(123);
226-
w.tag(2, WireType.Varint).bool(true);
242+
w.tag(2, WireType.LengthDelimited).fork().join();
243+
w.join();
227244
const bytes = w.finish();
228245
const msg = fromBinary(Edition2023MapEncodingMessageDesc, bytes);
229-
expect(msg.mapField).toStrictEqual({
230-
123: true,
246+
expect(msg.messageMap).toStrictEqual({
247+
123: create(Edition2023MapEncodingMessage_ChildDesc),
231248
});
232249
});
233-
test("should serialize LENGTH_PREFIXED", () => {
250+
test("should serialize map entry LENGTH_PREFIXED", () => {
234251
const msg = create(Edition2023MapEncodingMessageDesc);
235-
msg.mapField[123] = true;
252+
msg.stringMap[123] = "abc";
236253
const bytes = toBinary(Edition2023MapEncodingMessageDesc, msg);
237254
const r = new BinaryReader(bytes);
238255
{
@@ -242,17 +259,43 @@ describe("edition2023 serialization", () => {
242259
const length = r.uint32();
243260
expect(length).toBe(r.len - r.pos);
244261
}
262+
{
263+
const [number] = r.tag();
264+
expect(number).toBe(1);
265+
expect(r.int32()).toBe(123);
266+
}
267+
{
268+
const [number] = r.tag();
269+
expect(number).toBe(2);
270+
expect(r.string()).toBe("abc");
271+
}
272+
{
273+
expect(r.pos).toBe(r.len);
274+
}
275+
});
276+
test("should serialize map value message LENGTH_PREFIXED", () => {
277+
const msg = create(Edition2023MapEncodingMessageDesc);
278+
msg.messageMap[123] = create(Edition2023MapEncodingMessage_ChildDesc);
279+
const bytes = toBinary(Edition2023MapEncodingMessageDesc, msg);
280+
const r = new BinaryReader(bytes);
245281
{
246282
const [number, wireType] = r.tag();
283+
expect(number).toBe(88);
284+
expect(wireType).toBe(WireType.LengthDelimited);
285+
const length = r.uint32();
286+
expect(length).toBe(r.len - r.pos);
287+
}
288+
{
289+
const [number] = r.tag();
247290
expect(number).toBe(1);
248-
expect(wireType).toBe(WireType.Varint);
249291
expect(r.int32()).toBe(123);
250292
}
251293
{
252294
const [number, wireType] = r.tag();
253295
expect(number).toBe(2);
254-
expect(wireType).toBe(WireType.Varint);
255-
expect(r.bool()).toBe(true);
296+
expect(wireType).toBe(WireType.LengthDelimited);
297+
const length = r.uint32();
298+
expect(length).toBe(0);
256299
}
257300
{
258301
expect(r.pos).toBe(r.len);

packages/protobuf-test/src/gen/js/extra/edition2023-map-encoding_pb.d.ts

+21-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/protobuf-test/src/gen/js/extra/edition2023-map-encoding_pb.js

+8-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/protobuf-test/src/gen/ts/extra/edition2023-map-encoding_pb.ts

+23-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/protobuf/src/desc-types.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,8 @@ type descFieldMapCommon<T extends ScalarType = ScalarType> = T extends Exclude<S
500500
readonly oneof: undefined;
501501
/**
502502
* Encode the map entry message delimited (a.k.a. proto2 group encoding),
503-
* or length-prefixed? This is always false for map fields.
503+
* or length-prefixed? As of Edition 2023, this is always false for map fields,
504+
* and also applies to map values, if they are messages.
504505
*/
505506
readonly delimitedEncoding: false;
506507
} : never;

0 commit comments

Comments
 (0)