Skip to content

Commit 5223d74

Browse files
committed
In/outgoing Span<byte> support, linux net45 build
- Closes #587 - Add netfx.props and tweak sbxee-dll csproj to allow building sbe.dll for net45 on Linux using dotnet sdk following: https://andrewlock.net/building-net-framework-asp-net-core-apps-on-linux-using-mono-and-the-net-cli/ - Remove packages.config files from projects already converted to SDK style project since those files serve no purpose other than confusing potential contributors... - Intoroduce minimal Span<byte> GetBytes/SetBytes implementation for copying to/from Span<byte> to sbe.dll itself. - Change code generation for array types to use Span<byte> internally keeping the original functionality intact with no (hopefully!) visible user facing changes except for the addtitional Get/Set methods accepting Span<byte> being added to the mix. - Add release notes section to README.md and some more touch ups - Add sbe-tests.csproj netcoreapp2.1 test target
1 parent cd8dc3a commit 5223d74

File tree

8 files changed

+153
-64
lines changed

8 files changed

+153
-64
lines changed

csharp/README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,24 @@ git bash and shell scripts are used for scripting.
1313

1414
The C# code is built using Visual Studio Community 2017.
1515

16+
The code, samples, tests and nuget package can be built/run using the [dotnet core sdk](https://www.microsoft.com/net/download):
17+
18+
* On windows, having the legacy .NET Framework installed as part of the OS, only the dotnet sdk needs to be installed to allow for compiling the SDK style projects
19+
* On Mac/Linux, the Mono framework is also required for producing release nuget packages / .NET Framework compatible DLLs.
20+
21+
## Release Notes
22+
23+
### 0.1.8.1-beta-2
24+
25+
C# Span support has been added to the code generation, and a set of corresponding utilities added to sbe-dll `DirectBuffer`. It is now possible to copy to/from a `Span<byte>` where previously only `byte[]` types were supported. This introduces a dependency on the [`System.Memory`](https://www.nuget.org/packages/System.Memory/) nuget package both for sbe-dll and generated code produced by sbe-tool.
26+
27+
### 0.1.8.1-beta-1
28+
29+
First beta release of the new SBE C# bindings and supporting sbe-tool / sbe-dll nuget packages.
30+
1631
Code Layout
1732
-----------
33+
1834
The Java code that performs the generation of C# code is
1935
[here](https://github.com/real-logic/simple-binary-encoding/tree/master/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp).
2036

@@ -30,7 +46,9 @@ solution](https://github.com/real-logic/simple-binary-encoding/blob/master/cshar
3046
* sbe-benchmarks (for performance testing and tuning)
3147
* sbe-example-car (sample code based on the Car example)
3248
* sbe-example-extension (sample code based on the Car extension)
33-
49+
50+
The project can be built either through the various .NET Core supporting IDEs such as Visual Studio 2017, JetBrains Rider, and Visual Studio Code as well as through the .NET Core SDK, using the `dotnet build` / `dotnet test` commands.
51+
3452
Roadmap
3553
-------
3654
The csharp generator is as of March 2018 a beta release. The current roadmap contains:

csharp/netfx.props

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2+
<PropertyGroup>
3+
<!-- When compiling .NET SDK 2.0 projects targeting .NET 4.x on Mono using 'dotnet build' you -->
4+
<!-- have to teach MSBuild where the Mono copy of the reference asssemblies is -->
5+
<TargetIsMono Condition="$(TargetFramework.StartsWith('net4')) and '$(OS)' == 'Unix'">true</TargetIsMono>
6+
7+
<!-- Look in the standard install locations -->
8+
<MonoBasePath Condition="'$(MonoBasePath)' == '' AND '$(TargetIsMono)' == 'true' AND EXISTS('/Library/Frameworks/Mono.framework/Versions/Current/lib/mono')">/Library/Frameworks/Mono.framework/Versions/Current/lib/mono</MonoBasePath>
9+
<MonoBasePath Condition="'$(MonoBasePath)' == '' AND '$(TargetIsMono)' == 'true' AND EXISTS('/usr/lib/mono')">/usr/lib/mono</MonoBasePath>
10+
<MonoBasePath Condition="'$(MonoBasePath)' == '' AND '$(TargetIsMono)' == 'true' AND EXISTS('/usr/local/lib/mono')">/usr/local/lib/mono</MonoBasePath>
11+
12+
<!-- If we found Mono reference assemblies, then use them -->
13+
<FrameworkPathOverride Condition="'$(MonoBasePath)' != '' AND '$(TargetFramework)' == 'net45'">$(MonoBasePath)/4.5-api</FrameworkPathOverride>
14+
<FrameworkPathOverride Condition="'$(MonoBasePath)' != '' AND '$(TargetFramework)' == 'net451'">$(MonoBasePath)/4.5.1-api</FrameworkPathOverride>
15+
<FrameworkPathOverride Condition="'$(MonoBasePath)' != '' AND '$(TargetFramework)' == 'net452'">$(MonoBasePath)/4.5.2-api</FrameworkPathOverride>
16+
<FrameworkPathOverride Condition="'$(MonoBasePath)' != '' AND '$(TargetFramework)' == 'net46'">$(MonoBasePath)/4.6-api</FrameworkPathOverride>
17+
<FrameworkPathOverride Condition="'$(MonoBasePath)' != '' AND '$(TargetFramework)' == 'net461'">$(MonoBasePath)/4.6.1-api</FrameworkPathOverride>
18+
<FrameworkPathOverride Condition="'$(MonoBasePath)' != '' AND '$(TargetFramework)' == 'net462'">$(MonoBasePath)/4.6.2-api</FrameworkPathOverride>
19+
<FrameworkPathOverride Condition="'$(MonoBasePath)' != '' AND '$(TargetFramework)' == 'net47'">$(MonoBasePath)/4.7-api</FrameworkPathOverride>
20+
<FrameworkPathOverride Condition="'$(MonoBasePath)' != '' AND '$(TargetFramework)' == 'net471'">$(MonoBasePath)/4.7.1-api</FrameworkPathOverride>
21+
<EnableFrameworkPathOverride Condition="'$(MonoBasePath)' != ''">true</EnableFrameworkPathOverride>
22+
23+
<!-- Add the Facades directory. Not sure how else to do this. Necessary at least for .NET 4.5 -->
24+
<AssemblySearchPaths Condition="'$(MonoBasePath)' != ''">$(FrameworkPathOverride)/Facades;$(AssemblySearchPaths)</AssemblySearchPaths>
25+
</PropertyGroup>
26+
</Project>

csharp/sbe-dll/DirectBuffer.cs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Org.SbeTool.Sbe.Dll
99
public sealed unsafe class DirectBuffer : IDisposable
1010
{
1111
/// <summary>
12-
/// Delegate invoked if buffer size is too small.
12+
/// Delegate invoked if buffer size is too small.
1313
/// </summary>
1414
/// <param name="existingBufferSize"></param>
1515
/// <param name="requestedBufferSize"></param>
@@ -66,7 +66,7 @@ public DirectBuffer(byte* pBuffer, int bufferLength, BufferOverflowDelegate buff
6666
/// <summary>
6767
/// Creates a DirectBuffer that can later be wrapped
6868
/// </summary>
69-
public DirectBuffer()
69+
public DirectBuffer()
7070
{
7171
}
7272

@@ -569,6 +569,22 @@ public void DoublePutLittleEndian(int index, double value)
569569

570570
#endregion
571571

572+
/// <summary>
573+
/// Creates a <see cref="Span{T}" /> on top of the underlying buffer
574+
/// </summary>
575+
/// <param name="index">index in the underlying buffer to start from.</param>
576+
/// <param name="length">length of the supplied buffer to use.</param>
577+
/// <returns>The new <see cref="Span{T}" /> wrapping the requested memory</returns>
578+
public Span<T> AsSpan<T>(int index, int length) => new Span<T>(_pBuffer + index, length);
579+
580+
/// <summary>
581+
/// Creates a <see cref="ReadOnlySpan{T}" /> on top of the underlying buffer
582+
/// </summary>
583+
/// <param name="index">index in the underlying buffer to start from.</param>
584+
/// <param name="length">length of the supplied buffer to use.</param>
585+
/// <returns>The new <see cref="ReadOnlySpan{T}" /> wrapping the requested memory</returns>
586+
public ReadOnlySpan<T> AsReadOnlySpan<T>(int index, int length) => new ReadOnlySpan<T>(_pBuffer + index, length);
587+
572588
/// <summary>
573589
/// Copies a range of bytes from the underlying into a supplied byte array.
574590
/// </summary>
@@ -585,6 +601,20 @@ public int GetBytes(int index, byte[] destination, int offsetDestination, int le
585601
return count;
586602
}
587603

604+
/// <summary>
605+
/// Copies a range of bytes from the underlying into a supplied <see cref="Span{T}" />.
606+
/// </summary>
607+
/// <param name="index">index in the underlying buffer to start from.</param>
608+
/// <param name="destination"><see cref="Span{T}" /> into which the bytes will be copied.</param>
609+
/// <returns>count of bytes copied.</returns>
610+
public int GetBytes(int index, Span<byte> destination)
611+
{
612+
int count = Math.Min(destination.Length, _capacity - index);
613+
AsReadOnlySpan<byte>(index, count).CopyTo(destination);
614+
615+
return count;
616+
}
617+
588618
/// <summary>
589619
/// Writes a byte array into the underlying buffer.
590620
/// </summary>
@@ -601,6 +631,20 @@ public int SetBytes(int index, byte[] src, int offset, int length)
601631
return count;
602632
}
603633

634+
/// <summary>
635+
/// Writes a <see cref="Span{T}" /> into the underlying buffer.
636+
/// </summary>
637+
/// <param name="index">index in the underlying buffer to start from.</param>
638+
/// <param name="src">source <see cref="Span{T}" /> to be copied to the underlying buffer.</param>
639+
/// <returns>count of bytes copied.</returns>
640+
public int SetBytes(int index, ReadOnlySpan<byte> src)
641+
{
642+
int count = Math.Min(src.Length, _capacity - index);
643+
src.CopyTo(AsSpan<byte>(index, count));
644+
645+
return count;
646+
}
647+
604648
/// <summary>
605649
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
606650
/// </summary>
@@ -638,4 +682,4 @@ private void FreeGCHandle()
638682
}
639683
}
640684
}
641-
}
685+
}

