C# support for directly reading and writting to memory other than byte[]. For exampl...
authorChristopher Cifra <christopher.cifra@live.com>
Thu, 23 Aug 2018 17:05:31 +0000 (12:05 -0500)
committerWouter van Oortmerssen <aardappel@gmail.com>
Thu, 23 Aug 2018 17:05:31 +0000 (10:05 -0700)
Public access to the backing buffer uses Span<T> instead of ArraySegment<T>.

Writing to the buffer now supports Span<T> in addition to T[].

To maintain backwards compatibility ENABLE_SPAN_T must be defined.

net/FlatBuffers/ByteBuffer.cs
net/FlatBuffers/FlatBufferBuilder.cs
net/FlatBuffers/FlatBuffers.csproj
net/FlatBuffers/Table.cs
src/idl_gen_general.cpp
tests/FlatBuffers.Test/FlatBuffersExampleTests.cs
tests/MyGame/Example/Monster.cs
tests/MyGame/Example/Stat.cs
tests/MyGame/Example/TypeAliases.cs
tests/union_vector/Movie.cs

index 7b170c1..cea7ca3 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-// There are 2 #defines that have an impact on performance of this ByteBuffer implementation
+// There are 3 #defines that have an impact on performance / features of this ByteBuffer implementation
 //
 //      UNSAFE_BYTEBUFFER 
 //          This will use unsafe code to manipulate the underlying byte array. This
 //          This will disable the bounds check asserts to the byte array. This can
 //          yield a small performance gain in normal code..
 //
+//      ENABLE_SPAN_T
+//          This will enable reading and writing blocks of memory with a Span<T> instead if just
+//          T[].  You can also enable writing directly to shared memory or other types of memory
+//          by providing a custom implementation of ByteBufferAllocator.
+//          ENABLE_SPAN_T also requires UNSAFE_BYTEBUFFER to be defined
+//
 // Using UNSAFE_BYTEBUFFER and BYTEBUFFER_NO_BOUNDS_CHECK together can yield a
 // performance gain of ~15% for some operations, however doing so is potentially 
 // dangerous. Do so at your own risk!
 using System;
 using System.Collections.Generic;
 using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using System.Text;
 
