Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c9651b7

Browse files
authoredApr 1, 2022
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
1 parent 26c3b3a commit c9651b7

11 files changed

+536
-14
lines changed
 

‎net/FlatBuffers/ByteBuffer.cs

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,18 @@ public static int ArraySize<T>(T[] x)
225225
return SizeOf<T>() * x.Length;
226226
}
227227

228+
/// <summary>
229+
/// Get the wire-size (in bytes) of an typed array segment, taking only the
230+
/// range specified by <paramref name="x"/> into account.
231+
/// </summary>
232+
/// <typeparam name="T">The type of the array</typeparam>
233+
/// <param name="x">The array segment to get the size of</param>
234+
/// <returns>The number of bytes the array segment takes on wire</returns>
235+
public static int ArraySize<T>(ArraySegment<T> x)
236+
{
237+
return SizeOf<T>() * x.Count;
238+
}
239+
228240
#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
229241
public static int ArraySize<T>(Span<T> x)
230242
{
@@ -869,7 +881,30 @@ public int Put<T>(int offset, T[] x)
869881
throw new ArgumentNullException("Cannot put a null array");
870882
}
871883

872-
if (x.Length == 0)
884+
return Put(offset, new ArraySegment<T>(x));
885+
}
886+
887+
/// <summary>
888+
/// Copies an array segment of type T into this buffer, ending at the
889+
/// given offset into this buffer. The starting offset is calculated
890+
/// based on the count of the array segment and is the value returned.
891+
/// </summary>
892+
/// <typeparam name="T">The type of the input data (must be a struct)
893+
/// </typeparam>
894+
/// <param name="offset">The offset into this buffer where the copy
895+
/// will end</param>
896+
/// <param name="x">The array segment to copy data from</param>
897+
/// <returns>The 'start' location of this buffer now, after the copy
898+
/// completed</returns>
899+
public int Put<T>(int offset, ArraySegment<T> x)
900+
where T : struct
901+
{
902+
if (x.Equals(default(ArraySegment<T>)))
903+
{
904+
throw new ArgumentNullException("Cannot put a uninitialized array segment");
905+
}
906+
907+
if (x.Count == 0)
873908
{
874909
throw new ArgumentException("Cannot put an empty array");
875910
}
@@ -889,7 +924,68 @@ public int Put<T>(int offset, T[] x)
889924
#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
890925
MemoryMarshal.Cast<T, byte>(x).CopyTo(_buffer.Span.Slice(offset, numBytes));
891926
#else
892-
Buffer.BlockCopy(x, 0, _buffer.Buffer, offset, numBytes);
927+
var srcOffset = ByteBuffer.SizeOf<T>() * x.Offset;
928+
Buffer.BlockCopy(x.Array, srcOffset, _buffer.Buffer, offset, numBytes);
929+
#endif
930+
}
931+
else
932+
{
933+
throw new NotImplementedException("Big Endian Support not implemented yet " +
934+
"for putting typed arrays");
935+
// if we are BE, we have to swap each element by itself
936+
//for(int i = x.Length - 1; i >= 0; i--)
937+
//{
938+
// todo: low priority, but need to genericize the Put<T>() functions
939+
//}
940+
}
941+
return offset;
942+
}
943+
944+
/// <summary>
945+
/// Copies an array segment of type T into this buffer, ending at the
946+
/// given offset into this buffer. The starting offset is calculated
947+
/// based on the count of the array segment and is the value returned.
948+
/// </summary>
949+
/// <typeparam name="T">The type of the input data (must be a struct)
950+
/// </typeparam>
951+
/// <param name="offset">The offset into this buffer where the copy
952+
/// will end</param>
953+
/// <param name="ptr">The pointer to copy data from</param>
954+
/// <param name="sizeInBytes">The number of bytes to copy</param>
955+
/// <returns>The 'start' location of this buffer now, after the copy
956+
/// completed</returns>
957+
public int Put<T>(int offset, IntPtr ptr, int sizeInBytes)
958+
where T : struct
959+
{
960+
if (ptr == IntPtr.Zero)
961+
{
962+
throw new ArgumentNullException("Cannot add a null pointer");
963+
}
964+
965+
if(sizeInBytes <= 0)
966+
{
967+
throw new ArgumentException("Cannot put an empty array");
968+
}
969+
970+
if (!IsSupportedType<T>())
971+
{
972+
throw new ArgumentException("Cannot put an array of type "
973+
+ typeof(T) + " into this buffer");
974+
}
975+
976+
if (BitConverter.IsLittleEndian)
977+
{
978+
offset -= sizeInBytes;
979+
AssertOffsetAndLength(offset, sizeInBytes);
980+
// if we are LE, just do a block copy
981+
#if ENABLE_SPAN_T && UNSAFE_BYTEBUFFER
982+
unsafe
983+
{
984+
var span = new Span<byte>(ptr.ToPointer(), sizeInBytes);
985+
span.CopyTo(_buffer.Span.Slice(offset, sizeInBytes));
986+
}
987+
#else
988+
Marshal.Copy(ptr, _buffer.Buffer, offset, sizeInBytes);
893989
#endif
894990
}
895991
else

‎net/FlatBuffers/FlatBufferBuilder.cs

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,31 @@ public void Put<T>(T[] x)
210210
_space = _bb.Put(_space, x);
211211
}
212212