csharp/sbe-dll/packages.config

Lines changed: 0 additions & 4 deletions
This file was deleted.

csharp/sbe-dll/sbe-dll.csproj

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2+
<Import Project="..\netfx.props" />
23

34
<PropertyGroup>
45
<TargetFrameworks>net45;netstandard2.0</TargetFrameworks>
@@ -10,11 +11,19 @@
1011
</PropertyGroup>
1112

1213
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net45|AnyCPU'">
13-
<DocumentationFile>bin\Release\net45\SBE.xml</DocumentationFile>
14+
<DocumentationFile>bin\release\net45\SBE.xml</DocumentationFile>
1415
</PropertyGroup>
1516

1617
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard2.0|AnyCPU'">
17-
<DocumentationFile>bin\Release\netstandard2.0\SBE.xml</DocumentationFile>
18+
<DocumentationFile>bin\release\netstandard2.0\SBE.xml</DocumentationFile>
1819
</PropertyGroup>
20+
21+
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
22+
<Reference Include="System.Runtime" />
23+
</ItemGroup>
24+
25+
<ItemGroup>
26+
<PackageReference Include="System.Memory" Version="4.5.1" />
27+
</ItemGroup>
1928

2029
</Project>

csharp/sbe-tests/packages.config

Lines changed: 0 additions & 5 deletions
This file was deleted.

