Compiler: added parsers

This commit is contained in:
dsavvinov
2016-07-15 16:15:05 +03:00
parent d9456f631e
commit d85191b1d7
8 changed files with 432 additions and 152 deletions
@@ -12,6 +12,8 @@ namespace protobuf {
namespace compiler {
namespace kotlin {
class FieldGenerator; // declared in "kotlin_file_generator.h"
void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
generateHeader(printer, isBuilder);
printer->Indent();
@@ -22,7 +24,7 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
* Also note that fields should be declared before init section.
*/
for (FieldGenerator *gen: properties) {
gen->generateCode(printer, isBuilder, simpleName);
gen->generateCode(printer, isBuilder);
printer->Print("\n");
}
@@ -54,13 +56,16 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
generateBuilder(printer);
printer->Print("\n");
generateMergeFrom(printer);
generateMergeMethods(printer);
}
// build() and setters are only for builders
if (isBuilder) {
printer->Print("\n");
generateBuildMethod(printer);
printer->Print("\n");
generateParseMethods(printer);
}
printer->Outdent();
@@ -70,11 +75,12 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
ClassGenerator::ClassGenerator(Descriptor const *descriptor)
: descriptor(descriptor) {
simpleName = descriptor->name();
builderName = "Builder" + simpleName;
int field_count = descriptor->field_count();
for (int i = 0; i < field_count; ++i) {
FieldDescriptor const * fieldDescriptor = descriptor->field(i);
properties.push_back(new FieldGenerator(fieldDescriptor));
properties.push_back(new FieldGenerator(fieldDescriptor, /* enclosingClass = */ this));
}
int nested_types_count = descriptor->nested_type_count();
@@ -111,24 +117,58 @@ void ClassGenerator::generateBuilder(io::Printer * printer) const {
generateCode(printer, /* isBuilder = */ true);
}
void ClassGenerator::generateMergeFrom(io::Printer * printer) const {
//TODO: Looks pretty dirty. Should reconsider process of generating readFrom, mergeFrom and writeTo.
void ClassGenerator::generateMergeMethods(io::Printer *printer) const {
map <string, string> vars;
printer->Print(vars, "fun mergeFrom (input: CodedInputStream) {\n");
// mergeWith(other: Message)
printer->Print("\n");
vars["className"] = simpleName;
printer->Print(vars, "fun mergeWith (other: $className$) {\n");
printer->Indent();
for (int i = 0; i < properties.size(); ++i) {
properties[i]->generateSerializationCode(printer, /* isRead = */ true);
vars["fieldName"] = properties[i]->simpleName;
// concatenate repeated fields
if (properties[i]->modifier == FieldDescriptor::LABEL_REPEATED) {
printer->Print(vars, "$fieldName$.addAll(other.$fieldName$)\n");
}
// Bytes type is handled separately
else if (properties[i]->protoType == FieldDescriptor::TYPE_BYTES) {
vars["initValue"] = properties[i]->getInitValue();
printer->Print(vars, "$fieldName$?.plus(other.$fieldName$ ?: $initValue$)\n");
}
// for all other cases just take other's field
else {
printer->Print(vars, "$fieldName$ = other.$fieldName$\n");
}
}
printer->Outdent();
printer->Print("}\n");
// mergeFrom(input: CodedInputStream)
printer->Print("\n");
printer->Print(vars, "fun mergeFrom (input: CodedInputStream) {\n");
printer->Indent();
vars["builderName"] = builderName;
printer->Print(vars, "val builder = $builderName$()\n");
printer->Print("mergeWith(builder.parseFrom(input).build())");
printer->Outdent();
printer->Print("}\n");
}
void ClassGenerator::generateSerializers(io::Printer * printer, bool isRead) const {
// readFrom(input: CodedInputStream) OR
// writeTo(output: CodedOutputStream)
map <string, string> vars;
vars["funName"]= isRead ? "readFrom" : "writeTo";
vars["returnType"] = isRead ? "Builder" + simpleName : "Unit";
vars["returnType"] = isRead ? builderName : "Unit";
vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream";
vars["arg"] = isRead ? "input" : "output";
vars["maybeSeparator"] = isRead ? "" : ", ";
@@ -151,7 +191,7 @@ void ClassGenerator::generateSerializersNoTag(io::Printer *printer, bool isRead)
vars["funName"]= isRead ? "readFromNoTag" : "writeToNoTag";
vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream";
vars["arg"] = isRead ? "input" : "output";
vars["returnType"] = isRead ? "Builder" + simpleName : "Unit";
vars["returnType"] = isRead ? builderName : "Unit";
vars["maybeReturn"] = isRead ? "return this\n" : "";
// generate function header
@@ -176,14 +216,14 @@ void ClassGenerator::generateHeader(io::Printer * printer, bool isBuilder) const
// build list of arguments like 'field1: Type1, field2: Type2, ... '
string argumentList = "";
for (int i = 0; i < properties.size(); ++i) {
argumentList += properties[i]->simpleName + ": " + properties[i]->fullType + " = " + properties[i]->initValue;
argumentList += properties[i]->simpleName + ": " + properties[i]->fullType + " = " + properties[i]->getInitValue();
if (i + 1 != properties.size()) {
argumentList += ", ";
}
}
map<string, string> vars;
vars["name"] = (isBuilder? "Builder" : "") + simpleName;
vars["name"] = isBuilder? builderName : simpleName;
vars["argumentList"] = argumentList;
vars["maybePrivate"] = isBuilder? "" : " private";
printer->Print(vars,
@@ -230,6 +270,69 @@ void ClassGenerator::generateInitSection(io::Printer * printer) const {
printer->Print("}\n");
}
void ClassGenerator::generateParseMethods(io::Printer *printer) const {
// parseFieldFrom(input: CodedInputStream): Boolean
map <string, string> vars;
vars["builderName"] = builderName;
printer->Print("fun parseFieldFrom(input: CodedInputStream): Boolean {\n");
printer->Indent();
// messages are not required to end with 0-tag, therefore parsing method should check for EOF
printer->Print("if (input.isAtEnd()) { return false }\n");
// read tag and check if some field will follow (0-tag inidcates end of message)
printer->Print("val tag = input.readInt32NoTag()\n");
printer->Print("if (tag == 0) { return false } \n");
// parse tag into field number and wire type
printer->Print("val fieldNumber = WireFormat.getTagFieldNumber(tag)\n");
printer->Print("val wireType = WireFormat.getTagWireType(tag)\n");
// 'when' to map fieldNumber into fieldName
printer->Print("when(fieldNumber) {\n");
printer->Indent();
for (int i = 0; i < properties.size(); ++i) {
vars["fieldNumber"] = std::to_string(properties[i]->fieldNumber);
vars["kotlinFunSuffix"] = properties[i]->getKotlinFunctionSuffix();
printer->Print(vars, "$fieldNumber$ -> ");
// code for serialization arrays and messages consists of more than one line and needs enclosing brackets
if (properties[i]->modifier == FieldDescriptor::LABEL_REPEATED
|| properties[i]->protoType == FieldDescriptor::TYPE_MESSAGE) {
printer->Print("{\n");
printer->Indent();
}
properties[i]->generateSerializationCode(printer, /* isRead = */ true, /* noTag = */ true);
if (properties[i]->modifier == FieldDescriptor::LABEL_REPEATED
|| properties[i]->protoType == FieldDescriptor::TYPE_MESSAGE) {
printer->Outdent();
printer->Print("}\n");
}
}
printer->Outdent();
printer->Print("}\n"); // when-clause
printer->Print("return true");
printer->Outdent();
printer->Print("}\n"); // parseFieldFrom body
// parseFrom(input: CodedInputStream)
printer->Print(vars,
"fun parseFrom(input: CodedInputStream): $builderName$ {\n");
printer->Indent();
printer->Print("while(parseFieldFrom(input)) {}\n");
printer->Print("return this\n");
printer->Outdent();
printer->Print("}\n");
}
const string ClassModifier::getName() const {
string result = "";
switch (type) {
@@ -16,6 +16,8 @@ namespace protobuf {
namespace compiler {
namespace kotlin {
class FieldGenerator; // declared in "kotlin_file_generator.h"
// wrapper for enum CLASS/INTERFACE with convenience method of getting name
class ClassModifier {
public:
@@ -36,7 +38,7 @@ public:
vector <FieldGenerator *> properties;
vector <ClassGenerator *> classesDeclarations;
vector <EnumGenerator *> enumsDeclaraions;
string builderName;
ClassGenerator (Descriptor const * descriptor);
~ClassGenerator ();
@@ -62,7 +64,8 @@ private:
*/
void generateSerializersNoTag(io::Printer *printer, bool isRead = false) const;
void generateSerializers(io::Printer * printer, bool isRead = false) const;
void generateMergeFrom(io::Printer * printer) const;
void generateMergeMethods(io::Printer *printer) const;
void generateParseMethods(io::Printer * printer) const;
};
} // namespace kotlin
@@ -45,6 +45,7 @@ string FieldGenerator::protobufToKotlinField() const {
/**
* Simply maps protobuf field type to corresponding Kotlin.
*/
// TODO: refactor in separate static namespace
string FieldGenerator::protobufToKotlinType() const {
FieldDescriptor::Type type = descriptor->type();
switch(type) {
@@ -87,14 +88,8 @@ string FieldGenerator::protobufToKotlinType() const {
}
}
string FieldGenerator::getInitValue() const {
if (descriptor->is_repeated())
#ifndef KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW
return "mutableListOf()";
#else
return "arrayOf()";
#endif
// TODO: refactor in separate static
string FieldGenerator::protobufTypeToInitValue(FieldDescriptor const * descriptor) const {
FieldDescriptor::Type type = descriptor->type();
switch(type) {
case FieldDescriptor::TYPE_BOOL:
@@ -135,8 +130,18 @@ string FieldGenerator::getInitValue() const {
return "0L"; // see notes for TYPE_FIXED64
}
}
string FieldGenerator::getInitValue() const {
if (descriptor->is_repeated())
#ifndef KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW
return "mutableListOf()";
#else
return "arrayOf()";
#endif
void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder, string className) const {
return protobufTypeToInitValue(descriptor);
}
void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
map<string, string> vars;
vars["name"] = simpleName;
vars["field"] = protobufToKotlinField();
@@ -149,30 +154,35 @@ void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder, string c
// generate setter for builder
if (isBuilder) {
generateSetter(printer, /* builderName = */ "Builder" + className);
generateSetter(printer);
}
// generate additional methods for repeated fields
if (modifier == FieldDescriptor::LABEL_REPEATED) {
generateRepeatedMethods(printer, isBuilder, /* builderName = */ "Builder" + className);
generateRepeatedMethods(printer, isBuilder);
}
}
FieldGenerator::FieldGenerator(FieldDescriptor const * descriptor)
FieldGenerator::FieldGenerator(FieldDescriptor const * descriptor, ClassGenerator const * enclosingClass)
: descriptor(descriptor)
, modifier(descriptor->label())
, enclosingClass(enclosingClass)
, simpleName(descriptor->name())
, underlyingType(protobufToKotlinType())
, fullType(protobufToKotlinField())
, initValue(getInitValue())
, protoType(descriptor->type())
, fieldNumber(descriptor->number())
{ }
// TODO: long, complicated and messy method. Refactor it ASAP
void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead, bool noTag) const {
map <string, string> vars;
vars["type"] = protobufTypeToKotlinFunctionSuffix(descriptor->type());
vars["fieldNumber"] = std::to_string(descriptor->number());
vars["type"] = protobufTypeToKotlinFunctionSuffix(descriptor->type()) + (noTag ? "NoTag" : "");
vars["fieldNumber"] = std::to_string(fieldNumber);
vars["maybeFieldNumber"] = noTag ? "" : std::to_string(fieldNumber);
vars["fieldName"] = simpleName;
vars["arg"] = isRead ? "input" : "output";
vars["maybeComma"] = ", ";
/**
* First of all, try to generate syntax for repeated fields because it's separate case.
@@ -180,56 +190,74 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
* - 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
* - Write all repeated elements via recursive call (again, without tags)
*/
if (modifier == FieldDescriptor::LABEL_REPEATED) {
printer->Print(vars, "if ($fieldName$.size > 0) {\n");
printer->Indent();
// tag
if (isRead) {
//TODO: dirty stub here! Normally, reading from input should be delegated to Parsers, with proper error handling and etc.
//Currently tag is ignored, and work of the library relies heavily on the field order guarantees.
//Thus, backward-compability and extensions are not supported.
printer->Print(vars, "val tag = input.readTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n");
if (!noTag) {
printer->Print(vars, "val tag = input.readTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n");
}
printer->Print(vars, "val listSize = input.readInt32NoTag()\n");
printer->Print(vars, "for (i in 1..listSize) {\n");
printer->Indent();
printer->Print(vars, "$fieldName$[i - 1].mergeFrom(input)\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 = FieldGenerator(descriptor, enclosingClass);
/* 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()
but this is incorrect, because array has old size, while 'i' iterates over new size, and of course
they are not necessary the same.
*/
// TODO: stub here, resolve name properly!
vars["fieldType"] = underlyingType + ".Builder" + underlyingType;
vars["initValue"] = getUnderlyingTypeInitValue();
printer->Print(vars, "val tmp: $fieldType$ = $fieldType$()\n");
singleFieldGen.simpleName = "tmp";
singleFieldGen.modifier = FieldDescriptor::LABEL_OPTIONAL;
singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ true);
printer->Print(vars, "$fieldName$.add(tmp.build())\n");
printer->Outdent();
printer->Print("}\n");
}
else {
printer->Print(vars, "if ($fieldName$.size > 0) {\n");
printer->Indent();
// tag
printer->Print(vars, "output.writeTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n");
// length
printer->Print(vars, "output.writeInt32NoTag($fieldName$.size)\n");
printer->Print(vars, "output.writeInt32NoTag($fieldName$.size)\n");
// all elements
printer->Print(vars, "for (item in $fieldName$) {\n");
printer->Indent();
/* hack: copy current FieldGenerator and change label to OPTIONAL. 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 safely5
removed as soon as target code will support inheritance and interfaces.
(then writing CodedOutputStream.writeMessage will be possible).
*/
FieldGenerator singleFieldGen = FieldGenerator(descriptor);
// hack: see above
FieldGenerator singleFieldGen = FieldGenerator(descriptor, enclosingClass);
singleFieldGen.simpleName = "item";
singleFieldGen.modifier = FieldDescriptor::LABEL_OPTIONAL;
singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ true);
printer->Outdent(); // for-loop
printer->Print("}\n");
printer->Outdent(); // if-clause
printer->Print("}\n");
}
printer->Outdent(); // if-clause
printer->Print("}\n");
return;
}
@@ -244,10 +272,10 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
if (descriptor->type() == FieldDescriptor::TYPE_ENUM) {
vars["converter"] = underlyingType + ".fromIntTo" + underlyingType;
if (isRead) {
printer->Print(vars, "$fieldName$ = $converter$(input.read$type$($fieldNumber$))\n");
printer->Print(vars, "$fieldName$ = $converter$(input.read$type$($maybeFieldNumber$))\n");
}
else {
printer->Print(vars, "output.write$type$ ($fieldNumber$, $fieldName$?.ord)\n");
printer->Print(vars, "output.write$type$ ($maybeFieldNumber$$maybeComma$$fieldName$?.ord)\n");
}
return;
}
@@ -257,17 +285,13 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
that message
*/
if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) {
vars["fieldName"] = noTag ? "item" : simpleName;
vars["maybeNoTag"] = noTag ? "NoTag" : "";
vars["fieldNumber"] = std::to_string(descriptor->number());
vars["maybeNoTag"] = "NoTag";
if (isRead) {
printer->Print(vars,
"val tag = input.readTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n"
"$fieldName$.readFrom$maybeNoTag$(input)\n");
}
else {
printer->Print(vars,
"output.writeTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n"
"$fieldName$.writeTo$maybeNoTag$(output)\n");
}
return;
@@ -275,13 +299,14 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
/* Finally, serialize trivial cases */
if (isRead) {
printer->Print(vars, "$fieldName$ = input.read$type$($fieldNumber$)\n");
printer->Print(vars, "$fieldName$ = input.read$type$($maybeFieldNumber$)\n");
}
else {
printer->Print(vars, "output.write$type$ ($fieldNumber$, $fieldName$)\n");
printer->Print(vars, "output.write$type$ ($maybeFieldNumber$$maybeComma$$fieldName$)\n");
}
}
// TODO: refactor in separate static class
string FieldGenerator::protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type type) const {
switch (type) {
case FieldDescriptor::TYPE_DOUBLE:
@@ -321,11 +346,11 @@ string FieldGenerator::protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type
}
}
void FieldGenerator::generateSetter(io::Printer *printer, string builderName) const {
void FieldGenerator::generateSetter(io::Printer *printer) const {
map <string, string> vars;
vars["camelCaseName"] = name_resolving::makeFirstLetterUpper(simpleName);
vars["fieldName"] = simpleName;
vars["builderName"] = builderName;
vars["builderName"] = enclosingClass->builderName;
vars["type"] = fullType;
printer->Print(vars,
"fun set$camelCaseName$(value: $type$): $builderName$ {\n");
@@ -337,12 +362,12 @@ void FieldGenerator::generateSetter(io::Printer *printer, string builderName) co
printer->Print("}\n");
}
void FieldGenerator::generateRepeatedMethods(io::Printer * printer, bool isBuilder, string builderName) const {
void FieldGenerator::generateRepeatedMethods(io::Printer * printer, bool isBuilder) const {
map <string, string> vars;
vars["elementType"] = underlyingType;
vars["arg"] = "value";
vars["fieldName"] = simpleName;
vars["builderName"] = builderName;
vars["builderName"] = enclosingClass->builderName;
// generate indexed setter for builders
if (isBuilder) {
@@ -382,6 +407,13 @@ void FieldGenerator::generateRepeatedMethods(io::Printer * printer, bool isBuild
#endif
}
string FieldGenerator::getKotlinFunctionSuffix() const {
return protobufTypeToKotlinFunctionSuffix(descriptor->type());
}
string FieldGenerator::getUnderlyingTypeInitValue() const {
return protobufTypeToInitValue(descriptor);
}
} // namespace kotlin
} // namespace compiler
@@ -8,19 +8,22 @@
#include <vector>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/descriptor.h>
#include "kotlin_class_generator.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace kotlin {
class ClassGenerator; // declared in kotlin_class_generator.h
class FieldGenerator {
private:
FieldDescriptor const * descriptor;
string protobufToKotlinField() const;
string protobufToKotlinType () const;
string getInitValue() const;
// TODO: refactor from field generator to some static utility namespace
/**
* Converts one of protobuf wire types to corresponding Kotlin type with proper
* naming, so it could be used as suffix after read/write, resulting in function
@@ -30,10 +33,13 @@ private:
* CodedInputStream.readSFixed32(fieldNumber: Int)
*/
string protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type type) const;
void generateSetter(io::Printer * printer, string builderName) const;
void generateRepeatedMethods(io::Printer * printer, bool isBuilder, string builderName) const;
string protobufTypeToInitValue(FieldDescriptor const * descriptor) const;
void generateSetter(io::Printer * printer) const;
void generateRepeatedMethods(io::Printer * printer, bool isBuilder) const;
public:
FieldDescriptor::Label modifier;
ClassGenerator const * enclosingClass; // class, in which that field is defined
string simpleName;
string underlyingType; // unwrapped type.
@@ -44,11 +50,15 @@ public:
* fullType = underlyingType for all other cases
*/
string fullType;
string getInitValue() const;
string getUnderlyingTypeInitValue() const;
FieldDescriptor::Type protoType;
int fieldNumber;
string initValue;
void generateCode(io::Printer * printer, bool isBuilder, string className) const;
void generateCode(io::Printer * printer, bool isBuilder) const;
void generateSerializationCode(io::Printer * printer, bool isRead = false, bool noTag = false) const;
FieldGenerator(FieldDescriptor const * descriptor);
FieldGenerator(FieldDescriptor const * descriptor, ClassGenerator const * enclosingClass);
string getKotlinFunctionSuffix() const;
};
@@ -94,12 +94,33 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
fun build(): PhoneNumber {
return PhoneNumber(number, type)
}
fun parseFieldFrom(input: CodedInputStream): Boolean {
if (input.isAtEnd()) { return false }
val tag = input.readInt32NoTag()
if (tag == 0) { return false }
val fieldNumber = WireFormat.getTagFieldNumber(tag)
val wireType = WireFormat.getTagWireType(tag)
when(fieldNumber) {
1 -> number = input.readStringNoTag()
2 -> type = PhoneType.fromIntToPhoneType(input.readEnumNoTag())
}
return true}
fun parseFrom(input: CodedInputStream): BuilderPhoneNumber {
while(parseFieldFrom(input)) {}
return this
}
}
fun mergeWith (other: PhoneNumber) {
number = other.number
type = other.type
}
fun mergeFrom (input: CodedInputStream) {
number = input.readString(1)
type = PhoneType.fromIntToPhoneType(input.readEnum(2))
}
val builder = BuilderPhoneNumber()
mergeWith(builder.parseFrom(input).build())}
}
@@ -114,9 +135,7 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
if (phones.size > 0) {
output.writeTag(4, WireType.LENGTH_DELIMITED)
output.writeInt32NoTag(phones.size)
output.writeInt32NoTag(phones.size)
for (item in phones) {
output.writeTag(4, WireType.LENGTH_DELIMITED)
item.writeToNoTag(output)
}
}
@@ -190,12 +209,12 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
name = input.readString(1)
id = input.readInt32(2)
email = input.readString(3)
if (phones.size > 0) {
val tag = input.readTag(4, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
phones[i - 1].mergeFrom(input)
}
val tag = input.readTag(4, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber()
tmp.readFromNoTag(input)
phones.add(tmp.build())
}
someBytes = input.readBytes(5)
return this
@@ -204,21 +223,46 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
fun build(): Person {
return Person(name, id, email, phones, someBytes)
}
fun parseFieldFrom(input: CodedInputStream): Boolean {
if (input.isAtEnd()) { return false }
val tag = input.readInt32NoTag()
if (tag == 0) { return false }
val fieldNumber = WireFormat.getTagFieldNumber(tag)
val wireType = WireFormat.getTagWireType(tag)
when(fieldNumber) {
1 -> name = input.readStringNoTag()
2 -> id = input.readInt32NoTag()
3 -> email = input.readStringNoTag()
4 -> {
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber()
tmp.readFromNoTag(input)
phones.add(tmp.build())
}
}
5 -> someBytes = input.readBytesNoTag()
}
return true}
fun parseFrom(input: CodedInputStream): BuilderPerson {
while(parseFieldFrom(input)) {}
return this
}
}
fun mergeWith (other: Person) {
name = other.name
id = other.id
email = other.email
phones.addAll(other.phones)
someBytes?.plus(other.someBytes ?: ByteArray(0))
}
fun mergeFrom (input: CodedInputStream) {
name = input.readString(1)
id = input.readInt32(2)
email = input.readString(3)
if (phones.size > 0) {
val tag = input.readTag(4, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
phones[i - 1].mergeFrom(input)
}
}
someBytes = input.readBytes(5)
}
val builder = BuilderPerson()
mergeWith(builder.parseFrom(input).build())}
}
@@ -239,9 +283,7 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
if (people.size > 0) {
output.writeTag(1, WireType.LENGTH_DELIMITED)
output.writeInt32NoTag(people.size)
output.writeInt32NoTag(people.size)
for (item in people) {
output.writeTag(1, WireType.LENGTH_DELIMITED)
item.writeToNoTag(output)
}
}
@@ -279,12 +321,12 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
}
fun readFromNoTag (input: CodedInputStream): BuilderAddressBook {
if (people.size > 0) {
val tag = input.readTag(1, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
people[i - 1].mergeFrom(input)
}
val tag = input.readTag(1, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
val tmp: Person.BuilderPerson = Person.BuilderPerson()
tmp.readFromNoTag(input)
people.add(tmp.build())
}
return this
}
@@ -292,17 +334,38 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
fun build(): AddressBook {
return AddressBook(people)
}
fun parseFieldFrom(input: CodedInputStream): Boolean {
if (input.isAtEnd()) { return false }
val tag = input.readInt32NoTag()
if (tag == 0) { return false }
val fieldNumber = WireFormat.getTagFieldNumber(tag)
val wireType = WireFormat.getTagWireType(tag)
when(fieldNumber) {
1 -> {
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
val tmp: Person.BuilderPerson = Person.BuilderPerson()
tmp.readFromNoTag(input)
people.add(tmp.build())
}
}
}
return true}
fun parseFrom(input: CodedInputStream): BuilderAddressBook {
while(parseFieldFrom(input)) {}
return this
}
}
fun mergeWith (other: AddressBook) {
people.addAll(other.people)
}
fun mergeFrom (input: CodedInputStream) {
if (people.size > 0) {
val tag = input.readTag(1, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
people[i - 1].mergeFrom(input)
}
}
}
val builder = BuilderAddressBook()
mergeWith(builder.parseFrom(input).build())}
}
+1 -1
View File
@@ -234,7 +234,7 @@ class CodedInputStream(input: java.io.InputStream) {
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.LENGTH_DELIMITED, actualWireType)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, expectedWireType, actualWireType)
return tag
}
+102 -39
View File
@@ -94,12 +94,33 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
fun build(): PhoneNumber {
return PhoneNumber(number, type)
}
fun parseFieldFrom(input: CodedInputStream): Boolean {
if (input.isAtEnd()) { return false }
val tag = input.readInt32NoTag()
if (tag == 0) { return false }
val fieldNumber = WireFormat.getTagFieldNumber(tag)
val wireType = WireFormat.getTagWireType(tag)
when(fieldNumber) {
1 -> number = input.readStringNoTag()
2 -> type = PhoneType.fromIntToPhoneType(input.readEnumNoTag())
}
return true}
fun parseFrom(input: CodedInputStream): BuilderPhoneNumber {
while(parseFieldFrom(input)) {}
return this
}
}
fun mergeWith (other: PhoneNumber) {
number = other.number
type = other.type
}
fun mergeFrom (input: CodedInputStream) {
number = input.readString(1)
type = PhoneType.fromIntToPhoneType(input.readEnum(2))
}
val builder = BuilderPhoneNumber()
mergeWith(builder.parseFrom(input).build())}
}
@@ -114,9 +135,7 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
if (phones.size > 0) {
output.writeTag(4, WireType.LENGTH_DELIMITED)
output.writeInt32NoTag(phones.size)
output.writeInt32NoTag(phones.size)
for (item in phones) {
output.writeTag(4, WireType.LENGTH_DELIMITED)
item.writeToNoTag(output)
}
}
@@ -190,12 +209,12 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
name = input.readString(1)
id = input.readInt32(2)
email = input.readString(3)
if (phones.size > 0) {
val tag = input.readTag(4, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
phones[i - 1].mergeFrom(input)
}
val tag = input.readTag(4, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber()
tmp.readFromNoTag(input)
phones.add(tmp.build())
}
someBytes = input.readBytes(5)
return this
@@ -204,21 +223,46 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
fun build(): Person {
return Person(name, id, email, phones, someBytes)
}
fun parseFieldFrom(input: CodedInputStream): Boolean {
if (input.isAtEnd()) { return false }
val tag = input.readInt32NoTag()
if (tag == 0) { return false }
val fieldNumber = WireFormat.getTagFieldNumber(tag)
val wireType = WireFormat.getTagWireType(tag)
when(fieldNumber) {
1 -> name = input.readStringNoTag()
2 -> id = input.readInt32NoTag()
3 -> email = input.readStringNoTag()
4 -> {
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber()
tmp.readFromNoTag(input)
phones.add(tmp.build())
}
}
5 -> someBytes = input.readBytesNoTag()
}
return true}
fun parseFrom(input: CodedInputStream): BuilderPerson {
while(parseFieldFrom(input)) {}
return this
}
}
fun mergeWith (other: Person) {
name = other.name
id = other.id
email = other.email
phones.addAll(other.phones)
someBytes?.plus(other.someBytes ?: ByteArray(0))
}
fun mergeFrom (input: CodedInputStream) {
name = input.readString(1)
id = input.readInt32(2)
email = input.readString(3)
if (phones.size > 0) {
val tag = input.readTag(4, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
phones[i - 1].mergeFrom(input)
}
}
someBytes = input.readBytes(5)
}
val builder = BuilderPerson()
mergeWith(builder.parseFrom(input).build())}
}
@@ -239,9 +283,7 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
if (people.size > 0) {
output.writeTag(1, WireType.LENGTH_DELIMITED)
output.writeInt32NoTag(people.size)
output.writeInt32NoTag(people.size)
for (item in people) {
output.writeTag(1, WireType.LENGTH_DELIMITED)
item.writeToNoTag(output)
}
}
@@ -279,12 +321,12 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
}
fun readFromNoTag (input: CodedInputStream): BuilderAddressBook {
if (people.size > 0) {
val tag = input.readTag(1, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
people[i - 1].mergeFrom(input)
}
val tag = input.readTag(1, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
val tmp: Person.BuilderPerson = Person.BuilderPerson()
tmp.readFromNoTag(input)
people.add(tmp.build())
}
return this
}
@@ -292,17 +334,38 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
fun build(): AddressBook {
return AddressBook(people)
}
fun parseFieldFrom(input: CodedInputStream): Boolean {
if (input.isAtEnd()) { return false }
val tag = input.readInt32NoTag()
if (tag == 0) { return false }
val fieldNumber = WireFormat.getTagFieldNumber(tag)
val wireType = WireFormat.getTagWireType(tag)
when(fieldNumber) {
1 -> {
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
val tmp: Person.BuilderPerson = Person.BuilderPerson()
tmp.readFromNoTag(input)
people.add(tmp.build())
}
}
}
return true}
fun parseFrom(input: CodedInputStream): BuilderAddressBook {
while(parseFieldFrom(input)) {}
return this
}
}
fun mergeWith (other: AddressBook) {
people.addAll(other.people)
}
fun mergeFrom (input: CodedInputStream) {
if (people.size > 0) {
val tag = input.readTag(1, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
people[i - 1].mergeFrom(input)
}
}
}
val builder = BuilderAddressBook()
mergeWith(builder.parseFrom(input).build())}
}
+9 -3
View File
@@ -16,14 +16,20 @@ fun testMessageSerialization() {
.setEmail("wtf@dasda.com") // all setters return this builder, so you could chain modifiers in LINQ-style
.setId(42)
.setName("John Doe")
.setPhones(arrayOf( // repeated fields stored as Array<>, so use arrayOf() for creating repeated fields
.addPhoneNumber( // repeated fields stored as Array<>, so use arrayOf() for creating repeated fields
Person.PhoneNumber.BuilderPhoneNumber()
.setNumber("342143-23423-42")
.setType(Person.PhoneType.HOME)
.build()
))
)
.addPhoneNumber(
Person.PhoneNumber.BuilderPhoneNumber()
.setNumber("8-800-555-35-35")
.setType(Person.PhoneType.WORK)
.build()
)
.build() // don't forget to call build() to produce message
msg.writeTo(outs)
msg.writeToNoTag(outs)
// Now let's use output stream as input to read our message from it!
var ins = CodedInputStream(ByteArrayInputStream(s.toByteArray()))