Add overloads for C# ByteBuffer/FlatBufferBuilder to allow adding vector blocks from ArraySegments or IntPtr (#7193)

* Add overloads to Add/Put for ArraySegment and IntPtr

In order to allow using code to reduce memory allocations, add overloads to ByteBuffer's and FlatBuffersBuilder's Put/Add methods that take ArraySegment<T> or IntPtr respectively.
Also, adaptions to the c# code generator in flatc to emit corresponding CreateVectorBlock() overloads

* Add missing files generated with generate_code.py

The previous commit changed the C# code generate, but didn't contain the updated generated test files.

* Incorporate review findings

(1) Adhere to 80 characters limit.
(2) In FlatBufferBuilder.Add(IntPtr,int), move zero length check topmost and add sanity check against negative input
This commit is contained in:
Jamie-Jameson
2022-04-01 22:35:07 +02:00
committed by GitHub
parent 26c3b3adab
commit c9651b7420
11 changed files with 536 additions and 14 deletions

View File

@@ -15,6 +15,7 @@
*/
using System;
using System.Runtime.InteropServices;
namespace FlatBuffers.Test
{
@@ -408,6 +409,60 @@ namespace FlatBuffers.Test
Assert.ArrayEqual(data, bbArray);
}
public void ByteBuffer_Put_ArraySegment_Helper<T>(ArraySegment<T> data, int typeSize)
where T : struct
{
// Create the Byte Buffer
var uut = new ByteBuffer(1024);
// Put the data into the buffer and make sure the offset is
// calculated correctly
int nOffset = uut.Put(1024, data);
Assert.AreEqual(1024 - typeSize * data.Count, nOffset);
// Get the full array back out and ensure they are equivalent
var bbArray = uut.ToArray<T>(nOffset, data.Count);
Assert.ArrayEqual(data.ToArray(), bbArray);
}
public unsafe void ByteBuffer_Put_IntPtr_Helper<T>(T[] data, int typeSize)
where T : struct
{
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
try
{
var dataPtr = handle.AddrOfPinnedObject();
var sizeInBytes = data.Length * typeSize;
// Create the Byte Buffer
var uut = new ByteBuffer(1024);
// Put the data into the buffer and make sure the offset is
// calculated correctly
int nOffset = uut.Put<T>(1024, dataPtr, sizeInBytes);
Assert.AreEqual(1024 - sizeInBytes, nOffset);
// Get the full array back out and ensure they are equivalent
var bbArray = uut.ToArray<T>(nOffset, data.Length);
Assert.ArrayEqual(data, bbArray);
}
finally
{
handle.Free();
}
}
public void ByteBuffer_Put_ArrayTypes_Helper<T>(T[] data, int typeSize)
where T : struct
{
ByteBuffer_Put_Array_Helper(data, typeSize);
var arraySegment = CreateArraySegment(data);
ByteBuffer_Put_ArraySegment_Helper(arraySegment, typeSize);
ByteBuffer_Put_IntPtr_Helper(data, typeSize);
}
[FlatBuffersTestMethod]
public void ByteBuffer_Put_Array_Float()
{
@@ -425,7 +480,7 @@ namespace FlatBuffers.Test
data[7] = 15.9994F;
data[8] = 18.9984F;
ByteBuffer_Put_Array_Helper(data, sizeof(float));
ByteBuffer_Put_ArrayTypes_Helper(data, sizeof(float));
}
[FlatBuffersTestMethod]
@@ -445,7 +500,7 @@ namespace FlatBuffers.Test
data[7] = 15.9994;
data[8] = 18.9984;
ByteBuffer_Put_Array_Helper(data, sizeof(double));
ByteBuffer_Put_ArrayTypes_Helper(data, sizeof(double));
}
[FlatBuffersTestMethod]
@@ -465,7 +520,7 @@ namespace FlatBuffers.Test
data[7] = 15;
data[8] = 18;
ByteBuffer_Put_Array_Helper(data, sizeof(int));
ByteBuffer_Put_ArrayTypes_Helper(data, sizeof(int));
}
@@ -486,7 +541,7 @@ namespace FlatBuffers.Test
data[7] = 15;
data[8] = 18;
ByteBuffer_Put_Array_Helper(data, sizeof(uint));
ByteBuffer_Put_ArrayTypes_Helper(data, sizeof(uint));
}
[FlatBuffersTestMethod]
@@ -506,7 +561,7 @@ namespace FlatBuffers.Test
data[7] = true;
data[8] = false;
ByteBuffer_Put_Array_Helper(data, sizeof(bool));
ByteBuffer_Put_ArrayTypes_Helper(data, sizeof(bool));
}
[FlatBuffersTestMethod]
@@ -526,7 +581,7 @@ namespace FlatBuffers.Test
data[7] = 15;
data[8] = 18;
ByteBuffer_Put_Array_Helper(data, sizeof(long));
ByteBuffer_Put_ArrayTypes_Helper(data, sizeof(long));
}
[FlatBuffersTestMethod]
@@ -546,7 +601,7 @@ namespace FlatBuffers.Test
data[7] = 15;
data[8] = 18;
ByteBuffer_Put_Array_Helper(data, sizeof(byte));
ByteBuffer_Put_ArrayTypes_Helper(data, sizeof(byte));
}
[FlatBuffersTestMethod]
@@ -566,7 +621,16 @@ namespace FlatBuffers.Test
data[7] = 15;
data[8] = 18;
ByteBuffer_Put_Array_Helper(data, sizeof(sbyte));
ByteBuffer_Put_ArrayTypes_Helper(data, sizeof(sbyte));
}
private static ArraySegment<T> CreateArraySegment<T>(T[] data)
where T : struct
{
const int arraySegmentPadding = 7;
var newData = new T[data.Length + 2 * arraySegmentPadding];
Array.Copy(data, 0, newData, arraySegmentPadding, data.Length);
return new ArraySegment<T>(newData, arraySegmentPadding, data.Length);
}
[FlatBuffersTestMethod]
@@ -578,10 +642,17 @@ namespace FlatBuffers.Test
// create a null array and try to put it into the buffer
float[] data = null;
Assert.Throws<ArgumentNullException>(() => uut.Put(1024, data));
ArraySegment<float> dataArraySegment = default;
Assert.Throws<ArgumentNullException>(() => uut.Put(1024, dataArraySegment));
IntPtr dataPtr = IntPtr.Zero;
int dataPtrLength = 100;
Assert.Throws<ArgumentNullException>(() => uut.Put<float>(1024, dataPtr, dataPtrLength));
}
[FlatBuffersTestMethod]
public void ByteBuffer_Put_Array_Empty_Throws()
public unsafe void ByteBuffer_Put_Array_Empty_Throws()
{
// Create the Byte Buffer
var uut = new ByteBuffer(1024);
@@ -589,6 +660,32 @@ namespace FlatBuffers.Test
// create an array of length == 0, and try to put it into the buffer
float[] data = new float[0];
Assert.Throws<ArgumentException>(() => uut.Put(1024, data));
var dataArraySegment = new ArraySegment<float>(new float[10], 5, 0);
Assert.Throws<ArgumentException>(() => uut.Put(1024, dataArraySegment));
fixed(float* floatPtr = data)
{
var dataPtr = (IntPtr)floatPtr;
var dataPtrLength = 0;
Assert.Throws<ArgumentException>(() => uut.Put<float>(1024, dataPtr, dataPtrLength));
}
}
[FlatBuffersTestMethod]
public unsafe void ByteBuffer_Put_IntPtr_NegativeSize_Throws()
{
// Create the Byte Buffer
var uut = new ByteBuffer(1024);
// create an array of length == 0, and try to put it into the buffer
float[] data = new float[10];
fixed(float* floatPtr = data)
{
var dataPtr = (IntPtr)floatPtr;
var dataPtrLength = -1;
Assert.Throws<ArgumentException>(() => uut.Put<float>(1024, dataPtr, dataPtrLength));
}
}
#pragma warning disable 0169
@@ -601,7 +698,7 @@ namespace FlatBuffers.Test
#pragma warning restore 0169
[FlatBuffersTestMethod]
public void ByteBuffer_Put_Array_IncorrectType_Throws()
public unsafe void ByteBuffer_Put_Array_IncorrectType_Throws()
{
// Create the Byte Buffer
var uut = new ByteBuffer(1024);
@@ -610,6 +707,16 @@ namespace FlatBuffers.Test
// able to be put into the buffer
var data = new dummyStruct[10];
Assert.Throws<ArgumentException>(() => uut.Put(1024, data));
var dataArraySegment = new ArraySegment<dummyStruct>(data);
Assert.Throws<ArgumentException>(() => uut.Put(1024, dataArraySegment));
fixed(dummyStruct* floatPtr = data)
{
var dataPtr = (IntPtr)floatPtr;
var dataPtrLength = data.Length * sizeof(dummyStruct);
Assert.Throws<ArgumentException>(() => uut.Put<dummyStruct>(1024, dataPtr, dataPtrLength));
}
}
[FlatBuffersTestMethod]