csharp/sbe-tests/sbe-tests.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2+
<Import Project="..\netfx.props" />
23

34
<PropertyGroup>
4-
<TargetFrameworks>net45</TargetFrameworks>
5+
<TargetFrameworks>net45;netcoreapp2.1</TargetFrameworks>
56
<RootNamespace>Org.SbeTool.Sbe.Tests</RootNamespace>
67
<AssemblyName>Org.SbeTool.Sbe.UnitTests</AssemblyName>
78
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>

sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java

Lines changed: 48 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -358,16 +358,21 @@ private CharSequence generateVarData(final List<Token> tokens, final String inde
358358
sizeOfLengthField));
359359

360360
sb.append(String.format("\n" +
361-
indent + "public int Get%1$s(byte[] dst, int dstOffset, int length)\n" +
361+
indent + "public int Get%1$s(byte[] dst, int dstOffset, int length) =>\n" +
362+
indent + INDENT + "Get%1$s(new Span<byte>(dst, dstOffset, length));\n",
363+
propertyName));
364+
365+
sb.append(String.format("\n" +
366+
indent + "public int Get%1$s(Span<byte> dst)\n" +
362367
indent + "{\n" +
363368
"%2$s" +
364369
indent + INDENT + "const int sizeOfLengthField = %3$d;\n" +
365370
indent + INDENT + "int limit = _parentMessage.Limit;\n" +
366371
indent + INDENT + "_buffer.CheckLimit(limit + sizeOfLengthField);\n" +
367372
indent + INDENT + "int dataLength = (int)_buffer.%4$sGet%5$s(limit);\n" +
368-
indent + INDENT + "int bytesCopied = Math.Min(length, dataLength);\n" +
373+
indent + INDENT + "int bytesCopied = Math.Min(dst.Length, dataLength);\n" +
369374
indent + INDENT + "_parentMessage.Limit = limit + sizeOfLengthField + dataLength;\n" +
370-
indent + INDENT + "_buffer.GetBytes(limit + sizeOfLengthField, dst, dstOffset, bytesCopied);\n\n" +
375+
indent + INDENT + "_buffer.GetBytes(limit + sizeOfLengthField, dst.Slice(0, bytesCopied));\n\n" +
371376
indent + INDENT + "return bytesCopied;\n" +
372377
indent + "}\n",
373378
propertyName,
@@ -377,14 +382,19 @@ private CharSequence generateVarData(final List<Token> tokens, final String inde
377382
byteOrderStr));
378383

379384
sb.append(String.format("\n" +
380-
indent + "public int Set%1$s(byte[] src, int srcOffset, int length)\n" +
385+
indent + "public int Set%1$s(byte[] src, int srcOffset, int length) =>\n" +
386+
indent + INDENT + "Set%1$s(new ReadOnlySpan<byte>(src, srcOffset, length));\n",
387+
propertyName));
388+
389+
sb.append(String.format("\n" +
390+
indent + "public int Set%1$s(ReadOnlySpan<byte> src)\n" +
381391
indent + "{\n" +
382392
indent + INDENT + "const int sizeOfLengthField = %2$d;\n" +
383393
indent + INDENT + "int limit = _parentMessage.Limit;\n" +
384-
indent + INDENT + "_parentMessage.Limit = limit + sizeOfLengthField + length;\n" +
385-
indent + INDENT + "_buffer.%3$sPut%5$s(limit, (%4$s)length);\n" +
386-
indent + INDENT + "_buffer.SetBytes(limit + sizeOfLengthField, src, srcOffset, length);\n\n" +
387-
indent + INDENT + "return length;\n" +
394+
indent + INDENT + "_parentMessage.Limit = limit + sizeOfLengthField + src.Length;\n" +
395+
indent + INDENT + "_buffer.%3$sPut%5$s(limit, (%4$s)src.Length);\n" +
396+
indent + INDENT + "_buffer.SetBytes(limit + sizeOfLengthField, src);\n\n" +
397+
indent + INDENT + "return src.Length;\n" +
388398
indent + "}\n",
389399
propertyName,
390400
sizeOfLengthField,
@@ -756,44 +766,30 @@ private CharSequence generateArrayProperty(
756766

757767
sb.append(String.format("\n" +
758768
indent + "public const int %sLength = %d;\n",
759-
propName,
760-
fieldLength));
769+
propName, fieldLength));
761770

762771
sb.append(String.format("\n" +
763772
indent + "public %1$s Get%2$s(int index)\n" +
764773
indent + "{\n" +
765-
indent + INDENT + "if (index < 0 || index >= %3$d)\n" +
766-
indent + INDENT + "{\n" +
774+
indent + INDENT + "if (index < 0 || index >= %3$d) {\n" +
767775
indent + INDENT + INDENT + "throw new IndexOutOfRangeException(\"index out of range: index=\" + index);\n" +
768776
indent + INDENT + "}\n\n" +
769777
"%4$s" +
770778
indent + INDENT + "return _buffer.%5$sGet%8$s(_offset + %6$d + (index * %7$d));\n" +
771779
indent + "}\n",
772-
typeName,
773-
propName,
774-
fieldLength,
780+
typeName, propName, fieldLength,
775781
generateFieldNotPresentCondition(token.version(), token.encoding(), indent),
776-
typePrefix,
777-
offset,
778-
typeSize,
779-
byteOrderStr));
782+
typePrefix, offset, typeSize, byteOrderStr));
780783

