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:
@@ -26,4 +26,9 @@ clean:
|
||||
|
||||
generate:
|
||||
./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
|
||||
+30
-27
@@ -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)
|
||||
printer->Print("\n");
|
||||
generateSerializers(printer, /* isRead = */ isBuilder);
|
||||
printer->Print("\n");
|
||||
generateSerializersNoTag(printer, /* isRead = */ isBuilder);
|
||||
|
||||
// builder, mergeFrom and only for fair classes
|
||||
if (!isBuilder) {
|
||||
@@ -69,6 +67,9 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
|
||||
generateParseMethods(printer);
|
||||
}
|
||||
|
||||
// getSize()
|
||||
generateGetSizeMethod(printer);
|
||||
|
||||
printer->Outdent();
|
||||
printer->Print("}\n");
|
||||
}
|
||||
@@ -172,32 +173,10 @@ void ClassGenerator::generateMergeMethods(io::Printer *printer) const {
|
||||
printer->Print("}\n");
|
||||
}
|
||||
|
||||
void ClassGenerator::generateSerializers(io::Printer * printer, bool isRead) const {
|
||||
// readFrom(input: CodedInputStream) OR
|
||||
// writeTo(output: CodedOutputStream)
|
||||
|
||||
void ClassGenerator::generateSerializers(io::Printer *printer, bool isRead) const {
|
||||
map <string, string> vars;
|
||||
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["funName"]= isRead ? "readFrom" : "writeTo";
|
||||
vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream";
|
||||
vars["arg"] = isRead ? "input" : "output";
|
||||
vars["returnType"] = isRead ? builderName : "Unit";
|
||||
@@ -341,6 +320,30 @@ void ClassGenerator::generateParseMethods(io::Printer *printer) const {
|
||||
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 {
|
||||
string result = "";
|
||||
|
||||
+1
-1
@@ -62,10 +62,10 @@ private:
|
||||
* both methods are structurally the same with some trivial substitutions
|
||||
* (read -> write and etc.)
|
||||
*/
|
||||
void generateSerializersNoTag(io::Printer *printer, bool isRead = false) const;
|
||||
void generateSerializers(io::Printer * printer, bool isRead = false) const;
|
||||
void generateMergeMethods(io::Printer *printer) const;
|
||||
void generateParseMethods(io::Printer * printer) const;
|
||||
void generateGetSizeMethod(io::Printer * printer) const;
|
||||
};
|
||||
|
||||
} // namespace kotlin
|
||||
|
||||
+137
-21
@@ -16,12 +16,10 @@ namespace kotlin {
|
||||
|
||||
string FieldGenerator::getInitValue() const {
|
||||
if (descriptor->is_repeated())
|
||||
#ifndef KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW
|
||||
return "mutableListOf()";
|
||||
#else
|
||||
return "arrayOf()";
|
||||
#endif
|
||||
|
||||
else if (protoType == FieldDescriptor::TYPE_MESSAGE) {
|
||||
return enclosingClass->builderName + "().build()";
|
||||
}
|
||||
return name_resolving::protobufTypeToInitValue(descriptor);
|
||||
}
|
||||
|
||||
@@ -82,8 +80,9 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
|
||||
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->Print(vars, "val expectedSize = input.readInt32NoTag()\n");
|
||||
printer->Print("var readSize = 0\n");
|
||||
printer->Print(vars, "while(readSize != expectedSize) {\n");
|
||||
printer->Indent();
|
||||
|
||||
/* 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
|
||||
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.
|
||||
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.
|
||||
*/
|
||||
// TODO: stub here, resolve name properly!
|
||||
vars["fieldType"] = underlyingType + ".Builder" + underlyingType;
|
||||
vars["initValue"] = getUnderlyingTypeInitValue();
|
||||
printer->Print(vars, "val tmp: $fieldType$ = $fieldType$()\n");
|
||||
vars["initValue"] = underlyingType + ".Builder" + underlyingType + "()";
|
||||
printer->Print(vars, "val tmp: $fieldType$ = $initValue$\n");
|
||||
singleFieldGen.simpleName = "tmp";
|
||||
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");
|
||||
|
||||
@@ -124,7 +131,9 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
|
||||
printer->Print(vars, "output.writeTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n");
|
||||
|
||||
// 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
|
||||
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);
|
||||
singleFieldGen.simpleName = "item";
|
||||
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->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
|
||||
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) {
|
||||
vars["maybeNoTag"] = "NoTag";
|
||||
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,
|
||||
"$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 {
|
||||
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,
|
||||
"$fieldName$.writeTo$maybeNoTag$(output)\n");
|
||||
"$fieldName$.writeTo(output)\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -224,7 +266,6 @@ void FieldGenerator::generateRepeatedMethods(io::Printer * printer, bool isBuild
|
||||
printer->Print("}\n");
|
||||
}
|
||||
|
||||
#ifndef KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW
|
||||
if (isBuilder) {
|
||||
// generate single-add for builders
|
||||
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->Print("}\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
string FieldGenerator::getKotlinFunctionSuffix() const {
|
||||
@@ -257,9 +297,85 @@ string FieldGenerator::getKotlinFunctionSuffix() const {
|
||||
}
|
||||
|
||||
string FieldGenerator::getUnderlyingTypeInitValue() const {
|
||||
if (protoType == FieldDescriptor::TYPE_MESSAGE) {
|
||||
return "Builder" + underlyingType + "().build()";
|
||||
}
|
||||
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 compiler
|
||||
} // namespace protobuf
|
||||
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
|
||||
void generateCode(io::Printer * printer, bool isBuilder) 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);
|
||||
string getKotlinFunctionSuffix() const;
|
||||
|
||||
|
||||
+1
-6
@@ -83,19 +83,14 @@ string protobufToKotlinField(FieldDescriptor const * descriptor) {
|
||||
case FieldDescriptor::LABEL_OPTIONAL:
|
||||
break;
|
||||
case FieldDescriptor::LABEL_REPEATED:
|
||||
#ifndef KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW
|
||||
preamble = "MutableList <";
|
||||
postamble = "> ";
|
||||
break;
|
||||
#else
|
||||
preamble = "Array <";
|
||||
postamble = "> ";
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return preamble + protobufToKotlinType(descriptor) + postamble;
|
||||
}
|
||||
|
||||
// TODO: think about nested arrays
|
||||
string protobufTypeToInitValue(FieldDescriptor const * descriptor) {
|
||||
FieldDescriptor::Type type = descriptor->type();
|
||||
switch(type) {
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "kotlin_file_generator.h"
|
||||
#include <iostream>
|
||||
|
||||
//#define KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW
|
||||
int main(int argc, const char* const * argv) {
|
||||
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 {
|
||||
writeToNoTag(output)
|
||||
}
|
||||
|
||||
fun writeToNoTag (output: CodedOutputStream): Unit {
|
||||
output.writeString (1, number)
|
||||
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 {
|
||||
return readFromNoTag(input)
|
||||
}
|
||||
|
||||
fun readFromNoTag (input: CodedInputStream): BuilderPhoneNumber {
|
||||
number = input.readString(1)
|
||||
type = PhoneType.fromIntToPhoneType(input.readEnum(2))
|
||||
return this
|
||||
@@ -110,6 +102,12 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
|
||||
while(parseFieldFrom(input)) {}
|
||||
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) {
|
||||
val builder = BuilderPhoneNumber()
|
||||
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 {
|
||||
writeToNoTag(output)
|
||||
}
|
||||
|
||||
fun writeToNoTag (output: CodedOutputStream): Unit {
|
||||
output.writeString (1, name)
|
||||
output.writeInt32 (2, id)
|
||||
output.writeString (3, email)
|
||||
if (phones.size > 0) {
|
||||
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) {
|
||||
item.writeToNoTag(output)
|
||||
output.writeTag(4, WireType.LENGTH_DELIMITED)
|
||||
output.writeInt32NoTag(item.getSize())
|
||||
item.writeTo(output)
|
||||
}
|
||||
}
|
||||
output.writeBytes (5, someBytes)
|
||||
@@ -202,18 +212,19 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
|
||||
}
|
||||
|
||||
fun readFrom (input: CodedInputStream): BuilderPerson {
|
||||
return readFromNoTag(input)
|
||||
}
|
||||
|
||||
fun readFromNoTag (input: CodedInputStream): BuilderPerson {
|
||||
name = input.readString(1)
|
||||
id = input.readInt32(2)
|
||||
email = input.readString(3)
|
||||
val tag = input.readTag(4, WireType.LENGTH_DELIMITED)
|
||||
val listSize = input.readInt32NoTag()
|
||||
for (i in 1..listSize) {
|
||||
val expectedSize = input.readInt32NoTag()
|
||||
var readSize = 0
|
||||
while(readSize != expectedSize) {
|
||||
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())
|
||||
}
|
||||
someBytes = input.readBytes(5)
|
||||
@@ -235,10 +246,15 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
|
||||
2 -> id = input.readInt32NoTag()
|
||||
3 -> email = input.readStringNoTag()
|
||||
4 -> {
|
||||
val listSize = input.readInt32NoTag()
|
||||
for (i in 1..listSize) {
|
||||
val expectedSize = input.readInt32NoTag()
|
||||
var readSize = 0
|
||||
while(readSize != expectedSize) {
|
||||
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())
|
||||
}
|
||||
}
|
||||
@@ -249,6 +265,21 @@ class Person private constructor (name: kotlin.String = "", id: Int = 0, email:
|
||||
while(parseFieldFrom(input)) {}
|
||||
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) {
|
||||
val builder = BuilderPerson()
|
||||
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 {
|
||||
writeToNoTag(output)
|
||||
}
|
||||
|
||||
fun writeToNoTag (output: CodedOutputStream): Unit {
|
||||
if (people.size > 0) {
|
||||
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) {
|
||||
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 {
|
||||
return readFromNoTag(input)
|
||||
}
|
||||
|
||||
fun readFromNoTag (input: CodedInputStream): BuilderAddressBook {
|
||||
val tag = input.readTag(1, WireType.LENGTH_DELIMITED)
|
||||
val listSize = input.readInt32NoTag()
|
||||
for (i in 1..listSize) {
|
||||
val expectedSize = input.readInt32NoTag()
|
||||
var readSize = 0
|
||||
while(readSize != expectedSize) {
|
||||
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())
|
||||
}
|
||||
return this
|
||||
@@ -343,10 +396,15 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
|
||||
val wireType = WireFormat.getTagWireType(tag)
|
||||
when(fieldNumber) {
|
||||
1 -> {
|
||||
val listSize = input.readInt32NoTag()
|
||||
for (i in 1..listSize) {
|
||||
val expectedSize = input.readInt32NoTag()
|
||||
var readSize = 0
|
||||
while(readSize != expectedSize) {
|
||||
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())
|
||||
}
|
||||
}
|
||||
@@ -356,6 +414,17 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
|
||||
while(parseFieldFrom(input)) {}
|
||||
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) {
|
||||
val builder = BuilderAddressBook()
|
||||
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,5 +1,8 @@
|
||||
import java.nio.ByteBuffer
|
||||
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.
|
||||
@@ -13,7 +16,6 @@ import java.nio.ByteOrder
|
||||
|
||||
// TODO: refactor correctness checks into readTag
|
||||
class CodedInputStream(input: java.io.InputStream) {
|
||||
|
||||
val bufferedInput: java.io.BufferedInputStream
|
||||
init {
|
||||
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()
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import java.nio.ByteBuffer
|
||||
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.
|
||||
@@ -75,7 +78,7 @@ class CodedOutputStream(val output: java.io.OutputStream) {
|
||||
}
|
||||
|
||||
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?) {
|
||||
@@ -148,7 +151,7 @@ class CodedOutputStream(val output: java.io.OutputStream) {
|
||||
|
||||
fun writeStringNoTag(value: String) {
|
||||
writeInt32NoTag(value.length)
|
||||
output.write(value.toByteArray())
|
||||
output.write(value.toByteArray(Charsets.UTF_8))
|
||||
}
|
||||
|
||||
fun writeBytes(fieldNumber: Int, value: ByteArray?) {
|
||||
@@ -209,7 +212,7 @@ class CodedOutputStream(val output: java.io.OutputStream) {
|
||||
var resSize = 0
|
||||
do {
|
||||
// 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
|
||||
curValue = curValue ushr VARINT_INFO_BITS_COUNT
|
||||
@@ -251,8 +254,5 @@ class CodedOutputStream(val output: java.io.OutputStream) {
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
var name : kotlin.String?
|
||||
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
|
||||
private set
|
||||
|
||||
var id : Int?
|
||||
var id : Int
|
||||
private set
|
||||
|
||||
var email : kotlin.String?
|
||||
var email : kotlin.String
|
||||
private set
|
||||
|
||||
var phones : MutableList <PhoneNumber>
|
||||
private set
|
||||
|
||||
var someBytes : ByteArray?
|
||||
var someBytes : ByteArray
|
||||
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)) {
|
||||
var number : kotlin.String?
|
||||
class PhoneNumber private constructor (number: kotlin.String = "", type: PhoneType = PhoneType.fromIntToPhoneType(0)) {
|
||||
var number : kotlin.String
|
||||
private set
|
||||
|
||||
var type : PhoneType?
|
||||
var type : PhoneType
|
||||
private set
|
||||
|
||||
|
||||
@@ -52,25 +52,21 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
|
||||
}
|
||||
|
||||
fun writeTo (output: CodedOutputStream): Unit {
|
||||
writeToNoTag(output)
|
||||
}
|
||||
|
||||
fun writeToNoTag (output: CodedOutputStream): Unit {
|
||||
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)) {
|
||||
var number : kotlin.String?
|
||||
class BuilderPhoneNumber constructor (number: kotlin.String = "", type: PhoneType = PhoneType.fromIntToPhoneType(0)) {
|
||||
var number : kotlin.String
|
||||
private set
|
||||
fun setNumber(value: kotlin.String?): BuilderPhoneNumber {
|
||||
fun setNumber(value: kotlin.String): BuilderPhoneNumber {
|
||||
number = value
|
||||
return this
|
||||
}
|
||||
|
||||
var type : PhoneType?
|
||||
var type : PhoneType
|
||||
private set
|
||||
fun setType(value: PhoneType?): BuilderPhoneNumber {
|
||||
fun setType(value: PhoneType): BuilderPhoneNumber {
|
||||
type = value
|
||||
return this
|
||||
}
|
||||
@@ -82,10 +78,6 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
|
||||
}
|
||||
|
||||
fun readFrom (input: CodedInputStream): BuilderPhoneNumber {
|
||||
return readFromNoTag(input)
|
||||
}
|
||||
|
||||
fun readFromNoTag (input: CodedInputStream): BuilderPhoneNumber {
|
||||
number = input.readString(1)
|
||||
type = PhoneType.fromIntToPhoneType(input.readEnum(2))
|
||||
return this
|
||||
@@ -110,6 +102,12 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
|
||||
while(parseFieldFrom(input)) {}
|
||||
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) {
|
||||
val builder = BuilderPhoneNumber()
|
||||
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 {
|
||||
writeToNoTag(output)
|
||||
}
|
||||
|
||||
fun writeToNoTag (output: CodedOutputStream): Unit {
|
||||
output.writeString (1, name)
|
||||
output.writeInt32 (2, id)
|
||||
output.writeString (3, email)
|
||||
if (phones.size > 0) {
|
||||
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) {
|
||||
item.writeToNoTag(output)
|
||||
output.writeTag(4, WireType.LENGTH_DELIMITED)
|
||||
output.writeInt32NoTag(item.getSize())
|
||||
item.writeTo(output)
|
||||
}
|
||||
}
|
||||
output.writeBytes (5, someBytes)
|
||||
}
|
||||
|
||||
class BuilderPerson constructor (name: kotlin.String? = "", id: Int? = 0, email: kotlin.String? = "", phones: MutableList <PhoneNumber> = mutableListOf(), someBytes: ByteArray? = ByteArray(0)) {
|
||||
var name : kotlin.String?
|
||||
class BuilderPerson constructor (name: kotlin.String = "", id: Int = 0, email: kotlin.String = "", phones: MutableList <PhoneNumber> = mutableListOf(), someBytes: ByteArray = ByteArray(0)) {
|
||||
var name : kotlin.String
|
||||
private set
|
||||
fun setName(value: kotlin.String?): BuilderPerson {
|
||||
fun setName(value: kotlin.String): BuilderPerson {
|
||||
name = value
|
||||
return this
|
||||
}
|
||||
|
||||
var id : Int?
|
||||
var id : Int
|
||||
private set
|
||||
fun setId(value: Int?): BuilderPerson {
|
||||
fun setId(value: Int): BuilderPerson {
|
||||
id = value
|
||||
return this
|
||||
}
|
||||
|
||||
var email : kotlin.String?
|
||||
var email : kotlin.String
|
||||
private set
|
||||
fun setEmail(value: kotlin.String?): BuilderPerson {
|
||||
fun setEmail(value: kotlin.String): BuilderPerson {
|
||||
email = value
|
||||
return this
|
||||
}
|
||||
@@ -185,9 +195,9 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
|
||||
return this
|
||||
}
|
||||
|
||||
var someBytes : ByteArray?
|
||||
var someBytes : ByteArray
|
||||
private set
|
||||
fun setSomeBytes(value: ByteArray?): BuilderPerson {
|
||||
fun setSomeBytes(value: ByteArray): BuilderPerson {
|
||||
someBytes = value
|
||||
return this
|
||||
}
|
||||
@@ -202,18 +212,19 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
|
||||
}
|
||||
|
||||
fun readFrom (input: CodedInputStream): BuilderPerson {
|
||||
return readFromNoTag(input)
|
||||
}
|
||||
|
||||
fun readFromNoTag (input: CodedInputStream): BuilderPerson {
|
||||
name = input.readString(1)
|
||||
id = input.readInt32(2)
|
||||
email = input.readString(3)
|
||||
val tag = input.readTag(4, WireType.LENGTH_DELIMITED)
|
||||
val listSize = input.readInt32NoTag()
|
||||
for (i in 1..listSize) {
|
||||
val expectedSize = input.readInt32NoTag()
|
||||
var readSize = 0
|
||||
while(readSize != expectedSize) {
|
||||
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())
|
||||
}
|
||||
someBytes = input.readBytes(5)
|
||||
@@ -235,10 +246,15 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
|
||||
2 -> id = input.readInt32NoTag()
|
||||
3 -> email = input.readStringNoTag()
|
||||
4 -> {
|
||||
val listSize = input.readInt32NoTag()
|
||||
for (i in 1..listSize) {
|
||||
val expectedSize = input.readInt32NoTag()
|
||||
var readSize = 0
|
||||
while(readSize != expectedSize) {
|
||||
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())
|
||||
}
|
||||
}
|
||||
@@ -249,6 +265,21 @@ class Person private constructor (name: kotlin.String? = "", id: Int? = 0, email
|
||||
while(parseFieldFrom(input)) {}
|
||||
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
|
||||
email = other.email
|
||||
phones.addAll(other.phones)
|
||||
someBytes?.plus(other.someBytes ?: ByteArray(0))
|
||||
someBytes.plus(other.someBytes)
|
||||
}
|
||||
|
||||
fun mergeFrom (input: CodedInputStream) {
|
||||
val builder = BuilderPerson()
|
||||
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 {
|
||||
writeToNoTag(output)
|
||||
}
|
||||
|
||||
fun writeToNoTag (output: CodedOutputStream): Unit {
|
||||
if (people.size > 0) {
|
||||
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) {
|
||||
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 {
|
||||
return readFromNoTag(input)
|
||||
}
|
||||
|
||||
fun readFromNoTag (input: CodedInputStream): BuilderAddressBook {
|
||||
val tag = input.readTag(1, WireType.LENGTH_DELIMITED)
|
||||
val listSize = input.readInt32NoTag()
|
||||
for (i in 1..listSize) {
|
||||
val expectedSize = input.readInt32NoTag()
|
||||
var readSize = 0
|
||||
while(readSize != expectedSize) {
|
||||
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())
|
||||
}
|
||||
return this
|
||||
@@ -343,10 +396,15 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
|
||||
val wireType = WireFormat.getTagWireType(tag)
|
||||
when(fieldNumber) {
|
||||
1 -> {
|
||||
val listSize = input.readInt32NoTag()
|
||||
for (i in 1..listSize) {
|
||||
val expectedSize = input.readInt32NoTag()
|
||||
var readSize = 0
|
||||
while(readSize != expectedSize) {
|
||||
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())
|
||||
}
|
||||
}
|
||||
@@ -356,6 +414,17 @@ class AddressBook private constructor (people: MutableList <Person> = mutableLi
|
||||
while(parseFieldFrom(input)) {}
|
||||
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) {
|
||||
val builder = BuilderAddressBook()
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ fun testMessageSerialization() {
|
||||
.build()
|
||||
)
|
||||
.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!
|
||||
var ins = CodedInputStream(ByteArrayInputStream(s.toByteArray()))
|
||||
|
||||
@@ -4,8 +4,14 @@
|
||||
|
||||
|
||||
object WireFormat {
|
||||
// couple of constants for magic numbers
|
||||
val TAG_TYPE_BITS: Int = 3
|
||||
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 {
|
||||
return WireType from (tag and TAG_TYPE_MASK).toByte()
|
||||
@@ -14,4 +20,95 @@ object WireFormat {
|
||||
fun getTagFieldNumber(tag: Int): Int {
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user