Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Base type safety in C#. Clear FlatBufferBuilder in C#. #241

Merged
3 commits merged into from
Jul 29, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions include/flatbuffers/idl.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ namespace flatbuffers {
TD(FLOAT, "float", float, float, float32, float, float32) /* begin float */ \
TD(DOUBLE, "double", double, double, float64, double, float64) /* end float/scalar */
#define FLATBUFFERS_GEN_TYPES_POINTER(TD) \
TD(STRING, "string", Offset<void>, int, int, int, int) \
TD(VECTOR, "", Offset<void>, int, int, int, int) \
TD(STRING, "string", Offset<void>, int, int, StringOffset, int) \
TD(VECTOR, "", Offset<void>, int, int, VectorOffset, int) \
TD(STRUCT, "", Offset<void>, int, int, int, int) \
TD(UNION, "", Offset<void>, int, int, int, int)

Expand Down
5 changes: 5 additions & 0 deletions net/FlatBuffers/ByteBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public ByteBuffer(byte[] buffer)
_pos = 0;
}

public void Reset()
{
_pos = 0;
}

public int Position { get { return _pos; } }

// Pre-allocated helper arrays for convertion.
Expand Down
21 changes: 16 additions & 5 deletions net/FlatBuffers/FlatBufferBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -49,6 +49,17 @@ public FlatBufferBuilder(int initialSize)
_bb = new ByteBuffer(new byte[initialSize]);
}

public void Clear()
{
_space = _bb.Length;
_bb.Reset();
_minAlign = 1;
_vtable = null;
_objectStart = 0;
_vtables = new int[16];
_numVtables = 0;
_vectorNumElems = 0;
}

public int Offset { get { return _bb.Length - _space; } }

Expand Down Expand Up @@ -196,10 +207,10 @@ public void StartVector(int elemSize, int count, int alignment)
Prep(alignment, elemSize * count); // Just in case alignment > int.
}

public int EndVector()
public VectorOffset EndVector()
{
PutInt(_vectorNumElems);
return Offset;
return new VectorOffset(Offset);
}

public void Nested(int obj)
Expand Down Expand Up @@ -250,15 +261,15 @@ public void Slot(int voffset)
public void AddDouble(int o, double x, double d) { if (x != d) { AddDouble(x); Slot(o); } }
public void AddOffset(int o, int x, int d) { if (x != d) { AddOffset(x); Slot(o); } }

public int CreateString(string s)
public StringOffset CreateString(string s)
{
NotNested();
byte[] utf8 = Encoding.UTF8.GetBytes(s);
AddByte((byte)0);
StartVector(1, utf8.Length, 1);
Buffer.BlockCopy(utf8, 0, _bb.Data, _space -= utf8.Length,
utf8.Length);
return EndVector();
return new StringOffset(EndVector().Value);
}

// Structs are stored inline, so nothing additional is being added.
Expand Down
1 change: 1 addition & 0 deletions net/FlatBuffers/FlatBuffers.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<Compile Include="ByteBuffer.cs" />
<Compile Include="FlatBufferBuilder.cs" />
<Compile Include="FlatBufferConstants.cs" />
<Compile Include="Offset.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Struct.cs" />
<Compile Include="Table.cs" />
Expand Down
48 changes: 48 additions & 0 deletions net/FlatBuffers/Offset.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace FlatBuffers
{
/// <summary>
/// Offset class for typesafe assignments.
/// </summary>
public struct Offset<T> where T : class
{
public int Value;
public Offset(int value)
{
Value = value;
}
}

public struct StringOffset
{
public int Value;
public StringOffset(int value)
{
Value = value;
}
}

public struct VectorOffset
{
public int Value;
public VectorOffset(int value)
{
Value = value;
}
}
}
90 changes: 73 additions & 17 deletions src/idl_gen_general.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/idl.h"
#include "flatbuffers/util.h"
#include <algorithm>

namespace flatbuffers {

Expand Down Expand Up @@ -202,6 +203,10 @@ static std::string GenTypeBasic(const LanguageParameters &lang,
#undef FLATBUFFERS_TD
};

if(lang.language == GeneratorOptions::kCSharp && type.base_type == BASE_TYPE_STRUCT) {
return "Offset<" + type.struct_def->name + ">";
}

return gtypename[type.base_type * GeneratorOptions::kMAX + lang.language];
}

Expand Down Expand Up @@ -258,6 +263,32 @@ static Type DestinationType(const LanguageParameters &lang, const Type &type,
}
}

