Commit 0cdacdfb authored by Eric Erhardt's avatar Eric Erhardt Committed by Wouter van Oortmerssen

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.
parent bb584420
...@@ -92,6 +92,9 @@ test_script: ...@@ -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; ...@@ -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 ...@@ -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 ...@@ -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
#if UNSAFE_BYTEBUFFER
private GCHandle _handle;
~ByteArrayAllocator()
{
if (_handle.IsAllocated)
{
_handle.Free();
}
}
#endif #endif
private void InitPointer() private void InitBuffer()
{ {
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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 _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 new Span<byte>(_buffer.Buffer, _buffer.Length).Slice(pos, 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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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
*(float*)(ptr + offset) = value; fixed (byte* ptr = _buffer.Buffer)
} #endif
else
{ {
*(uint*)(ptr + offset) = ReverseBytes(*(uint*)(&value)); if (BitConverter.IsLittleEndian)
{
*(float*)(ptr + offset) = 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
*(double*)(ptr + offset) = value; fixed (byte* ptr = _buffer.Buffer)
#endif
}
else
{ {
*(ulong*)(ptr + offset) = ReverseBytes(*(ulong*)(&value)); if (BitConverter.IsLittleEndian)
{
*(double*)(ptr + offset) = value;
}
else
{
*(ulong*)(ptr + offset) = ReverseBytes(*(ulong*)(&value));
}
} }
} }
#else // !UNSAFE_BYTEBUFFER #else // !UNSAFE_BYTEBUFFER
...@@ -613,17 +601,17 @@ namespace FlatBuffers ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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
{ {
......
/*
* 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();
}
}
}
}
<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>
/*
* 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);
}
}
}
\ No newline at end of file
...@@ -31,6 +31,10 @@ ...@@ -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">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment