Skip to content

Commit

Permalink
Introduce mandatory mechanism for specifying protobuf field numbers
Browse files Browse the repository at this point in the history
Previously, field numbers were assigned automatically during protobuf schema
generation, based on iteration order of properties in a data class. Since
properties are iterated in alphabetical order, it is very easy to modify
the data class in a way that changes a lot of field numbers, introducing
a breaking change to the protobuf schema.

This commit introduces the annotation `@ProtobufField` as a mandatory
mechanism for explicitly providing field numbers. If a data class that
uses `@ProtobufGen` has a property without `@ProtobufField`, the protobuf
code generator fails.

The fields in the protobuf schema are generated in the field number order,
not in alphabetical order. This is to make sure that a Vert.x-generated
and `protoc`-generated serializers produce outputs that are not just mutually
_compatible_, but also _binary identical_.
  • Loading branch information
Ladicek committed Sep 12, 2023
1 parent 7aa82a4 commit cca6885
Show file tree
Hide file tree
Showing 12 changed files with 538 additions and 431 deletions.
70 changes: 35 additions & 35 deletions vertx-codegen-protobuf/src/converters/generated/dataobjects.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,48 +11,48 @@ import "vertx-struct.proto";
import "datetime.proto";

message Address {
float latitude = 1;
string name = 1;
float longitude = 2;
string name = 3;
float latitude = 3;
}

message RecursiveItem {
RecursiveItem childA = 1;
RecursiveItem childB = 2;
RecursiveItem childC = 3;
string id = 4;
string id = 1;
RecursiveItem childA = 2;
RecursiveItem childB = 3;
RecursiveItem childC = 4;
}

