From e083e466b85c1ae512193cee74e0dbd60be5ab87 Mon Sep 17 00:00:00 2001 From: Michael Collins Date: Sun, 4 Oct 2015 19:57:39 -0700 Subject: [PATCH] Add Get Bytes Method Generator for C# I updated idl_gen_general.cpp to add support for generating a Get Bytes method for a vector to the generated C# source code. Given a byte vector field named Foo, a method named GetFooBytes() will be generated in the C# source code that will return an ArraySegment value referencing the vector data in the underlying ByteBuffer. I added a method to Table.cs named __vector_as_arraysegment that is used by the code generated by the change to the C# generator. __vector_as_arraysegment will take the offset of the vector and will return the ArraySegment value corresponding to the bytes that store the vector data. I updated FlatBuffersExampleTests.cs to add tests to validate my implementation of Table.__vector_as_arraysegment. I added tests to demonstrate that the bytes for the monster's name can be extracted from the underlying byte array. I also added tests to show that Table.__vector_as_arraysegment returns a null value if the vector is not present in the FlatBuffer. I used the updated flatc.exe program to regenerate the C# source files for the MyGame example. The new Monster class includes the GetXXXBytes methods to return the byte arrays containing data for vectors. --- net/FlatBuffers/Table.cs | 15 ++++++++ src/idl_gen_general.cpp | 36 ++++++++++++------- tests/FlatBuffers.Test/Assert.cs | 8 +++++ .../FlatBuffersExampleTests.cs | 13 +++++++ tests/MyGame/Example/Monster.cs | 5 +++ tests/MyGame/Example/Stat.cs | 2 ++ tests/MyGame/Example/Test.cs | 1 + .../MyGame/Example/TestSimpleTableWithEnum.cs | 1 + tests/MyGame/Example/Vec3.cs | 1 + 9 files changed, 70 insertions(+), 12 deletions(-) diff --git a/net/FlatBuffers/Table.cs b/net/FlatBuffers/Table.cs index 09b002850..bd5e36419 100644 --- a/net/FlatBuffers/Table.cs +++ b/net/FlatBuffers/Table.cs @@ -67,6 +67,21 @@ namespace FlatBuffers return offset + bb.GetInt(offset) + sizeof(int); // data starts after the length } + // Get the data of a vector whoses offset is stored at "offset" in this object as an + // ArraySegment<byte>. If the vector is not present in the ByteBuffer, + // then a null value will be returned. + protected ArraySegment? __vector_as_arraysegment(int offset) { + var o = this.__offset(offset); + if (0 == o) + { + return null; + } + + var pos = this.__vector(o); + var len = this.__vector_len(o); + return new ArraySegment(this.bb.Data, pos, len); + } + // Initialize any Table-derived type to point to the union at the given offset. protected TTable __union(TTable t, int offset) where TTable : Table { diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index 88aa4daee..15dd48204 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -145,7 +145,7 @@ LanguageParameters language_parameters[] = { "", "Position", "Offset", - "using FlatBuffers;\n\n", + "using System;\nusing FlatBuffers;\n\n", { nullptr, "///", @@ -823,17 +823,29 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, code += "}\n"; } // Generate a ByteBuffer accessor for strings & vectors of scalars. - if (((field.value.type.base_type == BASE_TYPE_VECTOR && - IsScalar(field.value.type.VectorType().base_type)) || - field.value.type.base_type == BASE_TYPE_STRING) && - lang.language == IDLOptions::kJava) { - code += " public ByteBuffer "; - code += MakeCamel(field.name, lang.first_camel_upper); - code += "AsByteBuffer() { return __vector_as_bytebuffer("; - code += NumToString(field.value.offset) + ", "; - code += NumToString(field.value.type.base_type == BASE_TYPE_STRING ? 1 : - InlineSize(field.value.type.VectorType())); - code += "); }\n"; + if ((field.value.type.base_type == BASE_TYPE_VECTOR && + IsScalar(field.value.type.VectorType().base_type)) || + field.value.type.base_type == BASE_TYPE_STRING) { + switch (lang.language) { + case IDLOptions::kJava: + code += " public ByteBuffer "; + code += MakeCamel(field.name, lang.first_camel_upper); + code += "AsByteBuffer() { return __vector_as_bytebuffer("; + code += NumToString(field.value.offset) + ", "; + code += NumToString(field.value.type.base_type == BASE_TYPE_STRING ? 1 : + InlineSize(field.value.type.VectorType())); + code += "); }\n"; + break; + case IDLOptions::kCSharp: + code += " public ArraySegment? Get"; + code += MakeCamel(field.name, lang.first_camel_upper); + code += "Bytes() { return __vector_as_arraysegment("; + code += NumToString(field.value.offset); + code += "); }\n"; + break; + default: + break; + } } // generate mutators for scalar fields or vectors of scalars diff --git a/tests/FlatBuffers.Test/Assert.cs b/tests/FlatBuffers.Test/Assert.cs index 1bcf95f28..83344cf3b 100644 --- a/tests/FlatBuffers.Test/Assert.cs +++ b/tests/FlatBuffers.Test/Assert.cs @@ -107,6 +107,14 @@ namespace FlatBuffers.Test } } + public static void IsFalse(bool value) + { + if (value) + { + throw new AssertFailedException(false, value); + } + } + public static void Throws(Action action) where T : Exception { var caught = false; diff --git a/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs index 53d74c395..43754c77f 100644 --- a/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs +++ b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs @@ -15,6 +15,7 @@ */ using System.IO; +using System.Text; using MyGame.Example; namespace FlatBuffers.Test @@ -184,6 +185,18 @@ namespace FlatBuffers.Test Assert.AreEqual("test2", monster.GetTestarrayofstring(1)); Assert.AreEqual(false, monster.Testbool); + + var nameBytes = monster.GetNameBytes().Value; + Assert.AreEqual("MyMonster", Encoding.UTF8.GetString(nameBytes.Array, nameBytes.Offset, nameBytes.Count)); + + if (0 == monster.TestarrayofboolsLength) + { + Assert.IsFalse(monster.GetTestarrayofboolsBytes().HasValue); + } + else + { + Assert.IsTrue(monster.GetTestarrayofboolsBytes().HasValue); + } } [FlatBuffersTestMethod] diff --git a/tests/MyGame/Example/Monster.cs b/tests/MyGame/Example/Monster.cs index c31bd7b56..b324a8d5c 100644 --- a/tests/MyGame/Example/Monster.cs +++ b/tests/MyGame/Example/Monster.cs @@ -3,6 +3,7 @@ namespace MyGame.Example { +using System; using FlatBuffers; /// an example documentation comment: monster object @@ -19,8 +20,10 @@ public sealed class Monster : Table { public short Hp { get { int o = __offset(8); return o != 0 ? bb.GetShort(o + bb_pos) : (short)100; } } public bool MutateHp(short hp) { int o = __offset(8); if (o != 0) { bb.PutShort(o + bb_pos, hp); return true; } else { return false; } } public string Name { get { int o = __offset(10); return o != 0 ? __string(o + bb_pos) : null; } } + public ArraySegment? GetNameBytes() { return __vector_as_arraysegment(10); } public byte GetInventory(int j) { int o = __offset(14); return o != 0 ? bb.Get(__vector(o) + j * 1) : (byte)0; } public int InventoryLength { get { int o = __offset(14); return o != 0 ? __vector_len(o) : 0; } } + public ArraySegment? GetInventoryBytes() { return __vector_as_arraysegment(14); } public bool MutateInventory(int j, byte inventory) { int o = __offset(14); if (o != 0) { bb.Put(__vector(o) + j * 1, inventory); return true; } else { return false; } } public Color Color { get { int o = __offset(16); return o != 0 ? (Color)bb.GetSbyte(o + bb_pos) : Color.Blue; } } public bool MutateColor(Color color) { int o = __offset(16); if (o != 0) { bb.PutSbyte(o + bb_pos, (sbyte)color); return true; } else { return false; } } @@ -41,6 +44,7 @@ public sealed class Monster : Table { public Monster GetEnemy(Monster obj) { int o = __offset(28); return o != 0 ? obj.__init(__indirect(o + bb_pos), bb) : null; } public byte GetTestnestedflatbuffer(int j) { int o = __offset(30); return o != 0 ? bb.Get(__vector(o) + j * 1) : (byte)0; } public int TestnestedflatbufferLength { get { int o = __offset(30); return o != 0 ? __vector_len(o) : 0; } } + public ArraySegment? GetTestnestedflatbufferBytes() { return __vector_as_arraysegment(30); } public bool MutateTestnestedflatbuffer(int j, byte testnestedflatbuffer) { int o = __offset(30); if (o != 0) { bb.Put(__vector(o) + j * 1, testnestedflatbuffer); return true; } else { return false; } } public Stat Testempty { get { return GetTestempty(new Stat()); } } public Stat GetTestempty(Stat obj) { int o = __offset(32); return o != 0 ? obj.__init(__indirect(o + bb_pos), bb) : null; } @@ -64,6 +68,7 @@ public sealed class Monster : Table { public bool MutateTesthashu64Fnv1a(ulong testhashu64_fnv1a) { int o = __offset(50); if (o != 0) { bb.PutUlong(o + bb_pos, testhashu64_fnv1a); return true; } else { return false; } } public bool GetTestarrayofbools(int j) { int o = __offset(52); return o != 0 ? 0!=bb.Get(__vector(o) + j * 1) : false; } public int TestarrayofboolsLength { get { int o = __offset(52); return o != 0 ? __vector_len(o) : 0; } } + public ArraySegment? GetTestarrayofboolsBytes() { return __vector_as_arraysegment(52); } public bool MutateTestarrayofbools(int j, bool testarrayofbools) { int o = __offset(52); if (o != 0) { bb.Put(__vector(o) + j * 1, (byte)(testarrayofbools ? 1 : 0)); return true; } else { return false; } } public static void StartMonster(FlatBufferBuilder builder) { builder.StartObject(25); } diff --git a/tests/MyGame/Example/Stat.cs b/tests/MyGame/Example/Stat.cs index 22a2e009a..2b3f5375f 100644 --- a/tests/MyGame/Example/Stat.cs +++ b/tests/MyGame/Example/Stat.cs @@ -3,6 +3,7 @@ namespace MyGame.Example { +using System; using FlatBuffers; public sealed class Stat : Table { @@ -11,6 +12,7 @@ public sealed class Stat : Table { public Stat __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; } public string Id { get { int o = __offset(4); return o != 0 ? __string(o + bb_pos) : null; } } + public ArraySegment? GetIdBytes() { return __vector_as_arraysegment(4); } public long Val { get { int o = __offset(6); return o != 0 ? bb.GetLong(o + bb_pos) : (long)0; } } public bool MutateVal(long val) { int o = __offset(6); if (o != 0) { bb.PutLong(o + bb_pos, val); return true; } else { return false; } } public ushort Count { get { int o = __offset(8); return o != 0 ? bb.GetUshort(o + bb_pos) : (ushort)0; } } diff --git a/tests/MyGame/Example/Test.cs b/tests/MyGame/Example/Test.cs index 182c26b7b..5bd2e58dd 100644 --- a/tests/MyGame/Example/Test.cs +++ b/tests/MyGame/Example/Test.cs @@ -3,6 +3,7 @@ namespace MyGame.Example { +using System; using FlatBuffers; public sealed class Test : Struct { diff --git a/tests/MyGame/Example/TestSimpleTableWithEnum.cs b/tests/MyGame/Example/TestSimpleTableWithEnum.cs index f21441d18..daaa0b97a 100644 --- a/tests/MyGame/Example/TestSimpleTableWithEnum.cs +++ b/tests/MyGame/Example/TestSimpleTableWithEnum.cs @@ -3,6 +3,7 @@ namespace MyGame.Example { +using System; using FlatBuffers; public sealed class TestSimpleTableWithEnum : Table { diff --git a/tests/MyGame/Example/Vec3.cs b/tests/MyGame/Example/Vec3.cs index 56b9b4759..e5fa2f674 100644 --- a/tests/MyGame/Example/Vec3.cs +++ b/tests/MyGame/Example/Vec3.cs @@ -3,6 +3,7 @@ namespace MyGame.Example { +using System; using FlatBuffers; public sealed class Vec3 : Struct {