Supported nested messages, repeated fields in Proto compiler. Fixed Kotin runtime appropriately.
This commit is contained in:
@@ -13,53 +13,62 @@ namespace compiler {
|
|||||||
namespace kotlin {
|
namespace kotlin {
|
||||||
|
|
||||||
void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
|
void ClassGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
|
||||||
// print class header
|
generateHeader(printer, isBuilder);
|
||||||
map<string, string> vars;
|
|
||||||
vars["modifier"] = modifier.getName();
|
|
||||||
vars["name"] = (isBuilder? "Builder" : "") + simpleName;
|
|
||||||
printer->Print(vars,
|
|
||||||
"$modifier$ $name$ private constructor () {"
|
|
||||||
"\n"
|
|
||||||
);
|
|
||||||
printer->Indent();
|
printer->Indent();
|
||||||
|
|
||||||
// generate code for nested classes declarations
|
/**
|
||||||
for (ClassGenerator *gen: classesDeclarations) {
|
* Field generator should know if it is generating code for builder.
|
||||||
gen->generateCode(printer, isBuilder);
|
* or for fair class to choose between 'val' and 'var'.
|
||||||
printer->Print("\n\n"); // separate each definition from next code block with empty line
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
// generate code for nested enums declarations
|
|
||||||
for (EnumGenerator *gen: enumsDeclaraions) {
|
|
||||||
gen->generateCode(printer);
|
|
||||||
printer->Print("\n\n"); // separate each definitions from next code block with empty line
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate code for fields
|
|
||||||
for (FieldGenerator *gen: properties) {
|
for (FieldGenerator *gen: properties) {
|
||||||
gen->generateCode(printer);
|
gen->generateCode(printer, isBuilder);
|
||||||
printer->Print("\n");
|
printer->Print("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate constructor for builders
|
printer->Print("\n");
|
||||||
if (isBuilder) {
|
generateInitSection(printer);
|
||||||
printer->Print("\n");
|
|
||||||
generateConstructor(printer);
|
// enum declarations and nested classes declarations only for fair classes
|
||||||
|
if (!isBuilder) {
|
||||||
|
for (EnumGenerator *gen: enumsDeclaraions) {
|
||||||
|
gen->generateCode(printer);
|
||||||
|
printer->Print("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ClassGenerator *gen: classesDeclarations) {
|
||||||
|
gen->generateCode(printer);
|
||||||
|
printer->Print("\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate builder for fair classes
|
// 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 and mergeFrom only for fair classes
|
||||||
if (!isBuilder) {
|
if (!isBuilder) {
|
||||||
printer->Print("\n");
|
printer->Print("\n");
|
||||||
generateBuilder(printer);
|
generateBuilder(printer);
|
||||||
|
|
||||||
|
printer->Print("\n");
|
||||||
|
generateMergeFrom(printer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// build() is only for builders
|
||||||
|
if (isBuilder) {
|
||||||
|
printer->Print("\n");
|
||||||
|
generateBuildMethod(printer);
|
||||||
}
|
}
|
||||||
|
|
||||||
printer->Outdent();
|
printer->Outdent();
|
||||||
printer->Print("}");
|
printer->Print("}\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassGenerator::ClassGenerator(Descriptor const *descriptor) {
|
ClassGenerator::ClassGenerator(Descriptor const *descriptor)
|
||||||
|
: descriptor(descriptor) {
|
||||||
simpleName = descriptor->name(); // TODO: think about more careful class naming
|
simpleName = descriptor->name(); // TODO: think about more careful class naming
|
||||||
modifier = ClassModifier(ClassModifier::CLASS);
|
|
||||||
|
|
||||||
int field_count = descriptor->field_count();
|
int field_count = descriptor->field_count();
|
||||||
for (int i = 0; i < field_count; ++i) {
|
for (int i = 0; i < field_count; ++i) {
|
||||||
@@ -96,52 +105,124 @@ ClassGenerator::~ClassGenerator() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClassGenerator::generateBuilder(io::Printer *) const {
|
void ClassGenerator::generateBuilder(io::Printer * printer) const {
|
||||||
|
//XXX: just reuse generateCode with flag isBuilder set
|
||||||
|
generateCode(printer, /* isBuilder = */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClassGenerator::generateConstructor(io::Printer *printer, bool isBuilder) const {
|
void ClassGenerator::generateMergeFrom(io::Printer * printer) const {
|
||||||
// generate header
|
//TODO: Looks pretty dirty. Should reconsider process of generating readFrom, mergeFrom and writeTo.
|
||||||
printer->Print("private constructor(\n");
|
map <string, string> vars;
|
||||||
|
printer->Print(vars, "fun mergeFrom (input: CodedInputStream) {\n");
|
||||||
|
printer->Indent();
|
||||||
|
|
||||||
// place each argument of constructor in separate line for a prettier code
|
|
||||||
// we indent twice to make arguments indentation larger than indentation of inner block
|
|
||||||
printer->Indent();
|
|
||||||
printer->Indent();
|
|
||||||
for (int i = 0; i < properties.size(); ++i) {
|
for (int i = 0; i < properties.size(); ++i) {
|
||||||
// generate argument definition
|
properties[i]->generateSerializationCode(printer, /* isRead = */ true);
|
||||||
map<string, string> vars;
|
|
||||||
vars["name"] = properties[i] ->simpleName;
|
|
||||||
vars["field"] = properties[i]->fieldName;
|
|
||||||
printer->Print(vars,
|
|
||||||
"$name$: $field$");
|
|
||||||
|
|
||||||
// if it's last property, then print closing bracket for argument list, otherwise put comma
|
|
||||||
if (i + 1 == properties.size()) {
|
|
||||||
printer->Print(") : this()\n");
|
|
||||||
printer->Outdent();
|
|
||||||
printer->Outdent();
|
|
||||||
printer->Print("{");
|
|
||||||
printer->Indent();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
printer->Print(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
printer->Print("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// print body of constructor - just assign arguments to corresponding fields
|
printer->Outdent();
|
||||||
|
printer->Print("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClassGenerator::generateSerializers(io::Printer * printer, bool isRead) const {
|
||||||
|
map <string, string> vars;
|
||||||
|
vars["funName"]= isRead ? "readFrom" : "writeTo";
|
||||||
|
vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream";
|
||||||
|
vars["arg"] = isRead ? "input" : "output";
|
||||||
|
vars["maybeSeparator"] = isRead ? "" : ", ";
|
||||||
|
|
||||||
|
// generate function header
|
||||||
|
printer->Print(vars,
|
||||||
|
"fun $funName$ ($arg$: $stream$) {"
|
||||||
|
"\n");
|
||||||
|
printer->Indent();
|
||||||
|
|
||||||
|
//TODO: write message tag and size
|
||||||
|
printer->Print(vars, "$funName$NoTag($arg$)\n");
|
||||||
|
|
||||||
|
printer->Outdent();
|
||||||
|
printer->Print("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClassGenerator::generateSerializersNoTag(io::Printer *printer, bool isRead) const {
|
||||||
|
map <string, string> vars;
|
||||||
|
vars["funName"]= isRead ? "readFromNoTag" : "writeToNoTag";
|
||||||
|
vars["stream"] = isRead ? "CodedInputStream" : "CodedOutputStream";
|
||||||
|
vars["arg"] = isRead ? "input" : "output";
|
||||||
|
|
||||||
|
// generate function header
|
||||||
|
printer->Print(vars,
|
||||||
|
"fun $funName$ ($arg$: $stream$) {"
|
||||||
|
"\n");
|
||||||
|
printer->Indent();
|
||||||
|
|
||||||
|
// generate code for serialization/deserialization of fields
|
||||||
|
for (int i = 0; i < properties.size(); ++i) {
|
||||||
|
properties[i]->generateSerializationCode(printer, isRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
printer->Outdent();
|
||||||
|
printer->Print("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
if (i + 1 != properties.size()) {
|
||||||
|
argumentList += ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
map<string, string> vars;
|
||||||
|
vars["name"] = (isBuilder? "Builder" : "") + simpleName;
|
||||||
|
vars["argumentList"] = argumentList;
|
||||||
|
vars["maybePrivate"] = isBuilder? "" : " private";
|
||||||
|
printer->Print(vars,
|
||||||
|
"class $name$$maybePrivate$ constructor ($argumentList$) {"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClassGenerator::generateBuildMethod(io::Printer * printer) const {
|
||||||
|
map <string, string> vars;
|
||||||
|
vars["returnType"] = simpleName;
|
||||||
|
printer->Print(vars,
|
||||||
|
"fun build(): $returnType$ {\n");
|
||||||
|
printer->Indent();
|
||||||
|
|
||||||
|
// pass all fields to constructor of enclosing class
|
||||||
|
printer->Print(vars,
|
||||||
|
"return $returnType$(");
|
||||||
|
for (int i = 0; i < properties.size(); ++i) {
|
||||||
|
printer->Print(properties[i]->simpleName.c_str());
|
||||||
|
if (i + 1 != properties.size()) {
|
||||||
|
printer->Print(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printer->Print(")\n");
|
||||||
|
printer->Outdent();
|
||||||
|
printer->Print("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClassGenerator::generateInitSection(io::Printer * printer) const {
|
||||||
|
printer->Print("init {\n");
|
||||||
|
printer->Indent();
|
||||||
|
|
||||||
for (int i = 0; i < properties.size(); ++i) {
|
for (int i = 0; i < properties.size(); ++i) {
|
||||||
map <string, string> vars;
|
map <string, string> vars;
|
||||||
vars["name"] = properties[i]->simpleName;
|
vars["name"] = properties[i]->simpleName;
|
||||||
printer->Print(vars,
|
printer->Print(vars,
|
||||||
"this.$name$ = $name$"
|
"this.$name$ = $name$"
|
||||||
"\n"
|
"\n"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
printer->Outdent();
|
printer->Outdent();
|
||||||
printer->Print("}");
|
printer->Print("}\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ public:
|
|||||||
|
|
||||||
class ClassGenerator {
|
class ClassGenerator {
|
||||||
public:
|
public:
|
||||||
ClassModifier modifier;
|
|
||||||
string simpleName;
|
string simpleName;
|
||||||
vector <FieldGenerator *> properties;
|
vector <FieldGenerator *> properties;
|
||||||
vector <ClassGenerator *> classesDeclarations;
|
vector <ClassGenerator *> classesDeclarations;
|
||||||
@@ -41,15 +40,30 @@ public:
|
|||||||
ClassGenerator (Descriptor const * descriptor);
|
ClassGenerator (Descriptor const * descriptor);
|
||||||
~ClassGenerator ();
|
~ClassGenerator ();
|
||||||
|
|
||||||
|
|
||||||
|
void generateCode (io::Printer * printer, bool isBuilder = false) const;
|
||||||
|
private:
|
||||||
|
Descriptor const * descriptor;
|
||||||
|
void generateBuilder (io::Printer * printer) const;
|
||||||
|
void generateBuildMethod (io::Printer * printer) const;
|
||||||
|
void generateInitSection (io::Printer * printer) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag isBuilder used for reducing code repeating, as code for class itself
|
* 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
|
* and for its inner builder are structurally very alike and can be generated
|
||||||
* with very little differences (like changing 'val's to 'var's and etc.)
|
* with very little differences (like changing 'val's to 'var's and etc.)
|
||||||
*/
|
*/
|
||||||
void generateCode (io::Printer * printer, bool isBuilder = false) const;
|
void generateHeader(io::Printer * printer, bool isBuilder = false) const;
|
||||||
private:
|
|
||||||
void generateBuilder (io::Printer * printer) const;
|
/**
|
||||||
void generateConstructor(io::Printer * printer, bool isBuilder = false) const;
|
* IsRead flag indicates that readFrom method should be generated, otherwise
|
||||||
|
* writeTo method is generated. Motivation is similar to the isBuilder flag:
|
||||||
|
* 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 generateMergeFrom(io::Printer * printer) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace kotlin
|
} // namespace kotlin
|
||||||
|
|||||||
@@ -47,12 +47,58 @@ void EnumGenerator::generateCode(io::Printer * printer) const {
|
|||||||
if (i + 1 != enumValues.size()) {
|
if (i + 1 != enumValues.size()) {
|
||||||
printer->Print(",");
|
printer->Print(",");
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
printer->Print(";"); // semicolon is necessary as companion object will follow
|
||||||
|
}
|
||||||
printer->Print("\n");
|
printer->Print("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printer->Print("\n");
|
||||||
|
gemerateEnumConverters(printer);
|
||||||
|
|
||||||
printer->Outdent();
|
printer->Outdent();
|
||||||
printer->Print("}");
|
printer->Print("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EnumGenerator::gemerateEnumConverters(io::Printer *printer) const {
|
||||||
|
map <string, string> vars;
|
||||||
|
vars["dollar"] = "$";
|
||||||
|
vars["type"] = simpleName;
|
||||||
|
|
||||||
|
printer->Print("companion object {\n");
|
||||||
|
printer->Indent();
|
||||||
|
|
||||||
|
printer->Print(vars, "fun fromIntTo$type$ (ord: Int): $type$ {\n");
|
||||||
|
printer->Indent();
|
||||||
|
|
||||||
|
printer->Print("return when (ord) {\n");
|
||||||
|
printer->Indent();
|
||||||
|
|
||||||
|
// map ints to enum values
|
||||||
|
for (int j = 0; j < enumValues.size(); ++j) {
|
||||||
|
vars["ordinal"] = std::to_string(enumValues[j]->ordinal);
|
||||||
|
vars["value"] = enumValues[j]->simpleName;
|
||||||
|
|
||||||
|
printer->Print(vars,
|
||||||
|
"$ordinal$ -> $type$.$value$\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// catch cast errors in else-clause
|
||||||
|
printer->Print(vars,
|
||||||
|
"else -> throw InvalidProtocolBufferException("
|
||||||
|
"\"Error: got unexpected int $dollar${ord} while parsing $type$ \""
|
||||||
|
");\n");
|
||||||
|
|
||||||
|
printer->Outdent(); // when-clause
|
||||||
|
printer->Print("}\n");
|
||||||
|
|
||||||
|
printer->Outdent(); // function body
|
||||||
|
printer->Print("}\n");
|
||||||
|
|
||||||
|
printer->Outdent(); // companion object body
|
||||||
|
printer->Print("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
EnumGenerator::~EnumGenerator() {
|
EnumGenerator::~EnumGenerator() {
|
||||||
for (int i = 0; i < enumValues.size(); ++i) {
|
for (int i = 0; i < enumValues.size(); ++i) {
|
||||||
delete enumValues[i];
|
delete enumValues[i];
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ public:
|
|||||||
vector <EnumValueGenerator *> enumValues;
|
vector <EnumValueGenerator *> enumValues;
|
||||||
|
|
||||||
void generateCode(io::Printer *) const;
|
void generateCode(io::Printer *) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void gemerateEnumConverters(io::Printer *printer) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace kotlin
|
} // namespace kotlin
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ string FieldGenerator::protobufToKotlinField() const {
|
|||||||
postamble = "?";
|
postamble = "?";
|
||||||
break;
|
break;
|
||||||
case FieldDescriptor::LABEL_REPEATED:
|
case FieldDescriptor::LABEL_REPEATED:
|
||||||
preamble = "List <";
|
preamble = "Array <";
|
||||||
postamble = "> ";
|
postamble = "> ";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -90,16 +90,18 @@ string FieldGenerator::getInitValue() const {
|
|||||||
return fieldName + "()";
|
return fieldName + "()";
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldGenerator::generateCode(io::Printer *printer) const {
|
void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
|
||||||
/** Generate Kotlin-code for field.
|
|
||||||
* Note that we use 'val' everywhere, as we want messages to be immutable.
|
|
||||||
* For constructing Messages corresponding Builders should be used.
|
|
||||||
*/
|
|
||||||
map<string, string> vars;
|
map<string, string> vars;
|
||||||
vars["name"] = simpleName;
|
vars["name"] = simpleName;
|
||||||
vars["field"] = protobufToKotlinField();
|
vars["field"] = protobufToKotlinField();
|
||||||
vars["initValue"] = initValue;
|
printer->Print(vars, "var $name$ : $field$\n");
|
||||||
printer->Print(vars, "val $name$ : $field$ = $initValue$");
|
|
||||||
|
// make setter private for fair classes
|
||||||
|
if (!isBuilder) {
|
||||||
|
printer->Indent();
|
||||||
|
printer->Print("private set\n");
|
||||||
|
printer->Outdent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldGenerator::FieldGenerator(FieldDescriptor const * descriptor)
|
FieldGenerator::FieldGenerator(FieldDescriptor const * descriptor)
|
||||||
@@ -107,9 +109,159 @@ FieldGenerator::FieldGenerator(FieldDescriptor const * descriptor)
|
|||||||
, modifier(descriptor->label())
|
, modifier(descriptor->label())
|
||||||
, simpleName(descriptor->name())
|
, simpleName(descriptor->name())
|
||||||
, fieldName(protobufToKotlinField())
|
, fieldName(protobufToKotlinField())
|
||||||
|
, fieldType(protobufToKotlinType())
|
||||||
, initValue(getInitValue())
|
, initValue(getInitValue())
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
void FieldGenerator::generateSerializationCode(io::Printer *printer, bool isRead, bool noTag) const {
|
||||||
|
map <string, string> vars;
|
||||||
|
vars["type"] = protobufTypeToKotlinFunctionSuffix(descriptor->type());
|
||||||
|
vars["fieldNumber"] = std::to_string(descriptor->number());
|
||||||
|
vars["fieldName"] = simpleName;
|
||||||
|
vars["arg"] = isRead ? "input" : "output";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First of all, try to generate syntax for repeated fields because it's separate case.
|
||||||
|
* Do this according to protobuf format:
|
||||||
|
* - Check if size of array is > 0, because empty repeated fields shouldn't appear in message
|
||||||
|
* - Write tag explicitly
|
||||||
|
* - Write length as int32 (note that tag shouldn't be added)
|
||||||
|
* - Write all repeated elements via recursive call
|
||||||
|
*/
|
||||||
|
if (modifier == FieldDescriptor::LABEL_REPEATED) {
|
||||||
|
printer->Print(vars, "if ($fieldName$.size > 0) {\n");
|
||||||
|
printer->Indent();
|
||||||
|
|
||||||
|
// tag
|
||||||
|
if (isRead) {
|
||||||
|
//TODO: dirty stub here! Normally, reading from input should be delegated to Parsers, with proper error handling and etc.
|
||||||
|
//Currently tag is ignored, and fields order is critical for serialization/deserialization. Therefore,
|
||||||
|
//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");
|
||||||
|
printer->Indent();
|
||||||
|
printer->Print(vars, "$fieldName$[i - 1].mergeFrom(input)\n");
|
||||||
|
printer->Outdent();
|
||||||
|
printer->Print("}\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// length
|
||||||
|
printer->Print(vars, "$arg$.writeInt32NoTag($fieldName$.size)\n");
|
||||||
|
|
||||||
|
// all elements
|
||||||
|
printer->Print(vars, "for (item in $fieldName$) {\n");
|
||||||
|
printer->Indent();
|
||||||
|
|
||||||
|
/* hack: copy current FieldGenerator and change label to OPTIONAL. This will allow
|
||||||
|
to re-use this function for generating serialization code for elements of array.
|
||||||
|
More importantly, this will care about nested types too.
|
||||||
|
However, this hack isn't necessary and could be safely removed as soon as target
|
||||||
|
code will support inheritance and interfaces
|
||||||
|
(then writing CodedOutputStream.writeMessage will be possible).
|
||||||
|
*/
|
||||||
|
FieldGenerator singleFieldGen = FieldGenerator(descriptor);
|
||||||
|
singleFieldGen.modifier = FieldDescriptor::LABEL_OPTIONAL;
|
||||||
|
singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ true);
|
||||||
|
|
||||||
|
printer->Outdent(); // for-loop
|
||||||
|
printer->Print("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printer->Outdent(); // if-clause
|
||||||
|
printer->Print("}\n");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Then check for conversions 'int -> enum-value' and \enum-value -> int' if current
|
||||||
|
field is enum.
|
||||||
|
This is necessary, because CodedStream stores enums as Ints in wire, delegating
|
||||||
|
responsibility for casting those Ints to enum values and vice versa to the caller.
|
||||||
|
Example: enumField = fromIntToMyEnumName(input.readEnum(42))
|
||||||
|
Example: output.writeEnum(42, enumField.ord)
|
||||||
|
*/
|
||||||
|
if (descriptor->type() == FieldDescriptor::TYPE_ENUM) {
|
||||||
|
vars["converter"] = fieldType + ".fromIntTo" + fieldType;
|
||||||
|
if (isRead) {
|
||||||
|
printer->Print(vars, "$fieldName$ = $converter$(input.read$type$($fieldNumber$))\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printer->Print(vars, "output.write$type$ ($fieldNumber$, $fieldName$?.ord)\n");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Then check for nested messages. Then we re-use writeTo method, that should be defined in
|
||||||
|
that message
|
||||||
|
*/
|
||||||
|
if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) {
|
||||||
|
vars["fieldName"] = noTag ? "item" : simpleName;
|
||||||
|
vars["maybeNoTag"] = noTag ? "NoTag" : "";
|
||||||
|
if (isRead) {
|
||||||
|
printer->Print(vars, "$fieldName$.readFrom$maybeNoTag$(input)\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printer->Print(vars, "$fieldName$.writeTo$maybeNoTag$(output)\n");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finally, serialize trivial cases */
|
||||||
|
if (isRead) {
|
||||||
|
printer->Print(vars, "$fieldName$ = input.read$type$($fieldNumber$)\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printer->Print(vars, "output.write$type$ ($fieldNumber$, $fieldName$)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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:
|
||||||
|
return "Double";
|
||||||
|
case FieldDescriptor::TYPE_FLOAT:
|
||||||
|
return "Float";
|
||||||
|
case FieldDescriptor::TYPE_INT64:
|
||||||
|
return "Int64";
|
||||||
|
case FieldDescriptor::TYPE_UINT64:
|
||||||
|
return "UInt64";
|
||||||
|
case FieldDescriptor::TYPE_INT32:
|
||||||
|
return "Int32";
|
||||||
|
case FieldDescriptor::TYPE_FIXED64:
|
||||||
|
return "Fixed64";
|
||||||
|
case FieldDescriptor::TYPE_FIXED32:
|
||||||
|
return "Fixed32";
|
||||||
|
case FieldDescriptor::TYPE_BOOL:
|
||||||
|
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
|
||||||
|
case FieldDescriptor::TYPE_BYTES:
|
||||||
|
return ""; // TODO: support bytes
|
||||||
|
case FieldDescriptor::TYPE_UINT32:
|
||||||
|
return "UInt32";
|
||||||
|
case FieldDescriptor::TYPE_ENUM:
|
||||||
|
return "Enum";
|
||||||
|
case FieldDescriptor::TYPE_SFIXED32:
|
||||||
|
return "SFixed32";
|
||||||
|
case FieldDescriptor::TYPE_SFIXED64:
|
||||||
|
return "SFixed64";
|
||||||
|
case FieldDescriptor::TYPE_SINT32:
|
||||||
|
return "SInt32";
|
||||||
|
case FieldDescriptor::TYPE_SINT64:
|
||||||
|
return "SInt64";
|
||||||
|
}
|
||||||
|
}
|
||||||
} // namespace kotlin
|
} // namespace kotlin
|
||||||
} // namspace compiler
|
} // namspace compiler
|
||||||
} // namespace protobuf
|
} // namespace protobuf
|
||||||
|
|||||||
@@ -21,13 +21,24 @@ private:
|
|||||||
string protobufToKotlinType () const;
|
string protobufToKotlinType () const;
|
||||||
string getInitValue() const;
|
string getInitValue() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts one of protobuf wire types to corresponding Kotlin type with proper
|
||||||
|
* naming, so it could be used as suffix after read/write, resulting in function
|
||||||
|
* in CodedInputStream/CodedOutputStream.
|
||||||
|
* Example: protobufToKotlinFunctionSuffix(TYPE_SFIXED32) returns "SFixed32", and
|
||||||
|
* in Kotlin runtime exists method
|
||||||
|
* CodedInputStream.readSFixed32(fieldNumber: Int)
|
||||||
|
*/
|
||||||
|
string protobufTypeToKotlinFunctionSuffix(FieldDescriptor::Type type) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FieldDescriptor::Label modifier;
|
FieldDescriptor::Label modifier;
|
||||||
string simpleName;
|
string simpleName;
|
||||||
string fieldName;
|
string fieldName;
|
||||||
|
string fieldType;
|
||||||
string initValue;
|
string initValue;
|
||||||
|
void generateCode(io::Printer * printer, bool isBuilder = false) const;
|
||||||
void generateCode(io::Printer *) const;
|
void generateSerializationCode(io::Printer * printer, bool isRead = false, bool noTag = false) const;
|
||||||
FieldGenerator(FieldDescriptor const * descriptor);
|
FieldGenerator(FieldDescriptor const * descriptor);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,25 +1,221 @@
|
|||||||
class Person private constructor () {
|
class Person private constructor (name: kotlin.String?, id: Int?, email: kotlin.String?, phones: Array <PhoneNumber> ) {
|
||||||
class PhoneNumber private constructor () {
|
var name : kotlin.String?
|
||||||
val number : kotlin.String? = null
|
private set
|
||||||
val type : PhoneType? = null
|
|
||||||
|
|
||||||
|
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) {
|
enum class PhoneType(val ord: Int) {
|
||||||
MOBILE (0),
|
MOBILE (0),
|
||||||
HOME (1),
|
HOME (1),
|
||||||
WORK (2)
|
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 private 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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val name : kotlin.String? = null
|
|
||||||
val id : Int? = null
|
|
||||||
val email : kotlin.String? = null
|
|
||||||
val phones : List <PhoneNumber> = listOf()
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddressBook private constructor () {
|
|
||||||
val people : List <Person> = listOf()
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2546
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
|||||||
|
class Person {
|
||||||
|
class PhoneNumber {
|
||||||
|
val number : kotlin.String?
|
||||||
|
val type : PhoneType?
|
||||||
|
}
|
||||||
|
enum class PhoneType(val ord: Int) {
|
||||||
|
MOBILE (0),
|
||||||
|
HOME (1),
|
||||||
|
WORK (2)
|
||||||
|
}
|
||||||
|
val name : kotlin.String?
|
||||||
|
val id : Int?
|
||||||
|
val email : kotlin.String?
|
||||||
|
val phones : List <PhoneNumber>
|
||||||
|
}
|
||||||
|
class AddressBook {
|
||||||
|
val people : List <Person>
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -24,14 +24,22 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
||||||
val actualWireType = WireFormat.getTagWireType(tag)
|
val actualWireType = WireFormat.getTagWireType(tag)
|
||||||
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType)
|
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType)
|
||||||
return readRawVarint32()
|
return readInt32NoTag()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that unsigned integer types are stored as their signed counterparts with top bit
|
// Note that unsigned integer types are stored as their signed counterparts with top bit
|
||||||
// simply stored in the sign bit - similar to Java's protobuf implementation. Hence, all
|
// 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
|
// methods reading unsigned ints simply redirect call to corresponding signed-reading method
|
||||||
fun readUInt32(expectedFieldNumber: Int): Int {
|
fun readUInt32(expectedFieldNumber: Int): Int {
|
||||||
return readInt32(expectedFieldNumber)
|
val tag = readTag()
|
||||||
|
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
||||||
|
val actualWireType = WireFormat.getTagWireType(tag)
|
||||||
|
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType)
|
||||||
|
return readUInt32NoTag()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readUInt32NoTag(): Int {
|
||||||
|
return readInt32NoTag()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readInt64(expectedFieldNumber: Int): Long {
|
fun readInt64(expectedFieldNumber: Int): Long {
|
||||||
@@ -39,12 +47,20 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
||||||
val actualWireType = WireFormat.getTagWireType(tag)
|
val actualWireType = WireFormat.getTagWireType(tag)
|
||||||
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, actualWireType, WireType.VARINT)
|
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, actualWireType, WireType.VARINT)
|
||||||
return readRawVarint64()
|
return readInt64NoTag()
|
||||||
}
|
}
|
||||||
|
|
||||||
// See note on unsigned integers implementations above
|
// See note on unsigned integers implementations above
|
||||||
fun readUInt64(expectedFieldNumber: Int): Long {
|
fun readUInt64(expectedFieldNumber: Int): Long {
|
||||||
return readUInt64(expectedFieldNumber)
|
val tag = readTag()
|
||||||
|
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
||||||
|
val actualWireType = WireFormat.getTagWireType(tag)
|
||||||
|
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, actualWireType, WireType.VARINT)
|
||||||
|
return readUInt64NoTag()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readUInt64NoTag(): Long {
|
||||||
|
return readInt64NoTag()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readBool(expectedFieldNumber: Int): Boolean {
|
fun readBool(expectedFieldNumber: Int): Boolean {
|
||||||
@@ -52,7 +68,11 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
||||||
val actualWireType = WireFormat.getTagWireType(tag)
|
val actualWireType = WireFormat.getTagWireType(tag)
|
||||||
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, actualWireType, WireType.VARINT)
|
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, actualWireType, WireType.VARINT)
|
||||||
val readValue = readRawVarint32()
|
return readBoolNoTag()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readBoolNoTag(): Boolean {
|
||||||
|
val readValue = readInt32NoTag()
|
||||||
val boolValue = when (readValue) {
|
val boolValue = when (readValue) {
|
||||||
0 -> false
|
0 -> false
|
||||||
1 -> true
|
1 -> true
|
||||||
@@ -63,7 +83,15 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
|
|
||||||
// Reading enums is like reading one int32 number. Caller is responsible for converting this ordinal to enum-object
|
// Reading enums is like reading one int32 number. Caller is responsible for converting this ordinal to enum-object
|
||||||
fun readEnum(expectedFieldNumber: Int): Int {
|
fun readEnum(expectedFieldNumber: Int): Int {
|
||||||
return readInt32(expectedFieldNumber)
|
val tag = readTag()
|
||||||
|
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
||||||
|
val actualWireType = WireFormat.getTagWireType(tag)
|
||||||
|
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType)
|
||||||
|
return readEnumNoTag()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readEnumNoTag(): Int {
|
||||||
|
return readUInt32NoTag()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readSInt32(expectedFieldNumber: Int): Int {
|
fun readSInt32(expectedFieldNumber: Int): Int {
|
||||||
@@ -71,7 +99,11 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
||||||
val actualWireType = WireFormat.getTagWireType(tag)
|
val actualWireType = WireFormat.getTagWireType(tag)
|
||||||
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType)
|
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType)
|
||||||
return readZigZag32()
|
return readSInt32NoTag()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readSInt32NoTag(): Int {
|
||||||
|
return readZigZag32NoTag()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readSInt64(expectedFieldNumber: Int): Long {
|
fun readSInt64(expectedFieldNumber: Int): Long {
|
||||||
@@ -79,7 +111,7 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
||||||
val actualWireType = WireFormat.getTagWireType(tag)
|
val actualWireType = WireFormat.getTagWireType(tag)
|
||||||
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType)
|
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.VARINT, actualWireType)
|
||||||
return readZigZag64()
|
return readZigZag64NoTag()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readFixed32(expectedFieldNumber: Int): Int {
|
fun readFixed32(expectedFieldNumber: Int): Int {
|
||||||
@@ -87,11 +119,23 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
||||||
val actualWireType = WireFormat.getTagWireType(tag)
|
val actualWireType = WireFormat.getTagWireType(tag)
|
||||||
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_32, actualWireType)
|
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_32, actualWireType)
|
||||||
|
return readFixed32NoInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readFixed32NoInt(): Int {
|
||||||
return readLittleEndianInt()
|
return readLittleEndianInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readSFixed32(expectedFieldNumber: Int): Int {
|
fun readSFixed32(expectedFieldNumber: Int): Int {
|
||||||
return readFixed32(expectedFieldNumber)
|
val tag = readTag()
|
||||||
|
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
||||||
|
val actualWireType = WireFormat.getTagWireType(tag)
|
||||||
|
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_32, actualWireType)
|
||||||
|
return readSFixed32NoTag()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readSFixed32NoTag(): Int {
|
||||||
|
return readLittleEndianInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readFixed64(expectedFieldNumber: Int): Long {
|
fun readFixed64(expectedFieldNumber: Int): Long {
|
||||||
@@ -99,11 +143,23 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
||||||
val actualWireType = WireFormat.getTagWireType(tag)
|
val actualWireType = WireFormat.getTagWireType(tag)
|
||||||
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_64, actualWireType)
|
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_64, actualWireType)
|
||||||
|
return readFixed64NoTag()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readFixed64NoTag(): Long {
|
||||||
return readLittleEndianLong()
|
return readLittleEndianLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readSFixed64(expectedFieldNumber: Int): Long {
|
fun readSFixed64(expectedFieldNumber: Int): Long {
|
||||||
return readFixed64(expectedFieldNumber)
|
val tag = readTag()
|
||||||
|
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
||||||
|
val actualWireType = WireFormat.getTagWireType(tag)
|
||||||
|
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_64, actualWireType)
|
||||||
|
return readSFixed64NoTag()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readSFixed64NoTag(): Long {
|
||||||
|
return readLittleEndianLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readDouble(expectedFieldNumber: Int): Double {
|
fun readDouble(expectedFieldNumber: Int): Double {
|
||||||
@@ -111,6 +167,10 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
||||||
val actualWireType = WireFormat.getTagWireType(tag)
|
val actualWireType = WireFormat.getTagWireType(tag)
|
||||||
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_64, actualWireType)
|
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_64, actualWireType)
|
||||||
|
return readDoubleNoTag()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readDoubleNoTag(): Double {
|
||||||
return readLittleEndianDouble()
|
return readLittleEndianDouble()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +179,10 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
||||||
val actualWireType = WireFormat.getTagWireType(tag)
|
val actualWireType = WireFormat.getTagWireType(tag)
|
||||||
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_32, actualWireType)
|
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.FIX_32, actualWireType)
|
||||||
|
return readFloatNoTag()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readFloatNoTag(): Float {
|
||||||
return readLittleEndianFloat()
|
return readLittleEndianFloat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,11 +191,14 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
val actualFieldNumber = WireFormat.getTagFieldNumber(tag)
|
||||||
val actualWireType = WireFormat.getTagWireType(tag)
|
val actualWireType = WireFormat.getTagWireType(tag)
|
||||||
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.LENGTH_DELIMITED, actualWireType)
|
checkFieldCorrectness(expectedFieldNumber, actualFieldNumber, WireType.LENGTH_DELIMITED, actualWireType)
|
||||||
val length = readRawVarint32()
|
return readStringNoTag()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readStringNoTag(): String {
|
||||||
|
val length = readInt32NoTag()
|
||||||
val value = String(readRawBytes(length))
|
val value = String(readRawBytes(length))
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ============ Utility methods ==================
|
/** ============ Utility methods ==================
|
||||||
* They are left non-private for cases when one wants to implement her/his own protocol format.
|
* 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.
|
* Then she/he can re-use low-level methods for operating with raw values, that are not annotated with Protobuf tags.
|
||||||
@@ -191,7 +258,7 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
if (isAtEnd()) {
|
if (isAtEnd()) {
|
||||||
return 0 // we can safely return 0 as sign of end of message, because 0-tags are illegal
|
return 0 // we can safely return 0 as sign of end of message, because 0-tags are illegal
|
||||||
}
|
}
|
||||||
val tag = readRawVarint32()
|
val tag = readInt32NoTag()
|
||||||
if (tag == 0) { // if we somehow had read 0-tag, then message is corrupted
|
if (tag == 0) { // if we somehow had read 0-tag, then message is corrupted
|
||||||
throw InvalidProtocolBufferException("Invalid tag 0")
|
throw InvalidProtocolBufferException("Invalid tag 0")
|
||||||
}
|
}
|
||||||
@@ -199,7 +266,7 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// reads varint not larger than 32-bit integer according to protobuf varint-encoding
|
// reads varint not larger than 32-bit integer according to protobuf varint-encoding
|
||||||
fun readRawVarint32(): Int {
|
fun readInt32NoTag(): Int {
|
||||||
var done: Boolean = false
|
var done: Boolean = false
|
||||||
var result: Int = 0
|
var result: Int = 0
|
||||||
var step: Int = 0
|
var step: Int = 0
|
||||||
@@ -207,10 +274,10 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
val byte: Int = bufferedInput.read()
|
val byte: Int = bufferedInput.read()
|
||||||
result = result or
|
result = result or
|
||||||
(
|
(
|
||||||
(byte and VARINT_INFO_BITS_MASK)
|
(byte and VARINT_INFO_BITS_MASK)
|
||||||
shl
|
shl
|
||||||
(VARINT_INFO_BITS_COUNT * step)
|
(VARINT_INFO_BITS_COUNT * step)
|
||||||
)
|
)
|
||||||
step++
|
step++
|
||||||
if ((byte and VARINT_UTIL_BIT_MASK) == 0) {
|
if ((byte and VARINT_UTIL_BIT_MASK) == 0) {
|
||||||
done = true
|
done = true
|
||||||
@@ -220,7 +287,7 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// reads varint not larger than 64-bit integer according to protobuf varint-encoding
|
// reads varint not larger than 64-bit integer according to protobuf varint-encoding
|
||||||
fun readRawVarint64(): Long {
|
fun readInt64NoTag(): Long {
|
||||||
var done: Boolean = false
|
var done: Boolean = false
|
||||||
var result: Long = 0
|
var result: Long = 0
|
||||||
var step: Int = 0
|
var step: Int = 0
|
||||||
@@ -228,10 +295,10 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
val byte: Int = bufferedInput.read()
|
val byte: Int = bufferedInput.read()
|
||||||
result = result or
|
result = result or
|
||||||
(
|
(
|
||||||
(byte and VARINT_INFO_BITS_MASK).toLong()
|
(byte and VARINT_INFO_BITS_MASK).toLong()
|
||||||
shl
|
shl
|
||||||
(VARINT_INFO_BITS_COUNT * step)
|
(VARINT_INFO_BITS_COUNT * step)
|
||||||
)
|
)
|
||||||
step++
|
step++
|
||||||
if ((byte and VARINT_UTIL_BIT_MASK) == 0 || byte == -1) {
|
if ((byte and VARINT_UTIL_BIT_MASK) == 0 || byte == -1) {
|
||||||
done = true
|
done = true
|
||||||
@@ -241,14 +308,14 @@ class CodedInputStream(input: java.io.InputStream) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// reads zig-zag encoded integer not larger than 32-bit long
|
// reads zig-zag encoded integer not larger than 32-bit long
|
||||||
fun readZigZag32(): Int {
|
fun readZigZag32NoTag(): Int {
|
||||||
val value = readRawVarint32()
|
val value = readInt32NoTag()
|
||||||
return (value shr 1) xor (-(value and 1)) // bit magic for decoding zig-zag number
|
return (value shr 1) xor (-(value and 1)) // bit magic for decoding zig-zag number
|
||||||
}
|
}
|
||||||
|
|
||||||
// reads zig-zag encoded integer not larger than 64-bit long
|
// reads zig-zag encoded integer not larger than 64-bit long
|
||||||
fun readZigZag64(): Long {
|
fun readZigZag64NoTag(): Long {
|
||||||
val value = readRawVarint64()
|
val value = readInt64NoTag()
|
||||||
return (value shr 1) xor (-(value and 1L)) // bit magic for decoding zig-zag number
|
return (value shr 1) xor (-(value and 1L)) // bit magic for decoding zig-zag number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,114 +8,184 @@ import java.nio.ByteOrder
|
|||||||
class CodedOutputStream(val output: java.io.OutputStream) {
|
class CodedOutputStream(val output: java.io.OutputStream) {
|
||||||
fun writeTag(fieldNumber: Int, type: WireType) {
|
fun writeTag(fieldNumber: Int, type: WireType) {
|
||||||
val tag = (fieldNumber shl 3) or type.ordinal
|
val tag = (fieldNumber shl 3) or type.ordinal
|
||||||
writeVarint32(tag)
|
writeInt32NoTag(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeInt32(fieldNumber: Int, value: Int) {
|
fun writeInt32(fieldNumber: Int, value: Int?) {
|
||||||
|
value ?: return
|
||||||
writeTag(fieldNumber, WireType.VARINT)
|
writeTag(fieldNumber, WireType.VARINT)
|
||||||
writeVarint32(value)
|
writeInt32NoTag(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that unsigned integer types are stored as their signed counterparts with top bit
|
// Note that unsigned integer types are stored as their signed counterparts with top bit
|
||||||
// simply stored in the sign bit - similar to Java's protobuf implementation. Hence, all
|
// simply stored in the sign bit - similar to Java's protobuf implementation. Hence, all
|
||||||
// methods, writing unsigned ints simply redirect call to corresponding signed-writing method
|
// methods, writing unsigned ints simply redirect call to corresponding signed-writing method
|
||||||
fun writeUInt32(fieldNumber: Int, value: Int) {
|
fun writeUInt32(fieldNumber: Int, value: Int?) {
|
||||||
|
value ?: return
|
||||||
writeInt32(fieldNumber, value)
|
writeInt32(fieldNumber, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeInt64(fieldNumber: Int, value: Long) {
|
fun writeInt64(fieldNumber: Int, value: Long?) {
|
||||||
|
value ?: return
|
||||||
writeTag(fieldNumber, WireType.VARINT)
|
writeTag(fieldNumber, WireType.VARINT)
|
||||||
writeVarint64(value)
|
writeInt64NoTag(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// See notes on unsigned integers implementation above
|
// See notes on unsigned integers implementation above
|
||||||
fun writeUIn64(fieldNumber: Int, value: Long) {
|
fun writeUInt64(fieldNumber: Int, value: Long?) {
|
||||||
|
value ?: return
|
||||||
writeInt64(fieldNumber, value)
|
writeInt64(fieldNumber, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeBool(fieldNumber: Int, value: Boolean) {
|
fun writeBool(fieldNumber: Int, value: Boolean?) {
|
||||||
writeInt32(fieldNumber, if (value) 1 else 0)
|
value ?: return
|
||||||
|
writeTag(fieldNumber, WireType.VARINT)
|
||||||
|
writeBoolNoTag(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeBoolNoTag(value: Boolean) {
|
||||||
|
writeInt32NoTag(if (value) 1 else 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writing enums is like writing one int32 number. Caller is responsible for converting enum-object to ordinal
|
// Writing enums is like writing one int32 number. Caller is responsible for converting enum-object to ordinal
|
||||||
fun writeEnum(fieldNumber: Int, value: Int) {
|
fun writeEnum(fieldNumber: Int, value: Int?) {
|
||||||
writeInt32(fieldNumber, value)
|
value ?: return
|
||||||
|
writeTag(fieldNumber, WireType.VARINT)
|
||||||
|
writeEnumNoTag(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeSInt32(fieldNumber: Int, value: Int) {
|
fun writeEnumNoTag(value: Int) {
|
||||||
writeInt32(fieldNumber, (value shl 1) xor (value shr 31))
|
writeInt32NoTag(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeSInt64(fieldNumber: Int, value: Long) {
|
fun writeSInt32(fieldNumber: Int, value: Int?) {
|
||||||
writeInt64(fieldNumber, (value shl 1) xor (value shr 31))
|
value ?: return
|
||||||
|
writeTag(fieldNumber, WireType.VARINT)
|
||||||
|
writeSInt32NoTag(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeFixed32(fieldNumber: Int, value: Int) {
|
fun writeSInt32NoTag(value: Int) {
|
||||||
|
writeInt32NoTag((value shl 1) xor (value shr 31))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeSInt64(fieldNumber: Int, value: Long?) {
|
||||||
|
value ?: return
|
||||||
|
writeTag(fieldNumber, WireType.VARINT)
|
||||||
|
writeSInt64NoTag(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeSInt64NoTag(value: Long) {
|
||||||
|
writeInt64NoTag((value shl 1) xor (value shr 31))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeFixed32(fieldNumber: Int, value: Int?) {
|
||||||
|
value ?: return
|
||||||
writeTag(fieldNumber, WireType.FIX_32)
|
writeTag(fieldNumber, WireType.FIX_32)
|
||||||
|
writeFixed32NoTag(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeFixed32NoTag(value: Int) {
|
||||||
writeLittleEndian(value)
|
writeLittleEndian(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// See notes on unsigned integers implementation above
|
// See notes on unsigned integers implementation above
|
||||||
fun writeSFixed32(fieldNumber: Int, value: Int) {
|
fun writeSFixed32(fieldNumber: Int, value: Int?) {
|
||||||
writeFixed32(fieldNumber, value)
|
value ?: return
|
||||||
|
writeTag(fieldNumber, WireType.FIX_32)
|
||||||
|
writeSFixed32NoTag(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeFixed64(fieldNumber: Int, value: Long) {
|
fun writeSFixed32NoTag(value: Int) {
|
||||||
|
writeLittleEndian(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeFixed64(fieldNumber: Int, value: Long?) {
|
||||||
|
value ?: return
|
||||||
writeTag(fieldNumber, WireType.FIX_64)
|
writeTag(fieldNumber, WireType.FIX_64)
|
||||||
|
writeFixed64NoTag(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeFixed64NoTag(value: Long) {
|
||||||
writeLittleEndian(value)
|
writeLittleEndian(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// See notes on unsigned integers implementation above
|
// See notes on unsigned integers implementation above
|
||||||
fun writeSFixed64(fieldNumber: Int, value: Long) {
|
fun writeSFixed64(fieldNumber: Int, value: Long?) {
|
||||||
writeFixed64(fieldNumber, value)
|
value ?: return
|
||||||
}
|
|
||||||
|
|
||||||
fun writeDouble(fieldNumber: Int, value: Double) {
|
|
||||||
writeTag(fieldNumber, WireType.FIX_64)
|
writeTag(fieldNumber, WireType.FIX_64)
|
||||||
|
writeSFixed64NoTag(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeSFixed64NoTag(value: Long) {
|
||||||
writeLittleEndian(value)
|
writeLittleEndian(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeFloat(fieldNumber: Int, value: Float) {
|
fun writeDouble(fieldNumber: Int, value: Double?) {
|
||||||
|
value ?: return
|
||||||
|
writeTag(fieldNumber, WireType.FIX_64)
|
||||||
|
writeDoubleNoTag(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeDoubleNoTag(value: Double) {
|
||||||
|
writeLittleEndian(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeFloat(fieldNumber: Int, value: Float?) {
|
||||||
|
value ?: return
|
||||||
writeTag(fieldNumber, WireType.FIX_32)
|
writeTag(fieldNumber, WireType.FIX_32)
|
||||||
|
writeFloatNoTag(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeFloatNoTag(value: Float) {
|
||||||
writeLittleEndian(value)
|
writeLittleEndian(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeString(fieldNumber: Int, value: String) {
|
fun writeString(fieldNumber: Int, value: String?) {
|
||||||
|
value ?: return
|
||||||
writeTag(fieldNumber, WireType.LENGTH_DELIMITED)
|
writeTag(fieldNumber, WireType.LENGTH_DELIMITED)
|
||||||
writeVarint32(value.length)
|
writeStringNoTag(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeStringNoTag(value: String) {
|
||||||
|
writeInt32NoTag(value.length)
|
||||||
output.write(value.toByteArray())
|
output.write(value.toByteArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeLittleEndian(value: Int) {
|
fun writeLittleEndian(value: Int?) {
|
||||||
|
value ?: return
|
||||||
val bb = ByteBuffer.allocate(4)
|
val bb = ByteBuffer.allocate(4)
|
||||||
bb.order(ByteOrder.LITTLE_ENDIAN)
|
bb.order(ByteOrder.LITTLE_ENDIAN)
|
||||||
bb.putInt(value)
|
bb.putInt(value)
|
||||||
output.write(bb.array())
|
output.write(bb.array())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeLittleEndian(value: Long) {
|
fun writeLittleEndian(value: Long?) {
|
||||||
|
value ?: return
|
||||||
val bb = ByteBuffer.allocate(8)
|
val bb = ByteBuffer.allocate(8)
|
||||||
bb.order(ByteOrder.LITTLE_ENDIAN)
|
bb.order(ByteOrder.LITTLE_ENDIAN)
|
||||||
bb.putLong(value)
|
bb.putLong(value)
|
||||||
output.write(bb.array())
|
output.write(bb.array())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeLittleEndian(value: Double) {
|
fun writeLittleEndian(value: Double?) {
|
||||||
|
value ?: return
|
||||||
val bb = ByteBuffer.allocate(8)
|
val bb = ByteBuffer.allocate(8)
|
||||||
bb.order(ByteOrder.LITTLE_ENDIAN)
|
bb.order(ByteOrder.LITTLE_ENDIAN)
|
||||||
bb.putDouble(value)
|
bb.putDouble(value)
|
||||||
output.write(bb.array())
|
output.write(bb.array())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeLittleEndian(value: Float) {
|
fun writeLittleEndian(value: Float?) {
|
||||||
|
value ?: return
|
||||||
val bb = ByteBuffer.allocate(4)
|
val bb = ByteBuffer.allocate(4)
|
||||||
bb.order(ByteOrder.LITTLE_ENDIAN)
|
bb.order(ByteOrder.LITTLE_ENDIAN)
|
||||||
bb.putFloat(value)
|
bb.putFloat(value)
|
||||||
output.write(bb.array())
|
output.write(bb.array())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeVarint32(value: Int) {
|
fun writeInt32NoTag(value: Int?) {
|
||||||
var curValue = value
|
value ?: return
|
||||||
|
var curValue: Int = value
|
||||||
|
|
||||||
// we have at most 32 information bits. With overhead of 1 bit per 7 bits we need at most 5 bytes for encoding
|
// we have at most 32 information bits. With overhead of 1 bit per 7 bits we need at most 5 bytes for encoding
|
||||||
val res = ByteArray(5)
|
val res = ByteArray(5)
|
||||||
@@ -139,8 +209,9 @@ class CodedOutputStream(val output: java.io.OutputStream) {
|
|||||||
output.write(res, 0, resSize)
|
output.write(res, 0, resSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeVarint64(value: Long) {
|
fun writeInt64NoTag(value: Long?) {
|
||||||
var curValue = value
|
value ?: return
|
||||||
|
var curValue: Long = value
|
||||||
|
|
||||||
// we have at most 64 information bits. With overhead of 1 bit per 7 bits we need at most 10 bytes for encoding
|
// we have at most 64 information bits. With overhead of 1 bit per 7 bits we need at most 10 bytes for encoding
|
||||||
val res = ByteArray(10)
|
val res = ByteArray(10)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
interface Message {
|
interface Message {
|
||||||
fun writeTo(output: CodedOutputStream)
|
fun writeTo(output: CodedOutputStream)
|
||||||
fun readFrom(input: CodedInputStream) : Message
|
|
||||||
fun getBuilder() : Builder
|
fun getBuilder() : Builder
|
||||||
|
|
||||||
//TODO: think about something similar to static method getDefaultInstance()
|
//TODO: think about something similar to static method getDefaultInstance()
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,12 +8,20 @@ import java.io.ByteArrayOutputStream
|
|||||||
fun testMessageSerialization() {
|
fun testMessageSerialization() {
|
||||||
val s = ByteArrayOutputStream()
|
val s = ByteArrayOutputStream()
|
||||||
val outs = CodedOutputStream(s)
|
val outs = CodedOutputStream(s)
|
||||||
val msg = PersonMessage(name = "John Doe", id = 42, hasCat = true)
|
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)
|
||||||
|
)
|
||||||
|
)
|
||||||
msg.writeTo(outs)
|
msg.writeTo(outs)
|
||||||
|
|
||||||
val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray()))
|
val ins = CodedInputStream(ByteArrayInputStream(s.toByteArray()))
|
||||||
val readMsg = PersonMessage("", 0, false)
|
val readMsg = Person("", 0, "", arrayOf())
|
||||||
readMsg.readFrom(ins)
|
readMsg.mergeFrom(ins)
|
||||||
|
|
||||||
assert(msg == readMsg)
|
assert(msg == readMsg)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user