forked from aeron-io/simple-binary-encoding
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSbeProgram.cs
More file actions
237 lines (189 loc) · 11 KB
/
SbeProgram.cs
File metadata and controls
237 lines (189 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
using System;
using System.Text;
using Baseline;
namespace Adaptive.SimpleBinaryEncoding.Examples
{
public class SbeProgram
{
private static readonly byte[] _vehicleCode;
private static readonly byte[] _manufacturerCode;
private static readonly byte[] _make;
private static readonly byte[] _model;
private static readonly MessageHeader MessageHeader = new MessageHeader();
private static readonly Car Car = new Car();
static SbeProgram()
{
try
{
// convert some sample strings to the correct encoding for this sample
_vehicleCode = Encoding.GetEncoding(Car.VehicleCodeCharacterEncoding).GetBytes("abcdef");
_manufacturerCode = Encoding.GetEncoding(Engine.ManufacturerCodeCharacterEncoding).GetBytes("123");
_make = Encoding.GetEncoding(Car.MakeCharacterEncoding).GetBytes("Honda");
_model = Encoding.GetEncoding(Car.MakeCharacterEncoding).GetBytes("Civic VTi");
}
catch (Exception ex)
{
throw new Exception("An error occured while reading encodings", ex);
}
}
private static void Main()
{
// This byte array is used for encoding and decoding, this is what ou would send on the wire or save to disk
var byteBuffer = new byte[4096];
// You need to "wrap" the array with a DirectBuffer, this class is used by the generated code to read and write efficiently to the underlying byte array
var directBuffer = new DirectBuffer(byteBuffer);
const short messageTemplateVersion = 0;
int bufferOffset = 0;
// Before encoding a message we need to create a SBE header which specify what we are going to encode (this will allow the decoder to detect that it's an encoded 'car' object)
// We will probably simplify this part soon, so the header gets applied automatically, but for now it's manual
MessageHeader.Wrap(directBuffer, bufferOffset, messageTemplateVersion); // position the MessageHeader on the DirectBuffer, at the correct position
MessageHeader.BlockLength = Car.BlockLength; // size that a car takes on the wire
MessageHeader.TemplateId = Car.TemplateId; // identifier for the car object (SBE template ID)
MessageHeader.Version = Car.TemplateVersion; // this can be overriden if we want to support different versions of the car object (advanced functionality)
// Now that we have encoded the header in the byte array we can encode the car object itself
bufferOffset += MessageHeader.Size;
Encode(Car, directBuffer, bufferOffset);
// Now we have encoded the message is the byte array, we are going to decode it
// first we decode the header (in a real world scenario you would need the header to decide which SBE decoder you are going to use
bufferOffset = 0;
// position the MessageHeader object at the beginning of the array
MessageHeader.Wrap(directBuffer, bufferOffset, messageTemplateVersion);
// Extract infos from the header
// In a real app you would use that to lookup the applicable flyweight to decode this type of message based on templateId and version.
int templateId = MessageHeader.TemplateId;
short actingVersion = MessageHeader.Version;
int actingBlockLength = MessageHeader.BlockLength;
bufferOffset += MessageHeader.Size;
// now we decode the message
Decode(Car, directBuffer, bufferOffset, actingBlockLength, actingVersion);
Console.ReadKey();
}
private static int Encode(Car car, DirectBuffer directBuffer, int bufferOffset)
{
const int srcOffset = 0;
// we position the car encoder on the direct buffer, at the correct offset (ie. just after the header)
car.WrapForEncode(directBuffer, bufferOffset);
car.SerialNumber = 1234; // we set the different fields, just as normal properties and they get written straight to the underlying byte buffer
car.ModelYear = 2013;
car.Available = BooleanType.TRUE; // enums are supports
car.Code = Model.A;
car.SetVehicleCode(_vehicleCode, srcOffset); // we set a constant string
for (int i = 0, size = Car.SomeNumbersLength; i < size; i++)
{
car.SetSomeNumbers(i, i); // this property is defined as a constant length array of integers
}
car.Extras = OptionalExtras.CruiseControl | OptionalExtras.SunRoof; // bit set (flag enums in C#) are supported
car.Engine.Capacity = 2000;
car.Engine.NumCylinders = 4;
car.Engine.SetManufacturerCode(_manufacturerCode, srcOffset);
// we have written all the constant length fields, now we can write the repeatable groups
var fuelFigures = car.FuelFiguresCount(3); // we specify that we are going to write 3 FueldFigures (the API is not very .NET friendly yet, we will address that)
fuelFigures.Next(); // move to the first element
fuelFigures.Speed = 30;
fuelFigures.Mpg = 35.9f;
fuelFigures.Next(); // second
fuelFigures.Speed = 55;
fuelFigures.Mpg = 49.0f;
fuelFigures.Next();
fuelFigures.Speed = 75;
fuelFigures.Mpg = 40.0f;
Car.PerformanceFiguresGroup perfFigures = car.PerformanceFiguresCount(2); // demonstrates how to create a nested group
perfFigures.Next();
perfFigures.OctaneRating = 95;
Car.PerformanceFiguresGroup.AccelerationGroup acceleration = perfFigures.AccelerationCount(3).Next(); // this group is going to be nested in the first element of the previous group
acceleration.Mph = 30;
acceleration.Seconds = 4.0f;
acceleration.Next();
acceleration.Mph = 60;
acceleration.Seconds = 7.5f;
acceleration.Next();
acceleration.Mph = 100;
acceleration.Seconds = 12.2f;
perfFigures.Next();
perfFigures.OctaneRating = 99;
acceleration = perfFigures.AccelerationCount(3).Next();
acceleration.Mph = 30;
acceleration.Seconds = 3.8f;
acceleration.Next();
acceleration.Mph = 60;
acceleration.Seconds = 7.1f;
acceleration.Next();
acceleration.Mph = 100;
acceleration.Seconds = 11.8f;
// once we have written all the repeatable groups we can write the variable length properties (you would use that for strings, byte[], etc)
car.SetMake(_make, srcOffset, _make.Length);
car.SetMake(_model, srcOffset, _model.Length);
return car.Size;
}
private static void Decode(Car car,
DirectBuffer directBuffer,
int bufferOffset,
int actingBlockLength,
int actingVersion)
{
var buffer = new byte[128];
var sb = new StringBuilder();
// position the car flyweight just after the header on the DirectBuffer
car.WrapForDecode(directBuffer, bufferOffset, actingBlockLength, actingVersion);
// decode the car properties on by one, directly from the buffer
sb.Append("\ncar.templateId=").Append(Car.TemplateId);
sb.Append("\ncar.serialNumber=").Append(car.SerialNumber);
sb.Append("\ncar.modelYear=").Append(car.ModelYear);
sb.Append("\ncar.available=").Append(car.Available);
sb.Append("\ncar.code=").Append(car.Code);
sb.Append("\ncar.someNumbers=");
for (int i = 0, size = Car.SomeNumbersLength; i < size; i++)
{
sb.Append(car.GetSomeNumbers(i)).Append(", ");
}
sb.Append("\ncar.vehicleCode=");
for (int i = 0, size = Car.VehicleCodeLength; i < size; i++)
{
sb.Append((char) car.GetVehicleCode(i));
}
OptionalExtras extras = car.Extras;
sb.Append("\ncar.extras.cruiseControl=").Append((extras & OptionalExtras.CruiseControl) == OptionalExtras.CruiseControl); // this is how you can find out if a specific flag is set in a flag enum
sb.Append("\ncar.extras.sportsPack=").Append((extras & OptionalExtras.SportsPack) == OptionalExtras.SportsPack);
sb.Append("\ncar.extras.sunRoof=").Append((extras & OptionalExtras.SunRoof) == OptionalExtras.SunRoof);
Engine engine = car.Engine;
sb.Append("\ncar.engine.capacity=").Append(engine.Capacity);
sb.Append("\ncar.engine.numCylinders=").Append(engine.NumCylinders);
sb.Append("\ncar.engine.maxRpm=").Append(engine.MaxRpm);
sb.Append("\ncar.engine.manufacturerCode=");
for (int i = 0, size = Engine.ManufacturerCodeLength; i < size; i++)
{
sb.Append((char) engine.GetManufacturerCode(i));
}
int length = engine.GetFuel(buffer, 0, buffer.Length);
sb.Append("\ncar.engine.fuel=").Append(Encoding.ASCII.GetString(buffer, 0, length)); // string requires a bit of work to decode
var fuelFiguresGroup = car.FuelFigures; // decode a repeatable group (we will change the API to support foreach soon)
while (fuelFiguresGroup.HasNext)
{
var fuelFigures = fuelFiguresGroup.Next();
sb.Append("\ncar.fuelFigures.speed=").Append(fuelFigures.Speed);
sb.Append("\ncar.fuelFigures.mpg=").Append(fuelFigures.Mpg);
}
// the nested group
var performanceFiguresGroup = car.PerformanceFigures;
while (performanceFiguresGroup.HasNext)
{
var performanceFigures = performanceFiguresGroup.Next();
sb.Append("\ncar.performanceFigures.octaneRating=").Append(performanceFigures.OctaneRating);
var accelerationGroup = performanceFigures.Acceleration;
while (accelerationGroup.HasNext)
{
var acceleration = accelerationGroup.Next();
sb.Append("\ncar.performanceFigures.acceleration.mph=").Append(acceleration.Mph);
sb.Append("\ncar.performanceFigures.acceleration.seconds=").Append(acceleration.Seconds);
}
}
// variable length fields
length = car.GetMake(buffer, 0, buffer.Length);
sb.Append("\ncar.make=").Append(Encoding.GetEncoding(Car.MakeCharacterEncoding).GetString(buffer, 0, length));
length = car.GetModel(buffer, 0, buffer.Length);
sb.Append("\ncar.model=").Append(Encoding.GetEncoding(Car.ModelCharacterEncoding).GetString(buffer, 0, length));
sb.Append("\ncar.size=").Append(car.Size);
Console.WriteLine(sb.ToString());
}
}
}