781784
sb.append(String.format("\n" +
782785
indent + "public void Set%1$s(int index, %2$s value)\n" +
783786
indent + "{\n" +
784-
indent + INDENT + "if (index < 0 || index >= %3$d)\n" +
785-
indent + INDENT + "{\n" +
787+
indent + INDENT + "if (index < 0 || index >= %3$d) {\n" +
786788
indent + INDENT + INDENT + "throw new IndexOutOfRangeException(\"index out of range: index=\" + index);\n" +
787789
indent + INDENT + "}\n\n" +
788790
indent + INDENT + "_buffer.%4$sPut%7$s(_offset + %5$d + (index * %6$d), value);\n" +
789791
indent + "}\n",
790-
propName,
791-
typeName,
792-
fieldLength,
793-
typePrefix,
794-
offset,
795-
typeSize,
796-
byteOrderStr));
792+
propName, typeName, fieldLength, typePrefix, offset, typeSize, byteOrderStr));
797793

798794
if (token.encoding().primitiveType() == PrimitiveType.CHAR)
799795
{
@@ -803,39 +799,43 @@ private CharSequence generateArrayProperty(
803799
indent + "public int Get%1$s(byte[] dst, int dstOffset)\n" +
804800
indent + "{\n" +
805801
indent + INDENT + "const int length = %2$d;\n" +
806-
indent + INDENT + "if (dstOffset < 0 || dstOffset > (dst.Length - length))\n" +
807-
indent + INDENT + "{\n" +
808-
indent + INDENT + INDENT + "throw new IndexOutOfRangeException(" +
809-
"\"dstOffset out of range for copy: offset=\" + dstOffset);\n" +
802+
"%3$s" +
803+
indent + INDENT + "return Get%1$s(new Span<byte>(dst, dstOffset, length));\n" +
804+
indent + "}\n",
805+
propName, fieldLength, generateArrayFieldNotPresentCondition(token.version(), indent), offset));
806+
807+
sb.append(String.format("\n" +
808+
indent + "public int Get%1$s(Span<byte> dst)\n" +
809+
indent + "{\n" +
810+
indent + INDENT + "const int length = %2$d;\n" +
811+
indent + INDENT + "if (dst.Length < length) {\n" +
812+
indent + INDENT + INDENT +
813+
"throw new ArgumentOutOfRangeException($\"dst.Length={dst.Length} is too large.\");\n" +
810814
indent + INDENT + "}\n\n" +
811815
"%3$s" +
812-
indent + INDENT + "_buffer.GetBytes(_offset + %4$d, dst, dstOffset, length);\n" +
816+
indent + INDENT + "_buffer.GetBytes(_offset + %4$d, dst);\n" +
813817
indent + INDENT + "return length;\n" +
814818
indent + "}\n",
815-
propName,
816-
fieldLength,
817-
generateArrayFieldNotPresentCondition(token.version(), indent),
818-
offset));
819+
propName, fieldLength, generateArrayFieldNotPresentCondition(token.version(), indent), offset));
819820

