Skip to content

Commit 6bd979a

Browse files
authored
Merge pull request aeron-io#860 from mward/rust-lang
New Rust code generator
2 parents 08b3c68 + b58daca commit 6bd979a

File tree

20 files changed

+4091
-1
lines changed

20 files changed

+4091
-1
lines changed

.github/workflows/ci.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,33 @@ jobs:
211211
echo "cmake-$Env:CMAKE_VERSION-win64-x64/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8
212212
- name: Build
213213
run: cppbuild/cppbuild.cmd
214+
215+
rust-build:
216+
name: Rust ${{ matrix.rust }}
217+
runs-on: ubuntu-20.04
218+
strategy:
219+
fail-fast: false
220+
matrix:
221+
rust: [ stable, beta, nightly ]
222+
steps:
223+
- name: Checkout code
224+
uses: actions/checkout@v2
225+
- name: Cache Gradle dependencies
226+
uses: actions/cache@v2
227+
with:
228+
path: ~/.gradle/caches
229+
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
230+
restore-keys: |
231+
${{ runner.os }}-gradle-
232+
- name: Cache Gradle wrappers
233+
uses: actions/cache@v2
234+
with:
235+
path: ~/.gradle/wrapper
236+
key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
237+
- name: Rust setup
238+
uses: actions-rs/toolchain@v1
239+
with:
240+
profile: minimal
241+
toolchain: ${{ matrix.rust }}
242+
override: true
243+
- run: ./gradlew runRustTests

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ csharp/sbe-generated/uk_co_real_logic_sbe_benchmarks_fix
106106
csharp/sbe-tests/*.sbe
107107
csharp/nuget/
108108

109+
# rust
110+
rust/target
111+
rust/Cargo.lock
112+
109113
# Mac
110114
.DS_Store
111115
/sbe-tool/src/main/golang/uk_co_real_logic_sbe_ir_generated/

README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Simple Binary Encoding (SBE)
1616

1717
[SBE](https://github.com/FIXTradingCommunity/fix-simple-binary-encoding) is an OSI layer 6 presentation for
1818
encoding and decoding binary application messages for low-latency financial applications. This repository contains
19-
the reference implementations in Java, C++, Golang, and C#.
19+
the reference implementations in Java, C++, Golang, C#, and Rust.
2020

2121
More details on the design and usage of SBE can be found on the [Wiki](https://github.com/real-logic/simple-binary-encoding/wiki).
2222

@@ -116,6 +116,25 @@ Users of CSharp generated code should see the [user documentation](https://githu
116116

117117
Developers wishing to enhance the CSharp generator should see the [developer documentation](https://github.com/real-logic/simple-binary-encoding/blob/master/csharp/README.md)
118118

119+
Rust Build
120+
------------
121+
The SBE Rust generator will produce 100% safe rust crates (no `unsafe` code will be generated). Generated crates do
122+
not have any dependencies on any libraries (including no SBE libraries). If you don't yet have Rust installed
123+
see [Rust: Getting Started](https://www.rust-lang.org/learn/get-started)
124+
125+
Generate the Rust codecs
126+
127+
$ ./gradlew generateRustCodecs
128+
129+
Run the Rust test from Gradle
130+
131+
$ ./gradlew runRustTests
132+
133+
Or run test directly with `Cargo`
134+
135+
$ cd rust
136+
$ cargo test
137+
119138
License (See LICENSE file for full license)
120139
-------------------------------------------
121140
Copyright 2013-2021 Real Logic Limited.

build.gradle

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,66 @@ project(':sbe-benchmarks') {
538538
javadoc.enabled = false
539539
}
540540

541+
/*
542+
* Rust codec targets used for testing
543+
*/
544+
task generateRustExamples(type: JavaExec) {
545+
mainClass = 'uk.co.real_logic.sbe.SbeTool'
546+
classpath = project(':sbe-all').sourceSets.main.runtimeClasspath
547+
systemProperties(
548+
'sbe.output.dir': 'generated/rust',
549+
'sbe.xinclude.aware': 'true',
550+
'sbe.target.language': 'Rust',
551+
'sbe.target.namespace': 'examples')
552+
args = ['sbe-samples/src/main/resources/example-schema.xml',
553+
'sbe-samples/src/main/resources/example-extension-schema.xml',
554+
'sbe-benchmarks/src/main/resources/car.xml',
555+
'sbe-benchmarks/src/main/resources/fix-message-samples.xml'
556+
]
557+
}
558+
559+
task generateRustTestCodecs(type: JavaExec) {
560+
mainClass = 'uk.co.real_logic.sbe.SbeTool'
561+
classpath = project(':sbe-all').sourceSets.main.runtimeClasspath
562+
systemProperties(
563+
'sbe.output.dir': 'generated/rust',
564+
'sbe.xinclude.aware': 'true',
565+
'sbe.target.language': 'Rust',
566+
'sbe.validation.xsd': validationXsdPath)
567+
args = ['sbe-tool/src/test/resources/issue435.xml',
568+
'sbe-tool/src/test/resources/example-bigendian-test-schema.xml',
569+
]
570+
}
571+
572+
task generateCarExampleDataFile(type: JavaExec) {
573+
mainClass = 'uk.co.real_logic.sbe.examples.ExampleUsingGeneratedStub'
574+
classpath = project(':sbe-samples').sourceSets.main.runtimeClasspath
575+
systemProperties('sbe.encoding.filename': 'rust/car_example_baseline_data.sbe')
576+
args = []
577+
standardOutput = new ByteArrayOutputStream()
578+
}
579+
580+
task generateCarExampleExtensionDataFile(type: JavaExec) {
581+
mainClass = 'uk.co.real_logic.sbe.examples.ExampleUsingGeneratedStubExtension'
582+
classpath = project(':sbe-samples').sourceSets.main.runtimeClasspath
583+
systemProperties('sbe.encoding.filename': 'rust/car_example_extension_data.sbe')
584+
args = []
585+
standardOutput = new ByteArrayOutputStream()
586+
}
587+
588+
task generateRustCodecs {
589+
description = 'Generate rust test codecs'
590+
dependsOn 'generateRustExamples', 'generateRustTestCodecs',
591+
'generateCarExampleDataFile', 'generateCarExampleExtensionDataFile'
592+
}
593+
594+
task runRustTests(type: Exec) {
595+
workingDir = './rust'
596+
executable = 'cargo'
597+
args = ['test']
598+
dependsOn 'generateRustCodecs'
599+
}
600+
541601
/*
542602
* Golang codec targets used for testing, benchmarking etc. We have
543603
* multiple targets as:

rust/Cargo.toml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[package]
2+
name = "sbe_rust_example"
3+
version = "0.1.0"
4+
authors = []
5+
edition = "2018"
6+
publish = false
7+
8+
[dependencies]
9+
examples_baseline = { path = "../generated/rust/baseline" }
10+
examples_extension = { path = "../generated/rust/extension" }
11+
examples_uk_co_real_logic_sbe_benchmarks = { path = "../generated/rust/uk_co_real_logic_sbe_benchmarks" }
12+
examples_uk_co_real_logic_sbe_benchmarks_fix = { path = "../generated/rust/uk_co_real_logic_sbe_benchmarks_fix" }
13+
14+
issue_435 = { path = "../generated/rust/issue435" }
15+
baseline_bigendian = { path = "../generated/rust/baseline-bigendian" }
16+
17+
[dev-dependencies]
18+
criterion = "0.3"
19+
20+
[[bench]]
21+
name = "car_benchmark"
22+
harness = false
23+
24+
[[bench]]
25+
name = "md_benchmark"
26+
harness = false

rust/benches/car_benchmark.rs

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
use criterion::{black_box, criterion_group, criterion_main, Criterion};
2+
use examples_uk_co_real_logic_sbe_benchmarks::*;
3+
4+
const MANUFACTURER: &[u8] = b"MANUFACTURER";
5+
const MODEL: &[u8] = b"MODEL";
6+
7+
struct State {
8+
buffer: Vec<u8>,
9+
}
10+
11+
pub fn car_encode_benchmark(c: &mut Criterion) {
12+
let mut state = State {
13+
buffer: vec![0u8; 1024],
14+
};
15+
16+
c.bench_function("encode car", |b| {
17+
b.iter(|| {
18+
let _length = encode(black_box(&mut state)).unwrap();
19+
})
20+
});
21+
}
22+
23+
fn encode(state: &mut State) -> SbeResult<usize> {
24+
let buffer = state.buffer.as_mut_slice();
25+
let mut car = CarEncoder::default();
26+
let mut fuel_figures = FuelFiguresEncoder::default();
27+
let mut performance_figures = PerformanceFiguresEncoder::default();
28+
let mut acceleration = AccelerationEncoder::default();
29+
let mut extras = OptionalExtras::default();
30+
31+
car = car.wrap(WriteBuf::new(buffer), message_header::ENCODED_LENGTH);
32+
car = car.header(0).parent()?;
33+
34+
car.code(Model::A);
35+
car.model_year(2005);
36+
car.serial_number(12345);
37+
car.available(BooleanType::T);
38+
car.vehicle_code([97, 98, 99, 100, 101, 102]); // abcdef
39+
car.some_numbers([0, 1, 2, 3, 4]);
40+
41+
extras.set_sports_pack(true);
42+
extras.set_sun_roof(true);
43+
car.extras(extras);
44+
45+
let mut engine = car.engine_encoder();
46+
engine.capacity(4200);
47+
engine.num_cylinders(8);
48+
engine.manufacturer_code([97, 98, 99]); // abc
49+
50+
car = engine.parent()?;
51+
fuel_figures = car.fuel_figures_encoder(3, fuel_figures);
52+
fuel_figures.advance()?;
53+
fuel_figures.speed(30);
54+
fuel_figures.mpg(35.9);
55+
56+
fuel_figures.advance()?;
57+
fuel_figures.speed(55);
58+
fuel_figures.mpg(49.0);
59+
60+
fuel_figures.advance()?;
61+
fuel_figures.speed(75);
62+
fuel_figures.mpg(40.0);
63+
64+
car = fuel_figures.parent()?;
65+
performance_figures = car.performance_figures_encoder(2, performance_figures);
66+
performance_figures.advance()?;
67+
performance_figures.octane_rating(95);
68+
69+
acceleration = performance_figures.acceleration_encoder(3, acceleration);
70+
acceleration.advance()?;
71+
acceleration.mph(30);
72+
acceleration.seconds(4.0);
73+
74+
acceleration.advance()?;
75+
acceleration.mph(60);
76+
acceleration.seconds(7.5);
77+
78+
acceleration.advance()?;
79+
acceleration.mph(100);
80+
acceleration.seconds(12.2);
81+
82+
performance_figures = acceleration.parent()?;
83+
performance_figures.advance()?;
84+
performance_figures.octane_rating(99);
85+
86+
acceleration = performance_figures.acceleration_encoder(3, acceleration);
87+
acceleration.advance()?;
88+
acceleration.mph(30);
89+
acceleration.seconds(3.8);
90+
91+
acceleration.advance()?;
92+
acceleration.mph(60);
93+
acceleration.seconds(7.1);
94+
95+
acceleration.advance()?;
96+
acceleration.mph(100);
97+
acceleration.seconds(11.8);
98+
99+
performance_figures = acceleration.parent()?;
100+
car = performance_figures.parent()?;
101+
102+
car.manufacturer(MANUFACTURER);
103+
car.model(MODEL);
104+
105+
Ok(car.encoded_length())
106+
}
107+
108+
pub fn car_decode_benchmark(c: &mut Criterion) {
109+
let mut state = State {
110+
buffer: vec![0u8; 1024],
111+
};
112+
let encoded_len = encode(&mut state).unwrap();
113+
114+
c.bench_function("decode car", |b| {
115+
b.iter(|| {
116+
let decoded_len = decode(black_box(&state)).unwrap();
117+
assert_eq!(encoded_len, decoded_len);
118+
})
119+
});
120+
}
121+
122+
fn decode(state: &State) -> SbeResult<usize> {
123+
let mut car = CarDecoder::default();
124+
125+
let buf = ReadBuf::new(state.buffer.as_slice());
126+
let header = MessageHeaderDecoder::default().wrap(buf, 0);
127+
car = car.header(header);
128+
129+
// Car...
130+
car.serial_number();
131+
car.model_year();
132+
car.available();
133+
car.code();
134+
135+
let len = car.some_numbers().len();
136+
for i in 0..len {
137+
let _ = car.some_numbers()[i];
138+
}
139+
140+
let len = car.vehicle_code().len();
141+
for i in 0..len {
142+
let _ = car.vehicle_code()[i];
143+
}
144+
145+
let extras = car.extras();
146+
extras.get_cruise_control();
147+
extras.get_sports_pack();
148+
extras.get_sun_roof();
149+
150+
let mut engine = car.engine_decoder();
151+
engine.capacity();
152+
engine.num_cylinders();
153+
engine.max_rpm();
154+
engine.manufacturer_code();
155+
engine.fuel();
156+
157+
car = engine.parent()?;
158+
let mut fuel_figures = car.fuel_figures_decoder();
159+
160+
while let Ok(Some(_)) = fuel_figures.advance() {
161+
fuel_figures.speed();
162+
fuel_figures.mpg();
163+
}
164+
165+
car = fuel_figures.parent()?;
166+
let mut performance_figures = car.performance_figures_decoder();
167+
168+
while let Ok(Some(_)) = performance_figures.advance() {
169+
performance_figures.octane_rating();
170+
let mut acceleration = performance_figures.acceleration_decoder();
171+
while let Ok(Some(_)) = acceleration.advance() {
172+
acceleration.mph();
173+
acceleration.seconds();
174+
}
175+
performance_figures = acceleration.parent()?;
176+
}
177+
178+
car = performance_figures.parent()?;
179+
let coord = car.manufacturer_decoder();
180+
car.manufacturer_slice(coord);
181+
let coord = car.model_decoder();
182+
car.model_slice(coord);
183+
184+
Ok(car.encoded_length())
185+
}
186+
187+
criterion_group!(benches, car_encode_benchmark, car_decode_benchmark);
188+
criterion_main!(benches);

0 commit comments

Comments
 (0)