mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-28 13:08:04 +00:00
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:
@@ -225,6 +225,18 @@ namespace FlatBuffers
|
||||
return SizeOf<T>() * x.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the wire-size (in bytes) of an typed array segment, taking only the
|
||||
/// range specified by <paramref name="x"/> into account.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the array</typeparam>
|
||||
/// <param name="x">The array segment to get the size of</param>
|
||||
/// <returns>The number of bytes the array segment takes on wire</returns>
|
||||
public static int ArraySize<T>(ArraySegment<T> x)
|
||||
{
|
||||
return SizeOf<T>() * x.Count;
|
||||
}
|
||||
|
||||
#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
|
||||
public static int ArraySize<T>(Span<T> x)
|
||||
{
|
||||
@@ -869,7 +881,30 @@ namespace FlatBuffers
|
||||
throw new ArgumentNullException("Cannot put a null array");
|
||||
}
|
||||
|
||||
if (x.Length == 0)
|
||||
return Put(offset, new ArraySegment<T>(x));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies an array segment of type T into this buffer, ending at the
|
||||
/// given offset into this buffer. The starting offset is calculated
|
||||
/// based on the count of the array segment 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 segment to copy data from</param>
|
||||
/// <returns>The 'start' location of this buffer now, after the copy
|
||||
/// completed</returns>
|
||||
public int Put<T>(int offset, ArraySegment<T> x)
|
||||
where T : struct
|
||||
{
|
||||
if (x.Equals(default(ArraySegment<T>)))
|
||||
{
|
||||
throw new ArgumentNullException("Cannot put a uninitialized array segment");
|
||||
}
|
||||
|
||||
if (x.Count == 0)
|
||||
{
|
||||
throw new ArgumentException("Cannot put an empty array");
|
||||
}
|
||||
@@ -889,7 +924,68 @@ namespace FlatBuffers
|
||||
#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
|
||||
MemoryMarshal.Cast<T, byte>(x).CopyTo(_buffer.Span.Slice(offset, numBytes));
|
||||
#else
|
||||
Buffer.BlockCopy(x, 0, _buffer.Buffer, offset, numBytes);
|
||||
var srcOffset = ByteBuffer.SizeOf<T>() * x.Offset;
|
||||
Buffer.BlockCopy(x.Array, srcOffset, _buffer.Buffer, 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies an array segment of type T into this buffer, ending at the
|
||||
/// given offset into this buffer. The starting offset is calculated
|
||||
/// based on the count of the array segment 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="ptr">The pointer to copy data from</param>
|
||||
/// <param name="sizeInBytes">The number of bytes to copy</param>
|
||||
/// <returns>The 'start' location of this buffer now, after the copy
|
||||
/// completed</returns>
|
||||
public int Put<T>(int offset, IntPtr ptr, int sizeInBytes)
|
||||
where T : struct
|
||||
{
|
||||
if (ptr == IntPtr.Zero)
|
||||
{
|
||||
throw new ArgumentNullException("Cannot add a null pointer");
|
||||
}
|
||||
|
||||
if(sizeInBytes <= 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)
|
||||
{
|
||||
offset -= sizeInBytes;
|
||||
AssertOffsetAndLength(offset, sizeInBytes);
|
||||
// if we are LE, just do a block copy
|
||||
#if ENABLE_SPAN_T && UNSAFE_BYTEBUFFER
|
||||
unsafe
|
||||
{
|
||||
var span = new Span<byte>(ptr.ToPointer(), sizeInBytes);
|
||||
span.CopyTo(_buffer.Span.Slice(offset, sizeInBytes));
|
||||
}
|
||||
#else
|
||||
Marshal.Copy(ptr, _buffer.Buffer, offset, sizeInBytes);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
|
||||
@@ -210,6 +210,31 @@ namespace FlatBuffers
|
||||
_space = _bb.Put(_space, x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Puts an array 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 array segment to copy data from</param>
|
||||
public void Put<T>(ArraySegment<T> x)
|
||||
where T : struct
|
||||
{
|
||||
_space = _bb.Put(_space, x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Puts data of type T into this builder at the
|
||||
/// current offset
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the input data </typeparam>
|
||||
/// <param name="ptr">The pointer to copy data from</param>
|
||||
/// <param name="sizeInBytes">The length of the data in bytes</param>
|
||||
public void Put<T>(IntPtr ptr, int sizeInBytes)
|
||||
where T : struct
|
||||
{
|
||||
_space = _bb.Put<T>(_space, ptr, sizeInBytes);
|
||||
}
|
||||
|
||||
#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
|
||||
/// <summary>
|
||||
/// Puts a span of type T into this builder at the
|
||||
@@ -297,13 +322,24 @@ namespace FlatBuffers
|
||||
/// <param name="x">The array to copy data from</param>
|
||||
public void Add<T>(T[] x)
|
||||
where T : struct
|
||||
{
|
||||
Add(new ArraySegment<T>(x));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an array 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 array segment to copy data from</param>
|
||||
public void Add<T>(ArraySegment<T> x)
|
||||
where T : struct
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
throw new ArgumentNullException("Cannot add a null array");
|
||||
}
|
||||
|
||||
if( x.Length == 0)
|
||||
if( x.Count == 0)
|
||||
{
|
||||
// don't do anything if the array is empty
|
||||
return;
|
||||
@@ -317,10 +353,52 @@ namespace FlatBuffers
|
||||
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));
|
||||
Prep(size, size * (x.Count - 1));
|
||||
Put(x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the data of type T pointed to by the given pointer to the buffer (aligns the data and grows if necessary).
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the input data</typeparam>
|
||||
/// <param name="ptr">The pointer to copy data from</param>
|
||||
/// <param name="sizeInBytes">The data size in bytes</param>
|
||||
public void Add<T>(IntPtr ptr, int sizeInBytes)
|
||||
where T : struct
|
||||
{
|
||||
if(sizeInBytes == 0)
|
||||
{
|
||||
// don't do anything if the array is empty
|
||||
return;
|
||||
}
|
||||
|
||||
if (ptr == IntPtr.Zero)
|
||||
{
|
||||
throw new ArgumentNullException("Cannot add a null pointer");
|
||||
}
|
||||
|
||||
if(sizeInBytes < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("sizeInBytes", "sizeInBytes cannot be negative");
|
||||
}
|
||||
|
||||
if(!ByteBuffer.IsSupportedType<T>())
|
||||
{
|
||||
throw new ArgumentException("Cannot add this Type array to the builder");
|
||||
}
|
||||
|
||||
int size = ByteBuffer.SizeOf<T>();
|
||||
if((sizeInBytes % size) != 0)
|
||||
{
|
||||
throw new ArgumentException("The given size in bytes " + sizeInBytes + " doesn't match the element size of T ( " + size + ")", "sizeInBytes");
|
||||
}
|
||||
|
||||
// Need to prep on size (for data alignment) and then we pass the
|
||||
// rest of the length (minus 1) as additional bytes
|
||||
Prep(size, sizeInBytes - size);
|
||||
Put<T>(ptr, sizeInBytes);
|
||||
}
|
||||
|
||||
#if ENABLE_SPAN_T && (UNSAFE_BYTEBUFFER || NETSTANDARD2_1)
|
||||
/// <summary>
|
||||
/// Add a span of type T to the buffer (aligns the data and grows if necessary).
|
||||
|
||||
Reference in New Issue
Block a user