+#if ENABLE_SPAN_T && !UNSAFE_BYTEBUFFER
+#error ENABLE_SPAN_T requires UNSAFE_BYTEBUFFER to also be defined
+#endif
+
 namespace FlatBuffers
 {
+    public abstract class ByteBufferAllocator : IDisposable
+    {
+#if UNSAFE_BYTEBUFFER
+        public unsafe byte* Buffer
+        {
+            get;
+            protected set;
+        }
+#else
+        public byte[] Buffer
+        {
+            get;
+            protected set;
+        }
+#endif
+
+        public int Length
+        {
+            get;
+            protected set;
+        }
+
+        public abstract void Dispose();
+
+        public abstract void GrowFront(int newSize);
+
+#if !ENABLE_SPAN_T
+        public abstract byte[] ByteArray { get; }
+#endif
+    }
+
+    public class ByteArrayAllocator : ByteBufferAllocator
+    {
+        private byte[] _buffer;
+
+        public ByteArrayAllocator(byte[] buffer)
+        {
+            _buffer = buffer;
+            InitPointer();
+        }
+
+        public override void GrowFront(int newSize)
+        {
+            if ((Length & 0xC0000000) != 0)
+                throw new Exception(
+                    "ByteBuffer: cannot grow buffer beyond 2 gigabytes.");
+
+            if (newSize < Length)
+                throw new Exception("ByteBuffer: cannot truncate buffer.");
+
+            byte[] newBuffer = new byte[newSize];
+            System.Buffer.BlockCopy(_buffer, 0, newBuffer, newSize - Length, Length);
+            _buffer = newBuffer;
+            InitPointer();
+        }
+
+        public override void Dispose()
+        {
+            GC.SuppressFinalize(this);
+#if UNSAFE_BYTEBUFFER
+            if (_handle.IsAllocated)
+            {
+                _handle.Free();
+            }
+#endif
+        }
+
+#if !ENABLE_SPAN_T
+        public override byte[] ByteArray => _buffer;
+#endif
+
+#if UNSAFE_BYTEBUFFER
+        private GCHandle _handle;
+
+        ~ByteArrayAllocator()
+        {
+            if (_handle.IsAllocated)
+            {
+                _handle.Free();
+            }
+        }
+#endif
+
+        private void InitPointer()
+        {
+            Length = _buffer.Length;
+#if UNSAFE_BYTEBUFFER
+            if (_handle.IsAllocated)
+            {
+                _handle.Free();
+            }
+            _handle = GCHandle.Alloc(_buffer, GCHandleType.Pinned);
+            unsafe
+            {
+                Buffer = (byte*)_handle.AddrOfPinnedObject().ToPointer();
+            }
+#else
+            Buffer = _buffer;
+#endif
+        }
+    }
+
+
     /// <summary>
     /// Class to mimic Java's ByteBuffer which is used heavily in Flatbuffers.
     /// </summary>
-    public class ByteBuffer
+    public class ByteBuffer : IDisposable
     {
-        protected byte[] _buffer;
+        private ByteBufferAllocator _buffer;
         private int _pos;  // Must track start of the buffer.
 
-        public int Length { get { return _buffer.Length; } }
+        public ByteBuffer(ByteBufferAllocator allocator, int position)
+        {
+            _buffer = allocator;
+            _pos = position;
+        }
 
         public ByteBuffer(int size) : this(new byte[size]) { }
 
@@ -52,15 +171,25 @@ namespace FlatBuffers
 
         public ByteBuffer(byte[] buffer, int pos)
         {
-            _buffer = buffer;
+            _buffer = new ByteArrayAllocator(buffer);
             _pos = pos;
         }
 
+        public void Dispose()
+        {
+            if (_buffer != null)
+            {
+                _buffer.Dispose();
+            }
+        }
+
         public int Position {
             get { return _pos; }
             set { _pos = value; }
         }
 
+        public int Length { get { return _buffer.Length; } }
+
         public void Reset()
         {
             _pos = 0;
@@ -77,17 +206,7 @@ namespace FlatBuffers
         // the end of the new buffer.
         public void GrowFront(int newSize)
         {
-            if ((Length & 0xC0000000) != 0)
-                throw new Exception(
-                    "ByteBuffer: cannot grow buffer beyond 2 gigabytes.");
-
-            if (newSize < Length)
-                throw new Exception("ByteBuffer: cannot truncate buffer.");
-
-            byte[] newBuffer = new byte[newSize];
-            Buffer.BlockCopy(_buffer, 0, newBuffer, newSize - Length,
-                             Length);
-            _buffer = newBuffer;
+            _buffer.GrowFront(newSize);
         }
 
         public byte[] ToArray(int pos, int len)
@@ -145,16 +264,38 @@ namespace FlatBuffers
             return SizeOf<T>() * x.Length;
         }
 
+#if ENABLE_SPAN_T
+        public static int ArraySize<T>(Span<T> x)
+        {
+            return SizeOf<T>() * x.Length;
+        }
+#endif
+
         // Get a portion of the buffer casted into an array of type T, given
         // the buffer position and length.
+#if ENABLE_SPAN_T
         public T[] ToArray<T>(int pos, int len)
             where T: struct
         {
+            unsafe
+            {
+                AssertOffsetAndLength(pos, len);
+                T[] arr = new T[len];
+                var typed = MemoryMarshal.Cast<byte, T>(new Span<byte>(_buffer.Buffer + pos, _buffer.Length));
+                typed.Slice(0, arr.Length).CopyTo(arr);
+                return arr;
+            }
+        }
+#else
+        public T[] ToArray<T>(int pos, int len)
+            where T : struct
+        {
             AssertOffsetAndLength(pos, len);
             T[] arr = new T[len];
-            Buffer.BlockCopy(_buffer, pos, arr, 0, ArraySize(arr));
+            Buffer.BlockCopy(_buffer.ByteArray, pos, arr, 0, ArraySize(arr));
             return arr;
         }
+#endif
 
         public byte[] ToSizedArray()
         {
@@ -166,15 +307,25 @@ namespace FlatBuffers
             return ToArray<byte>(0, Length);
         }
 
+
+#if ENABLE_SPAN_T
+        public unsafe Span<byte> ToSpan(int pos, int len)
+        {
+            return new Span<byte>(_buffer.Buffer, _buffer.Length).Slice(pos, len);
+        }
+#else
         public ArraySegment<byte> ToArraySegment(int pos, int len)
         {
-            return new ArraySegment<byte>(_buffer, pos, len);
+            return new ArraySegment<byte>(_buffer.ByteArray, pos, len);
         }
+#endif
 
+#if !ENABLE_SPAN_T
         public MemoryStream ToMemoryStream(int pos, int len)
         {
-            return new MemoryStream(_buffer, pos, len);
+            return new MemoryStream(_buffer.ByteArray, pos, len);
         }
+#endif
 
 #if !UNSAFE_BYTEBUFFER
         // Pre-allocated helper arrays for convertion.
@@ -217,14 +368,14 @@ namespace FlatBuffers
             {
                 for (int i = 0; i < count; i++)
                 {
-                    _buffer[offset + i] = (byte)(data >> i * 8);
+                    _buffer.Buffer[offset + i] = (byte)(data >> i * 8);
                 }
             }
             else
             {
                 for (int i = 0; i < count; i++)
                 {
-                    _buffer[offset + count - 1 - i] = (byte)(data >> i * 8);
+                    _buffer.Buffer[offset + count - 1 - i] = (byte)(data >> i * 8);
                 }
             }
         }
@@ -237,14 +388,14 @@ namespace FlatBuffers
             {
                 for (int i = 0; i < count; i++)
                 {
-                  r |= (ulong)_buffer[offset + i] << i * 8;
+                  r |= (ulong)_buffer.Buffer[offset + i] << i * 8;
                 }
             }
             else
             {
               for (int i = 0; i < count; i++)
               {
-                r |= (ulong)_buffer[offset + count - 1 - i] << i * 8;
+                r |= (ulong)_buffer.Buffer[offset + count - 1 - i] << i * 8;
               }
             }
             return r;
@@ -253,30 +404,57 @@ namespace FlatBuffers
 
         private void AssertOffsetAndLength(int offset, int length)
         {
-            #if !BYTEBUFFER_NO_BOUNDS_CHECK
+#if !BYTEBUFFER_NO_BOUNDS_CHECK
             if (offset < 0 ||
                 offset > _buffer.Length - length)
                 throw new ArgumentOutOfRangeException();
-            #endif
+#endif
+        }
+
+#if UNSAFE_BYTEBUFFER
+
+        public unsafe void PutSbyte(int offset, sbyte value)
+        {
+            AssertOffsetAndLength(offset, sizeof(sbyte));
+            _buffer.Buffer[offset] = (byte)value;
+        }
+
+        public unsafe void PutByte(int offset, byte value)
+        {
+            AssertOffsetAndLength(offset, sizeof(byte));
+            _buffer.Buffer[offset] = value;
+        }
+
+        public unsafe void PutByte(int offset, byte value, int count)
+        {
+            AssertOffsetAndLength(offset, sizeof(byte) * count);
+            for (var i = 0; i < count; ++i)
+                _buffer.Buffer[offset + i] = value;
         }
 
+        // this method exists in order to conform with Java ByteBuffer standards
+        public void Put(int offset, byte value)
+        {
+            PutByte(offset, value);
+        }
+#else
         public void PutSbyte(int offset, sbyte value)
         {
             AssertOffsetAndLength(offset, sizeof(sbyte));
-            _buffer[offset] = (byte)value;
+            _buffer.Buffer[offset] = (byte)value;
         }
 
         public void PutByte(int offset, byte value)
         {
             AssertOffsetAndLength(offset, sizeof(byte));
-            _buffer[offset] = value;
+            _buffer.Buffer[offset] = value;
         }
 
         public void PutByte(int offset, byte value, int count)
         {
             AssertOffsetAndLength(offset, sizeof(byte) * count);
             for (var i = 0; i < count; ++i)
-                _buffer[offset + i] = value;
+                _buffer.Buffer[offset + i] = value;
         }
 
         // this method exists in order to conform with Java ByteBuffer standards
@@ -284,13 +462,25 @@ namespace FlatBuffers
         {
             PutByte(offset, value);
         }
+#endif
 
+#if ENABLE_SPAN_T
+        public unsafe void PutStringUTF8(int offset, string value)
+        {
+            AssertOffsetAndLength(offset, value.Length);
+            fixed (char* s = value)
+            {
+                Encoding.UTF8.GetBytes(s, value.Length, _buffer.Buffer + offset, Length - offset);
+            }
+        }
+#else
         public void PutStringUTF8(int offset, string value)
         {
             AssertOffsetAndLength(offset, value.Length);
             Encoding.UTF8.GetBytes(value, 0, value.Length,
-                _buffer, offset);
+                _buffer.ByteArray, offset);
         }
