Protobuf: Fixed stub in length-delimited fields serialization. Now related methods write size of object correctly. Added necessary methods for estimating byte-size of objects in run-time in ProtoKot runtime-library.

This commit is contained in:
dsavvinov
2016-07-18 16:05:56 +03:00
parent f6790e43ae
commit 8552e0b778
13 changed files with 548 additions and 176 deletions
@@ -26,4 +26,9 @@ clean:
generate: generate:
./protoc --kotlin_out=./ ./test/addressbook.proto ./protoc --kotlin_out=./ ./test/addressbook.proto
.PHONY: clean all generate .PHONY: clean all generate
low: dummy
dummy: CXXFLAGS += -DKOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW
dummy: all
@@ -48,8 +48,6 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
// write serialization methods only for fair classes, read methods only for Builders) // write serialization methods only for fair classes, read methods only for Builders)
printer->Print("\n"); printer->Print("\n");
generateSerializers(printer, /* isRead = */ isBuilder); generateSerializers(printer, /* isRead = */ isBuilder);
printer->Print("\n");
generateSerializersNoTag(printer, /* isRead = */ isBuilder);
// builder, mergeFrom and only for fair classes // builder, mergeFrom and only for fair classes
if (!isBuilder) { if (!isBuilder) {
@@ -69,6 +67,9 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
generateParseMethods(printer); generateParseMethods(printer);
} }
// getSize()
generateGetSizeMethod(printer);
printer->Outdent(); printer->Outdent();
printer->Print("}\n"); printer->Print("}\n");
} }
@@ -172,32 +173,10 @@ void ClassGenerator::generateMergeMethods(io::Printer *printer) const {
printer->Print("}\n"); printer->Print("}\n");
} }
void ClassGenerator::generateSerializers(io::Printer * printer, bool isRead) const {
// readFrom(input: CodedInputStream) OR void ClassGenerator::generateSerializers(io::Printer *printer, bool isRead) const {
// writeTo(output: CodedOutputStream)
map <string, string> vars; map <string, string> vars;
vars["funName"]= isRead ? "readFrom" : "writeTo"; vars["funName"]= isRead ? "readFrom" : "writeTo";
vars["returnType"] = isRead ? builderName : "Unit";
vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream";
vars["arg"] = isRead ? "input" : "output";
vars["maybeSeparator"] = isRead ? "" : ", ";
vars["maybeReturn"] = isRead ? "return " : "";
// generate function header
printer->Print(vars,
"fun $funName$ ($arg$: $stream$): $returnType$ {"
"\n");
printer->Indent();
printer->Print(vars, "$maybeReturn$$funName$NoTag($arg$)\n");
printer->Outdent();
printer->Print("}\n");
}
void ClassGenerator::generateSerializersNoTag(io::Printer *printer, bool isRead) const {
map <string, string> vars;
vars["funName"]= isRead ? "readFromNoTag" : "writeToNoTag";
vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream"; vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream";
vars["arg"] = isRead ? "input" : "output"; vars["arg"] = isRead ? "input" : "output";
vars["returnType"] = isRead ? builderName : "Unit"; vars["returnType"] = isRead ? builderName : "Unit";
@@ -341,6 +320,30 @@ void ClassGenerator::generateParseMethods(io::Printer *printer) const {
printer->Print("}\n"); printer->Print("}\n");
} }
void ClassGenerator::generateGetSizeMethod(io::Printer *printer) const {
printer->Print("fun getSize(): Int {\n");
printer->Indent();
printer->Print("var size = 0\n");
for (int i = 0; i < properties.size(); ++i) {
properties[i]->generateSizeEstimationCode(printer, "size");
}
printer->Print("return size\n");
printer->Outdent();
printer->Print("}\n");
}
//int ClassGenerator::getSizeWithoutHeader() {
// int size = 0;
// for (int i = 0; i < properties.size(); ++i) {
// size += properties[i].getSizeWithHeader();
// }
// return 0;
//}
const string ClassModifier::getName() const { const string ClassModifier::getName() const {
string result = ""; string result = "";
@@ -62,10 +62,10 @@ private:
* both methods are structurally the same with some trivial substitutions * both methods are structurally the same with some trivial substitutions
* (read -> write and etc.) * (read -> write and etc.)
*/ */
void generateSerializersNoTag(io::Printer *printer, bool isRead = false) const;
void generateSerializers(io::Printer * printer, bool isRead = false) const; void generateSerializers(io::Printer * printer, bool isRead = false) const;
void generateMergeMethods(io::Printer *printer) const; void generateMergeMethods(io::Printer *printer) const;
void generateParseMethods(io::Printer * printer) const; void generateParseMethods(io::Printer * printer) const;
void generateGetSizeMethod(io::Printer * printer) const;
}; };
} // namespace kotlin } // namespace kotlin
@@ -16,12 +16,10 @@ namespace kotlin {
string FieldGenerator::getInitValue() const { string FieldGenerator::getInitValue() const {
if (descriptor->is_repeated()) if (descriptor->is_repeated())
#ifndef KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW
return "mutableListOf()"; return "mutableListOf()";
#else else if (protoType == FieldDescriptor::TYPE_MESSAGE) {
return "arrayOf()"; return enclosingClass->builderName + "().build()";
#endif }
return name_resolving::protobufTypeToInitValue(descriptor); return name_resolving::protobufTypeToInitValue(descriptor);
} }
@@ -82,8 +80,9 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
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 listSize = input.readInt32NoTag()\n"); printer->Print(vars, "val expectedSize = input.readInt32NoTag()\n");
printer->Print(vars, "for (i in 1..listSize) {\n"); printer->Print("var readSize = 0\n");
printer->Print(vars, "while(readSize != expectedSize) {\n");
printer->Indent(); printer->Indent();
/* 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
@@ -99,17 +98,25 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
/* Another dirty hack here: create tmp variable of a given type and read it from input stream /* Another dirty hack here: create tmp variable of a given type and read it from input stream
then add that tmp var into list. 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 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 This is incorrect because array has old size, while 'i' iterates over new size, which can lead
they are not necessary the same. to ArrayOutOfIndex errors.
*/ */
// TODO: stub here, resolve name properly! // TODO: stub here, resolve name properly!
vars["fieldType"] = underlyingType + ".Builder" + underlyingType; vars["fieldType"] = underlyingType + ".Builder" + underlyingType;
vars["initValue"] = getUnderlyingTypeInitValue(); vars["initValue"] = underlyingType + ".Builder" + underlyingType + "()";
printer->Print(vars, "val tmp: $fieldType$ = $fieldType$()\n"); printer->Print(vars, "val tmp: $fieldType$ = $initValue$\n");
singleFieldGen.simpleName = "tmp"; singleFieldGen.simpleName = "tmp";
singleFieldGen.modifier = FieldDescriptor::LABEL_OPTIONAL; singleFieldGen.modifier = FieldDescriptor::LABEL_OPTIONAL;
singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ true);
// Note that primitive types are packed by default in proto3, i.e. they are should be written without tag
bool isPrimitive = descriptor->type() != FieldDescriptor::TYPE_BYTES &&
descriptor->type() != FieldDescriptor::TYPE_MESSAGE &&
descriptor->type() != FieldDescriptor::TYPE_STRING &&
descriptor->type() != FieldDescriptor::TYPE_ENUM;
singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ isPrimitive);
singleFieldGen.generateSizeEstimationCode(printer, "readSize"); // add size of current element to total size
printer->Print(vars, "$fieldName$.add(tmp.build())\n"); printer->Print(vars, "$fieldName$.add(tmp.build())\n");
@@ -124,7 +131,9 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
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, "output.writeInt32NoTag($fieldName$.size)\n"); printer->Print(vars, "var arrayByteSize = 0\n");
generateSizeEstimationCode(printer, "arrayByteSize", /* noTag = */ true);
printer->Print(vars, "output.writeInt32NoTag(arrayByteSize)\n");
// all elements // all elements
printer->Print(vars, "for (item in $fieldName$) {\n"); printer->Print(vars, "for (item in $fieldName$) {\n");
@@ -134,7 +143,15 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
FieldGenerator singleFieldGen = FieldGenerator(descriptor, enclosingClass); FieldGenerator singleFieldGen = FieldGenerator(descriptor, enclosingClass);
singleFieldGen.simpleName = "item"; singleFieldGen.simpleName = "item";
singleFieldGen.modifier = FieldDescriptor::LABEL_OPTIONAL; singleFieldGen.modifier = FieldDescriptor::LABEL_OPTIONAL;
singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ true);
// TODO: maybe refactor this in name_resolving or separate method at least
// Note that primitive types are packed by default in proto3, i.e. they are should be written without tag
bool isPrimitive = descriptor->type() != FieldDescriptor::TYPE_BYTES &&
descriptor->type() != FieldDescriptor::TYPE_MESSAGE &&
descriptor->type() != FieldDescriptor::TYPE_STRING &&
descriptor->type() != FieldDescriptor::TYPE_ENUM;
singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ isPrimitive);
printer->Outdent(); // for-loop printer->Outdent(); // for-loop
printer->Print("}\n"); printer->Print("}\n");
@@ -166,17 +183,42 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
/* /*
Then check for nested messages. Then we re-use writeTo method, that should be defined in Then check for nested messages. Then we re-use writeTo method, that should be defined in
that message 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.
*/ */
if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) { if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) {
vars["maybeNoTag"] = "NoTag";
if (isRead) { if (isRead) {
vars["fieldNumber"] = std::to_string(fieldNumber);
vars["dollar"] = "$";
// read tag
printer->Print(vars, "input.readTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n");
// read expected size
printer->Print(vars, "val expectedSize = input.readInt32NoTag()\n");
// read message itself without tag
printer->Print(vars, printer->Print(vars,
"$fieldName$.readFrom$maybeNoTag$(input)\n"); "$fieldName$.readFrom(input)\n");
// check that actual size equal to expected size
printer->Print(vars, "if (expectedSize != $fieldName$.getSize()) { "
"throw InvalidProtocolBufferException ("
"\"Expected size $dollar${expectedSize} got $dollar${$fieldName$.getSize()}"
"\") }\n");
} }
else { else {
vars["fieldNumber"] = std::to_string(fieldNumber);
// write tag
printer->Print(vars, "output.writeTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n");
// write message length via runtime-call
printer->Print(vars, "output.writeInt32NoTag($fieldName$.getSize())\n");
// write message itself without tag
printer->Print(vars, printer->Print(vars,
"$fieldName$.writeTo$maybeNoTag$(output)\n"); "$fieldName$.writeTo(output)\n");
} }
return; return;
} }
@@ -224,7 +266,6 @@ void FieldGenerator::generateRepeatedMethods(io::Printer * printer, bool isBuild
printer->Print("}\n"); printer->Print("}\n");
} }
#ifndef KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW
if (isBuilder) { if (isBuilder) {
// generate single-add for builders // generate single-add for builders
printer->Print(vars, "fun add$elementType$($arg$: $elementType$): $builderName$ {\n"); printer->Print(vars, "fun add$elementType$($arg$: $elementType$): $builderName$ {\n");
@@ -249,7 +290,6 @@ void FieldGenerator::generateRepeatedMethods(io::Printer * printer, bool isBuild
printer->Outdent(); // function body printer->Outdent(); // function body
printer->Print("}\n"); printer->Print("}\n");
} }
#endif
} }
string FieldGenerator::getKotlinFunctionSuffix() const { string FieldGenerator::getKotlinFunctionSuffix() const {
@@ -257,9 +297,85 @@ string FieldGenerator::getKotlinFunctionSuffix() const {
} }
string FieldGenerator::getUnderlyingTypeInitValue() const { string FieldGenerator::getUnderlyingTypeInitValue() const {
if (protoType == FieldDescriptor::TYPE_MESSAGE) {
return "Builder" + underlyingType + "().build()";
}
return name_resolving::protobufTypeToInitValue(descriptor); return name_resolving::protobufTypeToInitValue(descriptor);
} }
void FieldGenerator::generateSizeEstimationCode(io::Printer *printer, string varName, bool noTag) const {
map<string, string> vars;
vars["varName"] = varName;
vars["fieldName"] = simpleName;
vars["fieldNumber"] = std::to_string(fieldNumber);
// First of all, generate code for repeated fields
if (modifier == FieldDescriptor::LABEL_REPEATED) {
// We will need total byte size of array, because that size is itself a part of the message and
// adds to total message size.
// For the sake of hygiene, temporary variables are created in anonymous scope
printer->Print("run {\n");
printer->Indent();
// Create a temporary variable that will collect array byte size
printer->Print("var arraySize = 0\n");
// iterate over all elements of array
printer->Print(vars, "for (item in $fieldName$) {\n");
printer->Indent();
// hack: reuse generateSizeEstimationCode in the same manner as in generateSerializationCode
FieldGenerator singleFieldGen = FieldGenerator(descriptor, enclosingClass);
singleFieldGen.modifier = FieldDescriptor::LABEL_OPTIONAL;
singleFieldGen.simpleName = "item";
singleFieldGen.generateSizeEstimationCode(printer, "arraySize");
printer->Outdent(); // for-loop
printer->Print("}\n");
// now add to total message size size of array, consisting of:
printer->Print(vars,
"$varName$ += arraySize"); // actual array size
if (!noTag) {
printer->Print(vars,
" + "
"WireFormat.getTagSize($fieldNumber$, WireType.LENGTH_DELIMITED)" // tag size
" + "
"WireFormat.getVarint32Size(arraySize)"); // runtime call, that will get size of varint, denoting size of array
}
printer->Print("\n");
printer->Outdent(); // anonymous scope
printer->Print("}\n");
return;
}
// 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()
if (protoType == FieldDescriptor::TYPE_MESSAGE) {
// don't forget about tag and length annotation
printer->Print(vars, "$varName$ += $fieldName$.getSize()"
" + "
"WireFormat.getTagSize($fieldNumber$, WireType.LENGTH_DELIMITED)"
" + "
"WireFormat.getVarint32Size($fieldName$.getSize())\n"
);
return;
}
// Next, process enums as they should be casted to ints manually
if (protoType == FieldDescriptor::TYPE_ENUM) {
vars["enumName"] = fullType;
printer->Print(vars, "$varName$ += WireFormat.getEnumSize($fieldNumber$, $fieldName$.ord)\n");
return;
}
// Finally, get size of all primitive types trivially via call to WireFormat in runtime
vars["kotlinSuffix"] = name_resolving::protobufTypeToKotlinFunctionSuffix(descriptor->type());
printer->Print(vars, "$varName$ += WireFormat.get$kotlinSuffix$Size($fieldNumber$, $fieldName$)\n");
return;
}
} // namespace kotlin } // namespace kotlin
} // namespace compiler } // namespace compiler
} // namespace protobuf } // namespace protobuf
@@ -45,6 +45,7 @@ public:
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) const;
void generateSizeEstimationCode(io::Printer * printer, string varName, bool noTag = false) const;
FieldGenerator(FieldDescriptor const * descriptor, ClassGenerator const * enclosingClass); FieldGenerator(FieldDescriptor const * descriptor, ClassGenerator const * enclosingClass);
string getKotlinFunctionSuffix() const; string getKotlinFunctionSuffix() const;
@@ -83,19 +83,14 @@ string protobufToKotlinField(FieldDescriptor const * descriptor) {
case FieldDescriptor::LABEL_OPTIONAL: case FieldDescriptor::LABEL_OPTIONAL:
break; break;
case FieldDescriptor::LABEL_REPEATED: case FieldDescriptor::LABEL_REPEATED:
#ifndef KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW
preamble = "MutableList <"; preamble = "MutableList <";
postamble = "> "; postamble = "> ";
break; break;
#else
preamble = "Array <";
postamble = "> ";
break;
#endif
} }
return preamble + protobufToKotlinType(descriptor) + postamble; return preamble + protobufToKotlinType(descriptor) + postamble;
} }
// TODO: think about nested arrays
string protobufTypeToInitValue(FieldDescriptor const * descriptor) { string protobufTypeToInitValue(FieldDescriptor const * descriptor) {
FieldDescriptor::Type type = descriptor->type(); FieldDescriptor::Type type = descriptor->type();
switch(type) { switch(type) {
@@ -10,7 +10,6 @@
#include "kotlin_file_generator.h" #include "kotlin_file_generator.h"
#include <iostream> #include <iostream>
//#define KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW
int main(int argc, const char* const * argv) { int main(int argc, const char* const * argv) {
google::protobuf::compiler::CommandLineInterface cli; google::protobuf::compiler::CommandLineInterface cli;
@@ -52,10 +52,6 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
} }
fun writeTo (output: CodedOutputStream): Unit { fun writeTo (output: CodedOutputStream): Unit {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream): Unit {
output.writeString (1, number) output.writeString (1, number)
output.writeEnum (2, type.ord) output.writeEnum (2, type.ord)
} }
@@ -82,10 +78,6 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
} }
fun readFrom (input: CodedInputStream): BuilderPhoneNumber { fun readFrom (input: CodedInputStream): BuilderPhoneNumber {
return readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream): BuilderPhoneNumber {
number = input.readString(1) number = input.readString(1)
type = PhoneType.fromIntToPhoneType(input.readEnum(2)) type = PhoneType.fromIntToPhoneType(input.readEnum(2))
return this return this
@@ -110,6 +102,12 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
while(parseFieldFrom(input)) {} while(parseFieldFrom(input)) {}
return this return this
} }
fun getSize(): Int {
var size = 0
size += WireFormat.getStringSize(1, number)
size += WireFormat.getEnumSize(2, type.ord)
return size
}
} }
@@ -121,22 +119,34 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
fun mergeFrom (input: CodedInputStream) { fun mergeFrom (input: CodedInputStream) {
val builder = BuilderPhoneNumber() val builder = BuilderPhoneNumber()
mergeWith(builder.parseFrom(input).build())} mergeWith(builder.parseFrom(input).build())}
fun getSize(): Int {
var size = 0
size += WireFormat.getStringSize(1, number)
size += WireFormat.getEnumSize(2, type.ord)
return size
}
} }
fun writeTo (output: CodedOutputStream): Unit { fun writeTo (output: CodedOutputStream): Unit {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream): Unit {
output.writeString (1, name) output.writeString (1, name)
output.writeInt32 (2, id) output.writeInt32 (2, id)
output.writeString (3, email) output.writeString (3, email)
if (phones.size > 0) { if (phones.size > 0) {
output.writeTag(4, WireType.LENGTH_DELIMITED) output.writeTag(4, WireType.LENGTH_DELIMITED)
output.writeInt32NoTag(phones.size) var arrayByteSize = 0
run {
var arraySize = 0
for (item in phones) {
arraySize += item.getSize() + WireFormat.getTagSize(4, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(item.getSize())
}
arrayByteSize += arraySize
}
output.writeInt32NoTag(arrayByteSize)
for (item in phones) { for (item in phones) {
item.writeToNoTag(output) output.writeTag(4, WireType.LENGTH_DELIMITED)
output.writeInt32NoTag(item.getSize())
item.writeTo(output)
} }
} }
output.writeBytes (5, someBytes) output.writeBytes (5, someBytes)
@@ -202,18 +212,19 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
} }
fun readFrom (input: CodedInputStream): BuilderPerson { fun readFrom (input: CodedInputStream): BuilderPerson {
return readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream): BuilderPerson {
name = input.readString(1) name = input.readString(1)
id = input.readInt32(2) id = input.readInt32(2)
email = input.readString(3) email = input.readString(3)
val tag = input.readTag(4, WireType.LENGTH_DELIMITED) val tag = input.readTag(4, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag() val expectedSize = input.readInt32NoTag()
for (i in 1..listSize) { var readSize = 0
while(readSize != expectedSize) {
val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber() val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber()
tmp.readFromNoTag(input) input.readTag(4, WireType.LENGTH_DELIMITED)
val expectedSize = input.readInt32NoTag()
tmp.readFrom(input)
if (expectedSize != tmp.getSize()) { throw InvalidProtocolBufferException ("Expected size ${expectedSize} got ${tmp.getSize()}") }
readSize += tmp.getSize() + WireFormat.getTagSize(4, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(tmp.getSize())
phones.add(tmp.build()) phones.add(tmp.build())
} }
someBytes = input.readBytes(5) someBytes = input.readBytes(5)
@@ -235,10 +246,15 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
2 -> id = input.readInt32NoTag() 2 -> id = input.readInt32NoTag()
3 -> email = input.readStringNoTag() 3 -> email = input.readStringNoTag()
4 -> { 4 -> {
val listSize = input.readInt32NoTag() val expectedSize = input.readInt32NoTag()
for (i in 1..listSize) { var readSize = 0
while(readSize != expectedSize) {
val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber() val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber()
tmp.readFromNoTag(input) input.readTag(4, WireType.LENGTH_DELIMITED)
val expectedSize = input.readInt32NoTag()
tmp.readFrom(input)
if (expectedSize != tmp.getSize()) { throw InvalidProtocolBufferException ("Expected size ${expectedSize} got ${tmp.getSize()}") }
readSize += tmp.getSize() + WireFormat.getTagSize(4, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(tmp.getSize())
phones.add(tmp.build()) phones.add(tmp.build())
} }
} }
@@ -249,6 +265,21 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
while(parseFieldFrom(input)) {} while(parseFieldFrom(input)) {}
return this return this
} }
fun getSize(): Int {
var size = 0
size += WireFormat.getStringSize(1, name)
size += WireFormat.getInt32Size(2, id)
size += WireFormat.getStringSize(3, email)
run {
var arraySize = 0
for (item in phones) {
arraySize += item.getSize() + WireFormat.getTagSize(4, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(item.getSize())
}
size += arraySize + WireFormat.getTagSize(4, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(arraySize)
}
size += WireFormat.getBytesSize(5, someBytes)
return size
}
} }
@@ -263,6 +294,21 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
fun mergeFrom (input: CodedInputStream) { fun mergeFrom (input: CodedInputStream) {
val builder = BuilderPerson() val builder = BuilderPerson()
mergeWith(builder.parseFrom(input).build())} mergeWith(builder.parseFrom(input).build())}
fun getSize(): Int {
var size = 0
size += WireFormat.getStringSize(1, name)
size += WireFormat.getInt32Size(2, id)
size += WireFormat.getStringSize(3, email)
run {
var arraySize = 0
for (item in phones) {
arraySize += item.getSize() + WireFormat.getTagSize(4, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(item.getSize())
}
size += arraySize + WireFormat.getTagSize(4, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(arraySize)
}
size += WireFormat.getBytesSize(5, someBytes)
return size
}
} }
@@ -276,15 +322,21 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
} }
fun writeTo (output: CodedOutputStream): Unit { fun writeTo (output: CodedOutputStream): Unit {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream): Unit {
if (people.size > 0) { if (people.size > 0) {
output.writeTag(1, WireType.LENGTH_DELIMITED) output.writeTag(1, WireType.LENGTH_DELIMITED)
output.writeInt32NoTag(people.size) var arrayByteSize = 0
run {
var arraySize = 0
for (item in people) {
arraySize += item.getSize() + WireFormat.getTagSize(1, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(item.getSize())
}
arrayByteSize += arraySize
}
output.writeInt32NoTag(arrayByteSize)
for (item in people) { for (item in people) {
item.writeToNoTag(output) output.writeTag(1, WireType.LENGTH_DELIMITED)
output.writeInt32NoTag(item.getSize())
item.writeTo(output)
} }
} }
} }
@@ -317,15 +369,16 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
} }
fun readFrom (input: CodedInputStream): BuilderAddressBook { fun readFrom (input: CodedInputStream): BuilderAddressBook {
return readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream): BuilderAddressBook {
val tag = input.readTag(1, WireType.LENGTH_DELIMITED) val tag = input.readTag(1, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag() val expectedSize = input.readInt32NoTag()
for (i in 1..listSize) { var readSize = 0
while(readSize != expectedSize) {
val tmp: Person.BuilderPerson = Person.BuilderPerson() val tmp: Person.BuilderPerson = Person.BuilderPerson()
tmp.readFromNoTag(input) input.readTag(1, WireType.LENGTH_DELIMITED)
val expectedSize = input.readInt32NoTag()
tmp.readFrom(input)
if (expectedSize != tmp.getSize()) { throw InvalidProtocolBufferException ("Expected size ${expectedSize} got ${tmp.getSize()}") }
readSize += tmp.getSize() + WireFormat.getTagSize(1, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(tmp.getSize())
people.add(tmp.build()) people.add(tmp.build())
} }
return this return this
@@ -343,10 +396,15 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
val wireType = WireFormat.getTagWireType(tag) val wireType = WireFormat.getTagWireType(tag)
when(fieldNumber) { when(fieldNumber) {
1 -> { 1 -> {
val listSize = input.readInt32NoTag() val expectedSize = input.readInt32NoTag()
for (i in 1..listSize) { var readSize = 0
while(readSize != expectedSize) {
val tmp: Person.BuilderPerson = Person.BuilderPerson() val tmp: Person.BuilderPerson = Person.BuilderPerson()
tmp.readFromNoTag(input) input.readTag(1, WireType.LENGTH_DELIMITED)
val expectedSize = input.readInt32NoTag()
tmp.readFrom(input)
if (expectedSize != tmp.getSize()) { throw InvalidProtocolBufferException ("Expected size ${expectedSize} got ${tmp.getSize()}") }
readSize += tmp.getSize() + WireFormat.getTagSize(1, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(tmp.getSize())
people.add(tmp.build()) people.add(tmp.build())
} }
} }
@@ -356,6 +414,17 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
while(parseFieldFrom(input)) {} while(parseFieldFrom(input)) {}
return this return this
} }
fun getSize(): Int {
var size = 0
run {
var arraySize = 0
for (item in people) {
arraySize += item.getSize() + WireFormat.getTagSize(1, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(item.getSize())
}
size += arraySize + WireFormat.getTagSize(1, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(arraySize)
}
return size
}
} }
@@ -366,6 +435,17 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
fun mergeFrom (input: CodedInputStream) { fun mergeFrom (input: CodedInputStream) {
val builder = BuilderAddressBook() val builder = BuilderAddressBook()
mergeWith(builder.parseFrom(input).build())} mergeWith(builder.parseFrom(input).build())}
fun getSize(): Int {
var size = 0
run {
var arraySize = 0
for (item in people) {
arraySize += item.getSize() + WireFormat.getTagSize(1, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(item.getSize())
}
size += arraySize + WireFormat.getTagSize(1, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(arraySize)
}
return size
}
} }
+3 -7
View File
@@ -1,5 +1,8 @@
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.ByteOrder import java.nio.ByteOrder
import WireFormat.VARINT_INFO_BITS_COUNT
import WireFormat.VARINT_INFO_BITS_MASK
import WireFormat.VARINT_UTIL_BIT_MASK
/** /**
* Created by Dmitry Savvinov on 7/6/16. * Created by Dmitry Savvinov on 7/6/16.
@@ -13,7 +16,6 @@ import java.nio.ByteOrder
// TODO: refactor correctness checks into readTag // TODO: refactor correctness checks into readTag
class CodedInputStream(input: java.io.InputStream) { class CodedInputStream(input: java.io.InputStream) {
val bufferedInput: java.io.BufferedInputStream val bufferedInput: java.io.BufferedInputStream
init { init {
bufferedInput = java.io.BufferedInputStream(input) // TODO: Java's realization uses hand-written buffers. Why? bufferedInput = java.io.BufferedInputStream(input) // TODO: Java's realization uses hand-written buffers. Why?
@@ -299,11 +301,5 @@ class CodedInputStream(input: java.io.InputStream) {
bufferedInput.reset() bufferedInput.reset()
return byte == -1 return byte == -1
} }
// couple of constants for magic numbers
val VARINT_INFO_BITS_COUNT: Int = 7
val VARINT_INFO_BITS_MASK: Int = 0b01111111 // mask for separating lowest 7 bits, where actual information stored
val VARINT_UTIL_BIT_MASK: Int = 0b10000000 // mask for separating highest bit, that indicates next byte presence
} }
+7 -7
View File
@@ -1,5 +1,8 @@
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.ByteOrder import java.nio.ByteOrder
import WireFormat.VARINT_INFO_BITS_COUNT
import WireFormat.VARINT_INFO_BITS_MASK
import WireFormat.VARINT_UTIL_BIT_MASK
/** /**
* Created by user on 7/6/16. * Created by user on 7/6/16.
@@ -75,7 +78,7 @@ class CodedOutputStream(val output: java.io.OutputStream) {
} }
fun writeSInt64NoTag(value: Long) { fun writeSInt64NoTag(value: Long) {
writeInt64NoTag((value shl 1) xor (value shr 31)) writeInt64NoTag((value shl 1) xor (value shr 63))
} }
fun writeFixed32(fieldNumber: Int, value: Int?) { fun writeFixed32(fieldNumber: Int, value: Int?) {
@@ -148,7 +151,7 @@ class CodedOutputStream(val output: java.io.OutputStream) {
fun writeStringNoTag(value: String) { fun writeStringNoTag(value: String) {
writeInt32NoTag(value.length) writeInt32NoTag(value.length)
output.write(value.toByteArray()) output.write(value.toByteArray(Charsets.UTF_8))
} }
fun writeBytes(fieldNumber: Int, value: ByteArray?) { fun writeBytes(fieldNumber: Int, value: ByteArray?) {
@@ -209,7 +212,7 @@ class CodedOutputStream(val output: java.io.OutputStream) {
var resSize = 0 var resSize = 0
do { do {
// encode current 7 bits // encode current 7 bits
var curByte = (curValue and VARINT_INFO_BITS_MASK) var curByte = (curValue and WireFormat.VARINT_INFO_BITS_MASK)
// discard encoded bits. Note that unsigned shift is needed for cases with negative numbers // discard encoded bits. Note that unsigned shift is needed for cases with negative numbers
curValue = curValue ushr VARINT_INFO_BITS_COUNT curValue = curValue ushr VARINT_INFO_BITS_COUNT
@@ -251,8 +254,5 @@ class CodedOutputStream(val output: java.io.OutputStream) {
output.write(res, 0, resSize) output.write(res, 0, resSize)
} }
// couple of constants for magic numbers
val VARINT_INFO_BITS_COUNT: Int = 7
val VARINT_INFO_BITS_MASK: Int = 0b01111111 // mask for separating lowest 7 bits, where actual information stored
val VARINT_UTIL_BIT_MASK: Int = 0b10000000 // mask for separating highest bit, that indicates next byte presence
} }
+144 -64
View File
@@ -1,17 +1,17 @@
class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email: kotlin.String? = "", phones: MutableList <PhoneNumber> = mutableListOf(), someBytes: ByteArray? = ByteArray(0)) { class Person private constructor (name: kotlin.String = "", id: Int = 0, email: kotlin.String = "", phones: MutableList <PhoneNumber> = mutableListOf(), someBytes: ByteArray = ByteArray(0)) {
var name : kotlin.String? var name : kotlin.String
private set private set
var id : Int? var id : Int
private set private set
var email : kotlin.String? var email : kotlin.String
private set private set
var phones : MutableList <PhoneNumber> var phones : MutableList <PhoneNumber>
private set private set
var someBytes : ByteArray? var someBytes : ByteArray
private set private set
@@ -38,11 +38,11 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
} }
} }
} }
class PhoneNumber private constructor (number: kotlin.String? = "", type: PhoneType? = PhoneType.fromIntToPhoneType(0)) { class PhoneNumber private constructor (number: kotlin.String = "", type: PhoneType = PhoneType.fromIntToPhoneType(0)) {
var number : kotlin.String? var number : kotlin.String
private set private set
var type : PhoneType? var type : PhoneType
private set private set
@@ -52,25 +52,21 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
} }
fun writeTo (output: CodedOutputStream): Unit { fun writeTo (output: CodedOutputStream): Unit {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream): Unit {
output.writeString (1, number) output.writeString (1, number)
output.writeEnum (2, type?.ord) output.writeEnum (2, type.ord)
} }
class BuilderPhoneNumber constructor (number: kotlin.String? = "", type: PhoneType? = PhoneType.fromIntToPhoneType(0)) { class BuilderPhoneNumber constructor (number: kotlin.String = "", type: PhoneType = PhoneType.fromIntToPhoneType(0)) {
var number : kotlin.String? var number : kotlin.String
private set private set
fun setNumber(value: kotlin.String?): BuilderPhoneNumber { fun setNumber(value: kotlin.String): BuilderPhoneNumber {
number = value number = value
return this return this
} }
var type : PhoneType? var type : PhoneType
private set private set
fun setType(value: PhoneType?): BuilderPhoneNumber { fun setType(value: PhoneType): BuilderPhoneNumber {
type = value type = value
return this return this
} }
@@ -82,10 +78,6 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
} }
fun readFrom (input: CodedInputStream): BuilderPhoneNumber { fun readFrom (input: CodedInputStream): BuilderPhoneNumber {
return readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream): BuilderPhoneNumber {
number = input.readString(1) number = input.readString(1)
type = PhoneType.fromIntToPhoneType(input.readEnum(2)) type = PhoneType.fromIntToPhoneType(input.readEnum(2))
return this return this
@@ -110,6 +102,12 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
while(parseFieldFrom(input)) {} while(parseFieldFrom(input)) {}
return this return this
} }
fun getSize(): Int {
var size = 0
size += WireFormat.getStringSize(1, number)
size += WireFormat.getEnumSize(2, type.ord)
return size
}
} }
@@ -121,45 +119,57 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
fun mergeFrom (input: CodedInputStream) { fun mergeFrom (input: CodedInputStream) {
val builder = BuilderPhoneNumber() val builder = BuilderPhoneNumber()
mergeWith(builder.parseFrom(input).build())} mergeWith(builder.parseFrom(input).build())}
fun getSize(): Int {
var size = 0
size += WireFormat.getStringSize(1, number)
size += WireFormat.getEnumSize(2, type.ord)
return size
}
} }
fun writeTo (output: CodedOutputStream): Unit { fun writeTo (output: CodedOutputStream): Unit {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream): Unit {
output.writeString (1, name) output.writeString (1, name)
output.writeInt32 (2, id) output.writeInt32 (2, id)
output.writeString (3, email) output.writeString (3, email)
if (phones.size > 0) { if (phones.size > 0) {
output.writeTag(4, WireType.LENGTH_DELIMITED) output.writeTag(4, WireType.LENGTH_DELIMITED)
output.writeInt32NoTag(phones.size) var arrayByteSize = 0
run {
var arraySize = 0
for (item in phones) {
arraySize += item.getSize() + WireFormat.getTagSize(4, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(item.getSize())
}
arrayByteSize += arraySize
}
output.writeInt32NoTag(arrayByteSize)
for (item in phones) { for (item in phones) {
item.writeToNoTag(output) output.writeTag(4, WireType.LENGTH_DELIMITED)
output.writeInt32NoTag(item.getSize())
item.writeTo(output)
} }
} }
output.writeBytes (5, someBytes) output.writeBytes (5, someBytes)
} }
class BuilderPerson constructor (name: kotlin.String? = "", id: Int? = 0, email: kotlin.String? = "", phones: MutableList <PhoneNumber> = mutableListOf(), someBytes: ByteArray? = ByteArray(0)) { class BuilderPerson constructor (name: kotlin.String = "", id: Int = 0, email: kotlin.String = "", phones: MutableList <PhoneNumber> = mutableListOf(), someBytes: ByteArray = ByteArray(0)) {
var name : kotlin.String? var name : kotlin.String
private set private set
fun setName(value: kotlin.String?): BuilderPerson { fun setName(value: kotlin.String): BuilderPerson {
name = value name = value
return this return this
} }
var id : Int? var id : Int
private set private set
fun setId(value: Int?): BuilderPerson { fun setId(value: Int): BuilderPerson {
id = value id = value
return this return this
} }
var email : kotlin.String? var email : kotlin.String
private set private set
fun setEmail(value: kotlin.String?): BuilderPerson { fun setEmail(value: kotlin.String): BuilderPerson {
email = value email = value
return this return this
} }
@@ -185,9 +195,9 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
return this return this
} }
var someBytes : ByteArray? var someBytes : ByteArray
private set private set
fun setSomeBytes(value: ByteArray?): BuilderPerson { fun setSomeBytes(value: ByteArray): BuilderPerson {
someBytes = value someBytes = value
return this return this
} }
@@ -202,18 +212,19 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
} }
fun readFrom (input: CodedInputStream): BuilderPerson { fun readFrom (input: CodedInputStream): BuilderPerson {
return readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream): BuilderPerson {
name = input.readString(1) name = input.readString(1)
id = input.readInt32(2) id = input.readInt32(2)
email = input.readString(3) email = input.readString(3)
val tag = input.readTag(4, WireType.LENGTH_DELIMITED) val tag = input.readTag(4, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag() val expectedSize = input.readInt32NoTag()
for (i in 1..listSize) { var readSize = 0
while(readSize != expectedSize) {
val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber() val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber()
tmp.readFromNoTag(input) input.readTag(4, WireType.LENGTH_DELIMITED)
val expectedSize = input.readInt32NoTag()
tmp.readFrom(input)
if (expectedSize != tmp.getSize()) { throw InvalidProtocolBufferException ("Expected size ${expectedSize} got ${tmp.getSize()}") }
readSize += tmp.getSize() + WireFormat.getTagSize(4, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(tmp.getSize())
phones.add(tmp.build()) phones.add(tmp.build())
} }
someBytes = input.readBytes(5) someBytes = input.readBytes(5)
@@ -235,10 +246,15 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
2 -> id = input.readInt32NoTag() 2 -> id = input.readInt32NoTag()
3 -> email = input.readStringNoTag() 3 -> email = input.readStringNoTag()
4 -> { 4 -> {
val listSize = input.readInt32NoTag() val expectedSize = input.readInt32NoTag()
for (i in 1..listSize) { var readSize = 0
while(readSize != expectedSize) {
val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber() val tmp: PhoneNumber.BuilderPhoneNumber = PhoneNumber.BuilderPhoneNumber()
tmp.readFromNoTag(input) input.readTag(4, WireType.LENGTH_DELIMITED)
val expectedSize = input.readInt32NoTag()
tmp.readFrom(input)
if (expectedSize != tmp.getSize()) { throw InvalidProtocolBufferException ("Expected size ${expectedSize} got ${tmp.getSize()}") }
readSize += tmp.getSize() + WireFormat.getTagSize(4, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(tmp.getSize())
phones.add(tmp.build()) phones.add(tmp.build())
} }
} }
@@ -249,6 +265,21 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
while(parseFieldFrom(input)) {} while(parseFieldFrom(input)) {}
return this return this
} }
fun getSize(): Int {
var size = 0
size += WireFormat.getStringSize(1, name)
size += WireFormat.getInt32Size(2, id)
size += WireFormat.getStringSize(3, email)
run {
var arraySize = 0
for (item in phones) {
arraySize += item.getSize() + WireFormat.getTagSize(4, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(item.getSize())
}
size += arraySize + WireFormat.getTagSize(4, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(arraySize)
}
size += WireFormat.getBytesSize(5, someBytes)
return size
}
} }
@@ -257,12 +288,27 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
id = other.id id = other.id
email = other.email email = other.email
phones.addAll(other.phones) phones.addAll(other.phones)
someBytes?.plus(other.someBytes ?: ByteArray(0)) someBytes.plus(other.someBytes)
} }
fun mergeFrom (input: CodedInputStream) { fun mergeFrom (input: CodedInputStream) {
val builder = BuilderPerson() val builder = BuilderPerson()
mergeWith(builder.parseFrom(input).build())} mergeWith(builder.parseFrom(input).build())}
fun getSize(): Int {
var size = 0
size += WireFormat.getStringSize(1, name)
size += WireFormat.getInt32Size(2, id)
size += WireFormat.getStringSize(3, email)
run {
var arraySize = 0
for (item in phones) {
arraySize += item.getSize() + WireFormat.getTagSize(4, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(item.getSize())
}
size += arraySize + WireFormat.getTagSize(4, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(arraySize)
}
size += WireFormat.getBytesSize(5, someBytes)
return size
}
} }
@@ -276,15 +322,21 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
} }
fun writeTo (output: CodedOutputStream): Unit { fun writeTo (output: CodedOutputStream): Unit {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream): Unit {
if (people.size > 0) { if (people.size > 0) {
output.writeTag(1, WireType.LENGTH_DELIMITED) output.writeTag(1, WireType.LENGTH_DELIMITED)
output.writeInt32NoTag(people.size) var arrayByteSize = 0
run {
var arraySize = 0
for (item in people) {
arraySize += item.getSize() + WireFormat.getTagSize(1, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(item.getSize())
}
arrayByteSize += arraySize
}
output.writeInt32NoTag(arrayByteSize)
for (item in people) { for (item in people) {
item.writeToNoTag(output) output.writeTag(1, WireType.LENGTH_DELIMITED)
output.writeInt32NoTag(item.getSize())
item.writeTo(output)
} }
} }
} }
@@ -317,15 +369,16 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
} }
fun readFrom (input: CodedInputStream): BuilderAddressBook { fun readFrom (input: CodedInputStream): BuilderAddressBook {
return readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream): BuilderAddressBook {
val tag = input.readTag(1, WireType.LENGTH_DELIMITED) val tag = input.readTag(1, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag() val expectedSize = input.readInt32NoTag()
for (i in 1..listSize) { var readSize = 0
while(readSize != expectedSize) {
val tmp: Person.BuilderPerson = Person.BuilderPerson() val tmp: Person.BuilderPerson = Person.BuilderPerson()
tmp.readFromNoTag(input) input.readTag(1, WireType.LENGTH_DELIMITED)
val expectedSize = input.readInt32NoTag()
tmp.readFrom(input)
if (expectedSize != tmp.getSize()) { throw InvalidProtocolBufferException ("Expected size ${expectedSize} got ${tmp.getSize()}") }
readSize += tmp.getSize() + WireFormat.getTagSize(1, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(tmp.getSize())
people.add(tmp.build()) people.add(tmp.build())
} }
return this return this
@@ -343,10 +396,15 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
val wireType = WireFormat.getTagWireType(tag) val wireType = WireFormat.getTagWireType(tag)
when(fieldNumber) { when(fieldNumber) {
1 -> { 1 -> {
val listSize = input.readInt32NoTag() val expectedSize = input.readInt32NoTag()
for (i in 1..listSize) { var readSize = 0
while(readSize != expectedSize) {
val tmp: Person.BuilderPerson = Person.BuilderPerson() val tmp: Person.BuilderPerson = Person.BuilderPerson()
tmp.readFromNoTag(input) input.readTag(1, WireType.LENGTH_DELIMITED)
val expectedSize = input.readInt32NoTag()
tmp.readFrom(input)
if (expectedSize != tmp.getSize()) { throw InvalidProtocolBufferException ("Expected size ${expectedSize} got ${tmp.getSize()}") }
readSize += tmp.getSize() + WireFormat.getTagSize(1, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(tmp.getSize())
people.add(tmp.build()) people.add(tmp.build())
} }
} }
@@ -356,6 +414,17 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
while(parseFieldFrom(input)) {} while(parseFieldFrom(input)) {}
return this return this
} }
fun getSize(): Int {
var size = 0
run {
var arraySize = 0
for (item in people) {
arraySize += item.getSize() + WireFormat.getTagSize(1, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(item.getSize())
}
size += arraySize + WireFormat.getTagSize(1, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(arraySize)
}
return size
}
} }
@@ -366,6 +435,17 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
fun mergeFrom (input: CodedInputStream) { fun mergeFrom (input: CodedInputStream) {
val builder = BuilderAddressBook() val builder = BuilderAddressBook()
mergeWith(builder.parseFrom(input).build())} mergeWith(builder.parseFrom(input).build())}
fun getSize(): Int {
var size = 0
run {
var arraySize = 0
for (item in people) {
arraySize += item.getSize() + WireFormat.getTagSize(1, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(item.getSize())
}
size += arraySize + WireFormat.getTagSize(1, WireType.LENGTH_DELIMITED) + WireFormat.getVarint32Size(arraySize)
}
return size
}
} }
+1 -1
View File
@@ -29,7 +29,7 @@ fun testMessageSerialization() {
.build() .build()
) )
.build() // don't forget to call build() to produce message .build() // don't forget to call build() to produce message
msg.writeToNoTag(outs) msg.writeTo(outs)
// Now let's use output stream as input to read our message from it! // Now let's use output stream as input to read our message from it!
var ins = CodedInputStream(ByteArrayInputStream(s.toByteArray())) var ins = CodedInputStream(ByteArrayInputStream(s.toByteArray()))
+97
View File
@@ -4,8 +4,14 @@
object WireFormat { object WireFormat {
// couple of constants for magic numbers
val TAG_TYPE_BITS: Int = 3 val TAG_TYPE_BITS: Int = 3
val TAG_TYPE_MASK: Int = (1 shl TAG_TYPE_BITS) - 1 val TAG_TYPE_MASK: Int = (1 shl TAG_TYPE_BITS) - 1
val VARINT_INFO_BITS_COUNT: Int = 7
val VARINT_INFO_BITS_MASK: Int = 0b01111111 // mask for separating lowest 7 bits, where actual information stored
val VARINT_UTIL_BIT_MASK: Int = 0b10000000 // mask for separating highest bit, that indicates next byte presence
val FIXED_32_BYTE_SIZE: Int = 4
val FIXED_64_BYTE_SIZE: Int = 8
fun getTagWireType(tag: Int): WireType { fun getTagWireType(tag: Int): WireType {
return WireType from (tag and TAG_TYPE_MASK).toByte() return WireType from (tag and TAG_TYPE_MASK).toByte()
@@ -14,4 +20,95 @@ object WireFormat {
fun getTagFieldNumber(tag: Int): Int { fun getTagFieldNumber(tag: Int): Int {
return tag ushr TAG_TYPE_BITS return tag ushr TAG_TYPE_BITS
} }
// TODO: refactor casts into function overloading as soon as translator will support it
fun getTagSize(fieldNumber: Int, wireType: WireType): Int {
return getVarint32Size((fieldNumber shl 3) or wireType.ordinal)
}
fun getVarint32Size(value: Int): Int {
var curValue = value
var size = 0
while (curValue != 0) {
size += 1
curValue = value ushr VARINT_INFO_BITS_COUNT
}
return size
}
fun getVarint64Size(value: Long): Int {
var curValue = value
var size = 0
while (curValue != 0L) {
size += 1
curValue = value ushr VARINT_INFO_BITS_COUNT
}
return size
}
fun getZigZag32Size(value: Int): Int {
return getVarint32Size((value shl 1) xor (value shr 31))
}
fun getZigZag64Size(value: Long): Int {
return getVarint64Size((value shl 1) xor (value shr 63))
}
fun getInt32Size(fieldNumber: Int, value: Int): Int {
return getTagSize(fieldNumber, WireType.VARINT) + getVarint32Size(value)
}
fun getUInt32Size(fieldNumber: Int, value: Int): Int {
return getInt32Size(fieldNumber, value)
}
fun getInt64Size(fieldNumber: Int, value: Long): Int {
return getTagSize(fieldNumber, WireType.VARINT) + getVarint64Size(value)
}
fun getUInt64Size(fieldNumber: Int, value: Long): Int {
return getInt64Size(fieldNumber, value)
}
fun getBoolSize(fieldNumber: Int, value: Boolean): Int {
val intValue = if (value) 1 else 0
return getInt32Size(fieldNumber, intValue)
}
fun getEnumSize(fieldNumber: Int, value: Int): Int {
return getInt32Size(fieldNumber, value)
}
fun getSInt32Size(fieldNumber: Int, value: Int): Int {
return getTagSize(fieldNumber, WireType.VARINT) + getZigZag32Size(value)
}
fun getSInt64Size(fieldNumber: Int, value: Long): Int {
return getTagSize(fieldNumber, WireType.VARINT) + getZigZag64Size(value)
}
fun getFixed32Size(fieldNumber: Int, value: Long): Int {
return getTagSize(fieldNumber, WireType.FIX_32) + FIXED_32_BYTE_SIZE
}
fun getFixed64Size(fieldNumber: Int, value: Long): Int {
return getTagSize(fieldNumber, WireType.FIX_64) + FIXED_64_BYTE_SIZE
}
fun getDoubleSize(fieldNumber: Int, value: Double): Int {
return getTagSize(fieldNumber, WireType.FIX_32) + FIXED_32_BYTE_SIZE
}
fun getFloatSize(fieldNumber: Int, value: Float): Int {
return getTagSize(fieldNumber, WireType.FIX_64) + FIXED_64_BYTE_SIZE
}
fun getStringSize(fieldNumber: Int, value: String): Int {
val encodedStringSize = value.toByteArray(Charsets.UTF_8).size //TODO: not sure if it's the best way to do it
return encodedStringSize + getTagSize(fieldNumber, WireType.LENGTH_DELIMITED) + getVarint32Size(encodedStringSize)
}
fun getBytesSize(fieldNumber: Int, value: ByteArray): Int {
return value.size + getTagSize(fieldNumber, WireType.LENGTH_DELIMITED) + getVarint32Size(value.size)
}
} }