Added support for bytes type. Fixed a bit messages serialization. Made some refactoring

This commit is contained in:
dsavvinov
2016-07-14 17:49:41 +03:00
parent 0ec0154c81
commit 340c3eca1a
28 changed files with 523 additions and 245 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ In folder /src/ you can find sources for ProtoKot-runtime, that will be used by
## Using generated code
Example:
// Messages work only with CodedStream classes, provided by ProtoKot-runtime library.
// One can create CodedStream passing any instance of corresponding Stream from Java's library.
val s = ByteArrayOutputStream()
@@ -1,3 +0,0 @@
bin/kotlin_class_generator.o: src/kotlin_class_generator.cc \
src/kotlin_class_generator.h src/kotlin_field_generator.h \
src/kotlin_enum_generator.h
@@ -1,2 +0,0 @@
bin/kotlin_enum_generator.o: src/kotlin_enum_generator.cc \
src/kotlin_enum_generator.h
@@ -1,2 +0,0 @@
bin/kotlin_field_generator.o: src/kotlin_field_generator.cc \
src/kotlin_field_generator.h
@@ -1,3 +0,0 @@
bin/kotlin_file_generator.o: src/kotlin_file_generator.cc \
src/kotlin_file_generator.h src/kotlin_class_generator.h \
src/kotlin_field_generator.h src/kotlin_enum_generator.h
@@ -1,3 +0,0 @@
bin/main.o: src/main.cc src/kotlin_file_generator.h \
src/kotlin_class_generator.h src/kotlin_field_generator.h \
src/kotlin_enum_generator.h
@@ -22,7 +22,7 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
* Also note that fields should be declared before init section.
*/
for (FieldGenerator *gen: properties) {
gen->generateCode(printer, isBuilder);
gen->generateCode(printer, isBuilder, simpleName);
printer->Print("\n");
}
@@ -61,9 +61,6 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
if (isBuilder) {
printer->Print("\n");
generateBuildMethod(printer);
printer->Print("\n");
generateSetters(printer);
}
printer->Outdent();
@@ -72,7 +69,7 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
ClassGenerator::ClassGenerator(Descriptor const *descriptor)
: descriptor(descriptor) {
simpleName = descriptor->name(); // TODO: think about more careful class naming
simpleName = descriptor->name();
int field_count = descriptor->field_count();
for (int i = 0; i < field_count; ++i) {
@@ -143,7 +140,6 @@ void ClassGenerator::generateSerializers(io::Printer * printer, bool isRead) con
"\n");
printer->Indent();
//TODO: write message tag and size
printer->Print(vars, "$maybeReturn$$funName$NoTag($arg$)\n");
printer->Outdent();
@@ -234,14 +230,6 @@ void ClassGenerator::generateInitSection(io::Printer * printer) const {
printer->Print("}\n");
}
void ClassGenerator::generateSetters(io::Printer *printer) const {
for (int i = 0; i < properties.size(); ++i) {
properties[i]->generateSetter(printer, "Builder" + simpleName);
printer->Print("\n");
}
}
const string ClassModifier::getName() const {
string result = "";
switch (type) {
@@ -47,7 +47,6 @@ private:
void generateBuilder (io::Printer * printer) const;
void generateBuildMethod (io::Printer * printer) const;
void generateInitSection (io::Printer * printer) const;
void generateSetters (io::Printer * printer) const;
/**
* Flag isBuilder used for reducing code repeating, as code for class itself
* and for its inner builder are structurally very alike and can be generated
@@ -67,7 +66,7 @@ private:
};
} // namespace kotlin
} // namspace compiler
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif //SRC_KOTLIN_CLASS_GENERATOR_H
@@ -114,6 +114,6 @@ EnumGenerator::EnumGenerator(EnumDescriptor const *descriptor) {
}
} // namespace kotlin
} // namspace compiler
} // namespace compiler
} // namespace protobuf
} // namespace google
@@ -36,7 +36,7 @@ private:
};
} // namespace kotlin
} // namspace compiler
} // namespace compiler
} // namespace protobuf
} // namespace google
@@ -6,6 +6,7 @@
#include <vector>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/descriptor.h>
#include "kotlin_name_resolver.h"
#include <iostream>
namespace google {
@@ -28,12 +29,19 @@ string FieldGenerator::protobufToKotlinField() const {
postamble = "?";
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() + postamble;
}
/**
* Simply maps protobuf field type to corresponding Kotlin.
*/
@@ -43,7 +51,7 @@ string FieldGenerator::protobufToKotlinType() const {
case FieldDescriptor::TYPE_BOOL:
return "Boolean";
case FieldDescriptor::TYPE_BYTES:
return ""; // TODO: support bytes type
return "ByteArray";
case FieldDescriptor::TYPE_DOUBLE:
return "Double";
case FieldDescriptor::TYPE_ENUM:
@@ -56,8 +64,6 @@ string FieldGenerator::protobufToKotlinType() const {
return "Long";
case FieldDescriptor::TYPE_FLOAT:
return "Float";
case FieldDescriptor::TYPE_GROUP:
return ""; // @deprecated //TODO: make proper error handling there
case FieldDescriptor::TYPE_INT32:
return "Int";
case FieldDescriptor::TYPE_INT64:
@@ -82,25 +88,73 @@ string FieldGenerator::protobufToKotlinType() const {
}
string FieldGenerator::getInitValue() const {
if (descriptor->is_optional()) {
return "null";
}
if (descriptor->is_repeated())
#ifndef KOTLIN_GENERATED_CODE_LANGUAGE_LEVEL_LOW
return "mutableListOf()";
#else
return "arrayOf()";
return fullType + "()";
#endif
FieldDescriptor::Type type = descriptor->type();
switch(type) {
case FieldDescriptor::TYPE_BOOL:
return "false";
case FieldDescriptor::TYPE_BYTES:
return "ByteArray(0)";
case FieldDescriptor::TYPE_DOUBLE:
return "0.0";
case FieldDescriptor::TYPE_ENUM: {
string enumType = descriptor->enum_type()->name();
return enumType + ".fromIntTo" + enumType + "(0)"; // produce enum from 0, as demanded by Google
}
case FieldDescriptor::TYPE_FIXED32:
return "0";
case FieldDescriptor::TYPE_FIXED64:
return "0L";
case FieldDescriptor::TYPE_FLOAT:
return "0f";
case FieldDescriptor::TYPE_INT32:
return "0";
case FieldDescriptor::TYPE_INT64:
return "0L";
case FieldDescriptor::TYPE_MESSAGE:
return string(descriptor->message_type()->name()) + "()";
case FieldDescriptor::TYPE_SFIXED32:
return "0";
case FieldDescriptor::TYPE_SFIXED64:
return "0L";
case FieldDescriptor::TYPE_SINT32:
return "0";
case FieldDescriptor::TYPE_SINT64:
return "0L";
case FieldDescriptor::TYPE_STRING:
return "\"\"";
case FieldDescriptor::TYPE_UINT32:
return "0"; // see notes for TYPE_FIXED32
case FieldDescriptor::TYPE_UINT64:
return "0L"; // see notes for TYPE_FIXED64
}
}
void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder, string className) const {
map<string, string> vars;
vars["name"] = simpleName;
vars["field"] = protobufToKotlinField();
printer->Print(vars, "var $name$ : $field$\n");
// make setter private for fair classes
if (!isBuilder) {
printer->Indent();
printer->Print("private set\n");
printer->Outdent();
// make setter private
printer->Indent();
printer->Print("private set\n");
printer->Outdent();
// generate setter for builder
if (isBuilder) {
generateSetter(printer, /* builderName = */ "Builder" + className);
}
// generate additional methods for repeated fields
if (modifier == FieldDescriptor::LABEL_REPEATED) {
generateRepeatedMethods(printer, isBuilder, /* builderName = */ "Builder" + className);
}
}
@@ -137,7 +191,7 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
//TODO: dirty stub here! Normally, reading from input should be delegated to Parsers, with proper error handling and etc.
//Currently tag is ignored, and work of the library relies heavily on the field order guarantees.
//Thus, backward-compability and extensions are not supported.
printer->Print(vars, "val tag = input.readTag()\n");
printer->Print(vars, "val tag = input.readTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n");
printer->Print(vars, "val listSize = input.readInt32NoTag()\n");
printer->Print(vars, "for (i in 1..listSize) {\n");
printer->Indent();
@@ -146,8 +200,12 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
printer->Print("}\n");
}
else {
// tag
printer->Print(vars, "output.writeTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n");
// length
printer->Print(vars, "$arg$.writeInt32NoTag($fieldName$.size)\n");
printer->Print(vars, "output.writeInt32NoTag($fieldName$.size)\n");
printer->Print(vars, "output.writeInt32NoTag($fieldName$.size)\n");
// all elements
printer->Print(vars, "for (item in $fieldName$) {\n");
@@ -201,11 +259,16 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) {
vars["fieldName"] = noTag ? "item" : simpleName;
vars["maybeNoTag"] = noTag ? "NoTag" : "";
vars["fieldNumber"] = std::to_string(descriptor->number());
if (isRead) {
printer->Print(vars, "$fieldName$.readFrom$maybeNoTag$(input)\n");
printer->Print(vars,
"val tag = input.readTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n"
"$fieldName$.readFrom$maybeNoTag$(input)\n");
}
else {
printer->Print(vars, "$fieldName$.writeTo$maybeNoTag$(output)\n");
printer->Print(vars,
"output.writeTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n"
"$fieldName$.writeTo$maybeNoTag$(output)\n");
}
return;
}
@@ -217,9 +280,6 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
else {
printer->Print(vars, "output.write$type$ ($fieldNumber$, $fieldName$)\n");
}
// TODO: support tricky types like enums/messages/repeated fields/etc
}
string FieldGenerator::protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type type) const {
@@ -242,12 +302,10 @@ string FieldGenerator::protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type
return "Bool";
case FieldDescriptor::TYPE_STRING:
return "String";
case FieldDescriptor::TYPE_GROUP:
return ""; // deprecated // TODO: think about proper error handling here
case FieldDescriptor::TYPE_MESSAGE:
return "Message"; // TODO: support messages
return "Message";
case FieldDescriptor::TYPE_BYTES:
return ""; // TODO: support bytes
return "Bytes";
case FieldDescriptor::TYPE_UINT32:
return "UInt32";
case FieldDescriptor::TYPE_ENUM:
@@ -265,25 +323,67 @@ string FieldGenerator::protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type
void FieldGenerator::generateSetter(io::Printer *printer, string builderName) const {
map <string, string> vars;
// TODO: refactor work with names into separate class
string camelCaseName = simpleName;
camelCaseName[0] = char(std::toupper(camelCaseName[0]));
vars["camelCaseName"] = camelCaseName;
vars["name"] = simpleName;
vars["camelCaseName"] = name_resolving::makeFirstLetterUpper(simpleName);
vars["fieldName"] = simpleName;
vars["builderName"] = builderName;
vars["type"] = fullType;
printer->Print(vars,
"fun set$camelCaseName$(value: $type$): $builderName$ {\n");
printer->Indent();
printer->Print(vars,
"$name$ = value\n"
"$fieldName$ = value\n"
"return this\n");
printer->Outdent();
printer->Print("}\n");
}
void FieldGenerator::generateRepeatedMethods(io::Printer * printer, bool isBuilder, string builderName) const {
map <string, string> vars;
vars["elementType"] = underlyingType;
vars["arg"] = "value";
vars["fieldName"] = simpleName;
vars["builderName"] = builderName;
// generate indexed setter for builders
if (isBuilder) {
printer->Print(vars, "fun set$elementType$(index: Int, $arg$: $elementType$): $builderName$ {\n");
printer->Indent();
printer->Print(vars, "$fieldName$[index] = $arg$\n");
printer->Print(vars, "return this\n");
printer->Outdent();
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");
printer->Indent();
printer->Print(vars, "$fieldName$.add($arg$)\n"
"return this\n");
printer->Outdent();
printer->Print(vars, "}\n");
// generate addAll for builders
printer->Print(vars, "fun addAll$elementType$($arg$: Iterable<$elementType$>): $builderName$ {\n");
printer->Indent();
printer->Print(vars, "for (item in $arg$) {\n");
printer->Indent();
printer->Print(vars, "$fieldName$.add(item)\n");
printer->Outdent(); // for-loop
printer->Print("}\n");
printer->Print("return this\n");
printer->Outdent(); // function body
printer->Print("}\n");
}
#endif
}
} // namespace kotlin
} // namspace compiler
} // namespace compiler
} // namespace protobuf
} // namespace google
@@ -30,7 +30,8 @@ private:
* CodedInputStream.readSFixed32(fieldNumber: Int)
*/
string protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type type) const;
void generateSetter(io::Printer * printer, string builderName) const;
void generateRepeatedMethods(io::Printer * printer, bool isBuilder, string builderName) const;
public:
FieldDescriptor::Label modifier;
string simpleName;
@@ -45,15 +46,14 @@ public:
string fullType;
string initValue;
void generateCode(io::Printer * printer, bool isBuilder = false) const;
void generateCode(io::Printer * printer, bool isBuilder, string className) const;
void generateSerializationCode(io::Printer * printer, bool isRead = false, bool noTag = false) const;
void generateSetter(io::Printer * printer, string builderName) const;
FieldGenerator(FieldDescriptor const * descriptor);
};
} // namespace kotlin
} // namspace compiler
} // namespace compiler
} // namespace protobuf
} // namespace google
@@ -10,6 +10,7 @@
#endif
#include "kotlin_file_generator.h"
#include <iostream>
#include "kotlin_name_resolver.h"
namespace google {
namespace protobuf {
@@ -26,14 +27,7 @@ void FileGenerator::generateCode(io::Printer *printer, std::vector<ClassGenerato
bool FileGenerator::Generate(const FileDescriptor *file, const string &parameter, GeneratorContext *context,
string *error) const {
std::vector<ClassGenerator *> classes;
//TODO: maybe wrap work with class names and stuff in separate class (like in Java implementation)
// Get output file name
string const proto_file_name = file->name();
size_t file_extension_index = proto_file_name.find(".proto");
string const file_name = proto_file_name.substr(0, file_extension_index) + ".kt";
string const file_name = name_resolving::getKotlinOutputByProtoName(file->name());
google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(file_name));
io::Printer printer(output.get(), '$', /* annotation_collector = */ NULL);
@@ -0,0 +1,36 @@
//
// Created by user on 7/14/16.
//
#include "kotlin_name_resolver.h"
#include <string>
namespace google {
namespace protobuf {
namespace compiler {
namespace kotlin {
namespace name_resolving {
using namespace std;
string makeFirstLetterUpper(std::string s) {
s[0] = std::toupper(s[0]);
return s;
}
string getFileNameWithoutExtension(string fullName) {
size_t file_extension_index = fullName.find_last_of(".");
return fullName.substr(0, file_extension_index);
}
string getKotlinOutputByProtoName(string protoName) {
string justName = getFileNameWithoutExtension(protoName);
return justName + ".kt";
}
} // namespace name_resolving
} // namespace kotlin
} // namespace compiler
} // namespace protobuf
} // namespace google
@@ -0,0 +1,29 @@
//
// Created by user on 7/14/16.
//
#ifndef GOOGLE_KOTLIN_NAME_RESOLVER_H
#define GOOGLE_KOTLIN_NAME_RESOLVER_H
#include <string>
namespace google {
namespace protobuf {
namespace compiler {
namespace kotlin {
namespace name_resolving {
std::string makeFirstLetterUpper(std::string s);
std::string getFileNameWithoutExtension(std::string fullName);
std::string getKotlinOutputByProtoName(std::string protoName);
} // namespace name_resolving
} // namespace kotlin
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif //GOOGLE_KOTLIN_NAME_RESOLVER_H
@@ -3,8 +3,6 @@
#include <google/protobuf/compiler/python/python_generator.h>
#include <google/protobuf/compiler/java/java_generator.h>
#include <google/protobuf/compiler/javanano/javanano_generator.h>
// TODO(teboring): Add it back when php implementation is ready
// #include <google/protobuf/compiler/php/php_generator.h>
#include <google/protobuf/compiler/ruby/ruby_generator.h>
#include <google/protobuf/compiler/csharp/csharp_generator.h>
#include <google/protobuf/compiler/objectivec/objectivec_generator.h>
@@ -12,6 +10,7 @@
#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;
@@ -1,4 +1,4 @@
class Person private constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array <PhoneNumber> = arrayOf()) {
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
@@ -8,7 +8,10 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null,
var email : kotlin.String?
private set
var phones : Array <PhoneNumber>
var phones : MutableList <PhoneNumber>
private set
var someBytes : ByteArray?
private set
@@ -17,6 +20,7 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null,
this.id = id
this.email = email
this.phones = phones
this.someBytes = someBytes
}
enum class PhoneType(val ord: Int) {
MOBILE (0),
@@ -34,7 +38,7 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null,
}
}
}
class PhoneNumber private constructor (number: kotlin.String? = null, type: PhoneType? = null) {
class PhoneNumber private constructor (number: kotlin.String? = "", type: PhoneType? = PhoneType.fromIntToPhoneType(0)) {
var number : kotlin.String?
private set
@@ -47,19 +51,29 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null,
this.type = type
}
fun writeTo (output: CodedOutputStream) {
fun writeTo (output: CodedOutputStream): Unit {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream) {
fun writeToNoTag (output: CodedOutputStream): Unit {
output.writeString (1, number)
output.writeEnum (2, type?.ord)
}
class BuilderPhoneNumber constructor (number: kotlin.String? = null, type: PhoneType? = null) {
class BuilderPhoneNumber constructor (number: kotlin.String? = "", type: PhoneType? = PhoneType.fromIntToPhoneType(0)) {
var number : kotlin.String?
private set
fun setNumber(value: kotlin.String?): BuilderPhoneNumber {
number = value
return this
}
var type : PhoneType?
private set
fun setType(value: PhoneType?): BuilderPhoneNumber {
type = value
return this
}
init {
@@ -67,29 +81,19 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null,
this.type = type
}
fun readFrom (input: CodedInputStream) {
readFromNoTag(input)
fun readFrom (input: CodedInputStream): BuilderPhoneNumber {
return readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream) {
fun readFromNoTag (input: CodedInputStream): BuilderPhoneNumber {
number = input.readString(1)
type = PhoneType.fromIntToPhoneType(input.readEnum(2))
}
return this
}
fun build(): PhoneNumber {
return PhoneNumber(number, type)
}
fun setNumber(value: kotlin.String?): BuilderPhoneNumber {
number = value
return this
}
fun setType(value: PhoneType?): BuilderPhoneNumber {
type = value
return this
}
}
fun mergeFrom (input: CodedInputStream) {
@@ -99,30 +103,75 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null,
}
fun writeTo (output: CodedOutputStream) {
fun writeTo (output: CodedOutputStream): Unit {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream) {
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)
output.writeInt32NoTag(phones.size)
for (item in phones) {
output.writeTag(4, WireType.LENGTH_DELIMITED)
item.writeToNoTag(output)
}
}
output.writeBytes (5, someBytes)
}
class BuilderPerson constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array <PhoneNumber> = arrayOf()) {
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 {
name = value
return this
}
var id : Int?
private set
fun setId(value: Int?): BuilderPerson {
id = value
return this
}
var email : kotlin.String?
private set
fun setEmail(value: kotlin.String?): BuilderPerson {
email = value
return this
}
var phones : Array <PhoneNumber>
var phones : MutableList <PhoneNumber>
private set
fun setPhones(value: MutableList <PhoneNumber> ): BuilderPerson {
phones = value
return this
}
fun setPhoneNumber(index: Int, value: PhoneNumber): BuilderPerson {
phones[index] = value
return this
}
fun addPhoneNumber(value: PhoneNumber): BuilderPerson {
phones.add(value)
return this
}
fun addAllPhoneNumber(value: Iterable<PhoneNumber>): BuilderPerson {
for (item in value) {
phones.add(item)
}
return this
}
var someBytes : ByteArray?
private set
fun setSomeBytes(value: ByteArray?): BuilderPerson {
someBytes = value
return this
}
init {
@@ -130,49 +179,31 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null,
this.id = id
this.email = email
this.phones = phones
this.someBytes = someBytes
}
fun readFrom (input: CodedInputStream) {
readFromNoTag(input)
fun readFrom (input: CodedInputStream): BuilderPerson {
return readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream) {
fun readFromNoTag (input: CodedInputStream): BuilderPerson {
name = input.readString(1)
id = input.readInt32(2)
email = input.readString(3)
if (phones.size > 0) {
val tag = input.readTag()
val tag = input.readTag(4, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
phones[i - 1].mergeFrom(input)
}
}
}
someBytes = input.readBytes(5)
return this
}
fun build(): Person {
return Person(name, id, email, phones)
return Person(name, id, email, phones, someBytes)
}
fun setName(value: kotlin.String?): BuilderPerson {
name = value
return this
}
fun setId(value: Int?): BuilderPerson {
id = value
return this
}
fun setEmail(value: kotlin.String?): BuilderPerson {
email = value
return this
}
fun setPhones(value: Array <PhoneNumber> ): BuilderPerson {
phones = value
return this
}
}
fun mergeFrom (input: CodedInputStream) {
@@ -180,18 +211,19 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null,
id = input.readInt32(2)
email = input.readString(3)
if (phones.size > 0) {
val tag = input.readTag()
val tag = input.readTag(4, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
phones[i - 1].mergeFrom(input)
}
}
someBytes = input.readBytes(5)
}
}
class AddressBook private constructor (people: Array <Person> = arrayOf()) {
var people : Array <Person>
class AddressBook private constructor (people: MutableList <Person> = mutableListOf()) {
var people : MutableList <Person>
private set
@@ -199,55 +231,72 @@ class AddressBook private constructor (people: Array <Person> = arrayOf()) {
this.people = people
}
fun writeTo (output: CodedOutputStream) {
fun writeTo (output: CodedOutputStream): Unit {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream) {
fun writeToNoTag (output: CodedOutputStream): Unit {
if (people.size > 0) {
output.writeTag(1, WireType.LENGTH_DELIMITED)
output.writeInt32NoTag(people.size)
output.writeInt32NoTag(people.size)
for (item in people) {
output.writeTag(1, WireType.LENGTH_DELIMITED)
item.writeToNoTag(output)
}
}
}
class BuilderAddressBook constructor (people: Array <Person> = arrayOf()) {
var people : Array <Person>
class BuilderAddressBook constructor (people: MutableList <Person> = mutableListOf()) {
var people : MutableList <Person>
private set
fun setPeople(value: MutableList <Person> ): BuilderAddressBook {
people = value
return this
}
fun setPerson(index: Int, value: Person): BuilderAddressBook {
people[index] = value
return this
}
fun addPerson(value: Person): BuilderAddressBook {
people.add(value)
return this
}
fun addAllPerson(value: Iterable<Person>): BuilderAddressBook {
for (item in value) {
people.add(item)
}
return this
}
init {
this.people = people
}
fun readFrom (input: CodedInputStream) {
readFromNoTag(input)
fun readFrom (input: CodedInputStream): BuilderAddressBook {
return readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream) {
fun readFromNoTag (input: CodedInputStream): BuilderAddressBook {
if (people.size > 0) {
val tag = input.readTag()
val tag = input.readTag(1, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
people[i - 1].mergeFrom(input)
}
}
}
return this
}
fun build(): AddressBook {
return AddressBook(people)
}
fun setPeople(value: Array <Person> ): BuilderAddressBook {
people = value
return this
}
}
fun mergeFrom (input: CodedInputStream) {
if (people.size > 0) {
val tag = input.readTag()
val tag = input.readTag(1, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
people[i - 1].mergeFrom(input)
@@ -38,6 +38,8 @@ message Person {
}
repeated PhoneNumber phones = 4;
bytes someBytes = 5;
}
// Our address book file is just one of these.
+32 -59
View File
@@ -11,7 +11,7 @@ import java.nio.ByteOrder
*
*/
// TODO: decide, if we want to optimize for performance (then we have to deal with spaghetti-code)
// TODO: refactor correctness checks into readTag
class CodedInputStream(input: java.io.InputStream) {
val bufferedInput: java.io.BufferedInputStream
@@ -20,7 +20,7 @@ class CodedInputStream(input: java.io.InputStream) {
}
fun readInt32(expectedFieldNumber: Int): Int {
val tag = readTag()
val tag = readTag(expectedFieldNumber, WireType.VARINT)
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType)
@@ -31,10 +31,7 @@ class CodedInputStream(input: java.io.InputStream) {
// simply stored in the sign bit - similar to Java's protobuf implementation. Hence, all
// methods reading unsigned ints simply redirect call to corresponding signed-reading method
fun readUInt32(expectedFieldNumber: Int): Int {
val tag = readTag()
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType)
val tag = readTag(expectedFieldNumber, WireType.VARINT)
return readUInt32NoTag()
}
@@ -43,19 +40,13 @@ class CodedInputStream(input: java.io.InputStream) {
}
fun readInt64(expectedFieldNumber: Int): Long {
val tag = readTag()
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, actualWireType, WireType.VARINT)
val tag = readTag(expectedFieldNumber, WireType.VARINT)
return readInt64NoTag()
}
// See note on unsigned integers implementations above
fun readUInt64(expectedFieldNumber: Int): Long {
val tag = readTag()
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, actualWireType, WireType.VARINT)
val tag = readTag(expectedFieldNumber, WireType.VARINT)
return readUInt64NoTag()
}
@@ -64,10 +55,7 @@ class CodedInputStream(input: java.io.InputStream) {
}
fun readBool(expectedFieldNumber: Int): Boolean {
val tag = readTag()
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, actualWireType, WireType.VARINT)
val tag = readTag(expectedFieldNumber, WireType.VARINT)
return readBoolNoTag()
}
@@ -83,10 +71,7 @@ class CodedInputStream(input: java.io.InputStream) {
// Reading enums is like reading one int32 number. Caller is responsible for converting this ordinal to enum-object
fun readEnum(expectedFieldNumber: Int): Int {
val tag = readTag()
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType)
val tag = readTag(expectedFieldNumber, WireType.VARINT)
return readEnumNoTag()
}
@@ -95,10 +80,7 @@ class CodedInputStream(input: java.io.InputStream) {
}
fun readSInt32(expectedFieldNumber: Int): Int {
val tag = readTag()
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType)
val tag = readTag(expectedFieldNumber, WireType.VARINT)
return readSInt32NoTag()
}
@@ -107,18 +89,12 @@ class CodedInputStream(input: java.io.InputStream) {
}
fun readSInt64(expectedFieldNumber: Int): Long {
val tag = readTag()
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType)
val tag = readTag(expectedFieldNumber, WireType.VARINT)
return readZigZag64NoTag()
}
fun readFixed32(expectedFieldNumber: Int): Int {
val tag = readTag()
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_32, actualWireType)
val tag = readTag(expectedFieldNumber, WireType.FIX_32)
return readFixed32NoInt()
}
@@ -127,10 +103,7 @@ class CodedInputStream(input: java.io.InputStream) {
}
fun readSFixed32(expectedFieldNumber: Int): Int {
val tag = readTag()
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_32, actualWireType)
val tag = readTag(expectedFieldNumber, WireType.FIX_32)
return readSFixed32NoTag()
}
@@ -139,10 +112,7 @@ class CodedInputStream(input: java.io.InputStream) {
}
fun readFixed64(expectedFieldNumber: Int): Long {
val tag = readTag()
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_64, actualWireType)
val tag = readTag(expectedFieldNumber, WireType.FIX_64)
return readFixed64NoTag()
}
@@ -151,10 +121,7 @@ class CodedInputStream(input: java.io.InputStream) {
}
fun readSFixed64(expectedFieldNumber: Int): Long {
val tag = readTag()
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_64, actualWireType)
val tag = readTag(expectedFieldNumber, WireType.FIX_64)
return readSFixed64NoTag()
}
@@ -163,10 +130,7 @@ class CodedInputStream(input: java.io.InputStream) {
}
fun readDouble(expectedFieldNumber: Int): Double {
val tag = readTag()
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_64, actualWireType)
val tag = readTag(expectedFieldNumber, WireType.FIX_64)
return readDoubleNoTag()
}
@@ -175,10 +139,7 @@ class CodedInputStream(input: java.io.InputStream) {
}
fun readFloat(expectedFieldNumber: Int): Float {
val tag = readTag()
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_32, actualWireType)
val tag = readTag(expectedFieldNumber, WireType.FIX_32)
return readFloatNoTag()
}
@@ -187,10 +148,7 @@ class CodedInputStream(input: java.io.InputStream) {
}
fun readString(expectedFieldNumber: Int): String {
val tag = readTag()
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.LENGTH_DELIMITED, actualWireType)
val tag = readTag(expectedFieldNumber, WireType.LENGTH_DELIMITED)
return readStringNoTag()
}
@@ -199,6 +157,17 @@ class CodedInputStream(input: java.io.InputStream) {
val value = String(readRawBytes(length))
return value
}
fun readBytes(expectedFieldNumber: Int): ByteArray {
val tag = readTag(expectedFieldNumber, WireType.LENGTH_DELIMITED)
return readBytesNoTag()
}
fun readBytesNoTag(): ByteArray {
val length = readInt32NoTag()
return readRawBytes(length)
}
/** ============ Utility methods ==================
* They are left non-private for cases when one wants to implement her/his own protocol format.
* Then she/he can re-use low-level methods for operating with raw values, that are not annotated with Protobuf tags.
@@ -254,7 +223,7 @@ class CodedInputStream(input: java.io.InputStream) {
}
// reads tag. Note that it returns 0 for the end of message!
fun readTag(): Int {
fun readTag(expectedFieldNumber: Int, expectedWireType: WireType): Int {
if (isAtEnd()) {
return 0 // we can safely return 0 as sign of end of message, because 0-tags are illegal
}
@@ -262,6 +231,10 @@ class CodedInputStream(input: java.io.InputStream) {
if (tag == 0) { // if we somehow had read 0-tag, then message is corrupted
throw InvalidProtocolBufferException("Invalid tag 0")
}
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
val actualWireType = WireFormat.getTagWireType(tag)
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.LENGTH_DELIMITED, actualWireType)
return tag
}
+16
View File
@@ -151,6 +151,22 @@ class CodedOutputStream(val output: java.io.OutputStream) {
output.write(value.toByteArray())
}
fun writeBytes(fieldNumber: Int, value: ByteArray?) {
value ?: return
writeTag(fieldNumber, WireType.LENGTH_DELIMITED)
writeBytesNoTag(value)
}
fun writeBytesNoTag(value: ByteArray) {
writeInt32NoTag(value.size)
output.write(value)
}
/** ============ Utility methods ==================
* They are left non-private for cases when one wants to implement her/his own protocol format.
* Then she/he can re-use low-level methods for operating with raw values, that are not annotated with Protobuf tags.
*/
fun writeLittleEndian(value: Int?) {
value ?: return
val bb = ByteBuffer.allocate(4)
+117 -33
View File
@@ -1,6 +1,4 @@
package Addressbook;
class Person private constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array <PhoneNumber> = arrayOf()) {
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
@@ -10,7 +8,10 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null,
var email : kotlin.String?
private set
var phones : Array <PhoneNumber>
var phones : MutableList <PhoneNumber>
private set
var someBytes : ByteArray?
private set
@@ -19,8 +20,8 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null,
this.id = id
this.email = email
this.phones = phones
this.someBytes = someBytes
}
enum class PhoneType(val ord: Int) {
MOBILE (0),
HOME (1),
@@ -37,7 +38,7 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null,
}
}
}
class PhoneNumber private constructor (number: kotlin.String? = null, type: PhoneType? = null) {
class PhoneNumber private constructor (number: kotlin.String? = "", type: PhoneType? = PhoneType.fromIntToPhoneType(0)) {
var number : kotlin.String?
private set
@@ -50,19 +51,29 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null,
this.type = type
}
fun writeTo (output: CodedOutputStream) {
fun writeTo (output: CodedOutputStream): Unit {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream) {
fun writeToNoTag (output: CodedOutputStream): Unit {
output.writeString (1, number)
output.writeEnum (2, type?.ord)
}
class BuilderPhoneNumber constructor (number: kotlin.String? = null, type: PhoneType? = null) {
class BuilderPhoneNumber constructor (number: kotlin.String? = "", type: PhoneType? = PhoneType.fromIntToPhoneType(0)) {
var number : kotlin.String?
private set
fun setNumber(value: kotlin.String?): BuilderPhoneNumber {
number = value
return this
}
var type : PhoneType?
private set
fun setType(value: PhoneType?): BuilderPhoneNumber {
type = value
return this
}
init {
@@ -70,13 +81,14 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null,
this.type = type
}
fun readFrom (input: CodedInputStream) {
readFromNoTag(input)
fun readFrom (input: CodedInputStream): BuilderPhoneNumber {
return readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream) {
fun readFromNoTag (input: CodedInputStream): BuilderPhoneNumber {
number = input.readString(1)
type = PhoneType.fromIntToPhoneType(input.readEnum(2))
return this
}
fun build(): PhoneNumber {
@@ -91,30 +103,75 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null,
}
fun writeTo (output: CodedOutputStream) {
fun writeTo (output: CodedOutputStream): Unit {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream) {
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)
output.writeInt32NoTag(phones.size)
for (item in phones) {
output.writeTag(4, WireType.LENGTH_DELIMITED)
item.writeToNoTag(output)
}
}
output.writeBytes (5, someBytes)
}
class BuilderPerson constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array <PhoneNumber> = arrayOf()) {
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 {
name = value
return this
}
var id : Int?
private set
fun setId(value: Int?): BuilderPerson {
id = value
return this
}
var email : kotlin.String?
private set
fun setEmail(value: kotlin.String?): BuilderPerson {
email = value
return this
}
var phones : Array <PhoneNumber>
var phones : MutableList <PhoneNumber>
private set
fun setPhones(value: MutableList <PhoneNumber> ): BuilderPerson {
phones = value
return this
}
fun setPhoneNumber(index: Int, value: PhoneNumber): BuilderPerson {
phones[index] = value
return this
}
fun addPhoneNumber(value: PhoneNumber): BuilderPerson {
phones.add(value)
return this
}
fun addAllPhoneNumber(value: Iterable<PhoneNumber>): BuilderPerson {
for (item in value) {
phones.add(item)
}
return this
}
var someBytes : ByteArray?
private set
fun setSomeBytes(value: ByteArray?): BuilderPerson {
someBytes = value
return this
}
init {
@@ -122,27 +179,30 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null,
this.id = id
this.email = email
this.phones = phones
this.someBytes = someBytes
}
fun readFrom (input: CodedInputStream) {
readFromNoTag(input)
fun readFrom (input: CodedInputStream): BuilderPerson {
return readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream) {
fun readFromNoTag (input: CodedInputStream): BuilderPerson {
name = input.readString(1)
id = input.readInt32(2)
email = input.readString(3)
if (phones.size > 0) {
val tag = input.readTag()
val tag = input.readTag(4, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
phones[i - 1].mergeFrom(input)
}
}
someBytes = input.readBytes(5)
return this
}
fun build(): Person {
return Person(name, id, email, phones)
return Person(name, id, email, phones, someBytes)
}
}
@@ -151,18 +211,19 @@ class Person private constructor (name: kotlin.String? = null, id: Int? = null,
id = input.readInt32(2)
email = input.readString(3)
if (phones.size > 0) {
val tag = input.readTag()
val tag = input.readTag(4, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
phones[i - 1].mergeFrom(input)
}
}
someBytes = input.readBytes(5)
}
}
class AddressBook private constructor (people: Array <Person> = arrayOf()) {
var people : Array <Person>
class AddressBook private constructor (people: MutableList <Person> = mutableListOf()) {
var people : MutableList <Person>
private set
@@ -170,39 +231,62 @@ class AddressBook private constructor (people: Array <Person> = arrayOf()) {
this.people = people
}
fun writeTo (output: CodedOutputStream) {
fun writeTo (output: CodedOutputStream): Unit {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream) {
fun writeToNoTag (output: CodedOutputStream): Unit {
if (people.size > 0) {
output.writeTag(1, WireType.LENGTH_DELIMITED)
output.writeInt32NoTag(people.size)
output.writeInt32NoTag(people.size)
for (item in people) {
output.writeTag(1, WireType.LENGTH_DELIMITED)
item.writeToNoTag(output)
}
}
}
class BuilderAddressBook constructor (people: Array <Person> = arrayOf()) {
var people : Array <Person>
class BuilderAddressBook constructor (people: MutableList <Person> = mutableListOf()) {
var people : MutableList <Person>
private set
fun setPeople(value: MutableList <Person> ): BuilderAddressBook {
people = value
return this
}
fun setPerson(index: Int, value: Person): BuilderAddressBook {
people[index] = value
return this
}
fun addPerson(value: Person): BuilderAddressBook {
people.add(value)
return this
}
fun addAllPerson(value: Iterable<Person>): BuilderAddressBook {
for (item in value) {
people.add(item)
}
return this
}
init {
this.people = people
}
fun readFrom (input: CodedInputStream) {
readFromNoTag(input)
fun readFrom (input: CodedInputStream): BuilderAddressBook {
return readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream) {
fun readFromNoTag (input: CodedInputStream): BuilderAddressBook {
if (people.size > 0) {
val tag = input.readTag()
val tag = input.readTag(1, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
people[i - 1].mergeFrom(input)
}
}
return this
}
fun build(): AddressBook {
@@ -212,7 +296,7 @@ class AddressBook private constructor (people: Array <Person> = arrayOf()) {
fun mergeFrom (input: CodedInputStream) {
if (people.size > 0) {
val tag = input.readTag()
val tag = input.readTag(1, WireType.LENGTH_DELIMITED)
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
people[i - 1].mergeFrom(input)
+27 -5
View File
@@ -5,18 +5,40 @@ import java.io.ByteArrayOutputStream
* Created by user on 7/8/16.
*/
import PersonMessage
fun testMessageSerialization() {
// Messages work only with CodedStream classes, provided by ProtoKot-runtime library.
// One can create CodedStream passing any instance of corresponding Stream from Java's library.
val s = ByteArrayOutputStream()
val outs = CodedOutputStream(s)
val msg =
// All messages are immutable. Use Builders for creating new messages
val msg = Person.BuilderPerson()
.setEmail("wtf@dasda.com") // all setters return this builder, so you could chain modifiers in LINQ-style
.setId(42)
.setName("John Doe")
.setPhones(arrayOf( // repeated fields stored as Array<>, so use arrayOf() for creating repeated fields
Person.PhoneNumber.BuilderPhoneNumber()
.setNumber("342143-23423-42")
.setType(Person.PhoneType.HOME)
.build()
))
.build() // don't forget to call build() to produce message
msg.writeTo(outs)
val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray()))
val readMsg = Person("", 0, "", arrayOf())
// Now let's use output stream as input to read our message from it!
var ins = CodedInputStream(ByteArrayInputStream(s.toByteArray()))
// Create default instance of message
var readMsg = Person.BuilderPerson().build()
// Read in that message data from input stream.
readMsg.mergeFrom(ins)
// Note, that currently mergeFrom is the only way to mutate instance of message.
// Don't rely on it, probably mergeFrom will be refactored lately to guarantee full immutability of mesages.
// Better way to read a message:
ins = CodedInputStream(ByteArrayInputStream(s.toByteArray()))
readMsg = Person.BuilderPerson().readFrom(ins).build()
assert(msg == readMsg)
}