summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Erhardt <eric.erhardt@microsoft.com>2019-02-21 16:36:55 -0600
committerWouter van Oortmerssen <aardappel@gmail.com>2019-02-21 23:36:55 +0100
commit0cdacdfb354bec1832bb525e1394754debbf6195 (patch)
tree4f7525982deb7370681b52101fb162228536a693
parentbb58442054adbc502e54df0ce31516927da0b715 (diff)
downloadflatbuffers-0cdacdfb354bec1832bb525e1394754debbf6195.tar.gz
flatbuffers-0cdacdfb354bec1832bb525e1394754debbf6195.tar.bz2
flatbuffers-0cdacdfb354bec1832bb525e1394754debbf6195.zip
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.
-rw-r--r--appveyor.yml3
-rw-r--r--net/FlatBuffers/ByteBuffer.cs315
-rw-r--r--tests/FlatBuffers.Benchmarks/FlatBufferBuilderBenchmark.cs101
-rw-r--r--tests/FlatBuffers.Benchmarks/FlatBuffers.Benchmarks.csproj21
-rw-r--r--tests/FlatBuffers.Benchmarks/Program.cs30
-rw-r--r--tests/FlatBuffers.Test/FlatBuffers.Test.csproj4
6 files changed, 322 insertions, 152 deletions
diff --git a/appveyor.yml b/appveyor.yml
index 93ed5ece..75a63c87 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -92,6 +92,9 @@ test_script:
- "cd FlatBuffers.Test"
- "msbuild.exe /property:Configuration=Release;OutputPath=tempcs /verbosity:minimal FlatBuffers.Test.csproj"
- "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.
- "cd ..\\.."
diff --git a/net/FlatBuffers/ByteBuffer.cs b/net/FlatBuffers/ByteBuffer.cs
index 1b2e1afe..5e212ddd 100644
--- a/net/FlatBuffers/ByteBuffer.cs
+++ b/net/FlatBuffers/ByteBuffer.cs
@@ -42,20 +42,24 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
+#if ENABLE_SPAN_T
+using System.Buffers.Binary;
+#endif
+
#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
+ public abstract class ByteBufferAllocator
{
-#if UNSAFE_BYTEBUFFER
- public unsafe byte* Buffer
- {
- get;
- protected set;
- }
+#if ENABLE_SPAN_T
+ public abstract Span<byte> Span { get; }
+ public abstract ReadOnlySpan<byte> ReadOnlySpan { get; }
+ public abstract Memory<byte> Memory { get; }
+ public abstract ReadOnlyMemory<byte> ReadOnlyMemory { get; }
+
#else
public byte[] Buffer
{
@@ -70,23 +74,17 @@ namespace FlatBuffers
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
+ public sealed class ByteArrayAllocator : ByteBufferAllocator
{
private byte[] _buffer;
public ByteArrayAllocator(byte[] buffer)
{
_buffer = buffer;
- InitPointer();
+ InitBuffer();
}
public override void GrowFront(int newSize)
@@ -101,63 +99,29 @@ namespace FlatBuffers
byte[] newBuffer = new byte[newSize];
System.Buffer.BlockCopy(_buffer, 0, newBuffer, newSize - Length, Length);
_buffer = newBuffer;
- InitPointer();
+ InitBuffer();
}
- public override void Dispose()
- {
- GC.SuppressFinalize(this);
-#if UNSAFE_BYTEBUFFER
- if (_handle.IsAllocated)
- {
- _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();
- }
- }
+#if ENABLE_SPAN_T
+ public override Span<byte> Span => _buffer;
+ public override ReadOnlySpan<byte> ReadOnlySpan => _buffer;
+ public override Memory<byte> Memory => _buffer;
+ public override ReadOnlyMemory<byte> ReadOnlyMemory => _buffer;
#endif
- private void InitPointer()
+ private void InitBuffer()
{
Length = _buffer.Length;
-#if UNSAFE_BYTEBUFFER
- if (_handle.IsAllocated)
- {
- _handle.Free();
- }
- _handle = GCHandle.Alloc(_buffer, GCHandleType.Pinned);
- unsafe
- {
- Buffer = (byte*)_handle.AddrOfPinnedObject().ToPointer();
- }
-#else
+#if !ENABLE_SPAN_T
Buffer = _buffer;
#endif
}
}
-
/// <summary>
/// Class to mimic Java's ByteBuffer which is used heavily in Flatbuffers.
/// </summary>
- public class ByteBuffer : IDisposable
+ public class ByteBuffer
{
private ByteBufferAllocator _buffer;
private int _pos; // Must track start of the buffer.
@@ -178,15 +142,8 @@ namespace FlatBuffers
_pos = pos;
}
- public void Dispose()
+ public int Position
{
- if (_buffer != null)
- {
- _buffer.Dispose();
- }
- }
-
- public int Position {
get { return _pos; }
set { _pos = value; }
}
@@ -278,16 +235,10 @@ namespace FlatBuffers
// the buffer position and length.
#if ENABLE_SPAN_T
public T[] ToArray<T>(int pos, int len)
- where T: struct
+ 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;
- }
+ AssertOffsetAndLength(pos, len);
+ return MemoryMarshal.Cast<byte, T>(_buffer.ReadOnlySpan.Slice(pos)).Slice(0, len).ToArray();
}
#else
public T[] ToArray<T>(int pos, int len)
@@ -295,7 +246,7 @@ namespace FlatBuffers
{
AssertOffsetAndLength(pos, 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;
}
#endif
@@ -310,23 +261,30 @@ namespace FlatBuffers
return ToArray<byte>(0, Length);
}
-
#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
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)
{
- return new MemoryStream(_buffer.ByteArray, pos, len);
+ return new MemoryStream(_buffer.Buffer, pos, len);
}
#endif
@@ -391,15 +349,15 @@ namespace FlatBuffers
{
for (int i = 0; i < count; i++)
{
- r |= (ulong)_buffer.Buffer[offset + i] << i * 8;
+ r |= (ulong)_buffer.Buffer[offset + i] << i * 8;
}
}
else
{
- for (int i = 0; i < count; i++)
- {
- r |= (ulong)_buffer.Buffer[offset + count - 1 - i] << i * 8;
- }
+ for (int i = 0; i < count; i++)
+ {
+ r |= (ulong)_buffer.Buffer[offset + count - 1 - i] << i * 8;
+ }
}
return r;
}
@@ -414,31 +372,26 @@ namespace FlatBuffers
#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));
- _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));
- _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);
- 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);
+ Span<byte> span = _buffer.Span.Slice(offset, count);
+ for (var i = 0; i < span.Length; ++i)
+ span[i] = value;
}
#else
public void PutSbyte(int offset, sbyte value)
@@ -459,13 +412,13 @@ namespace FlatBuffers
for (var i = 0; i < count; ++i)
_buffer.Buffer[offset + i] = value;
}
+#endif
// 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)
@@ -473,7 +426,10 @@ namespace FlatBuffers
AssertOffsetAndLength(offset, value.Length);
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
@@ -481,7 +437,7 @@ namespace FlatBuffers
{
AssertOffsetAndLength(offset, value.Length);
Encoding.UTF8.GetBytes(value, 0, value.Length,
- _buffer.ByteArray, offset);
+ _buffer.Buffer, offset);
}
#endif
@@ -495,10 +451,17 @@ namespace FlatBuffers
public unsafe void PutUshort(int offset, ushort value)
{
AssertOffsetAndLength(offset, sizeof(ushort));
- byte* ptr = _buffer.Buffer;
- *(ushort*)(ptr + offset) = BitConverter.IsLittleEndian
- ? value
- : ReverseBytes(value);
+#if ENABLE_SPAN_T
+ Span<byte> span = _buffer.Span.Slice(offset);
+ BinaryPrimitives.WriteUInt16LittleEndian(span, value);
+#else
+ fixed (byte* ptr = _buffer.Buffer)
+ {
+ *(ushort*)(ptr + offset) = BitConverter.IsLittleEndian
+ ? value
+ : ReverseBytes(value);
+ }
+#endif
}
public void PutInt(int offset, int value)
@@ -509,10 +472,17 @@ namespace FlatBuffers
public unsafe void PutUint(int offset, uint value)
{
AssertOffsetAndLength(offset, sizeof(uint));
- byte* ptr = _buffer.Buffer;
- *(uint*)(ptr + offset) = BitConverter.IsLittleEndian
- ? value
- : ReverseBytes(value);
+#if ENABLE_SPAN_T
+ Span<byte> span = _buffer.Span.Slice(offset);
+ BinaryPrimitives.WriteUInt32LittleEndian(span, value);
+#else
+ fixed (byte* ptr = _buffer.Buffer)
+ {
+ *(uint*)(ptr + offset) = BitConverter.IsLittleEndian
+ ? value
+ : ReverseBytes(value);
+ }
+#endif
}
public unsafe void PutLong(int offset, long value)
@@ -523,38 +493,56 @@ namespace FlatBuffers
public unsafe void PutUlong(int offset, ulong value)
{
AssertOffsetAndLength(offset, sizeof(ulong));
- byte* ptr = _buffer.Buffer;
- *(ulong*)(ptr + offset) = BitConverter.IsLittleEndian
- ? value
- : ReverseBytes(value);
+#if ENABLE_SPAN_T
+ Span<byte> span = _buffer.Span.Slice(offset);
+ BinaryPrimitives.WriteUInt64LittleEndian(span, value);
+#else
+ fixed (byte* ptr = _buffer.Buffer)
+ {
+ *(ulong*)(ptr + offset) = BitConverter.IsLittleEndian
+ ? value
+ : ReverseBytes(value);
+ }
+#endif
}
public unsafe void PutFloat(int offset, float value)
{
AssertOffsetAndLength(offset, sizeof(float));
- byte* ptr = _buffer.Buffer;
- if (BitConverter.IsLittleEndian)
- {
- *(float*)(ptr + offset) = value;
- }
- else
+#if ENABLE_SPAN_T
+ fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.Span))
+#else
+ fixed (byte* ptr = _buffer.Buffer)
+#endif
{
- *(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)
{
AssertOffsetAndLength(offset, sizeof(double));
- byte* ptr = _buffer.Buffer;
- if (BitConverter.IsLittleEndian)
- {
- *(double*)(ptr + offset) = value;
-
- }
- else
+#if ENABLE_SPAN_T
+ fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.Span))
+#else
+ fixed (byte* ptr = _buffer.Buffer)
+#endif
{
- *(ulong*)(ptr + offset) = ReverseBytes(*(ulong*)(&value));
+ if (BitConverter.IsLittleEndian)
+ {
+ *(double*)(ptr + offset) = value;
+ }
+ else
+ {
+ *(ulong*)(ptr + offset) = ReverseBytes(*(ulong*)(&value));
+ }
}
}
#else // !UNSAFE_BYTEBUFFER
@@ -613,17 +601,17 @@ namespace FlatBuffers
#endif // UNSAFE_BYTEBUFFER
-#if UNSAFE_BYTEBUFFER
- public unsafe sbyte GetSbyte(int index)
+#if ENABLE_SPAN_T
+ public sbyte GetSbyte(int index)
{
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));
- return _buffer.Buffer[index];
+ return _buffer.ReadOnlySpan[index];
}
#else
public sbyte GetSbyte(int index)
@@ -642,12 +630,15 @@ namespace FlatBuffers
#if ENABLE_SPAN_T
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
public string GetStringUTF8(int startPos, int len)
{
- return Encoding.UTF8.GetString(_buffer.ByteArray, startPos, len);
+ return Encoding.UTF8.GetString(_buffer.Buffer, startPos, len);
}
#endif
@@ -661,12 +652,17 @@ namespace FlatBuffers
public unsafe ushort GetUshort(int offset)
{
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
? *(ushort*)(ptr + offset)
: ReverseBytes(*(ushort*)(ptr + offset));
}
+#endif
}
public int GetInt(int offset)
@@ -677,12 +673,17 @@ namespace FlatBuffers
public unsafe uint GetUint(int offset)
{
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
? *(uint*)(ptr + offset)
: ReverseBytes(*(uint*)(ptr + offset));
}
+#endif
}
public long GetLong(int offset)
@@ -693,18 +694,27 @@ namespace FlatBuffers
public unsafe ulong GetUlong(int offset)
{
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
? *(ulong*)(ptr + offset)
: ReverseBytes(*(ulong*)(ptr + offset));
}
+#endif
}
public unsafe float GetFloat(int offset)
{
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)
{
@@ -721,7 +731,11 @@ namespace FlatBuffers
public unsafe double GetDouble(int offset)
{
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)
{
@@ -758,7 +772,7 @@ namespace FlatBuffers
public long GetLong(int index)
{
- return (long)ReadLittleEndian(index, sizeof(long));
+ return (long)ReadLittleEndian(index, sizeof(long));
}
public ulong GetUlong(int index)
@@ -819,12 +833,9 @@ namespace FlatBuffers
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));
- }
+ MemoryMarshal.Cast<T, byte>(x).CopyTo(_buffer.Span.Slice(offset, numBytes));
#else
- Buffer.BlockCopy(x, 0, _buffer.ByteArray, offset, numBytes);
+ Buffer.BlockCopy(x, 0, _buffer.Buffer, offset, numBytes);
#endif
}
else
@@ -841,7 +852,7 @@ namespace FlatBuffers
}
#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
{
if (x.Length == 0)
@@ -861,7 +872,7 @@ namespace FlatBuffers
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));
+ MemoryMarshal.Cast<T, byte>(x).CopyTo(_buffer.Span.Slice(offset, numBytes));
}
else
{
diff --git a/tests/FlatBuffers.Benchmarks/FlatBufferBuilderBenchmark.cs b/tests/FlatBuffers.Benchmarks/FlatBufferBuilderBenchmark.cs
new file mode 100644
index 00000000..1df5ac31
--- /dev/null
+++ b/tests/FlatBuffers.Benchmarks/FlatBufferBuilderBenchmark.cs
@@ -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();
+ }
+ }
+ }
+}
diff --git a/tests/FlatBuffers.Benchmarks/FlatBuffers.Benchmarks.csproj b/tests/FlatBuffers.Benchmarks/FlatBuffers.Benchmarks.csproj
new file mode 100644
index 00000000..b900384b
--- /dev/null
+++ b/tests/FlatBuffers.Benchmarks/FlatBuffers.Benchmarks.csproj
@@ -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>
diff --git a/tests/FlatBuffers.Benchmarks/Program.cs b/tests/FlatBuffers.Benchmarks/Program.cs
new file mode 100644
index 00000000..9e63b4bf
--- /dev/null
+++ b/tests/FlatBuffers.Benchmarks/Program.cs
@@ -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);
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/FlatBuffers.Test/FlatBuffers.Test.csproj b/tests/FlatBuffers.Test/FlatBuffers.Test.csproj
index 7510f9dd..bbba231a 100644
--- a/tests/FlatBuffers.Test/FlatBuffers.Test.csproj
+++ b/tests/FlatBuffers.Test/FlatBuffers.Test.csproj
@@ -31,6 +31,10 @@
<PropertyGroup>
<StartupObject />
</PropertyGroup>
+ <PropertyGroup Condition="'$(UnsafeByteBuffer)' == 'true'">
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <DefineConstants>$(DefineConstants);UNSAFE_BYTEBUFFER</DefineConstants>
+ </PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core">