Skip to content

Commit

Permalink
Merge pull request google#241 from RevenantX/master
Browse files Browse the repository at this point in the history
[BREAKING CHANGE] Base type safety in C#. Clear FlatBufferBuilder in C#.
  • Loading branch information
Wouter van Oortmerssen committed Jul 29, 2015
2 parents 6e192fa + 0b761ec commit 9a30d3d
Show file tree
Hide file tree
Showing 12 changed files with 177 additions and 53 deletions.
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

0 comments on commit 9a30d3d

Please sign in to comment.