Skip to content

Commit 2c1f6e1

Browse files
committed
[C#] Generate DTOs from SBE IR for non-perf-sensitive usecases.
In some applications performance is not cricital. Some users would like to use SBE across their whole "estate", but don't want the "sharp edges" associated with using flyweight codecs, e.g., accidental escape. In this commit, I've added a first cut of DTO generation for C# and a simple test based on the Car Example. The DTOs support encoding and decoding via the generated codecs using `EncodeInto(CodecT codec)` and `DecodeFrom(CodecT codec)` methods. Currently there is no support for equality/comparison or read-only views over the data; although, these have been requested. Here are some points that we may or may not wish to change in the future: 1. Non-present (due to the encoded version) string/array data and repeating groups are represented as `null` rather than empty. 2. Non-present primitive values are represented as their associated null value rather than using nullable types. 3. Non-present bitsets are represented as `0`. 4. DTOs are generated via a separate `CodeGenerator` rather than a flag to the existing C# `CodeGenerator`.
1 parent 510d3e8 commit 2c1f6e1

File tree

7 files changed

+1353
-116
lines changed

7 files changed

+1353
-116
lines changed

build.gradle

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -723,7 +723,7 @@ tasks.register('generateCSharpCodecsWithXIncludes', JavaExec) {
723723
'sbe-samples/src/main/resources/example-extension-schema.xml']
724724
}
725725

