Skip to content

Commit b936af4

Browse files
Hook up message encoding feature
1 parent a938467 commit b936af4

File tree

4 files changed

+103
-13
lines changed

4 files changed

+103
-13
lines changed

src/decoder.js

+8-10
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,14 @@ function missing(field) {
1616
*/
1717
function decoder(mtype) {
1818
/* eslint-disable no-unexpected-multiline */
19-
var gen = util.codegen(["r", "l"], mtype.name + "$decode")
19+
var gen = util.codegen(["r", "l", "e"], mtype.name + "$decode")
2020
("if(!(r instanceof Reader))")
2121
("r=Reader.create(r)")
2222
("var c=l===undefined?r.len:r.pos+l,m=new this.ctor" + (mtype.fieldsArray.filter(function(field) { return field.map; }).length ? ",k,value" : ""))
2323
("while(r.pos<c){")
24-
("var t=r.uint32()");
25-
if (mtype.group) gen
26-
("if((t&7)===4)")
27-
("break");
28-
gen
24+
("var t=r.uint32()")
25+
("if(t===e)")
26+
("break")
2927
("switch(t>>>3){");
3028

3129
var i = 0;
@@ -91,15 +89,15 @@ function decoder(mtype) {
9189
("}else");
9290

9391
// Non-packed
94-
if (types.basic[type] === undefined) gen(field.resolvedType.group
95-
? "%s.push(types[%i].decode(r))"
92+
if (types.basic[type] === undefined) gen(field.delimited
93+
? "%s.push(types[%i].decode(r,undefined,((t&~7)|4)))"
9694
: "%s.push(types[%i].decode(r,r.uint32()))", ref, i);
9795
else gen
9896
("%s.push(r.%s())", ref, type);
9997

10098
// Non-repeated
101-
} else if (types.basic[type] === undefined) gen(field.resolvedType.group
102-
? "%s=types[%i].decode(r)"
99+
} else if (types.basic[type] === undefined) gen(field.delimited
100+
? "%s=types[%i].decode(r,undefined,((t&~7)|4))"
103101
: "%s=types[%i].decode(r,r.uint32())", ref, i);
104102
else gen
105103
("%s=r.%s()", ref, type);

src/encoder.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ var Enum = require("./enum"),
1515
* @ignore
1616
*/
1717
function genTypePartial(gen, field, fieldIndex, ref) {
18-
return field.resolvedType.group
18+
return field.delimited
1919
? gen("types[%i].encode(%s,w.uint32(%i)).uint32(%i)", fieldIndex, ref, (field.id << 3 | 3) >>> 0, (field.id << 3 | 4) >>> 0)
2020
: gen("types[%i].encode(%s,w.uint32(%i).fork()).ldelim()", fieldIndex, ref, (field.id << 3 | 2) >>> 0);
2121
}

src/field.js

+17
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,20 @@ Object.defineProperty(Field.prototype, "optional", {
202202
}
203203
});
204204

205+
/**
206+
* Determines whether this field uses tag-delimited encoding. In proto2 this
207+
* corresponded to group syntax.
208+
* @name Field#delimited
209+
* @type {boolean}
210+
* @readonly
211+
*/
212+
Object.defineProperty(Field.prototype, "delimited", {
213+
get: function() {
214+
return this.resolvedType instanceof Type &&
215+
this._features.message_encoding === "DELIMITED";
216+
}
217+
});
218+
205219
/**
206220
* Determines whether this field is packed. Only relevant when repeated.
207221
* @name Field#packed
@@ -335,6 +349,9 @@ Field.prototype._inferLegacyProtoFeatures = function _inferLegacyProtoFeatures(e
335349
if (this.rule === "required") {
336350
features.field_presence = "LEGACY_REQUIRED";
337351
}
352+
if (this.resolvedType instanceof Type && this.resolvedType.group) {
353+
features.message_encoding = "DELIMITED";
354+
}
338355
if (this.getOption("packed") === true) {
339356
features.repeated_field_encoding = "PACKED";
340357
} else if (this.getOption("packed") === false) {

tests/comp_groups.js

+77-2
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ var protoRepeated = "message Test {\
1717
}";
1818

1919
tape.test("legacy groups", function(test) {
20-
var root = protobuf.parse(protoRequired).root;
20+
var root = protobuf.parse(protoRequired).root.resolveAll();
2121

22-
var Test = root.resolveAll().lookup("Test");
22+
var Test = root.lookup("Test");
2323
var MyGroupType = Test.get("MyGroup");
2424
var MyGroupField = Test.get("myGroup");
2525
var msg = {
@@ -30,6 +30,7 @@ tape.test("legacy groups", function(test) {
3030

3131
test.ok(MyGroupType instanceof protobuf.Type && MyGroupField instanceof protobuf.Field, "should parse to a type and a field");
3232
test.equal(MyGroupType.group, true, "should have the group flag set on the type");
33+
test.equal(MyGroupField.delimited, true, "should have the delimited flag set on the field");
3334
test.equal(MyGroupField.resolvedType, MyGroupType, "should reference the type from the field");
3435
var json = MyGroupType.toJSON();
3536
test.equal(json.group, true, "should export group=true to JSON");
@@ -77,3 +78,77 @@ tape.test("legacy groups", function(test) {
7778

7879
test.end();
7980
});
81+
82+
83+
tape.test("delimited encoding", function(test) {
84+
var root = protobuf.parse(`
85+
edition = "2023";
86+
message Message {
87+
uint32 a = 2;
88+
};
89+
message Test {
90+
Message msg = 1 [features.message_encoding = DELIMITED];
91+
}
92+
`).root.resolveAll();
93+
94+
var Test = root.lookup("Test");
95+
var Message = root.get("Message");
96+
var Field = Test.get("msg");
97+
var msg = {
98+
msg: {
99+
a: 111
100+
}
101+
};
102+
103+
test.ok(Message instanceof protobuf.Type && Field instanceof protobuf.Field, "should parse to a type and a field");
104+
test.notOk(Message.group, "should not have the group flag set on the type");
105+
test.ok(Field.delimited, "should have the delimited flag set on the field");
106+
test.equal(Field.resolvedType, Message, "should reference the type from the field");
107+
108+
test.test(test.name + " - should encode required", (function(Test, msg) { return function(test) {
109+
var buf = Test.encode(msg).finish();
110+
test.equal(buf.length, 4, "a total of 4 bytes");
111+
test.equal(buf[0], 1 << 3 | 3, "id 1, wireType 3");
112+
test.equal(buf[1], 2 << 3 | 0, "id 2, wireType 0");
113+
test.equal(buf[2], 111, "111");
114+
test.equal(buf[3], 1 << 3 | 4, "id 1, wireType 4");
115+
test.same(Test.decode(buf), msg, "and decode back the original message");
116+
test.end();
117+
};})(Test, msg));
118+
119+
// Same but repeated
120+
root = protobuf.parse(`
121+
edition = "2023";
122+
message Message {
123+
uint32 a = 2;
124+
};
125+
message Test {
126+
repeated Message msg = 1 [features.message_encoding = DELIMITED];
127+
}
128+
`).root;
129+
Test = root.resolveAll().lookup("Test");
130+
msg = {
131+
msg: [{
132+
a: 111
133+
},{
134+
a: 112
135+
}]
136+
};
137+
138+
test.test(test.name + " - should encode repeated", (function(Test, msg) { return function(test) {
139+
var buf = Test.encode(msg).finish();
140+
test.equal(buf.length, 8, "a total of 8 bytes");
141+
test.equal(buf[0], 1 << 3 | 3, "id 1, wireType 3");
142+
test.equal(buf[1], 2 << 3 | 0, "id 2, wireType 0");
143+
test.equal(buf[2], 111, "111");
144+
test.equal(buf[3], 1 << 3 | 4, "id 1, wireType 4");
145+
test.equal(buf[4], 1 << 3 | 3, "id 1, wireType 3");
146+
test.equal(buf[5], 2 << 3 | 0, "id 2, wireType 0");
147+
test.equal(buf[6], 112, "112");
148+
test.equal(buf[7], 1 << 3 | 4, "id 1, wireType 4");
149+
test.same(Test.decode(buf), msg, "and decode back the original message");
150+
test.end();
151+
};})(Test, msg));
152+
153+
test.end();
154+
});

0 commit comments

Comments
 (0)