213+
/// <summary>
214+
/// Puts an array of type T into this builder at the
215+
/// current offset
216+
/// </summary>
217+
/// <typeparam name="T">The type of the input data </typeparam>
218+
/// <param name="x">The array segment to copy data from</param>
219+
public void Put<T>(ArraySegment<T> x)
220+
where T : struct
221+
{
222+
_space = _bb.Put(_space, x);
223+
}
224+
225+
/// <summary>
226+
/// Puts data of type T into this builder at the
227+
/// current offset
228+
/// </summary>
229+
/// <typeparam name="T">The type of the input data </typeparam>
230+
/// <param name="ptr">The pointer to copy data from</param>
231+
/// <param name="sizeInBytes">The length of the data in bytes</param>
232+
public void Put<T>(IntPtr ptr, int sizeInBytes)
233+
where T : struct
234+
{
235+
_space = _bb.Put<T>(_space, ptr, sizeInBytes);
236+
}
237+
213238
#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
214239
/// <summary>
215240
/// Puts a span of type T into this builder at the
@@ -297,13 +322,24 @@ public void PutDouble(double x)
297322
/// <param name="x">The array to copy data from</param>
298323
public void Add<T>(T[] x)
299324
where T : struct
325+
{
326+
Add(new ArraySegment<T>(x));
327+
}
328+
329+
/// <summary>
330+
/// Add an array of type T to the buffer (aligns the data and grows if necessary).
331+
/// </summary>
332+
/// <typeparam name="T">The type of the input data</typeparam>
333+
/// <param name="x">The array segment to copy data from</param>
334+
public void Add<T>(ArraySegment<T> x)
335+
where T : struct
300336
{
301337
if (x == null)
302338
{
303339
throw new ArgumentNullException("Cannot add a null array");
304340
}
305341

306-
if( x.Length == 0)
342+
if( x.Count == 0)
307343
{
308344
// don't do anything if the array is empty
309345
return;
@@ -317,10 +353,52 @@ public void Add<T>(T[] x)
317353
int size = ByteBuffer.SizeOf<T>();
318354
// Need to prep on size (for data alignment) and then we pass the
319355
// rest of the length (minus 1) as additional bytes
320-
Prep(size, size * (x.Length - 1));
356+
Prep(size, size * (x.Count - 1));
321357
Put(x);
322358
}
323359

360+
/// <summary>
361+
/// Adds the data of type T pointed to by the given pointer to the buffer (aligns the data and grows if necessary).
362+
/// </summary>
363+
/// <typeparam name="T">The type of the input data</typeparam>
364+
/// <param name="ptr">The pointer to copy data from</param>
365+
/// <param name="sizeInBytes">The data size in bytes</param>
366+
public void Add<T>(IntPtr ptr, int sizeInBytes)
367+
where T : struct
368+
{
369+
if(sizeInBytes == 0)
370+
{
371+
// don't do anything if the array is empty
372+
return;
373+
}
374+
375+
if (ptr == IntPtr.Zero)
376+
{
377+
throw new ArgumentNullException("Cannot add a null pointer");
378+
}
379+
380+
if(sizeInBytes < 0)
381+
{
382+
throw new ArgumentOutOfRangeException("sizeInBytes", "sizeInBytes cannot be negative");
383+
}
384+
385+
if(!ByteBuffer.IsSupportedType<T>())
386+
{
387+
throw new ArgumentException("Cannot add this Type array to the builder");
388+
}
389+
390+
int size = ByteBuffer.SizeOf<T>();
391+
if((sizeInBytes % size) != 0)
392+
{
393+
throw new ArgumentException("The given size in bytes " + sizeInBytes + " doesn't match the element size of T ( " + size + ")", "sizeInBytes");
394+
}
395+
396+
// Need to prep on size (for data alignment) and then we pass the
397+
// rest of the length (minus 1) as additional bytes
398+
Prep(size, sizeInBytes - size);
399+
Put<T>(ptr, sizeInBytes);
400+
}
401+
324402
#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
325403
/// <summary>
326404
/// Add a span of type T to the buffer (aligns the data and grows if necessary).

‎src/idl_gen_csharp.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,7 @@ class CSharpGenerator : public BaseGenerator {
12401240
code += "); return ";
12411241
code += "builder.EndVector(); }\n";
12421242

1243+
// add Create...VectorBlock() overloads for T[], ArraySegment<T> and IntPtr
12431244
code += " public static VectorOffset ";
12441245
code += "Create";
12451246
code += Name(field);
@@ -1250,6 +1251,25 @@ class CSharpGenerator : public BaseGenerator {
12501251
code += ", data.Length, ";
12511252
code += NumToString(alignment);
12521253
code += "); builder.Add(data); return builder.EndVector(); }\n";
1254+
1255+
code += " public static VectorOffset ";
1256+
code += "Create";
1257+
code += Name(field);
1258+
code += "VectorBlock(FlatBufferBuilder builder, ";
1259+
code += "ArraySegment<" + GenTypeBasic(vector_type) + "> data) ";
1260+
code += "{ builder.StartVector(";
1261+
code += NumToString(elem_size);
1262+
code += ", data.Count, ";
1263+
code += NumToString(alignment);
1264+
code += "); builder.Add(data); return builder.EndVector(); }\n";
1265+
1266+
code += " public static VectorOffset ";
1267+
code += "Create";
1268+
code += Name(field);
1269+
code += "VectorBlock(FlatBufferBuilder builder, ";
1270+
code += "IntPtr dataPtr, int sizeInBytes) ";
1271+
code += "{ builder.StartVector(1, sizeInBytes, 1); ";
1272+
code += "builder.Add<" + GenTypeBasic(vector_type) + ">(dataPtr, sizeInBytes); return builder.EndVector(); }\n";
12531273
}
12541274
// Generate a method to start a vector, data to be added manually
12551275
// after.

0 commit comments

Comments
 (0)