+#endif
 
 #if UNSAFE_BYTEBUFFER
         // Unsafe but more efficient versions of Put*.
@@ -302,12 +492,10 @@ namespace FlatBuffers
         public unsafe void PutUshort(int offset, ushort value)
         {
             AssertOffsetAndLength(offset, sizeof(ushort));
-            fixed (byte* ptr = _buffer)
-            {
-                *(ushort*)(ptr + offset) = BitConverter.IsLittleEndian
-                    ? value
-                    : ReverseBytes(value);
-            }
+            byte* ptr = _buffer.Buffer;
+            *(ushort*)(ptr + offset) = BitConverter.IsLittleEndian
+                ? value
+                : ReverseBytes(value);
         }
 
         public void PutInt(int offset, int value)
@@ -318,12 +506,10 @@ namespace FlatBuffers
         public unsafe void PutUint(int offset, uint value)
         {
             AssertOffsetAndLength(offset, sizeof(uint));
-            fixed (byte* ptr = _buffer)
-            {
-                *(uint*)(ptr + offset) = BitConverter.IsLittleEndian
-                    ? value
-                    : ReverseBytes(value);
-            }
+            byte* ptr = _buffer.Buffer;
+            *(uint*)(ptr + offset) = BitConverter.IsLittleEndian
+                ? value
+                : ReverseBytes(value);
         }
 
         public unsafe void PutLong(int offset, long value)
@@ -334,44 +520,38 @@ namespace FlatBuffers
         public unsafe void PutUlong(int offset, ulong value)
         {
             AssertOffsetAndLength(offset, sizeof(ulong));
-            fixed (byte* ptr = _buffer)
-            {
-                *(ulong*)(ptr + offset) = BitConverter.IsLittleEndian
-                    ? value
-                    : ReverseBytes(value);
-            }
+            byte* ptr = _buffer.Buffer;
+            *(ulong*)(ptr + offset) = BitConverter.IsLittleEndian
+                ? value
+                : ReverseBytes(value);
         }
 
         public unsafe void PutFloat(int offset, float value)
         {
             AssertOffsetAndLength(offset, sizeof(float));
-            fixed (byte* ptr = _buffer)
+            byte* ptr = _buffer.Buffer;
+            if (BitConverter.IsLittleEndian)
             {
-                if (BitConverter.IsLittleEndian)
-                {
-                    *(float*)(ptr + offset) = value;
-                }
-                else
-                {
-                    *(uint*)(ptr + offset) = ReverseBytes(*(uint*)(&value));
-                }
+                *(float*)(ptr + offset) = value;
+            }
+            else
+            {
+                *(uint*)(ptr + offset) = ReverseBytes(*(uint*)(&value));
             }
         }
 
         public unsafe void PutDouble(int offset, double value)
         {
             AssertOffsetAndLength(offset, sizeof(double));
-            fixed (byte* ptr = _buffer)
+            byte* ptr = _buffer.Buffer;
+            if (BitConverter.IsLittleEndian)
             {
-                if (BitConverter.IsLittleEndian)
-                {
-                    *(double*)(ptr + offset) = value;
+                *(double*)(ptr + offset) = value;
 
-                }
-                else
-                {
-                    *(ulong*)(ptr + offset) = ReverseBytes(*(ulong*)(ptr + offset));
-                }
+            }
+            else
+            {
+                *(ulong*)(ptr + offset) = ReverseBytes(*(ulong*)(ptr + offset));
             }
         }
 #else // !UNSAFE_BYTEBUFFER
@@ -430,74 +610,43 @@ namespace FlatBuffers
 
 #endif // UNSAFE_BYTEBUFFER
 
-        /// <summary>
-        /// Copies an array of type T into this buffer, ending at the given
-        /// offset into this buffer. The starting offset is calculated based on the length
-        /// of the array and is the value returned.
-        /// </summary>
-        /// <typeparam name="T">The type of the input data (must be a struct)</typeparam>
-        /// <param name="offset">The offset into this buffer where the copy will end</param>
-        /// <param name="x">The array to copy data from</param>
-        /// <returns>The 'start' location of this buffer now, after the copy completed</returns>
-        public int Put<T>(int offset, T[] x)
-            where T : struct
+#if UNSAFE_BYTEBUFFER
+        public unsafe sbyte GetSbyte(int index)
         {
-            if(x == null)
-            {
-                throw new ArgumentNullException("Cannot put a null array");
-            }
-
-            if(x.Length == 0)
-            {
-                throw new ArgumentException("Cannot put an empty array");
-            }
-
-            if(!IsSupportedType<T>())
-            {
-                throw new ArgumentException("Cannot put an array of type "
-                    + typeof(T) + " into this buffer");
-            }
-
-            if (BitConverter.IsLittleEndian)
-            {
-                int numBytes = ByteBuffer.ArraySize(x);
-                offset -= numBytes;
-                AssertOffsetAndLength(offset, numBytes);
-                // if we are LE, just do a block copy
-                Buffer.BlockCopy(x, 0, _buffer, offset, numBytes);
-            }
-            else
-            {
-                throw new NotImplementedException("Big Endian Support not implemented yet " +
-                    "for putting typed arrays");
-                // if we are BE, we have to swap each element by itself
-                //for(int i = x.Length - 1; i >= 0; i--)
-                //{
-                //  todo: low priority, but need to genericize the Put<T>() functions
-                //}
-            }
-            return offset;
+            AssertOffsetAndLength(index, sizeof(sbyte));
+            return (sbyte)_buffer.Buffer[index];
         }
 
-
-
-
+        public unsafe byte Get(int index)
+        {
+            AssertOffsetAndLength(index, sizeof(byte));
+            return _buffer.Buffer[index];
+        }
+#else
         public sbyte GetSbyte(int index)
         {
             AssertOffsetAndLength(index, sizeof(sbyte));
-            return (sbyte)_buffer[index];
+            return (sbyte)_buffer.Buffer[index];
         }
 
         public byte Get(int index)
         {
             AssertOffsetAndLength(index, sizeof(byte));
-            return _buffer[index];
+            return _buffer.Buffer[index];
         }
+#endif
 
+#if ENABLE_SPAN_T
+        public unsafe string GetStringUTF8(int startPos, int len)
+        {
+            return Encoding.UTF8.GetString(_buffer.Buffer + startPos, len);
+        }
+#else
         public string GetStringUTF8(int startPos, int len)
         {
-            return Encoding.UTF8.GetString(_buffer, startPos, len);
+            return Encoding.UTF8.GetString(_buffer.ByteArray, startPos, len);
         }
