Protobuf: fixed bug in desrialization of arrays
This commit is contained in:
@@ -6,8 +6,11 @@ EXE = protoc
|
|||||||
SRCDIR = src
|
SRCDIR = src
|
||||||
BINDIR = bin
|
BINDIR = bin
|
||||||
TESTDIR = test
|
TESTDIR = test
|
||||||
|
KTPROJECT = protoc-test-project
|
||||||
|
KTOUT = $(KTPROJECT)/src
|
||||||
|
|
||||||
OBJECTS = $(patsubst $(SRCDIR)/%.cc,$(BINDIR)/%.o,$(wildcard $(SRCDIR)/*.cc))
|
OBJECTS = $(patsubst $(SRCDIR)/%.cc,$(BINDIR)/%.o,$(wildcard $(SRCDIR)/*.cc))
|
||||||
|
KTARTIFACTS = $(patsubst $(TESTDIR)/%.proto,$(KTOUT)/%.kt,$(wildcard $(TESTDIR)/*.proto))
|
||||||
|
|
||||||
all: $(EXE)
|
all: $(EXE)
|
||||||
|
|
||||||
@@ -22,16 +25,18 @@ include $(wildcard $(BINDIR)/*.d)
|
|||||||
$(BINDIR):
|
$(BINDIR):
|
||||||
mkdir -p $(BINDIR)
|
mkdir -p $(BINDIR)
|
||||||
|
|
||||||
|
$(KTOUT):
|
||||||
|
mkdir -p $(KTOUT)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BINDIR)
|
rm -rf $(BINDIR)
|
||||||
rm $(EXE)
|
rm $(EXE)
|
||||||
|
rm -rf $(KTPROJECT)
|
||||||
|
|
||||||
|
$(KTOUT)/%.kt: $(TESTDIR)/%.proto $(EXE)
|
||||||
|
./protoc --kotlin_out=$(KTOUT) $<
|
||||||
|
|
||||||
|
generate: $(KTOUT) $(KTARTIFACTS)
|
||||||
|
|
||||||
generate:
|
|
||||||
./protoc --kotlin_out=$$HOME/Downloads/carkot/proto/compiler/protoc-artifacts ./test/connect.proto
|
|
||||||
./protoc --kotlin_out=$$HOME/Downloads/carkot/proto/compiler/protoc-artifacts ./test/direction.proto
|
|
||||||
./protoc --kotlin_out=$$HOME/Downloads/carkot/proto/compiler/protoc-artifacts ./test/location.proto
|
|
||||||
./protoc --kotlin_out=$$HOME/Downloads/carkot/proto/compiler/protoc-artifacts ./test/route_done.proto
|
|
||||||
./protoc --kotlin_out=$$HOME/Downloads/carkot/proto/compiler/protoc-artifacts ./test/route.proto
|
|
||||||
./protoc --kotlin_out=$$HOME/Downloads/carkot/proto/compiler/protoc-artifacts ./test/rc_session.proto
|
|
||||||
|
|
||||||
.PHONY: clean all generate
|
.PHONY: clean all generate
|
||||||
|
|||||||
+2
-2
@@ -224,7 +224,7 @@ void ClassGenerator::generateHeader(io::Printer * printer, bool isBuilder) const
|
|||||||
// build list of arguments like 'field1: Type1, field2: Type2, ... '
|
// build list of arguments like 'field1: Type1, field2: Type2, ... '
|
||||||
string argumentList = "";
|
string argumentList = "";
|
||||||
for (int i = 0; i < properties.size(); ++i) {
|
for (int i = 0; i < properties.size(); ++i) {
|
||||||
argumentList += properties[i]->simpleName + ": " + properties[i]->getFullType();
|
argumentList += "var " + properties[i]->simpleName + ": " + properties[i]->getFullType();
|
||||||
if (i + 1 != properties.size()) {
|
if (i + 1 != properties.size()) {
|
||||||
argumentList += ", ";
|
argumentList += ", ";
|
||||||
}
|
}
|
||||||
@@ -315,7 +315,7 @@ void ClassGenerator::generateParseMethods(io::Printer *printer) const {
|
|||||||
// check that wire type of that field is equal to expected
|
// check that wire type of that field is equal to expected
|
||||||
printer->Print(vars, "if (wireType != $kotlinWireType$) { errorCode = 1; return false } \n");
|
printer->Print(vars, "if (wireType != $kotlinWireType$) { errorCode = 1; return false } \n");
|
||||||
|
|
||||||
properties[i]->generateSerializationCode(printer, /* isRead = */ true, /* noTag = */ true);
|
properties[i]->generateSerializationCode(printer, /* isRead = */ true, /* noTag = */ true, /* isField = */ false);
|
||||||
|
|
||||||
printer->Outdent();
|
printer->Outdent();
|
||||||
printer->Print("}\n");
|
printer->Print("}\n");
|
||||||
|
|||||||
+4
-4
@@ -21,7 +21,7 @@ class FieldGenerator; // declared in "kotlin_file_generator.h"
|
|||||||
class NameResolver; // declared in "kotlin_name_resolver.h"
|
class NameResolver; // declared in "kotlin_name_resolver.h"
|
||||||
class EnumGenerator; // declared in "kotlin_enum_generator.h"
|
class EnumGenerator; // declared in "kotlin_enum_generator.h"
|
||||||
|
|
||||||
class ClassGenerator {
|
class ClassGenerator { // TODO ProtoClass
|
||||||
public:
|
public:
|
||||||
string getSimpleType() const;
|
string getSimpleType() const;
|
||||||
string getFullType() const;
|
string getFullType() const;
|
||||||
@@ -30,12 +30,12 @@ public:
|
|||||||
string getBuilderInitValue() const;
|
string getBuilderInitValue() const;
|
||||||
|
|
||||||
vector <FieldGenerator *> properties;
|
vector <FieldGenerator *> properties;
|
||||||
vector <ClassGenerator *> classesDeclarations;
|
vector <ClassGenerator *> classesDeclarations; // TODO nestedClasses
|
||||||
vector <EnumGenerator *> enumsDeclaraions;
|
vector <EnumGenerator *> enumsDeclaraions; // TODO ProtoEnum
|
||||||
ClassGenerator (Descriptor const * descriptor, NameResolver * nameResolver);
|
ClassGenerator (Descriptor const * descriptor, NameResolver * nameResolver);
|
||||||
~ClassGenerator ();
|
~ClassGenerator ();
|
||||||
|
|
||||||
|
// KtClassGenerator(ProtoClass)::generate()
|
||||||
void generateCode (io::Printer * printer, bool isBuilder = false) const;
|
void generateCode (io::Printer * printer, bool isBuilder = false) const;
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
|
|||||||
+24
-32
@@ -50,7 +50,7 @@ void FieldGenerator::generateCode(io::Printer *printer, bool isBuilder) const {
|
|||||||
|
|
||||||
generateComment(printer);
|
generateComment(printer);
|
||||||
|
|
||||||
printer->Print(vars, "var $name$ : $field$\n");
|
// printer->Print(vars, "var $name$ : $field$\n");
|
||||||
|
|
||||||
// generate setter for builder
|
// generate setter for builder
|
||||||
if (isBuilder) {
|
if (isBuilder) {
|
||||||
@@ -81,20 +81,13 @@ void FieldGenerator::generateSerializationForPacked(io::Printer *printer, bool i
|
|||||||
|
|
||||||
bool isPrimitive = descriptor->type() != FieldDescriptor::TYPE_BYTES &&
|
bool isPrimitive = descriptor->type() != FieldDescriptor::TYPE_BYTES &&
|
||||||
descriptor->type() != FieldDescriptor::TYPE_MESSAGE &&
|
descriptor->type() != FieldDescriptor::TYPE_MESSAGE &&
|
||||||
descriptor->type() != FieldDescriptor::TYPE_STRING &&
|
descriptor->type() != FieldDescriptor::TYPE_STRING;
|
||||||
descriptor->type() != FieldDescriptor::TYPE_ENUM;
|
|
||||||
|
|
||||||
if (isRead) {
|
if (isRead) {
|
||||||
if (!noTag) {
|
if (!noTag) {
|
||||||
printer->Print(vars, "val tag = input.readTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n");
|
printer->Print(vars, "val tag = input.readTag($fieldNumber$, WireType.LENGTH_DELIMITED)\n");
|
||||||
}
|
}
|
||||||
printer->Print(vars, "val expectedByteSize = input.readInt32NoTag()\n");
|
printer->Print(vars, "val expectedByteSize = input.readInt32NoTag()\n");
|
||||||
|
|
||||||
/* Now we want to compute element size of array. For this, we are estimating byte size of a single element,
|
|
||||||
* and then divide byte size of whole array by byte size of single element.
|
|
||||||
*/
|
|
||||||
printer->Print(vars, "var singleElemSize = 0\n");
|
|
||||||
|
|
||||||
/* hack: copy current FieldGenerator and change label to OPTIONAL. Also change name to
|
/* hack: copy current FieldGenerator and change label to OPTIONAL. Also change name to
|
||||||
name of iterator in for-loop.
|
name of iterator in for-loop.
|
||||||
This will allow to re-use this function for generating serialization code for elements of array.
|
This will allow to re-use this function for generating serialization code for elements of array.
|
||||||
@@ -105,38 +98,36 @@ void FieldGenerator::generateSerializationForPacked(io::Printer *printer, bool i
|
|||||||
(then writing CodedOutputStream.writeMessage will be possible).
|
(then writing CodedOutputStream.writeMessage will be possible).
|
||||||
*/
|
*/
|
||||||
FieldGenerator singleFieldGen = getUnderlyingTypeGenerator();
|
FieldGenerator singleFieldGen = getUnderlyingTypeGenerator();
|
||||||
singleFieldGen.simpleName = "singleElemSize";
|
|
||||||
|
|
||||||
printer->Print("\n");
|
// Allocate new array and current size
|
||||||
singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ true, /* isField = */ false);
|
printer->Print(vars, "var newArray = $arrayType$(0)\n");
|
||||||
printer->Print("\n");
|
printer->Print(vars, "var readSize = 0\n");
|
||||||
|
|
||||||
printer->Print(vars, "val arraySize = expectedByteSize / singleElemSize\n");
|
|
||||||
|
|
||||||
// Allocate new array of estimated size
|
|
||||||
printer->Print(vars, "val newArray = $arrayType$(arraySize)\n");
|
|
||||||
|
|
||||||
// place declaration of new variable in anonymous scope for hygiene
|
// place declaration of new variable in anonymous scope for hygiene
|
||||||
printer->Print("run {\n");
|
printer->Print("do {\n");
|
||||||
printer->Indent();
|
printer->Indent();
|
||||||
printer->Print("var i = 0\n");
|
printer->Print("var i = 0\n");
|
||||||
printer->Print(vars, "while(i < arraySize) {\n");
|
printer->Print(vars, "while(readSize < expectedByteSize) {\n");
|
||||||
printer->Indent();
|
printer->Indent();
|
||||||
|
|
||||||
printer->Print(vars, "var tmp: $underlyingType$ = $initValue$\n");
|
printer->Print(vars, "var tmp = $arrayType$(1)\n");
|
||||||
singleFieldGen.simpleName = "tmp";
|
singleFieldGen.simpleName = "tmp[0]";
|
||||||
singleFieldGen.protoLabel = FieldDescriptor::LABEL_OPTIONAL;
|
singleFieldGen.protoLabel = FieldDescriptor::LABEL_OPTIONAL;
|
||||||
|
|
||||||
singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ true, /* isField = */ false);
|
singleFieldGen.generateSerializationCode(printer, isRead, /* noTag = */ true, /* isField = */ false);
|
||||||
|
|
||||||
printer->Print(vars, "$fieldName$[i] = tmp\n");
|
printer->Print(vars, "newArray = newArray.plus(tmp)\n");
|
||||||
printer->Print("i += 1\n");
|
|
||||||
|
|
||||||
|
// add size of read element to readSize
|
||||||
|
singleFieldGen.generateSizeEstimationCode(printer, "readSize", /* noTag = */ true, /* isField = */ false);
|
||||||
printer->Outdent(); // while-loop
|
printer->Outdent(); // while-loop
|
||||||
printer->Print("}\n");
|
printer->Print("}\n");
|
||||||
|
|
||||||
|
// finaly, assign new array to our field
|
||||||
|
printer->Print(vars, "$fieldName$ = newArray\n");
|
||||||
|
|
||||||
printer->Outdent(); // anonymous scope
|
printer->Outdent(); // anonymous scope
|
||||||
printer->Print("}\n");
|
printer->Print("} while (false)\n");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/**
|
/**
|
||||||
@@ -159,7 +150,7 @@ void FieldGenerator::generateSerializationForPacked(io::Printer *printer, bool i
|
|||||||
|
|
||||||
// all elements
|
// all elements
|
||||||
// place declaration of new variable in anonymous scope for hygiene
|
// place declaration of new variable in anonymous scope for hygiene
|
||||||
printer->Print("run {\n");
|
printer->Print("do {\n");
|
||||||
printer->Indent();
|
printer->Indent();
|
||||||
|
|
||||||
printer->Print("var i = 0\n");
|
printer->Print("var i = 0\n");
|
||||||
@@ -179,7 +170,7 @@ void FieldGenerator::generateSerializationForPacked(io::Printer *printer, bool i
|
|||||||
printer->Print("}\n");
|
printer->Print("}\n");
|
||||||
|
|
||||||
printer->Outdent(); // anonymous scope
|
printer->Outdent(); // anonymous scope
|
||||||
printer->Print("}\n");
|
printer->Print("} while(false)\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +210,7 @@ void FieldGenerator::generateSerializationForMessages(io::Printer * printer, boo
|
|||||||
if (isRead) {
|
if (isRead) {
|
||||||
// We will create some temporary variables
|
// We will create some temporary variables
|
||||||
// So we place following code into separate block for the sake of hygiene
|
// So we place following code into separate block for the sake of hygiene
|
||||||
printer->Print("run {\n");
|
printer->Print("do {\n");
|
||||||
printer->Indent();
|
printer->Indent();
|
||||||
|
|
||||||
// read tag
|
// read tag
|
||||||
@@ -239,8 +230,9 @@ void FieldGenerator::generateSerializationForMessages(io::Printer * printer, boo
|
|||||||
|
|
||||||
// check that actual size equal to expected size
|
// check that actual size equal to expected size
|
||||||
printer->Print(vars, "if (expectedSize != $fieldName$.getSizeNoTag()) { errorCode = 3; return false }\n");
|
printer->Print(vars, "if (expectedSize != $fieldName$.getSizeNoTag()) { errorCode = 3; return false }\n");
|
||||||
|
|
||||||
printer->Outdent();
|
printer->Outdent();
|
||||||
printer->Print("}\n");
|
printer->Print("} while(false) \n");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// write tag
|
// write tag
|
||||||
@@ -394,7 +386,7 @@ void FieldGenerator::generateSizeEstimationCode(io::Printer *printer, string var
|
|||||||
// We will need total byte size of array, because that size is itself a part of the message and
|
// We will need total byte size of array, because that size is itself a part of the message and
|
||||||
// adds to total message size.
|
// adds to total message size.
|
||||||
// For the sake of hygiene, temporary variables are created in anonymous scope
|
// For the sake of hygiene, temporary variables are created in anonymous scope
|
||||||
printer->Print("run {\n");
|
printer->Print("do {\n");
|
||||||
printer->Indent();
|
printer->Indent();
|
||||||
|
|
||||||
// Create a temporary variable that will collect array byte size
|
// Create a temporary variable that will collect array byte size
|
||||||
@@ -414,14 +406,14 @@ void FieldGenerator::generateSizeEstimationCode(io::Printer *printer, string var
|
|||||||
printer->Print(vars, "i += 1\n");
|
printer->Print(vars, "i += 1\n");
|
||||||
|
|
||||||
printer->Outdent(); // for-loop
|
printer->Outdent(); // for-loop
|
||||||
printer->Print("}\n");
|
printer->Print("} \n");
|
||||||
|
|
||||||
// now add size of array to total message size:
|
// now add size of array to total message size:
|
||||||
printer->Print(vars,
|
printer->Print(vars,
|
||||||
"$varName$ += arraySize"); // actual array size
|
"$varName$ += arraySize"); // actual array size
|
||||||
printer->Print("\n");
|
printer->Print("\n");
|
||||||
printer->Outdent(); // anonymous scope
|
printer->Outdent(); // anonymous scope
|
||||||
printer->Print("}\n");
|
printer->Print("} while(false)\n");
|
||||||
|
|
||||||
printer->Outdent(); // if-clause
|
printer->Outdent(); // if-clause
|
||||||
printer->Print("}\n");
|
printer->Print("}\n");
|
||||||
|
|||||||
+2
-1
@@ -17,13 +17,14 @@ namespace protobuf {
|
|||||||
namespace compiler {
|
namespace compiler {
|
||||||
namespace kotlin {
|
namespace kotlin {
|
||||||
|
|
||||||
void FileGenerator::generateCode(io::Printer *printer, std::vector<ClassGenerator *> & classes) const {
|
void FileGenerator::generateCode(io::Printer *printer, std::vector<ClassGenerator *> & classes) {
|
||||||
for (int i = 0; i < classes.size(); ++i) {
|
for (int i = 0; i < classes.size(); ++i) {
|
||||||
classes[i]->generateCode(printer);
|
classes[i]->generateCode(printer);
|
||||||
printer->Print("\n\n");
|
printer->Print("\n\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract more methods or inline generateCode() to have common style in this method
|
||||||
bool FileGenerator::Generate(const FileDescriptor *file, const string ¶meter, GeneratorContext *context,
|
bool FileGenerator::Generate(const FileDescriptor *file, const string ¶meter, GeneratorContext *context,
|
||||||
string *error) const {
|
string *error) const {
|
||||||
std::vector<ClassGenerator *> classes;
|
std::vector<ClassGenerator *> classes;
|
||||||
|
|||||||
+3
-2
@@ -21,13 +21,14 @@ public:
|
|||||||
FileGenerator();
|
FileGenerator();
|
||||||
~FileGenerator();
|
~FileGenerator();
|
||||||
|
|
||||||
void generateCode(io::Printer *, std::vector<ClassGenerator *> &) const;
|
|
||||||
|
|
||||||
// implements CodeGenerator ----------------------------------------
|
// implements CodeGenerator ----------------------------------------
|
||||||
bool Generate(const FileDescriptor* file,
|
bool Generate(const FileDescriptor* file,
|
||||||
const string& parameter,
|
const string& parameter,
|
||||||
GeneratorContext* context,
|
GeneratorContext* context,
|
||||||
string* error) const;
|
string* error) const;
|
||||||
|
private:
|
||||||
|
// TODO check code style
|
||||||
|
static void generateCode(io::Printer *, std::vector<ClassGenerator *> &);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace kotlin
|
} // namespace kotlin
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include "kotlin_file_generator.h"
|
#include "kotlin_file_generator.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
// TODO attacch Kotlin generator to protoc as plugin.
|
||||||
|
// Don't rewrite protoc main.
|
||||||
int main(int argc, const char* const * argv) {
|
int main(int argc, const char* const * argv) {
|
||||||
google::protobuf::compiler::CommandLineInterface cli;
|
google::protobuf::compiler::CommandLineInterface cli;
|
||||||
cli.AllowPlugins("protoc-");
|
cli.AllowPlugins("protoc-");
|
||||||
|
|||||||
+6
-5
@@ -16,10 +16,10 @@ if [ ! -e "$lprotobuf" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$ok" == 0 ]; then
|
if [ "$ok" == 0 ]; then
|
||||||
echo "$missing, required for building, is not found"
|
echo "$missing, required for building, is not found" \
|
||||||
echo "You have to build and install latest Protobuf version from https://github.com/google/protobuf"
|
"You have to build and install latest Protobuf version from https://github.com/google/protobuf" \
|
||||||
echo "Do you want to do it automatically? [y/n]"
|
"Do you want to do it automatically? [y/n]" \
|
||||||
echo "(Caution: this will take decent amount of time and internet traffic)"
|
"(Caution: this will take decent amount of time and internet traffic)"
|
||||||
response="n"
|
response="n"
|
||||||
read response
|
read response
|
||||||
if [ $response = "y" ]; then
|
if [ $response = "y" ]; then
|
||||||
@@ -42,9 +42,10 @@ if [ "$ok" == 0 ]; then
|
|||||||
make
|
make
|
||||||
|
|
||||||
echo "Installing"
|
echo "Installing"
|
||||||
sudo make install
|
#sudo make install
|
||||||
|
|
||||||
echo "Cleaning"
|
echo "Cleaning"
|
||||||
|
cd ../..
|
||||||
rm -rf protobuf_sources
|
rm -rf protobuf_sources
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user