message User {
Address address = 1;
string userName = 1;
int32 age = 2;
bool boolField = 3;
int32 byteField = 4;
int32 charField = 5;
double doubleField = 6;
float floatField = 7;
io.vertx.protobuf.Instant instantField = 8;
repeated int32 integerListField = 9;
map<string, int32> integerValueMap = 10;
repeated io.vertx.protobuf.Struct jsonListField = 11;
io.vertx.protobuf.Struct jsonObjectField = 12;
map<string, io.vertx.protobuf.Struct> jsonValueMap = 13;
int64 longField = 14;
bool primitiveBoolean = 15;
int32 primitiveByte = 16;
int32 primitiveChar = 17;
double primitiveDouble = 18;
float primitiveFloat = 19;
int32 primitiveInt = 20;
int64 primitiveLong = 21;
int32 primitiveShort = 22;
int32 shortField = 23;
map<string, string> stringValueMap = 24;
repeated Address structListField = 25;
map<string, Address> structValueMap = 26;
string userName = 27;
io.vertx.protobuf.ZonedDateTime zonedDateTimeField = 28;
repeated io.vertx.protobuf.ZonedDateTime zonedDateTimeListField = 29;
map<string, io.vertx.protobuf.ZonedDateTime> zonedDateTimeValueMap = 30;
repeated int32 integerListField = 3;
repeated Address structListField = 4;
repeated io.vertx.protobuf.ZonedDateTime zonedDateTimeListField = 5;
repeated io.vertx.protobuf.Struct jsonListField = 6;
Address address = 7;
int32 byteField = 8;
double doubleField = 9;
float floatField = 10;
int64 longField = 11;
bool boolField = 12;
int32 shortField = 13;
int32 charField = 14;
map<string, string> stringValueMap = 15;
map<string, int32> integerValueMap = 16;
map<string, Address> structValueMap = 17;
map<string, io.vertx.protobuf.Struct> jsonValueMap = 18;
map<string, io.vertx.protobuf.ZonedDateTime> zonedDateTimeValueMap = 19;
io.vertx.protobuf.ZonedDateTime zonedDateTimeField = 20;
io.vertx.protobuf.Instant instantField = 21;
io.vertx.protobuf.Struct jsonObjectField = 22;
bool primitiveBoolean = 23;
int32 primitiveByte = 24;
int32 primitiveShort = 25;
int32 primitiveInt = 26;
int64 primitiveLong = 27;
float primitiveFloat = 28;
double primitiveDouble = 29;
int32 primitiveChar = 30;
}

Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ public static void fromProto(CodedInputStream input, Address obj) throws IOExcep
int tag;
while ((tag = input.readTag()) != 0) {
switch (tag) {
case 13: {
obj.setLatitude(input.readFloat());
case 10: {
obj.setName(input.readString());
break;
}
case 21: {
obj.setLongitude(input.readFloat());
break;
}
case 26: {
obj.setName(input.readString());
case 29: {
obj.setLatitude(input.readFloat());
break;
}
}
Expand All @@ -44,14 +44,14 @@ public static void toProto(Address obj, CodedOutputStream output) throws IOExcep

public static int toProto(Address obj, CodedOutputStream output, ExpandableIntArray cache, int index) throws IOException {
index = index + 1;
if (obj.getLatitude() != null) {
output.writeFloat(1, obj.getLatitude());
if (obj.getName() != null) {
output.writeString(1, obj.getName());
}
if (obj.getLongitude() != null) {
output.writeFloat(2, obj.getLongitude());
}
if (obj.getName() != null) {
output.writeString(3, obj.getName());
if (obj.getLatitude() != null) {
output.writeFloat(3, obj.getLatitude());
}
return index;
}
Expand All @@ -65,14 +65,14 @@ public static int computeSize(Address obj) {
public static int computeSize(Address obj, ExpandableIntArray cache, final int baseIndex) {
int size = 0;
int index = baseIndex + 1;
if (obj.getLatitude() != null) {
size += CodedOutputStream.computeFloatSize(1, obj.getLatitude());
if (obj.getName() != null) {
size += CodedOutputStream.computeStringSize(1, obj.getName());
}
if (obj.getLongitude() != null) {
size += CodedOutputStream.computeFloatSize(2, obj.getLongitude());
}
if (obj.getName() != null) {
size += CodedOutputStream.computeStringSize(3, obj.getName());
if (obj.getLatitude() != null) {
size += CodedOutputStream.computeFloatSize(3, obj.getLatitude());
}
cache.set(baseIndex, size);
return index;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ public static void fromProto(CodedInputStream input, RecursiveItem obj) throws I
while ((tag = input.readTag()) != 0) {
switch (tag) {
case 10: {
obj.setId(input.readString());
break;
}
case 18: {
int length = input.readUInt32();
int limit = input.pushLimit(length);
RecursiveItem nested = new RecursiveItem();
Expand All @@ -29,7 +33,7 @@ public static void fromProto(CodedInputStream input, RecursiveItem obj) throws I
input.popLimit(limit);
break;
}
case 18: {
case 26: {
int length = input.readUInt32();
int limit = input.pushLimit(length);
RecursiveItem nested = new RecursiveItem();
Expand All @@ -38,7 +42,7 @@ public static void fromProto(CodedInputStream input, RecursiveItem obj) throws I
input.popLimit(limit);
break;
}
case 26: {
case 34: {
int length = input.readUInt32();
int limit = input.pushLimit(length);
RecursiveItem nested = new RecursiveItem();
Expand All @@ -47,10 +51,6 @@ public static void fromProto(CodedInputStream input, RecursiveItem obj) throws I
input.popLimit(limit);
break;
}
case 34: {
obj.setId(input.readString());
break;
}
}
}
}
Expand All @@ -63,24 +63,24 @@ public static void toProto(RecursiveItem obj, CodedOutputStream output) throws I

public static int toProto(RecursiveItem obj, CodedOutputStream output, ExpandableIntArray cache, int index) throws IOException {
index = index + 1;
if (obj.getId() != null) {
output.writeString(1, obj.getId());
}
if (obj.getChildA() != null) {
output.writeUInt32NoTag(10);
output.writeUInt32NoTag(18);
output.writeUInt32NoTag(cache.get(index));
index = RecursiveItemProtoConverter.toProto(obj.getChildA(), output, cache, index);
}
if (obj.getChildB() != null) {
output.writeUInt32NoTag(18);
output.writeUInt32NoTag(26);
output.writeUInt32NoTag(cache.get(index));
index = RecursiveItemProtoConverter.toProto(obj.getChildB(), output, cache, index);
}
if (obj.getChildC() != null) {
output.writeUInt32NoTag(26);
output.writeUInt32NoTag(34);
output.writeUInt32NoTag(cache.get(index));
index = RecursiveItemProtoConverter.toProto(obj.getChildC(), output, cache, index);
}
if (obj.getId() != null) {
output.writeString(4, obj.getId());
}
return index;
}

Expand All @@ -93,33 +93,33 @@ public static int computeSize(RecursiveItem obj) {
public static int computeSize(RecursiveItem obj, ExpandableIntArray cache, final int baseIndex) {
int size = 0;
int index = baseIndex + 1;
if (obj.getId() != null) {
size += CodedOutputStream.computeStringSize(1, obj.getId());
}
if (obj.getChildA() != null) {
size += CodedOutputStream.computeUInt32SizeNoTag(10);
size += CodedOutputStream.computeUInt32SizeNoTag(18);
int savedIndex = index;
index = RecursiveItemProtoConverter.computeSize(obj.getChildA(), cache, index);
int dataSize = cache.get(savedIndex);
size += CodedOutputStream.computeUInt32SizeNoTag(dataSize);
size += dataSize;
}
if (obj.getChildB() != null) {
size += CodedOutputStream.computeUInt32SizeNoTag(18);
size += CodedOutputStream.computeUInt32SizeNoTag(26);
int savedIndex = index;
index = RecursiveItemProtoConverter.computeSize(obj.getChildB(), cache, index);
int dataSize = cache.get(savedIndex);
size += CodedOutputStream.computeUInt32SizeNoTag(dataSize);
size += dataSize;
}
if (obj.getChildC() != null) {
size += CodedOutputStream.computeUInt32SizeNoTag(26);
size += CodedOutputStream.computeUInt32SizeNoTag(34);
int savedIndex = index;
index = RecursiveItemProtoConverter.computeSize(obj.getChildC(), cache, index);
int dataSize = cache.get(savedIndex);
size += CodedOutputStream.computeUInt32SizeNoTag(dataSize);
size += dataSize;
}
if (obj.getId() != null) {
size += CodedOutputStream.computeStringSize(4, obj.getId());
}
cache.set(baseIndex, size);
return index;
}
Expand Down
Loading

0 comments on commit cca6885

Please sign in to comment.