static std::string GenOffsetType(const LanguageParameters &lang, const StructDef &struct_def) {
if(lang.language == GeneratorOptions::kCSharp) {
return "Offset<" + struct_def.name + ">";
} else {
return "int";
}
}

static std::string GenOffsetConstruct(const LanguageParameters &lang,
const StructDef &struct_def,
const std::string &variable_name)
{
if(lang.language == GeneratorOptions::kCSharp) {
return "new Offset<" + struct_def.name + ">(" + variable_name + ")";
}
return variable_name;
}

static std::string GenVectorOffsetType(const LanguageParameters &lang) {
if(lang.language == GeneratorOptions::kCSharp) {
return "VectorOffset";
} else {
return "int";
}
}

// Generate destination type name
static std::string GenTypeNameDest(const LanguageParameters &lang, const Type &type)
{
Expand Down Expand Up @@ -330,7 +361,20 @@ static std::string DestinationValue(const LanguageParameters &lang,
}
}

static std::string GenDefaultValue(const Value &value) {
static std::string GenDefaultValue(const LanguageParameters &lang, const Value &value, bool for_buffer) {
if(lang.language == GeneratorOptions::kCSharp && !for_buffer) {
switch(value.type.base_type) {
case BASE_TYPE_STRING:
return "default(StringOffset)";
case BASE_TYPE_STRUCT:
return "default(Offset<" + value.type.struct_def->name + ">)";
case BASE_TYPE_VECTOR:
return "default(VectorOffset)";
default:
break;
}
}

return value.type.base_type == BASE_TYPE_BOOL
? (value.constant == "0" ? "false" : "true")
: value.constant;
Expand Down Expand Up @@ -617,7 +661,7 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
} else {
code += offset_prefix + getter;
code += "(o + bb_pos)" + dest_mask + " : " + default_cast;
code += GenDefaultValue(field.value);
code += GenDefaultValue(lang, field.value, false);
}
} else {
switch (field.value.type.base_type) {
Expand Down Expand Up @@ -702,13 +746,14 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
code += "\n";
if (struct_def.fixed) {
// create a struct constructor function
code += " public static int " + FunctionStart(lang, 'C') + "reate";
code += " public static " + GenOffsetType(lang, struct_def) + " ";
code += FunctionStart(lang, 'C') + "reate";
code += struct_def.name + "(FlatBufferBuilder builder";
GenStructArgs(lang, struct_def, code_ptr, "");
code += ") {\n";
GenStructBody(lang, struct_def, code_ptr, "");
code += " return builder.";
code += lang.get_fbb_offset;
code += " return ";
code += GenOffsetConstruct(lang, struct_def, "builder." + std::string(lang.get_fbb_offset));
code += ";\n }\n";
} else {
// Generate a method that creates a table in one go. This is only possible
Expand All @@ -728,9 +773,9 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
}
if (has_no_struct_fields && num_fields) {
// Generate a table constructor of the form:
// public static void createName(FlatBufferBuilder builder, args...)
code += " public static int " + FunctionStart(lang, 'C') + "reate";
code += struct_def.name;
// public static int createName(FlatBufferBuilder builder, args...)
code += " public static " + GenOffsetType(lang, struct_def) + " ";
code += FunctionStart(lang, 'C') + "reate" + struct_def.name;
code += "(FlatBufferBuilder builder";
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
Expand All @@ -744,7 +789,7 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
// Java doesn't have defaults, which means this method must always
// supply all arguments, and thus won't compile when fields are added.
if (lang.language != GeneratorOptions::kJava) {
code += " = " + GenDefaultValue(field.value);
code += " = " + GenDefaultValue(lang, field.value, false);
}
}
code += ") {\n builder.";
Expand Down Expand Up @@ -794,15 +839,18 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
code += GenMethod(lang, field.value.type) + "(";
code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
code += DestinationValue(lang, argname, field.value.type);
code += ", " + GenDefaultValue(field.value);
if(!IsScalar(field.value.type.base_type) && field.value.type.base_type != BASE_TYPE_UNION && lang.language == GeneratorOptions::kCSharp) {
code += ".Value";
}
code += ", " + GenDefaultValue(lang, field.value, true);
code += "); }\n";
if (field.value.type.base_type == BASE_TYPE_VECTOR) {
auto vector_type = field.value.type.VectorType();
auto alignment = InlineAlignment(vector_type);
auto elem_size = InlineSize(vector_type);
if (!IsStruct(vector_type)) {
// Generate a method to create a vector from a Java array.
code += " public static int " + FunctionStart(lang, 'C') + "reate";
code += " public static " + GenVectorOffsetType(lang) + " " + FunctionStart(lang, 'C') + "reate";
code += MakeCamel(field.name);
code += "Vector(FlatBufferBuilder builder, ";
code += GenTypeBasic(lang, vector_type) + "[] data) ";
Expand All @@ -814,8 +862,12 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
code += FunctionStart(lang, 'L') + "ength - 1; i >= 0; i--) builder.";
code += FunctionStart(lang, 'A') + "dd";
code += GenMethod(lang, vector_type);
code += "(data[i]); return builder.";
code += FunctionStart(lang, 'E') + "ndVector(); }\n";
code += "(data[i]";
if(lang.language == GeneratorOptions::kCSharp &&
(vector_type.base_type == BASE_TYPE_STRUCT || vector_type.base_type == BASE_TYPE_STRING))
code += ".Value";
code += "); return ";
code += "builder." + FunctionStart(lang, 'E') + "ndVector(); }\n";
}
// Generate a method to start a vector, data to be added manually after.
code += " public static void " + FunctionStart(lang, 'S') + "tart";
Expand All @@ -827,7 +879,7 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
code += "); }\n";
}
}
code += " public static int ";
code += " public static " + GenOffsetType(lang, struct_def) + " ";
code += FunctionStart(lang, 'E') + "nd" + struct_def.name;
code += "(FlatBufferBuilder builder) {\n int o = builder.";
code += FunctionStart(lang, 'E') + "ndObject();\n";
Expand All @@ -841,12 +893,16 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
code += "); // " + field.name + "\n";
}
}
code += " return o;\n }\n";
code += " return " + GenOffsetConstruct(lang, struct_def, "o") + ";\n }\n";
if (parser.root_struct_def_ == &struct_def) {
code += " public static void ";
code += FunctionStart(lang, 'F') + "inish" + struct_def.name;
code += "Buffer(FlatBufferBuilder builder, int offset) { ";
code += "builder." + FunctionStart(lang, 'F') + "inish(offset";
code += "Buffer(FlatBufferBuilder builder, " + GenOffsetType(lang, struct_def) + " offset) {";
code += " builder." + FunctionStart(lang, 'F') + "inish(offset";
if (lang.language == GeneratorOptions::kCSharp) {
code += ".Value";
}

if (parser.file_identifier_.length())
code += ", \"" + parser.file_identifier_ + "\"";
code += "); }\n";
Expand Down
3 changes: 3 additions & 0 deletions tests/FlatBuffers.Test/FlatBuffers.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
<Compile Include="..\..\net\FlatBuffers\ByteBuffer.cs">
<Link>FlatBuffers\ByteBuffer.cs</Link>
</Compile>
<Compile Include="..\..\net\FlatBuffers\Offset.cs">
<Link>FlatBuffers\Offset.cs</Link>
</Compile>
<Compile Include="..\..\net\FlatBuffers\FlatBufferBuilder.cs">
<Link>FlatBuffers\FlatBufferBuilder.cs</Link>
</Compile>
Expand Down
8 changes: 4 additions & 4 deletions tests/FlatBuffers.Test/FlatBuffersExampleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ public void CanCreateNewFlatBufferFromScratch()
var test4 = fbb.EndVector();

Monster.StartTestarrayofstringVector(fbb, 2);
fbb.AddOffset(test2);
fbb.AddOffset(test1);
fbb.AddOffset(test2.Value);
fbb.AddOffset(test1.Value);
var testArrayOfString = fbb.EndVector();


Expand All @@ -73,13 +73,13 @@ public void CanCreateNewFlatBufferFromScratch()
Monster.AddName(fbb, str);
Monster.AddInventory(fbb, inv);
Monster.AddTestType(fbb, Any.Monster);
Monster.AddTest(fbb, mon2);
Monster.AddTest(fbb, mon2.Value);
Monster.AddTest4(fbb, test4);
Monster.AddTestarrayofstring(fbb, testArrayOfString);
Monster.AddTestbool(fbb, false);
var mon = Monster.EndMonster(fbb);

fbb.Finish(mon);
fbb.Finish(mon.Value);

// Dump to output directory so we can inspect later, if needed
using (var ms = new MemoryStream(fbb.DataBuffer.Data, fbb.DataBuffer.Position, fbb.Offset))
Expand Down
Loading