Protobuf: changed all arrays in generated code into primitive arrays

This commit is contained in:
dsavvinov
2016-08-08 18:02:53 +03:00
parent 10fb92f9fa
commit 8c96f21515
5 changed files with 94 additions and 191 deletions
@@ -149,7 +149,7 @@ void ClassGenerator::generateMergeMethods(io::Printer *printer) const {
// concatenate repeated fields // concatenate repeated fields
if (properties[i]->getProtoLabel() == FieldDescriptor::LABEL_REPEATED) { if (properties[i]->getProtoLabel() == FieldDescriptor::LABEL_REPEATED) {
printer->Print(vars, "$fieldName$.addAll(other.$fieldName$)\n"); printer->Print(vars, "$fieldName$ = $fieldName$.plus((other.$fieldName$))\n");
} }
// Bytes type is handled separately // Bytes type is handled separately
@@ -17,7 +17,7 @@ namespace kotlin {
string FieldGenerator::getInitValue() const { string FieldGenerator::getInitValue() const {
if (getProtoLabel() == FieldDescriptor::LABEL_REPEATED) { if (getProtoLabel() == FieldDescriptor::LABEL_REPEATED) {
return "mutableListOf()"; return getFullType() + "(0)";
} }
if (getProtoType() == FieldDescriptor::TYPE_MESSAGE) { if (getProtoType() == FieldDescriptor::TYPE_MESSAGE) {
return getBuilderFullType() + "().build()"; return getBuilderFullType() + "().build()";
@@ -58,12 +58,13 @@ FieldGenerator::FieldGenerator(FieldDescriptor const * descriptor, ClassGenerato
, protoLabel(descriptor->label()) , protoLabel(descriptor->label())
{ } { }
void FieldGenerator::generateSerializationForPacked(io::Printer *printer, bool isRead, bool noTag) const { void FieldGenerator::generateSerializationForPacked(io::Printer *printer, bool isRead, bool noTag, bool isField) const {
map <string, string> vars; map <string, string> vars;
vars["fieldNumber"] = std::to_string(getFieldNumber()); vars["fieldNumber"] = std::to_string(getFieldNumber());
vars["builderType"] = getUnderlyingTypeGenerator().getFullType(); vars["underlyingType"] = getUnderlyingTypeGenerator().getFullType();
vars["initValue"] = getUnderlyingTypeGenerator().getInitValue(); vars["initValue"] = getUnderlyingTypeGenerator().getInitValue();
vars["fieldName"] = simpleName; vars["fieldName"] = simpleName;
vars["arrayType"] = getFullType();
bool isPrimitive = descriptor->type() != FieldDescriptor::TYPE_BYTES && bool isPrimitive = descriptor->type() != FieldDescriptor::TYPE_BYTES &&
descriptor->type() != FieldDescriptor::TYPE_MESSAGE && descriptor->type() != FieldDescriptor::TYPE_MESSAGE &&
@@ -74,10 +75,12 @@ void FieldGenerator::generateSerializationForPacked(io::Printer *printer, bool i
if (!noTag) { if (!noTag) {
printer->Print(vars, "val tag = input.readTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n"); printer->Print(vars, "val tag = input.readTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n");
} }
printer->Print(vars, "val expectedSize = input.readInt32NoTag()\n"); printer->Print(vars, "val expectedByteSize = input.readInt32NoTag()\n");
printer->Print("var readSize = 0\n");
printer->Print(vars, "while(readSize != expectedSize) {\n"); /* Now we want to compute element size of array. For this, we are estimating byte size of a single element,
printer->Indent(); * and then divide byte size of whole array by byte size of single element.
*/
printer->Print(vars, "var singleElemSize = 0\n");
/* hack: copy current FieldGenerator and change label to OPTIONAL. Also change name to /* hack: copy current FieldGenerator and change label to OPTIONAL. Also change name to
name of iterator in for-loop. name of iterator in for-loop.
@@ -89,40 +92,44 @@ void FieldGenerator::generateSerializationForPacked(io::Printer *printer, bool i
(then writing CodedOutputStream.writeMessage will be possible). (then writing CodedOutputStream.writeMessage will be possible).
*/ */
FieldGenerator singleFieldGen = getUnderlyingTypeGenerator(); FieldGenerator singleFieldGen = getUnderlyingTypeGenerator();
singleFieldGen.simpleName = "singleElemSize";
singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ true, /* isField = */ false);
/* Another dirty hack here: create tmp variable of a given type and read it from input stream printer->Print(vars, "val arraySize = expectedByteSize / singleElemSize\n");
then add that tmp var into list.
This is made because simple recursive call will generate code that tries to array[i].mergeFrom(). // Allocate new array of estimated size
This is incorrect because array has old size, while 'i' iterates over new size, which can lead printer->Print(vars, "val newArray = $arrayType$(arraySize)\n");
to ArrayOutOfIndex errors.
*/ printer->Print("var i = 0\n");
printer->Print(vars, "var tmp: $builderType$ = $initValue$\n"); printer->Print(vars, "while(i < arraySize) {\n");
printer->Indent();
printer->Print(vars, "var tmp: $underlyingType$ = $initValue$\n");
singleFieldGen.simpleName = "tmp"; singleFieldGen.simpleName = "tmp";
singleFieldGen.protoLabel = FieldDescriptor::LABEL_OPTIONAL; singleFieldGen.protoLabel = FieldDescriptor::LABEL_OPTIONAL;
singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ true); singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ true, /* isField = */ false);
singleFieldGen.generateSizeEstimationCode(printer, /* varName = */
"readSize"); // add size of current element to total size //TODO: think is it's ok that we estimate size with tag here
printer->Print(vars, "$fieldName$.add(tmp)\n"); printer->Print(vars, "$fieldName$[i] = tmp\n");
printer->Print("i += 1\n");
printer->Outdent(); printer->Outdent();
printer->Print("}\n"); printer->Print("}\n");
} }
else { else {
/** /**
* Protobuf format: * Protobuf format:
* - Check if size of array is > 0, because empty repeated fields shouldn't appear in message * - Check if size of array is > 0, because empty repeated fields shouldn't appear in message
* - Write tag explicitly * - Write tag explicitly
* - Write length as int32 (note that tag shouldn't be added) * - Write length as int32 (note that tag shouldn't be added)
* - Write all repeated elements via recursive call (for primitive types without tags) * - Write all repeated elements via recursive call (for primitive types without tags)
*/ */
// tag // tag
printer->Print(vars, "output.writeTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n"); printer->Print(vars, "output.writeTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n");
// length // length
printer->Print(vars, "var arrayByteSize = 0\n"); printer->Print(vars, "var arrayByteSize = 0\n");
generateSizeEstimationCode(printer, "arrayByteSize", /* noTag = */ true); generateSizeEstimationCode(printer, "arrayByteSize", /* noTag = */ true, /* isField = */ false);
printer->Print(vars, "output.writeInt32NoTag(arrayByteSize)\n"); printer->Print(vars, "output.writeInt32NoTag(arrayByteSize)\n");
// all elements // all elements
@@ -134,75 +141,14 @@ void FieldGenerator::generateSerializationForPacked(io::Printer *printer, bool i
singleFieldGen.simpleName = "item"; singleFieldGen.simpleName = "item";
singleFieldGen.protoLabel = FieldDescriptor::LABEL_OPTIONAL; singleFieldGen.protoLabel = FieldDescriptor::LABEL_OPTIONAL;
singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ isPrimitive); singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ isPrimitive, /* isField = */ false);
printer->Outdent(); // for-loop printer->Outdent(); // for-loop
printer->Print("}\n"); printer->Print("}\n");
} }
} }
void FieldGenerator::generateSerializationForRepeated(io::Printer *printer, bool isRead, bool noTag) const { void FieldGenerator::generateSerializationForEnums(io::Printer * printer, bool isRead, bool noTag, bool isField) const {
map <string, string> vars;
vars["fieldNumber"] = std::to_string(getFieldNumber());
vars["builderType"] = getUnderlyingTypeGenerator().getFullType();
vars["initValue"] = getUnderlyingTypeGenerator().getInitValue();
vars["fieldName"] = simpleName;
if (isRead) {
printer->Print("var readSize = 0\n");
/* hack: copy current FieldGenerator and change label to OPTIONAL. Also change name to
name of iterator in for-loop.
This will allow to re-use this function for generating serialization code for elements of array.
More importantly, this will care about nested types too.
Efficiently, it inlines serialization code for all underlying types.
This hack isn't necessary from the architectural point of view and could be safely
removed as soon as target code will support inheritance and interfaces.
(then writing CodedOutputStream.writeMessage will be possible).
*/
FieldGenerator singleFieldGen = getUnderlyingTypeGenerator();
/* Another dirty hack here: create tmp variable of a given type and read it from input stream
then add that tmp var into list.
This is made because simple recursive call will generate code that tries to array[i].mergeFrom().
This is incorrect because array has old size, while 'i' iterates over new size, which can lead
to ArrayOutOfIndex errors.
*/
printer->Print(vars, "var tmp: $builderType$ = $initValue$\n");
singleFieldGen.simpleName = "tmp";
singleFieldGen.protoLabel = FieldDescriptor::LABEL_OPTIONAL;
singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ true);
singleFieldGen.generateSizeEstimationCode(printer, /* varName = */
"readSize", /* noTag = */ true); // add size of current element to total size. Note that
printer->Print(vars, "$fieldName$.add(tmp)\n");
}
else {
/**
* Protobuf format:
* - Check if size of array is > 0, because empty repeated fields shouldn't appear in message
* - Write tag explicitly
* - Write length as int32 (note that tag shouldn't be added)
* - Write all repeated elements via recursive call (for primitive types without tags)
*/
// all elements
printer->Print(vars, "for (item in $fieldName$) {\n");
printer->Indent();
// hack: see above
FieldGenerator singleFieldGen = FieldGenerator(descriptor, enclosingClass, nameResolver);
singleFieldGen.simpleName = "item";
singleFieldGen.protoLabel = FieldDescriptor::LABEL_OPTIONAL;
singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ true);
printer->Outdent(); // for-loop
printer->Print("}\n");
}
}
void FieldGenerator::generateSerializationForEnums(io::Printer * printer, bool isRead, bool noTag) const {
map <string, string> vars; map <string, string> vars;
vars["converter"] = getEnumFromIntConverter(); vars["converter"] = getEnumFromIntConverter();
@@ -229,7 +175,7 @@ void FieldGenerator::generateSerializationForEnums(io::Printer * printer, bool i
} }
} }
void FieldGenerator::generateSerializationForMessages(io::Printer * printer, bool isRead, bool noTag) const { void FieldGenerator::generateSerializationForMessages(io::Printer * printer, bool isRead, bool noTag, bool isField) const {
map <string, string> vars; map <string, string> vars;
vars["fieldNumber"] = std::to_string(getFieldNumber()); vars["fieldNumber"] = std::to_string(getFieldNumber());
vars["dollar"] = "$"; vars["dollar"] = "$";
@@ -274,7 +220,7 @@ void FieldGenerator::generateSerializationForMessages(io::Printer * printer, boo
} }
} }
void FieldGenerator::generateSerializationForPrimitives(io::Printer * printer, bool isRead, bool noTag) const { void FieldGenerator::generateSerializationForPrimitives(io::Printer * printer, bool isRead, bool noTag, bool isField) const {
map <string, string> vars; map <string, string> vars;
vars["fieldName"] = simpleName; vars["fieldName"] = simpleName;
vars["suffix"] = getKotlinFunctionSuffix(); vars["suffix"] = getKotlinFunctionSuffix();
@@ -297,7 +243,7 @@ void FieldGenerator::generateSerializationForPrimitives(io::Printer * printer, b
} }
} }
void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead, bool noTag) const { void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead, bool noTag, bool isField) const {
map <string, string> vars; map <string, string> vars;
vars["fieldName"] = simpleName; vars["fieldName"] = simpleName;
vars["initValue"] = getInitValue(); vars["initValue"] = getInitValue();
@@ -307,60 +253,50 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
* Note that it should be first check because of Google's FieldDescriptor structure */ * Note that it should be first check because of Google's FieldDescriptor structure */
if (getProtoLabel() == FieldDescriptor::LABEL_REPEATED) { if (getProtoLabel() == FieldDescriptor::LABEL_REPEATED) {
// we shouldn't write fields with default values when writing // we shouldn't write fields with default values when writing
if (!isRead) { if (!isRead && isField) {
printer->Print(vars, "if ($fieldName$.size > 0) {\n"); printer->Print(vars, "if ($fieldName$.size > 0) {\n");
printer->Indent(); printer->Indent();
} }
bool isPrimitive = descriptor->type() != FieldDescriptor::TYPE_BYTES && bool isPrimitive = descriptor->type() != FieldDescriptor::TYPE_BYTES &&
descriptor->type() != FieldDescriptor::TYPE_MESSAGE && descriptor->type() != FieldDescriptor::TYPE_MESSAGE &&
descriptor->type() != FieldDescriptor::TYPE_STRING && descriptor->type() != FieldDescriptor::TYPE_STRING;
descriptor->type() != FieldDescriptor::TYPE_ENUM;
if (isPrimitive) { if (isPrimitive) {
generateSerializationForPacked(printer, isRead, noTag); generateSerializationForPacked(printer, isRead, noTag, isField);
} }
else { else {
generateSerializationForRepeated(printer, isRead, noTag); throw UnreachableStateException("Arrays of non-primitive types are not supported");
} }
} }
/* Then check is current field is enum. We have to handle it separately too, because
* we have to pass enums as Int's to CodedStreams as per protobuf-format */
else if (descriptor->type() == FieldDescriptor::TYPE_ENUM) {
// we shouldn't write fields with default values when writing
if (!isRead) {
printer->Print(vars, "if ($fieldName$ != $initValue$) {\n");
printer->Indent();
}
generateSerializationForEnums(printer, isRead, noTag);
}
/* Then check for nested messages. Here we re-use writeTo method, that should be defined in
* that message.
* Note that readFrom/writeTo methods write message as it's top-level message, i.e. without
* any tags. Therefore, we have to prepend tags and size manually. */
else if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) {
// we shouldn't write fields with default values when writing
if (!isRead) {
printer->Print(vars, "if ($fieldName$ != $initValue$) {\n");
printer->Indent();
}
generateSerializationForMessages(printer, isRead, noTag);
}
/* Finally, serialize trivial cases */
else { else {
// we shouldn't write fields with default values when writing // we shouldn't write fair fields with default values
if (!isRead) { if (!isRead && isField) {
printer->Print(vars, "if ($fieldName$ != $initValue$) {\n"); printer->Print(vars, "if ($fieldName$ != $initValue$) {\n");
printer->Indent(); printer->Indent();
} }
generateSerializationForPrimitives(printer, isRead, noTag); /* Then check is current field is enum. We have to handle it separately too, because
* we have to pass enums as Int's to CodedStreams as per protobuf-format */
if (descriptor->type() == FieldDescriptor::TYPE_ENUM) {
generateSerializationForEnums(printer, isRead, noTag, isField);
}
/* Then check for nested messages. Here we re-use writeTo method, that should be defined in
* that message.
* Note that readFrom/writeTo methods write message as it's top-level message, i.e. without
* any tags. Therefore, we have to prepend tags and size manually. */
else if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) {
generateSerializationForMessages(printer, isRead, noTag, isField);
}
/* Finally, serialize trivial cases */
else {
generateSerializationForPrimitives(printer, isRead, noTag, isField);
}
} }
if (!isRead) { if (!isRead && isField) {
printer->Outdent(); printer->Outdent();
printer->Print("}\n"); printer->Print("}\n");
} }
@@ -392,45 +328,20 @@ void FieldGenerator::generateRepeatedMethods(io::Printer * printer, bool isBuild
// generate indexed setter for builders // generate indexed setter for builders
if (isBuilder) { if (isBuilder) {
printer->Print(vars, "fun set$elementType$(index: Int, $arg$: $elementType$): $builderName$ {\n"); printer->Print(vars, "fun set$fieldName$ByIndex(index: Int, $arg$: $elementType$): $builderName$ {\n");
printer->Indent(); printer->Indent();
printer->Print(vars, "$fieldName$[index] = $arg$\n"); printer->Print(vars, "$fieldName$[index] = $arg$\n");
printer->Print(vars, "return this\n"); printer->Print(vars, "return this\n");
printer->Outdent(); printer->Outdent();
printer->Print("}\n"); printer->Print("}\n");
} }
if (isBuilder) {
// generate single-add for builders
printer->Print(vars, "fun add$elementType$($arg$: $elementType$): $builderName$ {\n");
printer->Indent();
printer->Print(vars, "$fieldName$.add($arg$)\n"
"return this\n");
printer->Outdent();
printer->Print(vars, "}\n");
// generate addAll for builders
printer->Print(vars, "fun addAll$elementType$($arg$: Iterable<$elementType$>): $builderName$ {\n");
printer->Indent();
printer->Print(vars, "for (item in $arg$) {\n");
printer->Indent();
printer->Print(vars, "$fieldName$.add(item)\n");
printer->Outdent(); // for-loop
printer->Print("}\n");
printer->Print("return this\n");
printer->Outdent(); // function body
printer->Print("}\n");
}
} }
string FieldGenerator::getKotlinFunctionSuffix() const { string FieldGenerator::getKotlinFunctionSuffix() const {
return name_resolving::protobufTypeToKotlinFunctionSuffix(descriptor->type()); return name_resolving::protobufTypeToKotlinFunctionSuffix(descriptor->type());
} }
void FieldGenerator::generateSizeEstimationCode(io::Printer *printer, string varName, bool noTag) const { void FieldGenerator::generateSizeEstimationCode(io::Printer *printer, string varName, bool noTag, bool isField) const {
map<string, string> vars; map<string, string> vars;
vars["varName"] = varName; vars["varName"] = varName;
vars["fieldName"] = simpleName; vars["fieldName"] = simpleName;
@@ -459,7 +370,7 @@ void FieldGenerator::generateSizeEstimationCode(io::Printer *printer, string var
FieldGenerator singleFieldGen = FieldGenerator(descriptor, enclosingClass, nameResolver); FieldGenerator singleFieldGen = FieldGenerator(descriptor, enclosingClass, nameResolver);
singleFieldGen.protoLabel = FieldDescriptor::LABEL_OPTIONAL; singleFieldGen.protoLabel = FieldDescriptor::LABEL_OPTIONAL;
singleFieldGen.simpleName = "item"; singleFieldGen.simpleName = "item";
singleFieldGen.generateSizeEstimationCode(printer, "arraySize", noTag); singleFieldGen.generateSizeEstimationCode(printer, "arraySize", noTag, /* isField = */ false);
printer->Outdent(); // for-loop printer->Outdent(); // for-loop
printer->Print("}\n"); printer->Print("}\n");
@@ -477,42 +388,38 @@ void FieldGenerator::generateSizeEstimationCode(io::Printer *printer, string var
return; return;
} }
// If we are generating code for fair field (not for dummy), then we should check for default value
// Note that we can't lift that check up, because repeated fields should be treated separately
if (isField) {
printer->Print(vars, "if ($fieldName$ != $initValue$) {\n");
printer->Indent();
}
// Then, call getSize recursively for nested messages // Then, call getSize recursively for nested messages
// TODO: currently suboptimal repeatative calls getSize() are being made. We can optimize it later via caching calls to getSize() // TODO: currently suboptimal repeatative calls getSize() are being made. We can optimize it later via caching calls to getSize()
if (getProtoType() == FieldDescriptor::TYPE_MESSAGE) { if (getProtoType() == FieldDescriptor::TYPE_MESSAGE) {
printer->Print(vars, "if ($fieldName$ != $initValue$) {\n");
printer->Indent();
vars["maybeNoTag"] = noTag ? "NoTag" : ""; vars["maybeNoTag"] = noTag ? "NoTag" : "";
vars["maybeFieldNumber"] = noTag ? "" : std::to_string(getFieldNumber()); vars["maybeFieldNumber"] = noTag ? "" : std::to_string(getFieldNumber());
printer->Print(vars, "$varName$ += $fieldName$.getSize$maybeNoTag$($maybeFieldNumber$)" printer->Print(vars, "$varName$ += $fieldName$.getSize$maybeNoTag$($maybeFieldNumber$)"
"\n"); "\n");
printer->Outdent(); // if-clause
printer->Print("}\n");
return;
} }
// Next, process enums as they should be casted to ints manually // Next, process enums as they should be casted to ints manually
if (getProtoType() == FieldDescriptor::TYPE_ENUM) { else if (getProtoType() == FieldDescriptor::TYPE_ENUM) {
printer->Print(vars, "if ($fieldName$ != $initValue$) {\n");
printer->Indent();
printer->Print(vars, "$varName$ += WireFormat.getEnumSize($fieldNumber$, $fieldName$.ord)\n"); printer->Print(vars, "$varName$ += WireFormat.getEnumSize($fieldNumber$, $fieldName$.ord)\n");
printer->Outdent(); // if-clause
printer->Print("}\n");
return;
} }
// Finally, get size of all primitive types trivially via call to WireFormat in runtime // Finally, get size of all primitive types trivially via call to WireFormat in runtime
vars["kotlinSuffix"] = getKotlinFunctionSuffix(); else {
printer->Print(vars, "if ($fieldName$ != $initValue$) {\n"); vars["kotlinSuffix"] = getKotlinFunctionSuffix();
printer->Indent(); printer->Print(vars, "$varName$ += WireFormat.get$kotlinSuffix$Size($fieldNumber$, $fieldName$)\n");
}
printer->Print(vars, "$varName$ += WireFormat.get$kotlinSuffix$Size($fieldNumber$, $fieldName$)\n"); if (isField) {
printer->Outdent(); // if-clause
printer->Outdent(); // if-clause printer->Print("}\n");
printer->Print("}\n"); }
return;
} }
FieldDescriptor::Label FieldGenerator::getProtoLabel() const { FieldDescriptor::Label FieldGenerator::getProtoLabel() const {
@@ -543,7 +450,7 @@ string FieldGenerator::getSimpleType() const {
string FieldGenerator::getFullType() const { string FieldGenerator::getFullType() const {
if (getProtoLabel() == FieldDescriptor::LABEL_REPEATED) { if (getProtoLabel() == FieldDescriptor::LABEL_REPEATED) {
return "MutableList <" + getUnderlyingTypeGenerator().getFullType() + ">"; return getUnderlyingTypeGenerator().getFullType() + "Array";
} }
if (getProtoType() == FieldDescriptor::TYPE_MESSAGE || if (getProtoType() == FieldDescriptor::TYPE_MESSAGE ||
getProtoType() == FieldDescriptor::TYPE_ENUM) { getProtoType() == FieldDescriptor::TYPE_ENUM) {
@@ -27,11 +27,11 @@ private:
void generateSetter(io::Printer * printer) const; void generateSetter(io::Printer * printer) const;
void generateRepeatedMethods(io::Printer * printer, bool isBuilder) const; void generateRepeatedMethods(io::Printer * printer, bool isBuilder) const;
void generateSerializationForPacked (io::Printer * printer, bool isRead, bool noTag) const; void generateSerializationForPacked (io::Printer * printer, bool isRead, bool noTag, bool isField) const;
void generateSerializationForRepeated (io::Printer * printer, bool isRead, bool noTag) const; void generateSerializationForRepeated (io::Printer * printer, bool isRead, bool noTag, bool isField) const;
void generateSerializationForEnums (io::Printer * printer, bool isRead, bool noTag) const; void generateSerializationForEnums (io::Printer * printer, bool isRead, bool noTag, bool isField) const;
void generateSerializationForMessages (io::Printer * printer, bool isRead, bool noTag) const; void generateSerializationForMessages (io::Printer * printer, bool isRead, bool noTag, bool isField) const;
void generateSerializationForPrimitives (io::Printer * printer, bool isRead, bool noTag) const; void generateSerializationForPrimitives (io::Printer * printer, bool isRead, bool noTag, bool isField) const;
public: public:
ClassGenerator const * enclosingClass; // class, in which that field is defined ClassGenerator const * enclosingClass; // class, in which that field is defined
NameResolver * nameResolver; NameResolver * nameResolver;
@@ -84,8 +84,8 @@ public:
string getWireType() const; string getWireType() const;
void generateCode(io::Printer * printer, bool isBuilder) const; void generateCode(io::Printer * printer, bool isBuilder) const;
void generateSerializationCode(io::Printer * printer, bool isRead = false, bool noTag = false) const; void generateSerializationCode (io::Printer * printer, bool isRead = false, bool noTag = false, bool isField = true) const;
void generateSizeEstimationCode(io::Printer * printer, string varName, bool noTag = false) const; void generateSizeEstimationCode(io::Printer * printer, string varName, bool noTag = false, bool isField = true) const;
FieldGenerator(FieldDescriptor const * descriptor, ClassGenerator const * enclosingClass, NameResolver * nameResolver); FieldGenerator(FieldDescriptor const * descriptor, ClassGenerator const * enclosingClass, NameResolver * nameResolver);
}; };
@@ -5,12 +5,8 @@ option java_package = "proto.car";
option java_outer_classname = "Route"; option java_outer_classname = "Route";
message RouteRequest { message RouteRequest {
repeated WayPoint way_points = 1; repeated int32 dinstances = 1;
repeated int32 angle_deltas = 2;
message WayPoint {
int32 distance = 2;
int32 angle_delta = 3;
}
} }
message RouteResponse { message RouteResponse {