820821
sb.append(String.format("\n" +
821822
indent + "public void Set%1$s(byte[] src, int srcOffset)\n" +
822823
indent + "{\n" +
824+
indent + INDENT + "Set%1$s(new ReadOnlySpan<byte>(src, srcOffset, src.Length - srcOffset));\n" +
825+
indent + "}\n",
826+
propName, fieldLength, offset));
827+
828+
sb.append(String.format("\n" +
829+
indent + "public void Set%1$s(ReadOnlySpan<byte> src)\n" +
830+
indent + "{\n" +
823831
indent + INDENT + "const int length = %2$d;\n" +
824-
indent + INDENT + "if (srcOffset < 0 || srcOffset > src.Length)\n" +
825-
indent + INDENT + "{\n" +
826-
indent + INDENT + INDENT +
827-
"throw new IndexOutOfRangeException(\"srcOffset out of range for copy: offset=\" + srcOffset);\n" +
828-
indent + INDENT + "}\n\n" +
829-
indent + INDENT + "if (src.Length > length)\n" +
830-
indent + INDENT + "{\n" +
832+
indent + INDENT + "if (src.Length > length) {\n" +
831833
indent + INDENT + INDENT +
832834
"throw new ArgumentOutOfRangeException($\"src.Length={src.Length} is too large.\");\n" +
833835
indent + INDENT + "}\n\n" +
834-
indent + INDENT + "_buffer.SetBytes(_offset + %3$d, src, srcOffset, src.Length - srcOffset);\n" +
836+
indent + INDENT + "_buffer.SetBytes(_offset + %3$d, src);\n" +
835837
indent + "}\n",
836-
propName,
837-
fieldLength,
838-
offset));
838+
propName, fieldLength, offset));
839839
}
840840

841841
return sb;

0 commit comments

Comments
 (0)