Basic operations are now fully supported in the .proto-compiler. Added README.md with brief manual on usage

This commit is contained in:
dsavvinov
2016-07-14 12:45:31 +03:00
parent 5419699cfc
commit d3d91b26be
10 changed files with 388 additions and 296 deletions
+58
View File
@@ -0,0 +1,58 @@
# ProtoKot compiler
## Building
$ cd google/protobuf/compiler/kotlin
$ make
This will build current version of .proto compiler, placed as *protoc* executable file in current directory.
*Note:* You need g++ and protobuf installed (-lprotoc used for linking)
## Using compiler
$ ./protoc kotlin_out==$(DST_DIR) $(PATH_TO_PROTO)
where $(DST_DIR) stand for path to place, where generated files should be stored, and $(PATH_TO_PROTO) stand for path to .proto-file.
## Linking with Kotlin-runtime
In folder /src/ you can find sources for ProtoKot-runtime, that will be used by generated messages.
## 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()
val outs = CodedOutputStream(s)
// 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)
// 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)
@@ -19,6 +19,7 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
/**
* Field generator should know if it is generating code for builder.
* or for fair class to choose between 'val' and 'var'.
* Also note that fields should be declared before init section.
*/
for (FieldGenerator *gen: properties) {
gen->generateCode(printer, isBuilder);
@@ -47,7 +48,7 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
printer->Print("\n");
generateSerializersNoTag(printer, /* isRead = */ isBuilder);
// builder and mergeFrom only for fair classes
// builder, mergeFrom and only for fair classes
if (!isBuilder) {
printer->Print("\n");
generateBuilder(printer);
@@ -56,10 +57,13 @@ void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
generateMergeFrom(printer);
}
// build() is only for builders
// build() and setters are only for builders
if (isBuilder) {
printer->Print("\n");
generateBuildMethod(printer);
printer->Print("\n");
generateSetters(printer);
}
printer->Outdent();
@@ -127,18 +131,20 @@ void ClassGenerator::generateMergeFrom(io::Printer * printer) const {
void ClassGenerator::generateSerializers(io::Printer * printer, bool isRead) const {
map <string, string> vars;
vars["funName"]= isRead ? "readFrom" : "writeTo";
vars["returnType"] = isRead ? "Builder" + simpleName : "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$) {"
"fun $funName$ ($arg$: $stream$): $returnType$ {"
"\n");
printer->Indent();
//TODO: write message tag and size
printer->Print(vars, "$funName$NoTag($arg$)\n");
printer->Print(vars, "$maybeReturn$$funName$NoTag($arg$)\n");
printer->Outdent();
printer->Print("}\n");
@@ -149,10 +155,12 @@ void ClassGenerator::generateSerializersNoTag(io::Printer *printer, bool isRead)
vars["funName"]= isRead ? "readFromNoTag" : "writeToNoTag";
vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream";
vars["arg"] = isRead ? "input" : "output";
vars["returnType"] = isRead ? "Builder" + simpleName : "Unit";
vars["maybeReturn"] = isRead ? "return this\n" : "";
// generate function header
printer->Print(vars,
"fun $funName$ ($arg$: $stream$) {"
"fun $funName$ ($arg$: $stream$): $returnType$ {"
"\n");
printer->Indent();
@@ -161,6 +169,7 @@ void ClassGenerator::generateSerializersNoTag(io::Printer *printer, bool isRead)
properties[i]->generateSerializationCode(printer, isRead);
}
printer->Print(vars, "$maybeReturn$");
printer->Outdent();
printer->Print("}\n");
}
@@ -171,7 +180,7 @@ void ClassGenerator::generateHeader(io::Printer * printer, bool isBuilder) const
// build list of arguments like 'field1: Type1, field2: Type2, ... '
string argumentList = "";
for (int i = 0; i < properties.size(); ++i) {
argumentList += properties[i]->simpleName + ": " + properties[i]->fieldName;
argumentList += properties[i]->simpleName + ": " + properties[i]->fullType + " = " + properties[i]->initValue;
if (i + 1 != properties.size()) {
argumentList += ", ";
}
@@ -225,6 +234,13 @@ 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 = "";
@@ -47,7 +47,7 @@ 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
@@ -86,8 +86,8 @@ string FieldGenerator::getInitValue() const {
return "null";
}
if (descriptor->is_repeated())
return "listOf()";
return fieldName + "()";
return "arrayOf()";
return fullType + "()";
}
void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
@@ -108,8 +108,8 @@ FieldGenerator::FieldGenerator(FieldDescriptor const * descriptor)
: descriptor(descriptor)
, modifier(descriptor->label())
, simpleName(descriptor->name())
, fieldName(protobufToKotlinField())
, fieldType(protobufToKotlinType())
, underlyingType(protobufToKotlinType())
, fullType(protobufToKotlinField())
, initValue(getInitValue())
{ }
@@ -135,8 +135,8 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
// tag
if (isRead) {
//TODO: dirty stub here! Normally, reading from input should be delegated to Parsers, with proper error handling and etc.
//Currently tag is ignored, and fields order is critical for serialization/deserialization. Therefore,
//backward-compability and extensions are not supported.
//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 listSize = input.readInt32NoTag()\n");
printer->Print(vars, "for (i in 1..listSize) {\n");
@@ -156,8 +156,9 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
/* hack: copy current FieldGenerator and change label to OPTIONAL. This will allow
to re-use this function for generating serialization code for elements of array.
More importantly, this will care about nested types too.
However, this hack isn't necessary and could be safely removed as soon as target
code will support inheritance and interfaces
Efficiently, it inlines serialization code for all underlying types.
This hack isn't necessary from the architectural point of view and could be safely5
removed as soon as target code will support inheritance and interfaces.
(then writing CodedOutputStream.writeMessage will be possible).
*/
FieldGenerator singleFieldGen = FieldGenerator(descriptor);
@@ -183,7 +184,7 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
Example: output.writeEnum(42, enumField.ord)
*/
if (descriptor->type() == FieldDescriptor::TYPE_ENUM) {
vars["converter"] = fieldType + ".fromIntTo" + fieldType;
vars["converter"] = underlyingType + ".fromIntTo" + underlyingType;
if (isRead) {
printer->Print(vars, "$fieldName$ = $converter$(input.read$type$($fieldNumber$))\n");
}
@@ -221,7 +222,6 @@ void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead
// TODO: support tricky types like enums/messages/repeated fields/etc
}
// TODO: think about refactoring this method to FieldGenerator, as it is related to field, not to Class in general
string FieldGenerator::protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type type) const {
switch (type) {
case FieldDescriptor::TYPE_DOUBLE:
@@ -262,6 +262,27 @@ string FieldGenerator::protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type
return "SInt64";
}
}
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["builderName"] = builderName;
vars["type"] = fullType;
printer->Print(vars,
"fun set$camelCaseName$(value: $type$): $builderName$ {\n");
printer->Indent();
printer->Print(vars,
"$name$ = value\n"
"return this\n");
printer->Outdent();
printer->Print("}\n");
}
} // namespace kotlin
} // namspace compiler
} // namespace protobuf
@@ -34,11 +34,20 @@ private:
public:
FieldDescriptor::Label modifier;
string simpleName;
string fieldName;
string fieldType;
string underlyingType; // unwrapped type.
/**
* Full type of field.
* fullType = Array<underlyingType> for REPEATED fields
* fullType = underlyingType? for OPTIONAL fields
* fullType = underlyingType for all other cases
*/
string fullType;
string initValue;
void generateCode(io::Printer * printer, bool isBuilder = false) 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);
};
@@ -1,4 +1,4 @@
class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin.String?, phones: Array <PhoneNumber> ) {
class Person private constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array <PhoneNumber> = arrayOf()) {
var name : kotlin.String?
private set
@@ -34,7 +34,7 @@ class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin.
}
}
}
class PhoneNumber private constructor (number: kotlin.String?, type: PhoneType?) {
class PhoneNumber private constructor (number: kotlin.String? = null, type: PhoneType? = null) {
var number : kotlin.String?
private set
@@ -56,7 +56,7 @@ class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin.
output.writeEnum (2, type?.ord)
}
class BuilderPhoneNumber constructor (number: kotlin.String?, type: PhoneType?) {
class BuilderPhoneNumber constructor (number: kotlin.String? = null, type: PhoneType? = null) {
var number : kotlin.String?
var type : PhoneType?
@@ -79,6 +79,17 @@ class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin.
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) {
@@ -104,7 +115,7 @@ class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin.
}
}
class BuilderPerson constructor (name: kotlin.String?, id: Int?, email: kotlin.String?, phones: Array <PhoneNumber> ) {
class BuilderPerson constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array <PhoneNumber> = arrayOf()) {
var name : kotlin.String?
var id : Int?
@@ -141,6 +152,27 @@ class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin.
fun build(): Person {
return Person(name, id, email, phones)
}
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) {
@@ -158,7 +190,7 @@ class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin.
}
class AddressBook private constructor (people: Array <Person> ) {
class AddressBook private constructor (people: Array <Person> = arrayOf()) {
var people : Array <Person>
private set
@@ -180,7 +212,7 @@ class AddressBook private constructor (people: Array <Person> ) {
}
}
class BuilderAddressBook constructor (people: Array <Person> ) {
class BuilderAddressBook constructor (people: Array <Person> = arrayOf()) {
var people : Array <Person>
@@ -205,6 +237,12 @@ class AddressBook private constructor (people: Array <Person> ) {
fun build(): AddressBook {
return AddressBook(people)
}
fun setPeople(value: Array <Person> ): BuilderAddressBook {
people = value
return this
}
}
fun mergeFrom (input: CodedInputStream) {
-62
View File
@@ -1,62 +0,0 @@
/**
* Created by user on 7/13/16.
*/
class AddressBook private constructor (people: Array <Person> ) {
var people : Array <Person>
private set
init {
this.people = people
}
fun writeTo (output: CodedOutputStream) {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream) {
if (people.size > 0) {
output.writeInt32NoTag(people.size)
for (item in people) {
item.writeToNoTag(output)
}
}
}
class BuilderAddressBook constructor (people: Array <Person> ) {
var people : Array <Person>
init {
this.people = people
}
fun readFrom (input: CodedInputStream) {
readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream) {
if (people.size > 0) {
val tag = input.readTag()
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
people[i - 1].mergeFrom(input)
}
}
}
fun build(): AddressBook {
return AddressBook(people)
}
}
fun mergeFrom (input: CodedInputStream) {
if (people.size > 0) {
val tag = input.readTag()
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
people[i - 1].mergeFrom(input)
}
}
}
}
-158
View File
@@ -1,158 +0,0 @@
class Person constructor (name: kotlin.String?, id: Int?, email: kotlin.String?, phones: Array <PhoneNumber> ) {
var name : kotlin.String?
private set
var id : Int?
private set
var email : kotlin.String?
private set
var phones : Array <PhoneNumber>
private set
init {
this.name = name
this.id = id
this.email = email
this.phones = phones
}
enum class PhoneType(val ord: Int) {
MOBILE (0),
HOME (1),
WORK (2);
companion object {
fun fromIntToPhoneType (ord: Int): PhoneType {
return when (ord) {
0 -> PhoneType.MOBILE
1 -> PhoneType.HOME
2 -> PhoneType.WORK
else -> throw InvalidProtocolBufferException("Error: got unexpected int ${ord} while parsing PhoneType ");
}
}
}
}
class PhoneNumber constructor (number: kotlin.String?, type: PhoneType?) {
var number : kotlin.String?
private set
var type : PhoneType?
private set
init {
this.number = number
this.type = type
}
fun writeTo (output: CodedOutputStream) {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream) {
output.writeString (1, number)
output.writeEnum (2, type?.ord)
}
class BuilderPhoneNumber constructor (number: kotlin.String?, type: PhoneType?) {
var number : kotlin.String?
var type : PhoneType?
init {
this.number = number
this.type = type
}
fun readFrom (input: CodedInputStream) {
readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream) {
number = input.readString(1)
type = PhoneType.fromIntToPhoneType(input.readEnum(2))
}
fun build(): PhoneNumber {
return PhoneNumber(number, type)
}
}
fun mergeFrom (input: CodedInputStream) {
number = input.readString(1)
type = PhoneType.fromIntToPhoneType(input.readEnum(2))
}
}
fun writeTo (output: CodedOutputStream) {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream) {
output.writeString (1, name)
output.writeInt32 (2, id)
output.writeString (3, email)
if (phones.size > 0) {
output.writeInt32NoTag(phones.size)
for (item in phones) {
item.writeToNoTag(output)
}
}
}
class BuilderPerson constructor (name: kotlin.String?, id: Int?, email: kotlin.String?, phones: Array <PhoneNumber> ) {
var name : kotlin.String?
var id : Int?
var email : kotlin.String?
var phones : Array <PhoneNumber>
init {
this.name = name
this.id = id
this.email = email
this.phones = phones
}
fun readFrom (input: CodedInputStream) {
readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream) {
name = input.readString(1)
id = input.readInt32(2)
email = input.readString(3)
if (phones.size > 0) {
val tag = input.readTag()
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
phones[i - 1].mergeFrom(input)
}
}
}
fun build(): Person {
return Person(name, id, email, phones)
}
}
fun mergeFrom (input: CodedInputStream) {
name = input.readString(1)
id = input.readInt32(2)
email = input.readString(3)
if (phones.size > 0) {
val tag = input.readTag()
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
phones[i - 1].mergeFrom(input)
}
}
}
}
+218 -42
View File
@@ -1,48 +1,224 @@
/**
* Created by user on 7/8/16.
*/
package Addressbook;
// We don't use Message interface for a moment because of LLVM-translator restrictions
class PersonMessage(val name: String, val id: Long, val hasCat: Boolean) // : Message
{
fun writeTo(output: CodedOutputStream) {
output.writeString(1, name)
output.writeInt64(2, id)
output.writeBool(3, hasCat)
class Person private constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array <PhoneNumber> = arrayOf()) {
var name : kotlin.String?
private set
var id : Int?
private set
var email : kotlin.String?
private set
var phones : Array <PhoneNumber>
private set
init {
this.name = name
this.id = id
this.email = email
this.phones = phones
}
fun readFrom(input: CodedInputStream) : PersonMessage {
val newName = input.readString(1)
val newId = input.readInt64(2)
val newHasCatFlag = input.readBool(3)
return PersonMessage(newName, newId, newHasCatFlag)
}
enum class PhoneType(val ord: Int) {
MOBILE (0),
HOME (1),
WORK (2);
fun getBuilder(): PersonBuilder {
return PersonBuilder()
}
// No interface, see above
class PersonBuilder { // : Message.Builder
// TODO: think how can we
var name_: String = ""
var id_: Long = 0
var hasCat_: Boolean = false
fun setName(name: String) {
name_ = name
}
fun setId(id: Long) {
id_ = id
}
fun setHasCat(hasCat: Boolean) {
hasCat_ = hasCat
}
fun build(): PersonMessage {
return PersonMessage(name_, id_, hasCat_) // Java implementation caches such instances
companion object {
fun fromIntToPhoneType (ord: Int): PhoneType {
return when (ord) {
0 -> PhoneType.MOBILE
1 -> PhoneType.HOME
2 -> PhoneType.WORK
else -> throw InvalidProtocolBufferException("Error: got unexpected int ${ord} while parsing PhoneType ");
}
}
}
}
}
class PhoneNumber private constructor (number: kotlin.String? = null, type: PhoneType? = null) {
var number : kotlin.String?
private set
var type : PhoneType?
private set
init {
this.number = number
this.type = type
}
fun writeTo (output: CodedOutputStream) {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream) {
output.writeString (1, number)
output.writeEnum (2, type?.ord)
}
class BuilderPhoneNumber constructor (number: kotlin.String? = null, type: PhoneType? = null) {
var number : kotlin.String?
var type : PhoneType?
init {
this.number = number
this.type = type
}
fun readFrom (input: CodedInputStream) {
readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream) {
number = input.readString(1)
type = PhoneType.fromIntToPhoneType(input.readEnum(2))
}
fun build(): PhoneNumber {
return PhoneNumber(number, type)
}
}
fun mergeFrom (input: CodedInputStream) {
number = input.readString(1)
type = PhoneType.fromIntToPhoneType(input.readEnum(2))
}
}
fun writeTo (output: CodedOutputStream) {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream) {
output.writeString (1, name)
output.writeInt32 (2, id)
output.writeString (3, email)
if (phones.size > 0) {
output.writeInt32NoTag(phones.size)
for (item in phones) {
item.writeToNoTag(output)
}
}
}
class BuilderPerson constructor (name: kotlin.String? = null, id: Int? = null, email: kotlin.String? = null, phones: Array <PhoneNumber> = arrayOf()) {
var name : kotlin.String?
var id : Int?
var email : kotlin.String?
var phones : Array <PhoneNumber>
init {
this.name = name
this.id = id
this.email = email
this.phones = phones
}
fun readFrom (input: CodedInputStream) {
readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream) {
name = input.readString(1)
id = input.readInt32(2)
email = input.readString(3)
if (phones.size > 0) {
val tag = input.readTag()
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
phones[i - 1].mergeFrom(input)
}
}
}
fun build(): Person {
return Person(name, id, email, phones)
}
}
fun mergeFrom (input: CodedInputStream) {
name = input.readString(1)
id = input.readInt32(2)
email = input.readString(3)
if (phones.size > 0) {
val tag = input.readTag()
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
phones[i - 1].mergeFrom(input)
}
}
}
}
class AddressBook private constructor (people: Array <Person> = arrayOf()) {
var people : Array <Person>
private set
init {
this.people = people
}
fun writeTo (output: CodedOutputStream) {
writeToNoTag(output)
}
fun writeToNoTag (output: CodedOutputStream) {
if (people.size > 0) {
output.writeInt32NoTag(people.size)
for (item in people) {
item.writeToNoTag(output)
}
}
}
class BuilderAddressBook constructor (people: Array <Person> = arrayOf()) {
var people : Array <Person>
init {
this.people = people
}
fun readFrom (input: CodedInputStream) {
readFromNoTag(input)
}
fun readFromNoTag (input: CodedInputStream) {
if (people.size > 0) {
val tag = input.readTag()
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
people[i - 1].mergeFrom(input)
}
}
}
fun build(): AddressBook {
return AddressBook(people)
}
}
fun mergeFrom (input: CodedInputStream) {
if (people.size > 0) {
val tag = input.readTag()
val listSize = input.readInt32NoTag()
for (i in 1..listSize) {
people[i - 1].mergeFrom(input)
}
}
}
}
+3 -9
View File
@@ -5,18 +5,12 @@ import java.io.ByteArrayOutputStream
* Created by user on 7/8/16.
*/
import PersonMessage
fun testMessageSerialization() {
val s = ByteArrayOutputStream()
val outs = CodedOutputStream(s)
val msg = Person(
name = "John Doe",
id = 42,
email = "wtf@dsada.com",
phones = arrayOf (
Person.PhoneNumber("8-800-555-35-35", Person.PhoneType.WORK),
Person.PhoneNumber("228-322", Person.PhoneType.HOME)
)
)
val msg =
msg.writeTo(outs)
val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray()))