Add Get Bytes Method Generator for C#
authorMichael Collins <michael.collins@neudesic.com>
Mon, 5 Oct 2015 02:57:39 +0000 (19:57 -0700)
committerMichael Collins <michael.collins@neudesic.com>
Fri, 4 Dec 2015 18:44:43 +0000 (11:44 -0700)
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<byte> 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<byte> 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
src/idl_gen_general.cpp
tests/FlatBuffers.Test/Assert.cs
tests/FlatBuffers.Test/FlatBuffersExampleTests.cs
tests/MyGame/Example/Monster.cs
tests/MyGame/Example/Stat.cs
tests/MyGame/Example/Test.cs
tests/MyGame/Example/TestSimpleTableWithEnum.cs
tests/MyGame/Example/Vec3.cs

index 09b0028..bd5e364 100644 (file)
@@ -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&lt;byte&gt;. If the vector is not present in the ByteBuffer,
+        // then a null value will be returned.
+        protected ArraySegment<byte>? __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<byte>(this.bb.Data, pos, len);
+        }
+
         // Initialize any Table-derived type to point to the union at the given offset.
         protected TTable __union<TTable>(TTable t, int offset) where TTable : Table
         {
index 88aa4da..15dd482 100644 (file)
@@ -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<byte>? 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
index 1bcf95f..83344cf 100644 (file)
@@ -107,6 +107,14 @@ namespace FlatBuffers.Test
             }
         }
 
+        public static void IsFalse(bool value)
+        {
+            if (value)
+            {
+                throw new AssertFailedException(false, value);
+            }
+        }
+
         public static void Throws<T>(Action action) where T : Exception
         {
             var caught = false;
index 53d74c3..43754c7 100644 (file)
@@ -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]
index c31bd7b..b324a8d 100644 (file)
@@ -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<byte>? 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<byte>? 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<byte>? 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<byte>? 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); }
index 22a2e00..2b3f537 100644 (file)
@@ -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<byte>? 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; } }
index 182c26b..5bd2e58 100644 (file)
@@ -3,6 +3,7 @@
 namespace MyGame.Example
 {
 
+using System;
 using FlatBuffers;
 
 public sealed class Test : Struct {
index f21441d..daaa0b9 100644 (file)
@@ -3,6 +3,7 @@
 namespace MyGame.Example
 {
 
+using System;
 using FlatBuffers;
 
 public sealed class TestSimpleTableWithEnum : Table {
index 56b9b47..e5fa2f6 100644 (file)
@@ -3,6 +3,7 @@
 namespace MyGame.Example
 {
 
+using System;
 using FlatBuffers;
 
 public sealed class Vec3 : Struct {