Skip to content

Commit

Permalink
feat: support $unknownFields
Browse files Browse the repository at this point in the history
This add `$unknownFields` property to generated code. It is populated on
`.decode()` when decoder encounters currently unknown field, and
similarly used by encoder to serialize unknown fields as they are into a
binary data.

If user stores unknown fields and then upgrades their protobufs to the
latest spec - there is a chance that some of the previously unknown
fields will become known. This is handled in the encoder by attempting
to decode the fields first.

The name of the field starts with `$` because it is not a valid
character for a field name according to protobuf v3 specification.
  • Loading branch information
indutny-signal committed Jul 12, 2023
1 parent 42e5a9c commit 5875cbf
Show file tree
Hide file tree
Showing 27 changed files with 4,267 additions and 1,356 deletions.
1 change: 1 addition & 0 deletions .eslintignore
@@ -1,4 +1,5 @@
**/node_modules/*
bench/data/static_pbjs.js
bin/*
cli/wrappers/*
coverage/*
Expand Down
156 changes: 114 additions & 42 deletions bench/data/static_pbjs.js
Expand Up @@ -24,6 +24,15 @@ $root.Test = (function() {
Test.encode = function encode(message, writer) {
if (!writer)
writer = $Writer.create();
var fullyUnknown = [];
if (message.$unknownFields && $root.Test.decode)
for (var i = 0; i < message.$unknownFields.length; ++i)
try {
var known = $root.Test.decode(message.$unknownFields[i]);
fullyUnknown = fullyUnknown.concat(known.$unknownFields || []);
message = Object.assign(known, message);
} catch (_) {
}
if (message.string != null && Object.hasOwnProperty.call(message, "string"))
writer.uint32(10).string(message.string);
if (message.uint32 != null && Object.hasOwnProperty.call(message, "uint32"))
Expand All @@ -32,6 +41,8 @@ $root.Test = (function() {
$root.Test.Inner.encode(message.inner, writer.uint32(26).fork()).ldelim();
if (message.float != null && Object.hasOwnProperty.call(message, "float"))
writer.uint32(37).float(message.float);
for (var i = 0; i < fullyUnknown.length; ++i)
writer._unknownField(fullyUnknown[i]);
return writer;
};

Expand All @@ -40,22 +51,30 @@ $root.Test = (function() {
reader = $Reader.create(reader);
var end = length === undefined ? reader.len : reader.pos + length, message = new $root.Test();
while (reader.pos < end) {
var unknownStartPos = reader.pos;
var tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.string = reader.string();
break;
case 2:
message.uint32 = reader.uint32();
break;
case 3:
message.inner = $root.Test.Inner.decode(reader, reader.uint32());
break;
case 4:
message.float = reader.float();
break;
case 1: {
message.string = reader.string();
break;
}
case 2: {
message.uint32 = reader.uint32();
break;
}
case 3: {
message.inner = $root.Test.Inner.decode(reader, reader.uint32());
break;
}
case 4: {
message.float = reader.float();
break;
}
default:
reader.skipType(tag & 7);
if (!message.$unknownFields)
message.$unknownFields = [];
message.$unknownFields.push(reader.buf.slice(unknownStartPos, reader.pos));
break;
}
}
Expand All @@ -78,12 +97,23 @@ $root.Test = (function() {
Inner.encode = function encode(message, writer) {
if (!writer)
writer = $Writer.create();
var fullyUnknown = [];
if (message.$unknownFields && $root.Test.Inner.decode)
for (var i = 0; i < message.$unknownFields.length; ++i)
try {
var known = $root.Test.Inner.decode(message.$unknownFields[i]);
fullyUnknown = fullyUnknown.concat(known.$unknownFields || []);
message = Object.assign(known, message);
} catch (_) {
}
if (message.int32 != null && Object.hasOwnProperty.call(message, "int32"))
writer.uint32(8).int32(message.int32);
if (message.innerInner != null && Object.hasOwnProperty.call(message, "innerInner"))
$root.Test.Inner.InnerInner.encode(message.innerInner, writer.uint32(18).fork()).ldelim();
if (message.outer != null && Object.hasOwnProperty.call(message, "outer"))
$root.Outer.encode(message.outer, writer.uint32(26).fork()).ldelim();
for (var i = 0; i < fullyUnknown.length; ++i)
writer._unknownField(fullyUnknown[i]);
return writer;
};

Expand All @@ -92,19 +122,26 @@ $root.Test = (function() {
reader = $Reader.create(reader);
var end = length === undefined ? reader.len : reader.pos + length, message = new $root.Test.Inner();
while (reader.pos < end) {
var unknownStartPos = reader.pos;
var tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.int32 = reader.int32();
break;
case 2:
message.innerInner = $root.Test.Inner.InnerInner.decode(reader, reader.uint32());
break;
case 3:
message.outer = $root.Outer.decode(reader, reader.uint32());
break;
case 1: {
message.int32 = reader.int32();
break;
}
case 2: {
message.innerInner = $root.Test.Inner.InnerInner.decode(reader, reader.uint32());
break;
}
case 3: {
message.outer = $root.Outer.decode(reader, reader.uint32());
break;
}
default:
reader.skipType(tag & 7);
if (!message.$unknownFields)
message.$unknownFields = [];
message.$unknownFields.push(reader.buf.slice(unknownStartPos, reader.pos));
break;
}
}
Expand All @@ -127,12 +164,23 @@ $root.Test = (function() {
InnerInner.encode = function encode(message, writer) {
if (!writer)
writer = $Writer.create();
var fullyUnknown = [];
if (message.$unknownFields && $root.Test.Inner.InnerInner.decode)
for (var i = 0; i < message.$unknownFields.length; ++i)
try {
var known = $root.Test.Inner.InnerInner.decode(message.$unknownFields[i]);
fullyUnknown = fullyUnknown.concat(known.$unknownFields || []);
message = Object.assign(known, message);
} catch (_) {
}
if (message.long != null && Object.hasOwnProperty.call(message, "long"))
writer.uint32(8).int64(message.long);
if (message["enum"] != null && Object.hasOwnProperty.call(message, "enum"))
writer.uint32(16).int32(message["enum"]);
if (message.sint32 != null && Object.hasOwnProperty.call(message, "sint32"))
writer.uint32(24).sint32(message.sint32);
for (var i = 0; i < fullyUnknown.length; ++i)
writer._unknownField(fullyUnknown[i]);
return writer;
};

Expand All @@ -141,19 +189,26 @@ $root.Test = (function() {
reader = $Reader.create(reader);
var end = length === undefined ? reader.len : reader.pos + length, message = new $root.Test.Inner.InnerInner();
while (reader.pos < end) {
var unknownStartPos = reader.pos;
var tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.long = reader.int64();
break;
case 2:
message["enum"] = reader.int32();
break;
case 3:
message.sint32 = reader.sint32();
break;
case 1: {
message.long = reader.int64();
break;
}
case 2: {
message["enum"] = reader.int32();
break;
}
case 3: {
message.sint32 = reader.sint32();
break;
}
default:
reader.skipType(tag & 7);
if (!message.$unknownFields)
message.$unknownFields = [];
message.$unknownFields.push(reader.buf.slice(unknownStartPos, reader.pos));
break;
}
}
Expand Down Expand Up @@ -195,6 +250,15 @@ $root.Outer = (function() {
Outer.encode = function encode(message, writer) {
if (!writer)
writer = $Writer.create();
var fullyUnknown = [];
if (message.$unknownFields && $root.Outer.decode)
for (var i = 0; i < message.$unknownFields.length; ++i)
try {
var known = $root.Outer.decode(message.$unknownFields[i]);
fullyUnknown = fullyUnknown.concat(known.$unknownFields || []);
message = Object.assign(known, message);
} catch (_) {
}
if (message.bool != null && message.bool.length) {
writer.uint32(10).fork();
for (var i = 0; i < message.bool.length; ++i)
Expand All @@ -203,6 +267,8 @@ $root.Outer = (function() {
}
if (message.double != null && Object.hasOwnProperty.call(message, "double"))
writer.uint32(17).double(message.double);
for (var i = 0; i < fullyUnknown.length; ++i)
writer._unknownField(fullyUnknown[i]);
return writer;
};

Expand All @@ -211,23 +277,29 @@ $root.Outer = (function() {
reader = $Reader.create(reader);
var end = length === undefined ? reader.len : reader.pos + length, message = new $root.Outer();
while (reader.pos < end) {
var unknownStartPos = reader.pos;
var tag = reader.uint32();
switch (tag >>> 3) {
case 1:
if (!(message.bool && message.bool.length))
message.bool = [];
if ((tag & 7) === 2) {
var end2 = reader.uint32() + reader.pos;
while (reader.pos < end2)
case 1: {
if (!(message.bool && message.bool.length))
message.bool = [];
if ((tag & 7) === 2) {
var end2 = reader.uint32() + reader.pos;
while (reader.pos < end2)
message.bool.push(reader.bool());
} else
message.bool.push(reader.bool());
} else
message.bool.push(reader.bool());
break;
case 2:
message.double = reader.double();
break;
break;
}
case 2: {
message.double = reader.double();
break;
}
default:
reader.skipType(tag & 7);
if (!message.$unknownFields)
message.$unknownFields = [];
message.$unknownFields.push(reader.buf.slice(unknownStartPos, reader.pos));
break;
}
}
Expand Down
7 changes: 7 additions & 0 deletions cli/lib/tsd-jsdoc/publish.js
Expand Up @@ -558,6 +558,13 @@ function handleClass(element, parent) {
handleElement(child, element);
});

writeln();
if (is_interface) {
writeln("$unknownFields?: ReadonlyArray<Uint8Array>;");
} else {
writeln("public $unknownFields?: ReadonlyArray<Uint8Array>;");
}

--indent;
writeln("}");

Expand Down

0 comments on commit 5875cbf

Please sign in to comment.