From 1a185cb90dca309651dd513cfcbd08e339b68318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=A5=E6=9D=A1?= Date: Thu, 24 Sep 2020 05:16:00 +0900 Subject: [PATCH] [C#]Change to ENABLE_SPAN_T that doesn't require UNSAFE_BYTEBUFFER. (#6073) * ENABLE_SPAN_T doesn't require UNSAFE_BYTEBUFFER. Change to ENABLE_SPAN_T that doesn't require UNSAFE_BYTEBUFFER. * Selectable framework. Changed target framework to allow selection of 2.0 or 2.1 (or higher) * Added target framework version check. * Add core test project. * Added run on .Net Core. --- net/FlatBuffers/ByteBuffer.cs | 111 ++++++++----- net/FlatBuffers/FlatBuffers.Core.csproj | 2 +- .../FlatBuffers.Core.Test.csproj | 156 ++++++++++++++++++ tests/FlatBuffers.Test/NetTest.sh | 23 +++ 4 files changed, 249 insertions(+), 43 deletions(-) create mode 100644 tests/FlatBuffers.Test/FlatBuffers.Core.Test.csproj diff --git a/net/FlatBuffers/ByteBuffer.cs b/net/FlatBuffers/ByteBuffer.cs index a4664d09b66..0fb090ff7aa 100644 --- a/net/FlatBuffers/ByteBuffer.cs +++ b/net/FlatBuffers/ByteBuffer.cs @@ -42,19 +42,15 @@ using System.Runtime.InteropServices; using System.Text; -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 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 { -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 public abstract Span Span { get; } public abstract ReadOnlySpan ReadOnlySpan { get; } public abstract Memory Memory { get; } @@ -102,7 +98,7 @@ public override void GrowFront(int newSize) InitBuffer(); } -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 public override Span Span => _buffer; public override ReadOnlySpan ReadOnlySpan => _buffer; public override Memory Memory => _buffer; @@ -112,7 +108,7 @@ public override void GrowFront(int newSize) private void InitBuffer() { Length = _buffer.Length; -#if !ENABLE_SPAN_T +#if !ENABLE_SPAN_T || !NETSTANDARD2_1 Buffer = _buffer; #endif } @@ -224,7 +220,7 @@ public static int ArraySize(T[] x) return SizeOf() * x.Length; } -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 public static int ArraySize(Span x) { return SizeOf() * x.Length; @@ -233,7 +229,7 @@ public static int ArraySize(Span x) // Get a portion of the buffer casted into an array of type T, given // the buffer position and length. -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 public T[] ToArray(int pos, int len) where T : struct { @@ -261,7 +257,7 @@ public byte[] ToFullArray() return ToArray(0, Length); } -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 public ReadOnlyMemory ToReadOnlyMemory(int pos, int len) { return _buffer.ReadOnlyMemory.Slice(pos, len); @@ -324,7 +320,7 @@ static public ulong ReverseBytes(ulong input) ((input & 0xFF00000000000000UL) >> 56)); } -#if !UNSAFE_BYTEBUFFER +#if !UNSAFE_BYTEBUFFER && !ENABLE_SPAN_T // Helper functions for the safe (but slower) version. protected void WriteLittleEndian(int offset, int count, ulong data) { @@ -364,7 +360,46 @@ protected ulong ReadLittleEndian(int offset, int count) } return r; } -#endif // !UNSAFE_BYTEBUFFER +#elif ENABLE_SPAN_T && NETSTANDARD2_1 + protected void WriteLittleEndian(int offset, int count, ulong data) + { + if (BitConverter.IsLittleEndian) + { + for (int i = 0; i < count; i++) + { + _buffer.Span[offset + i] = (byte)(data >> i * 8); + } + } + else + { + for (int i = 0; i < count; i++) + { + _buffer.Span[offset + count - 1 - i] = (byte)(data >> i * 8); + } + } + } + + protected ulong ReadLittleEndian(int offset, int count) + { + AssertOffsetAndLength(offset, count); + ulong r = 0; + if (BitConverter.IsLittleEndian) + { + for (int i = 0; i < count; i++) + { + r |= (ulong)_buffer.Span[offset + i] << i * 8; + } + } + else + { + for (int i = 0; i < count; i++) + { + r |= (ulong)_buffer.Span[offset + count - 1 - i] << i * 8; + } + } + return r; + } +#endif // !UNSAFE_BYTEBUFFER && !ENABLE_SPAN_T private void AssertOffsetAndLength(int offset, int length) { @@ -375,7 +410,7 @@ private void AssertOffsetAndLength(int offset, int length) #endif } -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 public void PutSbyte(int offset, sbyte value) { @@ -423,17 +458,12 @@ public void Put(int offset, byte value) PutByte(offset, value); } -#if ENABLE_SPAN_T - public unsafe void PutStringUTF8(int offset, string value) +#if ENABLE_SPAN_T && NETSTANDARD2_1 + public void PutStringUTF8(int offset, string value) { AssertOffsetAndLength(offset, value.Length); - fixed (char* s = value) - { - fixed (byte* buffer = &MemoryMarshal.GetReference(_buffer.Span)) - { - Encoding.UTF8.GetBytes(s, value.Length, buffer + offset, Length - offset); - } - } + Encoding.UTF8.GetBytes(value.AsSpan().Slice(0, value.Length), + _buffer.Span.Slice(offset)); } #else public void PutStringUTF8(int offset, string value) @@ -454,7 +484,7 @@ public void PutShort(int offset, short value) public unsafe void PutUshort(int offset, ushort value) { AssertOffsetAndLength(offset, sizeof(ushort)); -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 Span span = _buffer.Span.Slice(offset); BinaryPrimitives.WriteUInt16LittleEndian(span, value); #else @@ -475,7 +505,7 @@ public void PutInt(int offset, int value) public unsafe void PutUint(int offset, uint value) { AssertOffsetAndLength(offset, sizeof(uint)); -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 Span span = _buffer.Span.Slice(offset); BinaryPrimitives.WriteUInt32LittleEndian(span, value); #else @@ -496,7 +526,7 @@ public unsafe void PutLong(int offset, long value) public unsafe void PutUlong(int offset, ulong value) { AssertOffsetAndLength(offset, sizeof(ulong)); -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 Span span = _buffer.Span.Slice(offset); BinaryPrimitives.WriteUInt64LittleEndian(span, value); #else @@ -512,7 +542,7 @@ public unsafe void PutUlong(int offset, ulong value) public unsafe void PutFloat(int offset, float value) { AssertOffsetAndLength(offset, sizeof(float)); -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.Span)) #else fixed (byte* ptr = _buffer.Buffer) @@ -532,7 +562,7 @@ public unsafe void PutFloat(int offset, float value) public unsafe void PutDouble(int offset, double value) { AssertOffsetAndLength(offset, sizeof(double)); -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.Span)) #else fixed (byte* ptr = _buffer.Buffer) @@ -605,7 +635,7 @@ public void PutDouble(int offset, double value) #endif // UNSAFE_BYTEBUFFER -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 public sbyte GetSbyte(int index) { AssertOffsetAndLength(index, sizeof(sbyte)); @@ -631,13 +661,10 @@ public byte Get(int index) } #endif -#if ENABLE_SPAN_T - public unsafe string GetStringUTF8(int startPos, int len) +#if ENABLE_SPAN_T && NETSTANDARD2_1 + public string GetStringUTF8(int startPos, int len) { - fixed (byte* buffer = &MemoryMarshal.GetReference(_buffer.ReadOnlySpan.Slice(startPos))) - { - return Encoding.UTF8.GetString(buffer, len); - } + return Encoding.UTF8.GetString(_buffer.Span.Slice(startPos, len)); } #else public string GetStringUTF8(int startPos, int len) @@ -656,7 +683,7 @@ public short GetShort(int offset) public unsafe ushort GetUshort(int offset) { AssertOffsetAndLength(offset, sizeof(ushort)); -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 ReadOnlySpan span = _buffer.ReadOnlySpan.Slice(offset); return BinaryPrimitives.ReadUInt16LittleEndian(span); #else @@ -677,7 +704,7 @@ public int GetInt(int offset) public unsafe uint GetUint(int offset) { AssertOffsetAndLength(offset, sizeof(uint)); -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 ReadOnlySpan span = _buffer.ReadOnlySpan.Slice(offset); return BinaryPrimitives.ReadUInt32LittleEndian(span); #else @@ -698,7 +725,7 @@ public long GetLong(int offset) public unsafe ulong GetUlong(int offset) { AssertOffsetAndLength(offset, sizeof(ulong)); -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 ReadOnlySpan span = _buffer.ReadOnlySpan.Slice(offset); return BinaryPrimitives.ReadUInt64LittleEndian(span); #else @@ -714,7 +741,7 @@ public unsafe ulong GetUlong(int offset) public unsafe float GetFloat(int offset) { AssertOffsetAndLength(offset, sizeof(float)); -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.ReadOnlySpan)) #else fixed (byte* ptr = _buffer.Buffer) @@ -735,7 +762,7 @@ public unsafe float GetFloat(int offset) public unsafe double GetDouble(int offset) { AssertOffsetAndLength(offset, sizeof(double)); -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 fixed (byte* ptr = &MemoryMarshal.GetReference(_buffer.ReadOnlySpan)) #else fixed (byte* ptr = _buffer.Buffer) @@ -834,7 +861,7 @@ public int Put(int offset, T[] x) offset -= numBytes; AssertOffsetAndLength(offset, numBytes); // if we are LE, just do a block copy -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 MemoryMarshal.Cast(x).CopyTo(_buffer.Span.Slice(offset, numBytes)); #else Buffer.BlockCopy(x, 0, _buffer.Buffer, offset, numBytes); @@ -853,7 +880,7 @@ public int Put(int offset, T[] x) return offset; } -#if ENABLE_SPAN_T +#if ENABLE_SPAN_T && NETSTANDARD2_1 public int Put(int offset, Span x) where T : struct { diff --git a/net/FlatBuffers/FlatBuffers.Core.csproj b/net/FlatBuffers/FlatBuffers.Core.csproj index 4285dd7fad4..040e7716aeb 100644 --- a/net/FlatBuffers/FlatBuffers.Core.csproj +++ b/net/FlatBuffers/FlatBuffers.Core.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + netstandard2.0;netstandard2.1 diff --git a/tests/FlatBuffers.Test/FlatBuffers.Core.Test.csproj b/tests/FlatBuffers.Test/FlatBuffers.Core.Test.csproj new file mode 100644 index 00000000000..e3cb19536e9 --- /dev/null +++ b/tests/FlatBuffers.Test/FlatBuffers.Core.Test.csproj @@ -0,0 +1,156 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + true + $(DefineConstants);UNSAFE_BYTEBUFFER + + + + true + $(DefineConstants);ENABLE_SPAN_T + + + + + FlatBuffers\ByteBuffer.cs + + + FlatBuffers\ByteBufferUtil.cs + + + FlatBuffers\IFlatbufferObject.cs + + + FlatBuffers\Offset.cs + + + FlatBuffers\FlatBufferBuilder.cs + + + FlatBuffers\FlatBufferConstants.cs + + + FlatBuffers\Struct.cs + + + FlatBuffers\Table.cs + + + MyGame\Example2\Monster.cs + + + MyGame\Example\Any.cs + + + MyGame\Example\AnyAmbiguousAliases.cs + + + MyGame\Example\AnyUniqueAliases.cs + + + MyGame\Example\Color.cs + + + MyGame\Example\Race.cs + + + MyGame\Example\Monster.cs + + + MyGame\Example\Referrable.cs + + + MyGame\Example\Stat.cs + + + MyGame\Example\Test.cs + + + MyGame\Example\TestSimpleTableWithEnum.cs + + + MyGame\Example\TypeAliases.cs + + + MyGame\Example\Vec3.cs + + + MyGame\Example\Ability.cs + + + MyGame\Example\ArrayTable.cs + + + MyGame\Example\ArrayStruct.cs + + + MyGame\Example\NestedStruct.cs + + + MyGame\Example\TestEnum.cs + + + MyGame\InParentNamespace.cs + + + NamespaceA\NamespaceB\EnumInNestedNS.cs + + + NamespaceA\NamespaceB\StructInNestedNS.cs + + + NamespaceA\NamespaceB\TableInNestedNS.cs + + + NamespaceA\TableInFirstNS.cs + + + union_vector\Attacker.cs + + + union_vector\BookReader.cs + + + union_vector\Character.cs + + + union_vector\Movie.cs + + + union_vector\Rapunzel.cs + + + + + + + + + + + + + + Resources\monsterdata_test.mon + PreserveNewest + + + Resources\monsterdata_test.json + PreserveNewest + + + + + + + + diff --git a/tests/FlatBuffers.Test/NetTest.sh b/tests/FlatBuffers.Test/NetTest.sh index 44e77e11d2b..f8adf293bb2 100755 --- a/tests/FlatBuffers.Test/NetTest.sh +++ b/tests/FlatBuffers.Test/NetTest.sh @@ -1,6 +1,7 @@ #!/bin/sh PROJ_FILE=FlatBuffers.Test.csproj +CORE_PROJ_FILE=FlatBuffers.Core.Test.csproj TEMP_DOTNET_DIR=.dotnet_tmp TEMP_BIN=.tmp @@ -29,3 +30,25 @@ rm -fr $TEMP_BIN rm FlatBuffers.Test.sln rm -rf obj + +$DOTNET new sln +$DOTNET sln add $CORE_PROJ_FILE +$DOTNET restore -r linux-x64 $CORE_PROJ_FILE + +# Testing C# on Linux using .Net Core. +msbuild -property:Configuration=Release,OutputPath=$TEMP_BIN -verbosity:minimal $CORE_PROJ_FILE +$TEMP_BIN/FlatBuffers.Core.Test.exe +rm -fr $TEMP_BIN + +# Repeat with unsafe versions +msbuild -property:Configuration=Release,UnsafeByteBuffer=true,OutputPath=$TEMP_BIN -verbosity:minimal $CORE_PROJ_FILE +$TEMP_BIN/FlatBuffers.Core.Test.exe +rm -fr $TEMP_BIN + +# Repeat with SpanT versions +msbuild -property:Configuration=Release,EnableSpanT=true,OutputPath=$TEMP_BIN -verbosity:minimal $CORE_PROJ_FILE +$TEMP_BIN/FlatBuffers.Core.Test.exe +rm -fr $TEMP_BIN + +rm FlatBuffers.Core.Test.sln +rm -rf obj