mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-29 06:40:02 +00:00
Remove byte* property in ByteBufferAllocator (#5191)
* Remove byte* property in ByteBufferAllocator. This allows consumers to read/write into native memory, but without having to always pin the managed `byte[]` when working with managed memory. This allows for users to not need to Dispose() ByteBuffers when they are using the default ByteArrayAllocator class. Instead, we use `Span<byte> GetSpan()` methods to get access to the underlying memory buffer. Fix #5181 * Add a set of benchmark tests. * Add ReadOnly spans. This allows consumers to use ReadOnlyMemory<byte> as the backing storage for ByteBuffers, which is useful in read-only scenarios. * Run tests using ENABLE_SPAN_T in appveyor. * Fix FlatBuffers.Test.csproj to work on older MSBuild versions. * Change the test script to test UNSAFE_BYTEBUFFER * Address PR feedback. Remove IDisposable from ByteBuffer. * Respond to PR feedback.
This commit is contained in:
committed by
Wouter van Oortmerssen
parent
bb58442054
commit
0cdacdfb35
@@ -92,6 +92,9 @@ test_script:
|
|||||||
- "cd FlatBuffers.Test"
|
- "cd FlatBuffers.Test"
|
||||||
- "msbuild.exe /property:Configuration=Release;OutputPath=tempcs /verbosity:minimal FlatBuffers.Test.csproj"
|
- "msbuild.exe /property:Configuration=Release;OutputPath=tempcs /verbosity:minimal FlatBuffers.Test.csproj"
|
||||||
- "tempcs\\FlatBuffers.Test.exe"
|
- "tempcs\\FlatBuffers.Test.exe"
|
||||||
|
# Run tests with UNSAFE_BYTEBUFFER
|
||||||
|
- "msbuild.exe /property:Configuration=Release;UnsafeByteBuffer=true;OutputPath=tempcsUnsafe /verbosity:minimal FlatBuffers.Test.csproj"
|
||||||
|
- "tempcsUnsafe\\FlatBuffers.Test.exe"
|
||||||
# TODO: add more languages.
|
# TODO: add more languages.
|
||||||
- "cd ..\\.."
|
- "cd ..\\.."
|
||||||
|
|
||||||
|
|||||||
@@ -42,20 +42,24 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
#if ENABLE_SPAN_T
|
||||||
|
using System.Buffers.Binary;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if ENABLE_SPAN_T && !UNSAFE_BYTEBUFFER
|
#if ENABLE_SPAN_T && !UNSAFE_BYTEBUFFER
|
||||||
#error ENABLE_SPAN_T requires UNSAFE_BYTEBUFFER to also be defined
|
#error ENABLE_SPAN_T requires UNSAFE_BYTEBUFFER to also be defined
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace FlatBuffers
|
namespace FlatBuffers
|
||||||
{
|
{
|
||||||
public abstract class ByteBufferAllocator : IDisposable
|
public abstract class ByteBufferAllocator
|
||||||
{
|
{
|
||||||
#if UNSAFE_BYTEBUFFER
|
#if ENABLE_SPAN_T
|
||||||
public unsafe byte* Buffer
|
public abstract Span<byte> Span { get; }
|
||||||
{
|
public abstract ReadOnlySpan<byte> ReadOnlySpan { get; }
|
||||||
get;
|
public abstract Memory<byte> Memory { get; }
|
||||||
protected set;
|
public abstract ReadOnlyMemory<byte> ReadOnlyMemory { get; }
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
public byte[] Buffer
|
public byte[] Buffer
|
||||||
{
|
{
|
||||||
@@ -70,23 +74,17 @@ namespace FlatBuffers
|
|||||||
protected set;
|
protected set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void Dispose();
|
|
||||||
|
|
||||||
public abstract void GrowFront(int newSize);
|
public abstract void GrowFront(int newSize);
|
||||||
|
|
||||||
#if !ENABLE_SPAN_T
|
|
||||||
public abstract byte[] ByteArray { get; }
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ByteArrayAllocator : ByteBufferAllocator
|
public sealed class ByteArrayAllocator : ByteBufferAllocator
|
||||||
{
|
{
|
||||||
private byte[] _buffer;
|
private byte[] _buffer;
|
||||||
|
|
||||||
public ByteArrayAllocator(byte[] buffer)
|
public ByteArrayAllocator(byte[] buffer)
|
||||||
{
|
{
|
||||||
_buffer = buffer;
|
_buffer = buffer;
|
||||||
InitPointer();
|
InitBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void GrowFront(int newSize)
|
public override void GrowFront(int newSize)
|
||||||
@@ -101,63 +99,29 @@ namespace FlatBuffers
|
|||||||
byte[] newBuffer = new byte[newSize];
|
byte[] newBuffer = new byte[newSize];
|
||||||
System.Buffer.BlockCopy(_buffer, 0, newBuffer, newSize - Length, Length);
|
System.Buffer.BlockCopy(_buffer, 0, newBuffer, newSize - Length, Length);
|
||||||
_buffer = newBuffer;
|
_buffer = newBuffer;
|
||||||
InitPointer();
|
InitBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Dispose()
|
#if ENABLE_SPAN_T
|
||||||
{
|
public override Span<byte> Span => _buffer;
|
||||||
GC.SuppressFinalize(this);
|
public override ReadOnlySpan<byte> ReadOnlySpan => _buffer;
|
||||||
#if UNSAFE_BYTEBUFFER
|
public override Memory<byte> Memory => _buffer;
|
||||||
if (_handle.IsAllocated)
|
public override ReadOnlyMemory<byte> ReadOnlyMemory => _buffer;
|
||||||
{
|
|
||||||
_handle.Free();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !ENABLE_SPAN_T
|
|
||||||
public override byte[] ByteArray
|
|
||||||
{
|
|
||||||
get { return _buffer; }
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if UNSAFE_BYTEBUFFER
|
private void InitBuffer()
|
||||||
private GCHandle _handle;
|
|
||||||
|
|
||||||
~ByteArrayAllocator()
|
|
||||||
{
|
|
||||||
if (_handle.IsAllocated)
|
|
||||||
{
|
|
||||||
_handle.Free();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private void InitPointer()
|
|
||||||
{
|
{
|
||||||
Length = _buffer.Length;
|
Length = _buffer.Length;
|
||||||
#if UNSAFE_BYTEBUFFER
|
#if !ENABLE_SPAN_T
|
||||||
if (_handle.IsAllocated)
|
|
||||||
{
|
|
||||||
_handle.Free();
|
|
||||||
}
|
|
||||||
_handle = GCHandle.Alloc(_buffer, GCHandleType.Pinned);
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
Buffer = (byte*)_handle.AddrOfPinnedObject().ToPointer();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
Buffer = _buffer;
|
Buffer = _buffer;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class to mimic Java's ByteBuffer which is used heavily in Flatbuffers.
|
/// Class to mimic Java's ByteBuffer which is used heavily in Flatbuffers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ByteBuffer : IDisposable
|
public class ByteBuffer
|
||||||
{
|
{
|
||||||
private ByteBufferAllocator _buffer;
|
private ByteBufferAllocator _buffer;
|
||||||
private int _pos; // Must track start of the buffer.
|
private int _pos; // Must track start of the buffer.
|
||||||
@@ -178,15 +142,8 @@ namespace FlatBuffers
|
|||||||
_pos = pos;
|
_pos = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public int Position
|
||||||
{
|
{
|
||||||
if (_buffer != null)
|
|
||||||
{
|
|
||||||
_buffer.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Position {
|
|
||||||
get { return _pos; }
|
get { return _pos; }
|
||||||
set { _pos = value; }
|
set { _pos = value; }
|
||||||
}
|
}
|
||||||
@@ -278,16 +235,10 @@ namespace FlatBuffers
|
|||||||
// the buffer position and length.
|
// the buffer position and length.
|
||||||
#if ENABLE_SPAN_T
|
#if ENABLE_SPAN_T
|
||||||
public T[] ToArray<T>(int pos, int len)
|
public T[] ToArray<T>(int pos, int len)
|
||||||
where T: struct
|
where T : struct
|
||||||
{
|
{
|
||||||
unsafe
|
AssertOffsetAndLength(pos, len);
|
||||||
{
|
return MemoryMarshal.Cast<byte, T>(_buffer.ReadOnlySpan.Slice(pos)).Slice(0, len).ToArray();
|
||||||
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
|
#else
|
||||||
public T[] ToArray<T>(int pos, int len)
|
public T[] ToArray<T>(int pos, int len)
|
||||||
@@ -295,7 +246,7 @@ namespace FlatBuffers
|
|||||||
{
|
{
|
||||||
AssertOffsetAndLength(pos, len);
|
AssertOffsetAndLength(pos, len);
|
||||||
T[] arr = new T[len];
|
T[] arr = new T[len];
|
||||||
Buffer.BlockCopy(_buffer.ByteArray, pos, arr, 0, ArraySize(arr));
|
Buffer.BlockCopy(_buffer.Buffer, pos, arr, 0, ArraySize(arr));
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -310,23 +261,30 @@ namespace FlatBuffers
|
|||||||
return ToArray<byte>(0, Length);
|
return ToArray<byte>(0, Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if ENABLE_SPAN_T
|
#if ENABLE_SPAN_T
|
||||||
public unsafe Span<byte> ToSpan(int pos, int len)
|
public ReadOnlyMemory<byte> ToReadOnlyMemory(int pos, int len)
|
||||||
{
|
{
|
||||||
return new Span<byte>(_buffer.Buffer, _buffer.Length).Slice(pos, len);
|
return _buffer.ReadOnlyMemory.Slice(pos, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Memory<byte> ToMemory(int pos, int len)
|
||||||
|
{
|
||||||
|
return _buffer.Memory.Slice(pos, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Span<byte> ToSpan(int pos, int len)
|
||||||
|
{
|
||||||
|
return _buffer.Span.Slice(pos, len);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
public ArraySegment<byte> ToArraySegment(int pos, int len)
|
public ArraySegment<byte> ToArraySegment(int pos, int len)
|
||||||
{
|
{
|
||||||
return new ArraySegment<byte>(_buffer.ByteArray, pos, len);
|
return new ArraySegment<byte>(_buffer.Buffer, pos, len);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !ENABLE_SPAN_T
|
|
||||||
public MemoryStream ToMemoryStream(int pos, int len)
|
public MemoryStream ToMemoryStream(int pos, int len)
|
||||||
{
|
{
|
||||||
return new MemoryStream(_buffer.ByteArray, pos, len);
|
return new MemoryStream(_buffer.Buffer, pos, len);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -391,15 +349,15 @@ namespace FlatBuffers
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
r |= (ulong)_buffer.Buffer[offset + i] << i * 8;
|
r |= (ulong)_buffer.Buffer[offset + i] << i * 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
r |= (ulong)_buffer.Buffer[offset + count - 1 - i] << i * 8;
|
r |= (ulong)_buffer.Buffer[offset + count - 1 - i] << i * 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@@ -414,31 +372,26 @@ namespace FlatBuffers
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if UNSAFE_BYTEBUFFER
|
#if ENABLE_SPAN_T
|
||||||
|
|
||||||
public unsafe void PutSbyte(int offset, sbyte value)
|
public void PutSbyte(int offset, sbyte value)
|
||||||
{
|
{
|
||||||
AssertOffsetAndLength(offset, sizeof(sbyte));
|
AssertOffsetAndLength(offset, sizeof(sbyte));
|
||||||
_buffer.Buffer[offset] = (byte)value;
|
_buffer.Span[offset] = (byte)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void PutByte(int offset, byte value)
|
public void PutByte(int offset, byte value)
|
||||||
{
|
{
|
||||||
AssertOffsetAndLength(offset, sizeof(byte));
|
AssertOffsetAndLength(offset, sizeof(byte));
|
||||||
_buffer.Buffer[offset] = value;
|
_buffer.Span[offset] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void PutByte(int offset, byte value, int count)
|
public void PutByte(int offset, byte value, int count)
|
||||||
{
|
{
|
||||||
AssertOffsetAndLength(offset, sizeof(byte) * count);
|
AssertOffsetAndLength(offset, sizeof(byte) * count);
|
||||||
for (var i = 0; i < count; ++i)
|
Span<byte> span = _buffer.Span.Slice(offset, count);
|
||||||
_buffer.Buffer[offset + i] = value;
|
for (var i = 0; i < span.Length; ++i)
|
||||||
}
|
span[i] = value;
|
||||||
|
|
||||||
// this method exists in order to conform with Java ByteBuffer standards
|
|
||||||
public void Put(int offset, byte value)
|
|
||||||
{
|
|
||||||
PutByte(offset, value);
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
public void PutSbyte(int offset, sbyte value)
|
public void PutSbyte(int offset, sbyte value)
|
||||||
@@ -459,13 +412,13 @@ namespace FlatBuffers
|
|||||||
for (var i = 0; i < count; ++i)
|
for (var i = 0; i < count; ++i)
|
||||||
_buffer.Buffer[offset + i] = value;
|
_buffer.Buffer[offset + i] = value;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// this method exists in order to conform with Java ByteBuffer standards
|
// this method exists in order to conform with Java ByteBuffer standards
|
||||||
public void Put(int offset, byte value)
|
public void Put(int offset, byte value)
|
||||||
{
|
{
|
||||||
PutByte(offset, value);
|
PutByte(offset, value);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ENABLE_SPAN_T
|
#if ENABLE_SPAN_T
|
||||||
public unsafe void PutStringUTF8(int offset, string value)
|
public unsafe void PutStringUTF8(int offset, string value)
|
||||||
@@ -473,7 +426,10 @@ namespace FlatBuffers
|
|||||||
AssertOffsetAndLength(offset, value.Length);
|
AssertOffsetAndLength(offset, value.Length);
|
||||||
fixed (char* s = value)
|
fixed (char* s = value)
|
||||||
{
|
{
|
||||||
Encoding.UTF8.GetBytes(s, value.Length, _buffer.Buffer + offset, Length - offset);
|
fixed (byte* buffer = &MemoryMarshal.GetReference(_buffer.Span))
|
||||||
|
{
|
||||||
|
Encoding.UTF8.GetBytes(s, value.Length, buffer + offset, Length - offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -481,7 +437,7 @@ namespace FlatBuffers
|
|||||||
{
|
{
|
||||||
AssertOffsetAndLength(offset, value.Length);
|
AssertOffsetAndLength(offset, value.Length);
|
||||||
Encoding.UTF8.GetBytes(value, 0, value.Length,
|
Encoding.UTF8.GetBytes(value, 0, value.Length,
|
||||||
_buffer.ByteArray, offset);
|
_buffer.Buffer, offset);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -495,10 +451,17 @@ namespace FlatBuffers
|
|||||||
public unsafe void PutUshort(int offset, ushort value)
|
public unsafe void PutUshort(int offset, ushort value)
|
||||||
{
|
{
|
||||||
AssertOffsetAndLength(offset, sizeof(ushort));
|
AssertOffsetAndLength(offset, sizeof(ushort));
|
||||||
byte* ptr = _buffer.Buffer;
|
#if ENABLE_SPAN_T
|
||||||
*(ushort*)(ptr + offset) = BitConverter.IsLittleEndian
|
Span<byte> span = _buffer.Span.Slice(offset);
|
||||||
? value
|
BinaryPrimitives.WriteUInt16LittleEndian(span, value);
|
||||||
: ReverseBytes(value);
|
#else
|
||||||
|
fixed (byte* ptr = _buffer.Buffer)
|
||||||
|
{
|
||||||
|
*(ushort*)(ptr + offset) = BitConverter.IsLittleEndian
|
||||||
|
? value
|
||||||
|
: ReverseBytes(value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PutInt(int offset, int value)
|
public void PutInt(int offset, int value)
|
||||||
@@ -509,10 +472,17 @@ namespace FlatBuffers
|
|||||||
public unsafe void PutUint(int offset, uint value)
|
public unsafe void PutUint(int offset, uint value)
|
||||||
{
|
{
|
||||||
AssertOffsetAndLength(offset, sizeof(uint));
|
AssertOffsetAndLength(offset, sizeof(uint));
|
||||||
byte* ptr = _buffer.Buffer;
|
#if ENABLE_SPAN_T
|
||||||
*(uint*)(ptr + offset) = BitConverter.IsLittleEndian
|
Span<byte> span = _buffer.Span.Slice(offset);
|
||||||
? value
|
BinaryPrimitives.WriteUInt32LittleEndian(span, value);
|
||||||
: ReverseBytes(value);
|
#else
|
||||||
|
fixed (byte* ptr = _buffer.Buffer)
|
||||||
|
{
|
||||||
|
*(uint*)(ptr + offset) = BitConverter.IsLittleEndian
|
||||||
|
? value
|
||||||
|
: ReverseBytes(value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void PutLong(int offset, long value)
|
public unsafe void PutLong(int offset, long value)
|
||||||
@@ -523,38 +493,56 @@ namespace FlatBuffers
|
|||||||
public unsafe void PutUlong(int offset, ulong value)
|
public unsafe void PutUlong(int offset, ulong value)
|
||||||
{
|
{
|
||||||
AssertOffsetAndLength(offset, sizeof(ulong));
|
AssertOffsetAndLength(offset, sizeof(ulong));
|
||||||
byte* ptr = _buffer.Buffer;
|
#if ENABLE_SPAN_T
|
||||||
*(ulong*)(ptr + offset) = BitConverter.IsLittleEndian
|
Span<byte> span = _buffer.Span.Slice(offset);
|
||||||
? value
|
BinaryPrimitives.WriteUInt64LittleEndian(span, value);
|
||||||
: ReverseBytes(value);
|
#else
|
||||||
|
fixed (byte* ptr = _buffer.Buffer)
|
||||||
|
{
|
||||||
|
*(ulong*)(ptr + offset) = BitConverter.IsLittleEndian
|
||||||
|
? value
|
||||||
|
: ReverseBytes(value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void PutFloat(int offset, float value)
|
public unsafe void PutFloat(int offset, float value)
|
||||||
{
|
{
|
||||||
AssertOffsetAndLength(offset, sizeof(float));
|
AssertOffsetAndLength(offset, sizeof(float));
|
||||||
byte* ptr = _buffer.Buffer;
|
#if ENABLE_SPAN_T
|
||||||
if (BitConverter.IsLittleEndian)
|
fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.Span))
|
||||||
|
#else
|
||||||
|
fixed (byte* ptr = _buffer.Buffer)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
*(float*)(ptr + offset) = value;
|
if (BitConverter.IsLittleEndian)
|
||||||
}
|
{
|
||||||
else
|
*(float*)(ptr + offset) = value;
|
||||||
{
|
}
|
||||||
*(uint*)(ptr + offset) = ReverseBytes(*(uint*)(&value));
|
else
|
||||||
|
{
|
||||||
|
*(uint*)(ptr + offset) = ReverseBytes(*(uint*)(&value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void PutDouble(int offset, double value)
|
public unsafe void PutDouble(int offset, double value)
|
||||||
{
|
{
|
||||||
AssertOffsetAndLength(offset, sizeof(double));
|
AssertOffsetAndLength(offset, sizeof(double));
|
||||||
byte* ptr = _buffer.Buffer;
|
#if ENABLE_SPAN_T
|
||||||
if (BitConverter.IsLittleEndian)
|
fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.Span))
|
||||||
|
#else
|
||||||
|
fixed (byte* ptr = _buffer.Buffer)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
*(double*)(ptr + offset) = value;
|
if (BitConverter.IsLittleEndian)
|
||||||
|
{
|
||||||
}
|
*(double*)(ptr + offset) = value;
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
*(ulong*)(ptr + offset) = ReverseBytes(*(ulong*)(&value));
|
{
|
||||||
|
*(ulong*)(ptr + offset) = ReverseBytes(*(ulong*)(&value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else // !UNSAFE_BYTEBUFFER
|
#else // !UNSAFE_BYTEBUFFER
|
||||||
@@ -613,17 +601,17 @@ namespace FlatBuffers
|
|||||||
|
|
||||||
#endif // UNSAFE_BYTEBUFFER
|
#endif // UNSAFE_BYTEBUFFER
|
||||||
|
|
||||||
#if UNSAFE_BYTEBUFFER
|
#if ENABLE_SPAN_T
|
||||||
public unsafe sbyte GetSbyte(int index)
|
public sbyte GetSbyte(int index)
|
||||||
{
|
{
|
||||||
AssertOffsetAndLength(index, sizeof(sbyte));
|
AssertOffsetAndLength(index, sizeof(sbyte));
|
||||||
return (sbyte)_buffer.Buffer[index];
|
return (sbyte)_buffer.ReadOnlySpan[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe byte Get(int index)
|
public byte Get(int index)
|
||||||
{
|
{
|
||||||
AssertOffsetAndLength(index, sizeof(byte));
|
AssertOffsetAndLength(index, sizeof(byte));
|
||||||
return _buffer.Buffer[index];
|
return _buffer.ReadOnlySpan[index];
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
public sbyte GetSbyte(int index)
|
public sbyte GetSbyte(int index)
|
||||||
@@ -642,12 +630,15 @@ namespace FlatBuffers
|
|||||||
#if ENABLE_SPAN_T
|
#if ENABLE_SPAN_T
|
||||||
public unsafe string GetStringUTF8(int startPos, int len)
|
public unsafe string GetStringUTF8(int startPos, int len)
|
||||||
{
|
{
|
||||||
return Encoding.UTF8.GetString(_buffer.Buffer + startPos, len);
|
fixed (byte* buffer = &MemoryMarshal.GetReference(_buffer.ReadOnlySpan.Slice(startPos)))
|
||||||
|
{
|
||||||
|
return Encoding.UTF8.GetString(buffer, len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
public string GetStringUTF8(int startPos, int len)
|
public string GetStringUTF8(int startPos, int len)
|
||||||
{
|
{
|
||||||
return Encoding.UTF8.GetString(_buffer.ByteArray, startPos, len);
|
return Encoding.UTF8.GetString(_buffer.Buffer, startPos, len);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -661,12 +652,17 @@ namespace FlatBuffers
|
|||||||
public unsafe ushort GetUshort(int offset)
|
public unsafe ushort GetUshort(int offset)
|
||||||
{
|
{
|
||||||
AssertOffsetAndLength(offset, sizeof(ushort));
|
AssertOffsetAndLength(offset, sizeof(ushort));
|
||||||
byte* ptr = _buffer.Buffer;
|
#if ENABLE_SPAN_T
|
||||||
|
ReadOnlySpan<byte> span = _buffer.ReadOnlySpan.Slice(offset);
|
||||||
|
return BinaryPrimitives.ReadUInt16LittleEndian(span);
|
||||||
|
#else
|
||||||
|
fixed (byte* ptr = _buffer.Buffer)
|
||||||
{
|
{
|
||||||
return BitConverter.IsLittleEndian
|
return BitConverter.IsLittleEndian
|
||||||
? *(ushort*)(ptr + offset)
|
? *(ushort*)(ptr + offset)
|
||||||
: ReverseBytes(*(ushort*)(ptr + offset));
|
: ReverseBytes(*(ushort*)(ptr + offset));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetInt(int offset)
|
public int GetInt(int offset)
|
||||||
@@ -677,12 +673,17 @@ namespace FlatBuffers
|
|||||||
public unsafe uint GetUint(int offset)
|
public unsafe uint GetUint(int offset)
|
||||||
{
|
{
|
||||||
AssertOffsetAndLength(offset, sizeof(uint));
|
AssertOffsetAndLength(offset, sizeof(uint));
|
||||||
byte* ptr = _buffer.Buffer;
|
#if ENABLE_SPAN_T
|
||||||
|
ReadOnlySpan<byte> span = _buffer.ReadOnlySpan.Slice(offset);
|
||||||
|
return BinaryPrimitives.ReadUInt32LittleEndian(span);
|
||||||
|
#else
|
||||||
|
fixed (byte* ptr = _buffer.Buffer)
|
||||||
{
|
{
|
||||||
return BitConverter.IsLittleEndian
|
return BitConverter.IsLittleEndian
|
||||||
? *(uint*)(ptr + offset)
|
? *(uint*)(ptr + offset)
|
||||||
: ReverseBytes(*(uint*)(ptr + offset));
|
: ReverseBytes(*(uint*)(ptr + offset));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetLong(int offset)
|
public long GetLong(int offset)
|
||||||
@@ -693,18 +694,27 @@ namespace FlatBuffers
|
|||||||
public unsafe ulong GetUlong(int offset)
|
public unsafe ulong GetUlong(int offset)
|
||||||
{
|
{
|
||||||
AssertOffsetAndLength(offset, sizeof(ulong));
|
AssertOffsetAndLength(offset, sizeof(ulong));
|
||||||
byte* ptr = _buffer.Buffer;
|
#if ENABLE_SPAN_T
|
||||||
|
ReadOnlySpan<byte> span = _buffer.ReadOnlySpan.Slice(offset);
|
||||||
|
return BinaryPrimitives.ReadUInt64LittleEndian(span);
|
||||||
|
#else
|
||||||
|
fixed (byte* ptr = _buffer.Buffer)
|
||||||
{
|
{
|
||||||
return BitConverter.IsLittleEndian
|
return BitConverter.IsLittleEndian
|
||||||
? *(ulong*)(ptr + offset)
|
? *(ulong*)(ptr + offset)
|
||||||
: ReverseBytes(*(ulong*)(ptr + offset));
|
: ReverseBytes(*(ulong*)(ptr + offset));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe float GetFloat(int offset)
|
public unsafe float GetFloat(int offset)
|
||||||
{
|
{
|
||||||
AssertOffsetAndLength(offset, sizeof(float));
|
AssertOffsetAndLength(offset, sizeof(float));
|
||||||
byte* ptr = _buffer.Buffer;
|
#if ENABLE_SPAN_T
|
||||||
|
fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.ReadOnlySpan))
|
||||||
|
#else
|
||||||
|
fixed (byte* ptr = _buffer.Buffer)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
if (BitConverter.IsLittleEndian)
|
if (BitConverter.IsLittleEndian)
|
||||||
{
|
{
|
||||||
@@ -721,7 +731,11 @@ namespace FlatBuffers
|
|||||||
public unsafe double GetDouble(int offset)
|
public unsafe double GetDouble(int offset)
|
||||||
{
|
{
|
||||||
AssertOffsetAndLength(offset, sizeof(double));
|
AssertOffsetAndLength(offset, sizeof(double));
|
||||||
byte* ptr = _buffer.Buffer;
|
#if ENABLE_SPAN_T
|
||||||
|
fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.ReadOnlySpan))
|
||||||
|
#else
|
||||||
|
fixed (byte* ptr = _buffer.Buffer)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
if (BitConverter.IsLittleEndian)
|
if (BitConverter.IsLittleEndian)
|
||||||
{
|
{
|
||||||
@@ -758,7 +772,7 @@ namespace FlatBuffers
|
|||||||
|
|
||||||
public long GetLong(int index)
|
public long GetLong(int index)
|
||||||
{
|
{
|
||||||
return (long)ReadLittleEndian(index, sizeof(long));
|
return (long)ReadLittleEndian(index, sizeof(long));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong GetUlong(int index)
|
public ulong GetUlong(int index)
|
||||||
@@ -819,12 +833,9 @@ namespace FlatBuffers
|
|||||||
AssertOffsetAndLength(offset, numBytes);
|
AssertOffsetAndLength(offset, numBytes);
|
||||||
// if we are LE, just do a block copy
|
// if we are LE, just do a block copy
|
||||||
#if ENABLE_SPAN_T
|
#if ENABLE_SPAN_T
|
||||||
unsafe
|
MemoryMarshal.Cast<T, byte>(x).CopyTo(_buffer.Span.Slice(offset, numBytes));
|
||||||
{
|
|
||||||
MemoryMarshal.Cast<T, byte>(x).CopyTo(new Span<byte>(_buffer.Buffer, _buffer.Length).Slice(offset, numBytes));
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
Buffer.BlockCopy(x, 0, _buffer.ByteArray, offset, numBytes);
|
Buffer.BlockCopy(x, 0, _buffer.Buffer, offset, numBytes);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -841,7 +852,7 @@ namespace FlatBuffers
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_SPAN_T
|
#if ENABLE_SPAN_T
|
||||||
public unsafe int Put<T>(int offset, Span<T> x)
|
public int Put<T>(int offset, Span<T> x)
|
||||||
where T : struct
|
where T : struct
|
||||||
{
|
{
|
||||||
if (x.Length == 0)
|
if (x.Length == 0)
|
||||||
@@ -861,7 +872,7 @@ namespace FlatBuffers
|
|||||||
offset -= numBytes;
|
offset -= numBytes;
|
||||||
AssertOffsetAndLength(offset, numBytes);
|
AssertOffsetAndLength(offset, numBytes);
|
||||||
// if we are LE, just do a block copy
|
// 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));
|
MemoryMarshal.Cast<T, byte>(x).CopyTo(_buffer.Span.Slice(offset, numBytes));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
101
tests/FlatBuffers.Benchmarks/FlatBufferBuilderBenchmark.cs
Normal file
101
tests/FlatBuffers.Benchmarks/FlatBufferBuilderBenchmark.cs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using MyGame.Example;
|
||||||
|
|
||||||
|
namespace FlatBuffers.Benchmarks
|
||||||
|
{
|
||||||
|
//[EtwProfiler] - needs elevated privileges
|
||||||
|
[MemoryDiagnoser]
|
||||||
|
public class FlatBufferBuilderBenchmark
|
||||||
|
{
|
||||||
|
private const int NumberOfRows = 10_000;
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void BuildNestedMonster()
|
||||||
|
{
|
||||||
|
const string nestedMonsterName = "NestedMonsterName";
|
||||||
|
const short nestedMonsterHp = 600;
|
||||||
|
const short nestedMonsterMana = 1024;
|
||||||
|
|
||||||
|
for (int i = 0; i < NumberOfRows; i++)
|
||||||
|
{
|
||||||
|
// Create nested buffer as a Monster type
|
||||||
|
var fbb1 = new FlatBufferBuilder(16);
|
||||||
|
var str1 = fbb1.CreateString(nestedMonsterName);
|
||||||
|
Monster.StartMonster(fbb1);
|
||||||
|
Monster.AddName(fbb1, str1);
|
||||||
|
Monster.AddHp(fbb1, nestedMonsterHp);
|
||||||
|
Monster.AddMana(fbb1, nestedMonsterMana);
|
||||||
|
var monster1 = Monster.EndMonster(fbb1);
|
||||||
|
Monster.FinishMonsterBuffer(fbb1, monster1);
|
||||||
|
var fbb1Bytes = fbb1.SizedByteArray();
|
||||||
|
fbb1 = null;
|
||||||
|
|
||||||
|
// Create a Monster which has the first buffer as a nested buffer
|
||||||
|
var fbb2 = new FlatBufferBuilder(16);
|
||||||
|
var str2 = fbb2.CreateString("My Monster");
|
||||||
|
var nestedBuffer = Monster.CreateTestnestedflatbufferVector(fbb2, fbb1Bytes);
|
||||||
|
Monster.StartMonster(fbb2);
|
||||||
|
Monster.AddName(fbb2, str2);
|
||||||
|
Monster.AddHp(fbb2, 50);
|
||||||
|
Monster.AddMana(fbb2, 32);
|
||||||
|
Monster.AddTestnestedflatbuffer(fbb2, nestedBuffer);
|
||||||
|
var monster = Monster.EndMonster(fbb2);
|
||||||
|
Monster.FinishMonsterBuffer(fbb2, monster);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void BuildMonster()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NumberOfRows; i++)
|
||||||
|
{
|
||||||
|
var builder = new FlatBufferBuilder(16);
|
||||||
|
var str1 = builder.CreateString("MonsterName");
|
||||||
|
Monster.StartMonster(builder);
|
||||||
|
Monster.AddName(builder, str1);
|
||||||
|
Monster.AddHp(builder, 600);
|
||||||
|
Monster.AddMana(builder, 1024);
|
||||||
|
Monster.AddColor(builder, Color.Blue);
|
||||||
|
Monster.AddTestbool(builder, true);
|
||||||
|
Monster.AddTestf(builder, 0.3f);
|
||||||
|
Monster.AddTestf2(builder, 0.2f);
|
||||||
|
Monster.AddTestf3(builder, 0.1f);
|
||||||
|
|
||||||
|
var monster1 = Monster.EndMonster(builder);
|
||||||
|
Monster.FinishMonsterBuffer(builder, monster1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void TestTables()
|
||||||
|
{
|
||||||
|
FlatBufferBuilder builder = new FlatBufferBuilder(1024 * 1024 * 32);
|
||||||
|
for (int x = 0; x < 500000; ++x)
|
||||||
|
{
|
||||||
|
var offset = builder.CreateString("T");
|
||||||
|
builder.StartObject(4);
|
||||||
|
builder.AddDouble(3.2);
|
||||||
|
builder.AddDouble(4.2);
|
||||||
|
builder.AddDouble(5.2);
|
||||||
|
builder.AddOffset(offset.Value);
|
||||||
|
builder.EndObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
tests/FlatBuffers.Benchmarks/FlatBuffers.Benchmarks.csproj
Normal file
21
tests/FlatBuffers.Benchmarks/FlatBuffers.Benchmarks.csproj
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<DefineConstants>$(DefineConstants);UNSAFE_BYTEBUFFER;BYTEBUFFER_NO_BOUNDS_CHECK;ENABLE_SPAN_T</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="BenchmarkDotNet" Version="0.11.3" />
|
||||||
|
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.11.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\..\net\FlatBuffers\*.cs" Link="FlatBuffers\%(FileName).cs" />
|
||||||
|
<Compile Include="..\MyGame\**\*.cs" Link="MyGame\Example\%(FileName).cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
30
tests/FlatBuffers.Benchmarks/Program.cs
Normal file
30
tests/FlatBuffers.Benchmarks/Program.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using BenchmarkDotNet.Running;
|
||||||
|
|
||||||
|
namespace FlatBuffers.Benchmarks
|
||||||
|
{
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
BenchmarkSwitcher
|
||||||
|
.FromAssembly(typeof(Program).Assembly)
|
||||||
|
.Run(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,10 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<StartupObject />
|
<StartupObject />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(UnsafeByteBuffer)' == 'true'">
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<DefineConstants>$(DefineConstants);UNSAFE_BYTEBUFFER</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core">
|
<Reference Include="System.Core">
|
||||||
|
|||||||
Reference in New Issue
Block a user