mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-22 11:38:51 +00:00
C# support for directly reading and writting to memory other than byte[]. For example, ByteBuffer can be initialized with a custom allocator which uses shared memory / memory mapped files. (#4886)
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.
This commit is contained in:
committed by
Wouter van Oortmerssen
parent
e1f48ad35a
commit
d0321df8cf
@@ -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
|
||||
@@ -24,6 +24,12 @@
|
||||
// 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!
|
||||
@@ -32,19 +38,132 @@
|
||||
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,32 @@ 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
|
||||
}
|
||||
|
||||
public void PutSbyte(int offset, sbyte value)
|
||||
#if UNSAFE_BYTEBUFFER
|
||||
|
||||
public unsafe 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)
|
||||
public unsafe 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)
|
||||
public unsafe 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 +437,50 @@ namespace FlatBuffers
|
||||
{
|
||||
PutByte(offset, value);
|
||||
}
|
||||
#else
|
||||
public void PutSbyte(int offset, sbyte value)
|
||||
{
|
||||
AssertOffsetAndLength(offset, sizeof(sbyte));
|
||||
_buffer.Buffer[offset] = (byte)value;
|
||||
}
|
||||
|
||||
public void PutByte(int offset, byte value)
|
||||
{
|
||||
AssertOffsetAndLength(offset, sizeof(byte));
|
||||
_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.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);
|
||||
}
|
||||
#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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,17 @@ namespace FlatBuffers
|
||||
_bb = new ByteBuffer(initialSize);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
@@ -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<byte>. 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<byte>. 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
|
||||
|
||||
Reference in New Issue
Block a user