726-
tasks.register('generateCSharpCodecsTests', JavaExec) {
726+
tasks.register('generateCSharpTestCodecs', JavaExec) {
727727
mainClass.set('uk.co.real_logic.sbe.SbeTool')
728728
classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath
729729
systemProperties(
@@ -740,9 +740,21 @@ tasks.register('generateCSharpCodecsTests', JavaExec) {
740740
'sbe-benchmarks/src/main/resources/fix-message-samples.xml']
741741
}
742742

743+
tasks.register('generateCSharpTestDtos', JavaExec) {
744+
mainClass.set('uk.co.real_logic.sbe.SbeTool')
745+
classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath
746+
systemProperties(
747+
'sbe.output.dir': 'csharp/sbe-generated',
748+
'sbe.target.language': 'uk.co.real_logic.sbe.generation.csharp.CSharpDtos',
749+
'sbe.xinclude.aware': 'true',
750+
'sbe.validation.xsd': validationXsdPath)
751+
args = ['sbe-samples/src/main/resources/example-extension-schema.xml']
752+
}
753+
743754
tasks.register('generateCSharpCodecs') {
744755
description = 'Generate csharp codecs'
745-
dependsOn 'generateCSharpCodecsTests',
756+
dependsOn 'generateCSharpTestCodecs',
757+
'generateCSharpTestDtos',
746758
'generateCSharpCodecsWithXIncludes'
747759
}
748760

csharp/sbe-dll/DirectBuffer.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -694,21 +694,16 @@ public int SetBytes(int index, ReadOnlySpan<byte> src)
694694

695695
/// <summary>
696696
/// Writes a string into the underlying buffer, encoding using the provided <see cref="System.Text.Encoding"/>.
697-
/// If there is not enough room in the buffer for the bytes it will throw IndexOutOfRangeException.
698697
/// </summary>
699698
/// <param name="encoding">encoding to use to write the bytes from the string</param>
700699
/// <param name="src">source string</param>
701700
/// <param name="index">index in the underlying buffer to start writing bytes</param>
702701
/// <returns>count of bytes written</returns>
703702
public unsafe int SetBytesFromString(Encoding encoding, string src, int index)
704703
{
705-
int available = _capacity - index;
706704
int byteCount = encoding.GetByteCount(src);
707705

708-
if (byteCount > available)
709-
{
710-
ThrowHelper.ThrowIndexOutOfRangeException(_capacity);
711-
}
706+
CheckLimit(index + byteCount);
712707

713708
fixed (char* ptr = src)
714709
{

csharp/sbe-tests/DtoTests.cs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
using Extension;
2+
using Microsoft.VisualStudio.TestTools.UnitTesting;
3+
using Org.SbeTool.Sbe.Dll;
4+
5+
namespace Org.SbeTool.Sbe.Tests
6+
{
7+
[TestClass]
8+
public class DtoTests
9+
{
10+
[TestMethod]
11+
public void ShouldRoundTripCar()
12+
{
13+
var inputByteArray = new byte[1024];
14+
var inputBuffer = new DirectBuffer(inputByteArray);
15+
EncodeCar(inputBuffer);
16+
var decoder = new Car();
17+
decoder.WrapForDecode(inputBuffer, 0, Car.BlockLength, Car.SchemaVersion);
18+
var decoderString = decoder.ToString();
19+
var dto = new CarDto();
20+
dto.DecodeFrom(decoder);
21+
var outputByteArray = new byte[1024];
22+
var outputBuffer = new DirectBuffer(outputByteArray);
23+
var encoder = new Car();
24+
encoder.WrapForEncode(outputBuffer, 0);
25+
dto.EncodeInto(encoder);
26+
var dtoString = dto.ToString();
27+
CollectionAssert.AreEqual(inputByteArray, outputByteArray);
28+
Assert.AreEqual(decoderString, dtoString);
29+
}
30+
31+
private static void EncodeCar(DirectBuffer buffer)
32+
{
33+
var car = new Car();
34+
car.WrapForEncode(buffer, 0);
35+
car.SerialNumber = 1234;
36+
car.ModelYear = 2013;
37+
car.Available = BooleanType.T;
38+
car.Code = Model.A;
39+
car.SetVehicleCode("ABCDEF");
40+
41+
for (int i = 0, size = Car.SomeNumbersLength; i < size; i++)
42+
{
43+
car.SetSomeNumbers(i, (uint)i);
44+
}
45+
46+
car.Extras = OptionalExtras.CruiseControl | OptionalExtras.SportsPack;
47+
48+
car.CupHolderCount = 119;
49+
50+
car.Engine.Capacity = 2000;
51+
car.Engine.NumCylinders = 4;
52+
car.Engine.SetManufacturerCode("ABC");
53+
car.Engine.Efficiency = 35;
54+
car.Engine.BoosterEnabled = BooleanType.T;
55+
car.Engine.Booster.BoostType = BoostType.NITROUS;
56+
car.Engine.Booster.HorsePower = 200;
57+
58+
var fuelFigures = car.FuelFiguresCount(3);
59+
fuelFigures.Next();
60+
fuelFigures.Speed = 30;
61+
fuelFigures.Mpg = 35.9f;
62+
fuelFigures.SetUsageDescription("this is a description");
63+
64+
fuelFigures.Next();
65+
fuelFigures.Speed = 55;
66+
fuelFigures.Mpg = 49.0f;
67+
fuelFigures.SetUsageDescription("this is a description");
68+
69+
fuelFigures.Next();
70+
fuelFigures.Speed = 75;
71+
fuelFigures.Mpg = 40.0f;
72+
fuelFigures.SetUsageDescription("this is a description");
73+
74+
Car.PerformanceFiguresGroup perfFigures = car.PerformanceFiguresCount(2);
75+
perfFigures.Next();
76+
perfFigures.OctaneRating = 95;
77+
78+
Car.PerformanceFiguresGroup.AccelerationGroup acceleration = perfFigures.AccelerationCount(3).Next();
79+
acceleration.Mph = 30;
80+
acceleration.Seconds = 4.0f;
81+
82+
acceleration.Next();
83+
acceleration.Mph = 60;
84+
acceleration.Seconds = 7.5f;
85+
86+
acceleration.Next();
87+
acceleration.Mph = 100;
88+
acceleration.Seconds = 12.2f;
89+
90+
perfFigures.Next();
91+
perfFigures.OctaneRating = 99;
92+
acceleration = perfFigures.AccelerationCount(3).Next();
93+
94+
acceleration.Mph = 30;
95+
acceleration.Seconds = 3.8f;
96+
97+
acceleration.Next();
98+
acceleration.Mph = 60;
99+
acceleration.Seconds = 7.1f;
100+
101+
acceleration.Next();
102+
acceleration.Mph = 100;
103+
acceleration.Seconds = 11.8f;
104+
105+
car.SetManufacturer("Ford");
106+
car.SetModel("Fiesta");
107+
car.SetActivationCode("1234");
108+
}
109+
}
110+
}

0 commit comments

Comments
 (0)