+#endif
 
 #if UNSAFE_BYTEBUFFER
         // Unsafe but more efficient versions of Get*.
@@ -509,7 +658,7 @@ namespace FlatBuffers
         public unsafe ushort GetUshort(int offset)
         {
             AssertOffsetAndLength(offset, sizeof(ushort));
-            fixed (byte* ptr = _buffer)
+            byte* ptr = _buffer.Buffer;
             {
                 return BitConverter.IsLittleEndian
                     ? *(ushort*)(ptr + offset)
@@ -525,7 +674,7 @@ namespace FlatBuffers
         public unsafe uint GetUint(int offset)
         {
             AssertOffsetAndLength(offset, sizeof(uint));
-            fixed (byte* ptr = _buffer)
+            byte* ptr = _buffer.Buffer;
             {
                 return BitConverter.IsLittleEndian
                     ? *(uint*)(ptr + offset)
@@ -541,7 +690,7 @@ namespace FlatBuffers
         public unsafe ulong GetUlong(int offset)
         {
             AssertOffsetAndLength(offset, sizeof(ulong));
-            fixed (byte* ptr = _buffer)
+            byte* ptr = _buffer.Buffer;
             {
                 return BitConverter.IsLittleEndian
                     ? *(ulong*)(ptr + offset)
@@ -552,7 +701,7 @@ namespace FlatBuffers
         public unsafe float GetFloat(int offset)
         {
             AssertOffsetAndLength(offset, sizeof(float));
-            fixed (byte* ptr = _buffer)
+            byte* ptr = _buffer.Buffer;
             {
                 if (BitConverter.IsLittleEndian)
                 {
@@ -569,7 +718,7 @@ namespace FlatBuffers
         public unsafe double GetDouble(int offset)
         {
             AssertOffsetAndLength(offset, sizeof(double));
-            fixed (byte* ptr = _buffer)
+            byte* ptr = _buffer.Buffer;
             {
                 if (BitConverter.IsLittleEndian)
                 {
@@ -631,5 +780,98 @@ namespace FlatBuffers
             return doublehelper[0];
         }
 #endif // UNSAFE_BYTEBUFFER
+
+        /// <summary>
+        /// Copies an array of type T into this buffer, ending at the given
+        /// offset into this buffer. The starting offset is calculated based on the length
+        /// of the array and is the value returned.
+        /// </summary>
+        /// <typeparam name="T">The type of the input data (must be a struct)</typeparam>
+        /// <param name="offset">The offset into this buffer where the copy will end</param>
+        /// <param name="x">The array to copy data from</param>
+        /// <returns>The 'start' location of this buffer now, after the copy completed</returns>
+        public int Put<T>(int offset, T[] x)
+            where T : struct
+        {
+            if (x == null)
+            {
+                throw new ArgumentNullException("Cannot put a null array");
+            }
+
+            if (x.Length == 0)
+            {
+                throw new ArgumentException("Cannot put an empty array");
+            }
+
+            if (!IsSupportedType<T>())
+            {
+                throw new ArgumentException("Cannot put an array of type "
+                    + typeof(T) + " into this buffer");
+            }
+
+            if (BitConverter.IsLittleEndian)
+            {
+                int numBytes = ByteBuffer.ArraySize(x);
+                offset -= numBytes;
+                AssertOffsetAndLength(offset, numBytes);
+                // if we are LE, just do a block copy
+#if ENABLE_SPAN_T
+                unsafe
+                {
+                    MemoryMarshal.Cast<T, byte>(x).CopyTo(new Span<byte>(_buffer.Buffer, _buffer.Length).Slice(offset, numBytes));
+                }
+#else
+                Buffer.BlockCopy(x, 0, _buffer.ByteArray, offset, numBytes);
+#endif
+            }
+            else
+            {
+                throw new NotImplementedException("Big Endian Support not implemented yet " +
+                    "for putting typed arrays");
+                // if we are BE, we have to swap each element by itself
+                //for(int i = x.Length - 1; i >= 0; i--)
+                //{
+                //  todo: low priority, but need to genericize the Put<T>() functions
+                //}
+            }
+            return offset;
+        }
+
+#if ENABLE_SPAN_T
+        public unsafe int Put<T>(int offset, Span<T> x)
+            where T : struct
+        {
+            if (x.Length == 0)
+            {
+                throw new ArgumentException("Cannot put an empty array");
+            }
+
+            if (!IsSupportedType<T>())
+            {
+                throw new ArgumentException("Cannot put an array of type "
+                    + typeof(T) + " into this buffer");
+            }
+
+            if (BitConverter.IsLittleEndian)
+            {
+                int numBytes = ByteBuffer.ArraySize(x);
+                offset -= numBytes;
+                AssertOffsetAndLength(offset, numBytes);
+                // if we are LE, just do a block copy
+                MemoryMarshal.Cast<T, byte>(x).CopyTo(new Span<byte>(_buffer.Buffer, _buffer.Length).Slice(offset, numBytes));
+            }
+            else
+            {
+                throw new NotImplementedException("Big Endian Support not implemented yet " +
+                    "for putting typed arrays");
+                // if we are BE, we have to swap each element by itself
+                //for(int i = x.Length - 1; i >= 0; i--)
+                //{
+                //  todo: low priority, but need to genericize the Put<T>() functions
+                //}
+            }
+            return offset;
+        }
+#endif
     }
 }
index 33bba96..93f72be 100644 (file)
@@ -63,6 +63,17 @@ namespace FlatBuffers
         }
 
         /// <summary>
+        /// Create a FlatBufferBuilder backed by the pased in ByteBuffer
+        /// </summary>
+        /// <param name="buffer">The ByteBuffer to write to</param>
+        public FlatBufferBuilder(ByteBuffer buffer)
+        {
+            _bb = buffer;
+            _space = buffer.Length;
+            buffer.Reset();
+        }
+
+        /// <summary>
         /// Reset the FlatBufferBuilder by purging all data that it holds.
         /// </summary>
         public void Clear()
@@ -191,6 +202,20 @@ namespace FlatBuffers
             _space = _bb.Put(_space, x);
         }
 
+#if ENABLE_SPAN_T
+        /// <summary>
+        /// Puts a span of type T into this builder at the 
+        /// current offset
+        /// </summary>
+        /// <typeparam name="T">The type of the input data </typeparam>
+        /// <param name="x">The span to copy data from</param>
+        public void Put<T>(Span<T> x)
+            where T : struct
+        {
+            _space = _bb.Put(_space, x);
+        }
+#endif
+
         public void PutDouble(double x)
         {
             _bb.PutDouble(_space -= sizeof(double), x);
@@ -288,6 +313,28 @@ namespace FlatBuffers
             Put(x);
         }
 
+#if ENABLE_SPAN_T
+        /// <summary>
+        /// Add a span of type T to the buffer (aligns the data and grows if necessary).
+        /// </summary>
+        /// <typeparam name="T">The type of the input data</typeparam>
+        /// <param name="x">The span to copy data from</param>
+        public void Add<T>(Span<T> x)
+            where T : struct
+        {
+            if (!ByteBuffer.IsSupportedType<T>())
+            {
+                throw new ArgumentException("Cannot add this Type array to the builder");
+            }
+
+            int size = ByteBuffer.SizeOf<T>();
+            // Need to prep on size (for data alignment) and then we pass the
+            // rest of the length (minus 1) as additional bytes
+            Prep(size, size * (x.Length - 1));
+            Put(x);
+        }
+#endif
+
         /// <summary>
         /// Add a `double` to the buffer (aligns the data and grows if necessary).
         /// </summary>
@@ -511,6 +558,27 @@ namespace FlatBuffers
             return new StringOffset(EndVector().Value);
         }
 
+
+#if ENABLE_SPAN_T
+        /// <summary>
+        /// Creates a string in the buffer from a Span containing
+        /// a UTF8 string.
+        /// </summary>
+        /// <param name="chars">the UTF8 string to add to the buffer</param>
+        /// <returns>
+        /// The offset in the buffer where the encoded string starts.
+        /// </returns>
+        public StringOffset CreateUTF8String(Span<byte> chars)
+        {
+            NotNested();
+            AddByte(0);
+            var utf8StringLen = chars.Length;
+            StartVector(1, utf8StringLen, 1);
+            _space = _bb.Put(_space, chars);
+            return new StringOffset(EndVector().Value);
+        }
+#endif
+
         /// @cond FLATBUFFERS_INTERNAL
         // Structs are stored inline, so nothing additional is being added.
         // `d` is always 0.
@@ -568,7 +636,7 @@ namespace FlatBuffers
                     break;
                 }
 
-            endLoop: { }
+                endLoop: { }
             }
 
             if (existingVtable != 0) {
index e8189aa..b0c9ad3 100644 (file)
@@ -10,4 +10,7 @@
     <PackageLicenseUrl>https://github.com/google/flatbuffers/blob/master/LICENSE.txt</PackageLicenseUrl>
   </PropertyGroup>
 
+  <ItemGroup>
+    <PackageReference Include="System.Memory" Version="4.5.1" />
+  </ItemGroup>
 </Project>
\ No newline at end of file
index 07db5f4..cc7f3c1 100644 (file)
@@ -78,6 +78,23 @@ namespace FlatBuffers
             return offset + bb.GetInt(offset) + sizeof(int);  // data starts after the length
         }
 
+#if ENABLE_SPAN_T
+        // Get the data of a vector whoses offset is stored at "offset" in this object as an
+        // Spant&lt;byte&gt;. If the vector is not present in the ByteBuffer,
+        // then an empty span will be returned.
+        public Span<byte> __vector_as_span(int offset)
+        {
+            var o = this.__offset(offset);
+            if (0 == o)
+            {
+                return new Span<byte>();
+            }
+
+            var pos = this.__vector(o);
+            var len = this.__vector_len(o);
+            return bb.ToSpan(pos, len);
+        }
+#else
         // 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.
@@ -93,6 +110,7 @@ namespace FlatBuffers
             var len = this.__vector_len(o);
             return bb.ToArraySegment(pos, len);
         }
+#endif
 
         // Get the data of a vector whoses offset is stored at "offset" in this object as an
         // T[]. If the vector is not present in the ByteBuffer, then a null value will be
index eda0214..f3ed3e4 100644 (file)
@@ -1113,12 +1113,21 @@ class GeneralGenerator : public BaseGenerator {
             code += "); }\n";
             break;
           case IDLOptions::kCSharp:
+            code += "#if ENABLE_SPAN_T\n";
+            code += "  public Span<byte> Get";
+            code += MakeCamel(field.name, lang_.first_camel_upper);
+            code += "Bytes() { return ";
+            code += lang_.accessor_prefix + "__vector_as_span(";
+            code += NumToString(field.value.offset);
+            code += "); }\n";
+            code += "#else\n";
             code += "  public ArraySegment<byte>? Get";
             code += MakeCamel(field.name, lang_.first_camel_upper);
             code += "Bytes() { return ";
             code += lang_.accessor_prefix + "__vector_as_arraysegment(";
             code += NumToString(field.value.offset);
             code += "); }\n";
+            code += "#endif\n";
 
             // For direct blockcopying the data into a typed array
             code += "  public ";
index d438e0e..603de34 100644 (file)
@@ -110,14 +110,19 @@ namespace FlatBuffers.Test
                 Monster.FinishMonsterBuffer(fbb, mon);
             }
 
-
             // Dump to output directory so we can inspect later, if needed
+#if ENABLE_SPAN_T
+            var data = fbb.DataBuffer.ToSizedArray();
+            string filename = @"Resources/monsterdata_cstest" + (sizePrefix ? "_sp" : "") + ".mon";
+            File.WriteAllBytes(filename, data);
+#else
             using (var ms = fbb.DataBuffer.ToMemoryStream(fbb.DataBuffer.Position, fbb.Offset))
             {
                 var data = ms.ToArray();
                 string filename = @"Resources/monsterdata_cstest" + (sizePrefix ? "_sp" : "") + ".mon";
                 File.WriteAllBytes(filename, data);
             }
+#endif
 
             // Remove the size prefix if necessary for further testing
             ByteBuffer dataBuffer = fbb.DataBuffer;
@@ -243,6 +248,19 @@ namespace FlatBuffers.Test
 
             Assert.AreEqual(false, monster.Testbool);
 
+#if ENABLE_SPAN_T
+            var nameBytes = monster.GetNameBytes();
+            Assert.AreEqual("MyMonster", Encoding.UTF8.GetString(nameBytes.ToArray(), 0, nameBytes.Length));
+
+            if (0 == monster.TestarrayofboolsLength)
+            {
+                Assert.IsFalse(monster.GetTestarrayofboolsBytes().Length != 0);
+            }
+            else
+            {
+                Assert.IsTrue(monster.GetTestarrayofboolsBytes().Length == 0);
+            }
+#else
             var nameBytes = monster.GetNameBytes().Value;
             Assert.AreEqual("MyMonster", Encoding.UTF8.GetString(nameBytes.Array, nameBytes.Offset, nameBytes.Count));
 
@@ -254,6 +272,7 @@ namespace FlatBuffers.Test
             {
                 Assert.IsTrue(monster.GetTestarrayofboolsBytes().HasValue);
             }
+#endif
         }
 
         [FlatBuffersTestMethod]
index b3109e9..3df0d63 100644 (file)
@@ -25,11 +25,19 @@ public struct Monster : IFlatbufferObject
   public short Hp { get { int o = __p.__offset(8); return o != 0 ? __p.bb.GetShort(o + __p.bb_pos) : (short)100; } }
   public bool MutateHp(short hp) { int o = __p.__offset(8); if (o != 0) { __p.bb.PutShort(o + __p.bb_pos, hp); return true; } else { return false; } }
   public string Name { get { int o = __p.__offset(10); return o != 0 ? __p.__string(o + __p.bb_pos) : null; } }
+#if ENABLE_SPAN_T
+  public Span<byte> GetNameBytes() { return __p.__vector_as_span(10); }
+#else
   public ArraySegment<byte>? GetNameBytes() { return __p.__vector_as_arraysegment(10); }
+#endif
   public byte[] GetNameArray() { return __p.__vector_as_array<byte>(10); }
   public byte Inventory(int j) { int o = __p.__offset(14); return o != 0 ? __p.bb.Get(__p.__vector(o) + j * 1) : (byte)0; }
   public int InventoryLength { get { int o = __p.__offset(14); return o != 0 ? __p.__vector_len(o) : 0; } }
+#if ENABLE_SPAN_T
+  public Span<byte> GetInventoryBytes() { return __p.__vector_as_span(14); }
+#else
   public ArraySegment<byte>? GetInventoryBytes() { return __p.__vector_as_arraysegment(14); }
+#endif
   public byte[] GetInventoryArray() { return __p.__vector_as_array<byte>(14); }
   public bool MutateInventory(int j, byte inventory) { int o = __p.__offset(14); if (o != 0) { __p.bb.Put(__p.__vector(o) + j * 1, inventory); return true; } else { return false; } }
   public Color Color { get { int o = __p.__offset(16); return o != 0 ? (Color)__p.bb.GetSbyte(o + __p.bb_pos) : Color.Blue; } }
@@ -49,7 +57,11 @@ public struct Monster : IFlatbufferObject
   public Monster? Enemy { get { int o = __p.__offset(28); return o != 0 ? (Monster?)(new Monster()).__assign(__p.__indirect(o + __p.bb_pos), __p.bb) : null; } }
   public byte Testnestedflatbuffer(int j) { int o = __p.__offset(30); return o != 0 ? __p.bb.Get(__p.__vector(o) + j * 1) : (byte)0; }
   public int TestnestedflatbufferLength { get { int o = __p.__offset(30); return o != 0 ? __p.__vector_len(o) : 0; } }
+#if ENABLE_SPAN_T
+  public Span<byte> GetTestnestedflatbufferBytes() { return __p.__vector_as_span(30); }
+#else
   public ArraySegment<byte>? GetTestnestedflatbufferBytes() { return __p.__vector_as_arraysegment(30); }
+#endif
   public byte[] GetTestnestedflatbufferArray() { return __p.__vector_as_array<byte>(30); }
   public Monster? GetTestnestedflatbufferAsMonster() { int o = __p.__offset(30); return o != 0 ? (Monster?)(new Monster()).__assign(__p.__indirect(__p.__vector(o)), __p.bb) : null; }
   public bool MutateTestnestedflatbuffer(int j, byte testnestedflatbuffer) { int o = __p.__offset(30); if (o != 0) { __p.bb.Put(__p.__vector(o) + j * 1, testnestedflatbuffer); return true; } else { return false; } }
@@ -74,7 +86,11 @@ public struct Monster : IFlatbufferObject
   public bool MutateTesthashu64Fnv1a(ulong testhashu64_fnv1a) { int o = __p.__offset(50); if (o != 0) { __p.bb.PutUlong(o + __p.bb_pos, testhashu64_fnv1a); return true; } else { return false; } }
   public bool Testarrayofbools(int j) { int o = __p.__offset(52); return o != 0 ? 0!=__p.bb.Get(__p.__vector(o) + j * 1) : false; }
   public int TestarrayofboolsLength { get { int o = __p.__offset(52); return o != 0 ? __p.__vector_len(o) : 0; } }
+#if ENABLE_SPAN_T
+  public Span<byte> GetTestarrayofboolsBytes() { return __p.__vector_as_span(52); }
+#else
   public ArraySegment<byte>? GetTestarrayofboolsBytes() { return __p.__vector_as_arraysegment(52); }
+#endif
   public bool[] GetTestarrayofboolsArray() { return __p.__vector_as_array<bool>(52); }
   public bool MutateTestarrayofbools(int j, bool testarrayofbools) { int o = __p.__offset(52); if (o != 0) { __p.bb.Put(__p.__vector(o) + j * 1, (byte)(testarrayofbools ? 1 : 0)); return true; } else { return false; } }
   public float Testf { get { int o = __p.__offset(54); return o != 0 ? __p.bb.GetFloat(o + __p.bb_pos) : (float)3.14159f; } }
@@ -89,19 +105,31 @@ public struct Monster : IFlatbufferObject
   public int TestarrayofsortedstructLength { get { int o = __p.__offset(62); return o != 0 ? __p.__vector_len(o) : 0; } }
   public byte Flex(int j) { int o = __p.__offset(64); return o != 0 ? __p.bb.Get(__p.__vector(o) + j * 1) : (byte)0; }
   public int FlexLength { get { int o = __p.__offset(64); return o != 0 ? __p.__vector_len(o) : 0; } }
+#if ENABLE_SPAN_T
+  public Span<byte> GetFlexBytes() { return __p.__vector_as_span(64); }
+#else
   public ArraySegment<byte>? GetFlexBytes() { return __p.__vector_as_arraysegment(64); }
+#endif
   public byte[] GetFlexArray() { return __p.__vector_as_array<byte>(64); }
   public bool MutateFlex(int j, byte flex) { int o = __p.__offset(64); if (o != 0) { __p.bb.Put(__p.__vector(o) + j * 1, flex); return true; } else { return false; } }
   public Test? Test5(int j) { int o = __p.__offset(66); return o != 0 ? (Test?)(new Test()).__assign(__p.__vector(o) + j * 4, __p.bb) : null; }
   public int Test5Length { get { int o = __p.__offset(66); return o != 0 ? __p.__vector_len(o) : 0; } }
   public long VectorOfLongs(int j) { int o = __p.__offset(68); return o != 0 ? __p.bb.GetLong(__p.__vector(o) + j * 8) : (long)0; }
   public int VectorOfLongsLength { get { int o = __p.__offset(68); return o != 0 ? __p.__vector_len(o) : 0; } }
+#if ENABLE_SPAN_T
+  public Span<byte> GetVectorOfLongsBytes() { return __p.__vector_as_span(68); }
+#else
   public ArraySegment<byte>? GetVectorOfLongsBytes() { return __p.__vector_as_arraysegment(68); }
+#endif
   public long[] GetVectorOfLongsArray() { return __p.__vector_as_array<long>(68); }
   public bool MutateVectorOfLongs(int j, long vector_of_longs) { int o = __p.__offset(68); if (o != 0) { __p.bb.PutLong(__p.__vector(o) + j * 8, vector_of_longs); return true; } else { return false; } }
   public double VectorOfDoubles(int j) { int o = __p.__offset(70); return o != 0 ? __p.bb.GetDouble(__p.__vector(o) + j * 8) : (double)0; }
   public int VectorOfDoublesLength { get { int o = __p.__offset(70); return o != 0 ? __p.__vector_len(o) : 0; } }
+#if ENABLE_SPAN_T
+  public Span<byte> GetVectorOfDoublesBytes() { return __p.__vector_as_span(70); }
+#else
   public ArraySegment<byte>? GetVectorOfDoublesBytes() { return __p.__vector_as_arraysegment(70); }
+#endif
   public double[] GetVectorOfDoublesArray() { return __p.__vector_as_array<double>(70); }
   public bool MutateVectorOfDoubles(int j, double vector_of_doubles) { int o = __p.__offset(70); if (o != 0) { __p.bb.PutDouble(__p.__vector(o) + j * 8, vector_of_doubles); return true; } else { return false; } }
   public MyGame.InParentNamespace? ParentNamespaceTest { get { int o = __p.__offset(72); return o != 0 ? (MyGame.InParentNamespace?)(new MyGame.InParentNamespace()).__assign(__p.__indirect(o + __p.bb_pos), __p.bb) : null; } }
@@ -112,7 +140,11 @@ public struct Monster : IFlatbufferObject
   public bool MutateSingleWeakReference(ulong single_weak_reference) { int o = __p.__offset(76); if (o != 0) { __p.bb.PutUlong(o + __p.bb_pos, single_weak_reference); return true; } else { return false; } }
   public ulong VectorOfWeakReferences(int j) { int o = __p.__offset(78); return o != 0 ? __p.bb.GetUlong(__p.__vector(o) + j * 8) : (ulong)0; }
   public int VectorOfWeakReferencesLength { get { int o = __p.__offset(78); return o != 0 ? __p.__vector_len(o) : 0; } }
+#if ENABLE_SPAN_T
+  public Span<byte> GetVectorOfWeakReferencesBytes() { return __p.__vector_as_span(78); }
+#else
   public ArraySegment<byte>? GetVectorOfWeakReferencesBytes() { return __p.__vector_as_arraysegment(78); }
+#endif
   public ulong[] GetVectorOfWeakReferencesArray() { return __p.__vector_as_array<ulong>(78); }
   public bool MutateVectorOfWeakReferences(int j, ulong vector_of_weak_references) { int o = __p.__offset(78); if (o != 0) { __p.bb.PutUlong(__p.__vector(o) + j * 8, vector_of_weak_references); return true; } else { return false; } }
   public Referrable? VectorOfStrongReferrables(int j) { int o = __p.__offset(80); return o != 0 ? (Referrable?)(new Referrable()).__assign(__p.__indirect(__p.__vector(o) + j * 4), __p.bb) : null; }
@@ -122,14 +154,22 @@ public struct Monster : IFlatbufferObject
   public bool MutateCoOwningReference(ulong co_owning_reference) { int o = __p.__offset(82); if (o != 0) { __p.bb.PutUlong(o + __p.bb_pos, co_owning_reference); return true; } else { return false; } }
   public ulong VectorOfCoOwningReferences(int j) { int o = __p.__offset(84); return o != 0 ? __p.bb.GetUlong(__p.__vector(o) + j * 8) : (ulong)0; }
   public int VectorOfCoOwningReferencesLength { get { int o = __p.__offset(84); return o != 0 ? __p.__vector_len(o) : 0; } }
+#if ENABLE_SPAN_T
+  public Span<byte> GetVectorOfCoOwningReferencesBytes() { return __p.__vector_as_span(84); }
+#else
   public ArraySegment<byte>? GetVectorOfCoOwningReferencesBytes() { return __p.__vector_as_arraysegment(84); }
+#endif
   public ulong[] GetVectorOfCoOwningReferencesArray() { return __p.__vector_as_array<ulong>(84); }
   public bool MutateVectorOfCoOwningReferences(int j, ulong vector_of_co_owning_references) { int o = __p.__offset(84); if (o != 0) { __p.bb.PutUlong(__p.__vector(o) + j * 8, vector_of_co_owning_references); return true; } else { return false; } }
   public ulong NonOwningReference { get { int o = __p.__offset(86); return o != 0 ? __p.bb.GetUlong(o + __p.bb_pos) : (ulong)0; } }
   public bool MutateNonOwningReference(ulong non_owning_reference) { int o = __p.__offset(86); if (o != 0) { __p.bb.PutUlong(o + __p.bb_pos, non_owning_reference); return true; } else { return false; } }
   public ulong VectorOfNonOwningReferences(int j) { int o = __p.__offset(88); return o != 0 ? __p.bb.GetUlong(__p.__vector(o) + j * 8) : (ulong)0; }
   public int VectorOfNonOwningReferencesLength { get { int o = __p.__offset(88); return o != 0 ? __p.__vector_len(o) : 0; } }
+#if ENABLE_SPAN_T
+  public Span<byte> GetVectorOfNonOwningReferencesBytes() { return __p.__vector_as_span(88); }
+#else
   public ArraySegment<byte>? GetVectorOfNonOwningReferencesBytes() { return __p.__vector_as_arraysegment(88); }
+#endif
   public ulong[] GetVectorOfNonOwningReferencesArray() { return __p.__vector_as_array<ulong>(88); }
   public bool MutateVectorOfNonOwningReferences(int j, ulong vector_of_non_owning_references) { int o = __p.__offset(88); if (o != 0) { __p.bb.PutUlong(__p.__vector(o) + j * 8, vector_of_non_owning_references); return true; } else { return false; } }
 
index 9d78da5..fdd7e85 100644 (file)
@@ -18,7 +18,11 @@ public struct Stat : IFlatbufferObject
   public Stat __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; }
 
   public string Id { get { int o = __p.__offset(4); return o != 0 ? __p.__string(o + __p.bb_pos) : null; } }
+#if ENABLE_SPAN_T
+  public Span<byte> GetIdBytes() { return __p.__vector_as_span(4); }
+#else
   public ArraySegment<byte>? GetIdBytes() { return __p.__vector_as_arraysegment(4); }
+#endif
   public byte[] GetIdArray() { return __p.__vector_as_array<byte>(4); }
   public long Val { get { int o = __p.__offset(6); return o != 0 ? __p.bb.GetLong(o + __p.bb_pos) : (long)0; } }
   public bool MutateVal(long val) { int o = __p.__offset(6); if (o != 0) { __p.bb.PutLong(o + __p.bb_pos, val); return true; } else { return false; } }
index c4d8724..5ff1011 100644 (file)
@@ -39,12 +39,20 @@ public struct TypeAliases : IFlatbufferObject
   public bool MutateF64(double f64) { int o = __p.__offset(22); if (o != 0) { __p.bb.PutDouble(o + __p.bb_pos, f64); return true; } else { return false; } }
   public sbyte V8(int j) { int o = __p.__offset(24); return o != 0 ? __p.bb.GetSbyte(__p.__vector(o) + j * 1) : (sbyte)0; }
   public int V8Length { get { int o = __p.__offset(24); return o != 0 ? __p.__vector_len(o) : 0; } }
+#if ENABLE_SPAN_T
+  public Span<byte> GetV8Bytes() { return __p.__vector_as_span(24); }
+#else
   public ArraySegment<byte>? GetV8Bytes() { return __p.__vector_as_arraysegment(24); }
+#endif
   public sbyte[] GetV8Array() { return __p.__vector_as_array<sbyte>(24); }
   public bool MutateV8(int j, sbyte v8) { int o = __p.__offset(24); if (o != 0) { __p.bb.PutSbyte(__p.__vector(o) + j * 1, v8); return true; } else { return false; } }
   public double Vf64(int j) { int o = __p.__offset(26); return o != 0 ? __p.bb.GetDouble(__p.__vector(o) + j * 8) : (double)0; }
   public int Vf64Length { get { int o = __p.__offset(26); return o != 0 ? __p.__vector_len(o) : 0; } }
+#if ENABLE_SPAN_T
+  public Span<byte> GetVf64Bytes() { return __p.__vector_as_span(26); }
+#else
   public ArraySegment<byte>? GetVf64Bytes() { return __p.__vector_as_arraysegment(26); }
+#endif
   public double[] GetVf64Array() { return __p.__vector_as_array<double>(26); }
   public bool MutateVf64(int j, double vf64) { int o = __p.__offset(26); if (o != 0) { __p.bb.PutDouble(__p.__vector(o) + j * 8, vf64); return true; } else { return false; } }
 
index 30954aa..b229b4f 100644 (file)
@@ -19,7 +19,12 @@ public struct Movie : IFlatbufferObject
   public TTable? MainCharacter<TTable>() where TTable : struct, IFlatbufferObject { int o = __p.__offset(6); return o != 0 ? (TTable?)__p.__union<TTable>(o) : null; }
   public Character CharactersType(int j) { int o = __p.__offset(8); return o != 0 ? (Character)__p.bb.Get(__p.__vector(o) + j * 1) : (Character)0; }
   public int CharactersTypeLength { get { int o = __p.__offset(8); return o != 0 ? __p.__vector_len(o) : 0; } }
+#if ENABLE_SPAN_T
+  public Span<byte> GetCharactersTypeBytes() { return __p.__vector_as_span(8); }
+#else
   public ArraySegment<byte>? GetCharactersTypeBytes() { return __p.__vector_as_arraysegment(8); }
+#endif
+  public Character[] GetCharactersTypeArray() { return __p.__vector_as_array<Character>(8); }
   public TTable? Characters<TTable>(int j) where TTable : struct, IFlatbufferObject { int o = __p.__offset(10); return o != 0 ? (TTable?)__p.__union<TTable>(__p.__vector(o) + j * 4) : null; }
   public int CharactersLength { get { int o = __p.__offset(10); return o != 0 ? __p.__vector_len(o) : 0; } }
 
@@ -41,9 +46,11 @@ public struct Movie : IFlatbufferObject
   public static void AddMainCharacter(FlatBufferBuilder builder, int mainCharacterOffset) { builder.AddOffset(1, mainCharacterOffset, 0); }
   public static void AddCharactersType(FlatBufferBuilder builder, VectorOffset charactersTypeOffset) { builder.AddOffset(2, charactersTypeOffset.Value, 0); }
   public static VectorOffset CreateCharactersTypeVector(FlatBufferBuilder builder, Character[] data) { builder.StartVector(1, data.Length, 1); for (int i = data.Length - 1; i >= 0; i--) builder.AddByte((byte)data[i]); return builder.EndVector(); }
+  public static VectorOffset CreateCharactersTypeVectorBlock(FlatBufferBuilder builder, Character[] data) { builder.StartVector(1, data.Length, 1); builder.Add(data); return builder.EndVector(); }
   public static void StartCharactersTypeVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(1, numElems, 1); }
   public static void AddCharacters(FlatBufferBuilder builder, VectorOffset charactersOffset) { builder.AddOffset(3, charactersOffset.Value, 0); }
   public static VectorOffset CreateCharactersVector(FlatBufferBuilder builder, int[] data) { builder.StartVector(4, data.Length, 4); for (int i = data.Length - 1; i >= 0; i--) builder.AddOffset(data[i]); return builder.EndVector(); }
+  public static VectorOffset CreateCharactersVectorBlock(FlatBufferBuilder builder, int[] data) { builder.StartVector(4, data.Length, 4); builder.Add(data); return builder.EndVector(); }
   public static void StartCharactersVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(4, numElems, 4); }
   public static Offset<Movie> EndMovie(FlatBufferBuilder builder) {
     int o = builder.EndObject();