mirror of
https://github.com/speice-io/marketdata-shootout
synced 2024-11-27 02:18:54 -05:00
Add the SBE runner
Works for the test dataset, now time to make sure everyone agrees.
This commit is contained in:
parent
df5198993b
commit
e58861458e
@ -59,23 +59,26 @@
|
|||||||
-->
|
-->
|
||||||
<field name="timestamp" id="3" type="int64"/>
|
<field name="timestamp" id="3" type="int64"/>
|
||||||
<!--
|
<!--
|
||||||
SBE specifically doesn't have "union" types, so we include both a `trade` and `quote`
|
SBE specifically doesn't have "union" types, so we include a type tag, and both
|
||||||
here and the tag to identify.
|
`trade` and `quote` as "optional". This style was chosen to approximate how
|
||||||
|
Cap'n Proto and Flatbuffers do it:
|
||||||
https://github.com/real-logic/simple-binary-encoding/issues/232
|
https://github.com/real-logic/simple-binary-encoding/issues/232
|
||||||
|
|
||||||
In the future, there are a couple options:
|
However, space is actually reserved for *both* `trade` and `quote` in the message;
|
||||||
1. Create a "payload header" type and promote "trade" and "quote" to <sbe:message>,
|
that is, the payload size is the same if neither, one, or both are filled.
|
||||||
since the SBE message header is already able to distinguish between message types
|
Other ways you can try to emulate unions:
|
||||||
2. Create a "group" for each message type, and just include no elements of that type.
|
1. Use a "payload header" composite type and promote "trade" and "quote" to <sbe:message>;
|
||||||
Uses a `u16` for each message type, instead of padding bytes for the message size.
|
SBE can distinguish message types based on the SBE header.
|
||||||
3. Split up the message components and use "session types" to chain things together,
|
2. Create a "group" for each message type; adds an extra `u16` per type, but overall payload
|
||||||
as in https://polysync.io/blog/session-types-for-hearty-codecs
|
size goes down because we no longer reserve space that is potentially unused.
|
||||||
|
3. Split up the message components into individual <composite/> blocks, and write
|
||||||
For now, this message format was chosen to approximate the schemas of Cap'n Proto and Flatbuffers
|
a state machine (by hand) to chain the blocks. For a better explanation,
|
||||||
|
see "session types" in:
|
||||||
|
https://polysync.io/blog/session-types-for-hearty-codecs
|
||||||
-->
|
-->
|
||||||
<field name="msg_type" id="4" type="MsgType"/>
|
<field name="msg_type" id="4" type="MsgType"/>
|
||||||
<field name="trade" id="5" type="Trade"/>
|
<field name="trade" id="5" type="Trade" presence="optional"/>
|
||||||
<field name="quote" id="6" type="Quote"/>
|
<field name="quote" id="6" type="Quote" presence="optional"/>
|
||||||
<data name="symbol" id="100" type="varAsciiEncoding"/>
|
<data name="symbol" id="100" type="varAsciiEncoding"/>
|
||||||
</group>
|
</group>
|
||||||
</sbe:message>
|
</sbe:message>
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
// This file is needed for tests outside the main source tree to find the project files
|
|
||||||
pub mod marketdata_capnp;
|
|
17
src/main.rs
17
src/main.rs
@ -17,9 +17,11 @@ use crate::iex::IexParser;
|
|||||||
pub mod marketdata_capnp;
|
pub mod marketdata_capnp;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub mod marketdata_generated; // Flatbuffers
|
pub mod marketdata_generated; // Flatbuffers
|
||||||
|
pub mod marketdata_sbe;
|
||||||
|
|
||||||
mod capnp_runner;
|
mod capnp_runner;
|
||||||
mod flatbuffers_runner;
|
mod flatbuffers_runner;
|
||||||
|
mod sbe_runner;
|
||||||
mod iex;
|
mod iex;
|
||||||
mod parsers;
|
mod parsers;
|
||||||
|
|
||||||
@ -81,6 +83,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
let mut capnp_writer = capnp_runner::CapnpWriter::new();
|
let mut capnp_writer = capnp_runner::CapnpWriter::new();
|
||||||
for iex_payload in parser {
|
for iex_payload in parser {
|
||||||
//let iex_payload = parser.next().unwrap();
|
//let iex_payload = parser.next().unwrap();
|
||||||
@ -93,6 +96,20 @@ fn main() {
|
|||||||
while let Ok(_) = capnp_reader.deserialize_unpacked(&mut read_buf, &mut summarizer) {
|
while let Ok(_) = capnp_reader.deserialize_unpacked(&mut read_buf, &mut summarizer) {
|
||||||
parsed_msgs += 1;
|
parsed_msgs += 1;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
let mut sbe_writer = sbe_runner::SBEWriter::new();
|
||||||
|
for iex_payload in parser {
|
||||||
|
//let iex_payload = parser.next().unwrap();
|
||||||
|
sbe_writer.serialize(&iex_payload, &mut output_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
let sbe_reader = sbe_runner::SBEReader::new();
|
||||||
|
let mut read_buf = StreamVec::new(output_buf);
|
||||||
|
let mut parsed_msgs: u64 = 0;
|
||||||
|
while let Ok(_) = sbe_reader.deserialize(&mut read_buf, &mut summarizer) {
|
||||||
|
parsed_msgs += 1;
|
||||||
|
}
|
||||||
|
|
||||||
dbg!(parsed_msgs);
|
dbg!(parsed_msgs);
|
||||||
dbg!(summarizer);
|
dbg!(summarizer);
|
||||||
|
@ -144,17 +144,17 @@ pub mod multi_message {
|
|||||||
impl Pipeline {
|
impl Pipeline {
|
||||||
}
|
}
|
||||||
mod _private {
|
mod _private {
|
||||||
use capnp::private::layout;
|
use capnp::private::layout;
|
||||||
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 1, pointers: 1 };
|
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 1, pointers: 1 };
|
||||||
pub const TYPE_ID: u64 = 0xd13b_1bd4_36e1_ca9f;
|
pub const TYPE_ID: u64 = 0xd13b_1bd4_36e1_ca9f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod message {
|
pub mod message {
|
||||||
pub use self::Which::{Quote, Trade};
|
pub use self::Which::{Quote, Trade};
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Owned;
|
pub struct Owned;
|
||||||
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
||||||
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
|
||||||
@ -362,9 +362,9 @@ pub mod message {
|
|||||||
impl Pipeline {
|
impl Pipeline {
|
||||||
}
|
}
|
||||||
mod _private {
|
mod _private {
|
||||||
use capnp::private::layout;
|
use capnp::private::layout;
|
||||||
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 2, pointers: 2 };
|
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 2, pointers: 2 };
|
||||||
pub const TYPE_ID: u64 = 0x91d7_2965_3a3d_4be4;
|
pub const TYPE_ID: u64 = 0x91d7_2965_3a3d_4be4;
|
||||||
}
|
}
|
||||||
pub enum Which<A0,A1> {
|
pub enum Which<A0,A1> {
|
||||||
@ -506,9 +506,9 @@ pub mod trade {
|
|||||||
impl Pipeline {
|
impl Pipeline {
|
||||||
}
|
}
|
||||||
mod _private {
|
mod _private {
|
||||||
use capnp::private::layout;
|
use capnp::private::layout;
|
||||||
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 2, pointers: 0 };
|
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 2, pointers: 0 };
|
||||||
pub const TYPE_ID: u64 = 0xd29e_10bd_4e5f_c241;
|
pub const TYPE_ID: u64 = 0xd29e_10bd_4e5f_c241;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -668,9 +668,9 @@ pub mod level_update {
|
|||||||
impl Pipeline {
|
impl Pipeline {
|
||||||
}
|
}
|
||||||
mod _private {
|
mod _private {
|
||||||
use capnp::private::layout;
|
use capnp::private::layout;
|
||||||
|
|
||||||
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 2, pointers: 0 };
|
pub const STRUCT_SIZE: layout::StructSize = layout::StructSize { data: 2, pointers: 0 };
|
||||||
pub const TYPE_ID: u64 = 0xe664_c3b5_6628_c453;
|
pub const TYPE_ID: u64 = 0xe664_c3b5_6628_c453;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,13 @@ extern crate core;
|
|||||||
/// Errors that may occur during the course of encoding or decoding.
|
/// Errors that may occur during the course of encoding or decoding.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum CodecErr {
|
pub enum CodecErr {
|
||||||
/// Too few bytes in the byte-slice to read or write the data structure relevant
|
/// Too few bytes in the byte-slice to read or write the data structure relevant
|
||||||
/// to the current state of the codec
|
/// to the current state of the codec
|
||||||
NotEnoughBytes,
|
NotEnoughBytes,
|
||||||
|
|
||||||
/// Groups and vardata are constrained by the numeric type chosen to represent their
|
/// Groups and vardata are constrained by the numeric type chosen to represent their
|
||||||
/// length as well as optional maxima imposed by the schema
|
/// length as well as optional maxima imposed by the schema
|
||||||
SliceIsLongerThanAllowedBySchema,
|
SliceIsLongerThanAllowedBySchema,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type CodecResult<T> = core::result::Result<T, CodecErr>;
|
pub type CodecResult<T> = core::result::Result<T, CodecErr>;
|
||||||
@ -23,208 +23,206 @@ pub type CodecResult<T> = core::result::Result<T, CodecErr>;
|
|||||||
/// Scratch Decoder Data Wrapper - codec internal use only
|
/// Scratch Decoder Data Wrapper - codec internal use only
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ScratchDecoderData<'d> {
|
pub struct ScratchDecoderData<'d> {
|
||||||
data: &'d [u8],
|
data: &'d [u8],
|
||||||
pos: usize,
|
pos: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> ScratchDecoderData<'d> {
|
impl<'d> ScratchDecoderData<'d> {
|
||||||
/// Create a struct reference overlaid atop the data buffer
|
/// Create a struct reference overlaid atop the data buffer
|
||||||
/// such that the struct's contents directly reflect the buffer.
|
/// such that the struct's contents directly reflect the buffer.
|
||||||
/// Advances the `pos` index by the size of the struct in bytes.
|
/// Advances the `pos` index by the size of the struct in bytes.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_type<T>(&mut self, num_bytes: usize) -> CodecResult<&'d T> {
|
fn read_type<T>(&mut self, num_bytes: usize) -> CodecResult<&'d T> {
|
||||||
let end = self.pos + num_bytes;
|
let end = self.pos + num_bytes;
|
||||||
if end <= self.data.len() {
|
if end <= self.data.len() {
|
||||||
let s = self.data[self.pos..end].as_ptr() as *mut T;
|
let s = self.data[self.pos..end].as_ptr() as *mut T;
|
||||||
let v: &'d T = unsafe { &*s };
|
let v: &'d T = unsafe { &*s };
|
||||||
self.pos = end;
|
self.pos = end;
|
||||||
Ok(v)
|
Ok(v)
|
||||||
} else {
|
} else {
|
||||||
Err(CodecErr::NotEnoughBytes)
|
Err(CodecErr::NotEnoughBytes)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Advances the `pos` index by a set number of bytes.
|
/// Advances the `pos` index by a set number of bytes.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn skip_bytes(&mut self, num_bytes: usize) -> CodecResult<()> {
|
fn skip_bytes(&mut self, num_bytes: usize) -> CodecResult<()> {
|
||||||
let end = self.pos + num_bytes;
|
let end = self.pos + num_bytes;
|
||||||
if end <= self.data.len() {
|
if end <= self.data.len() {
|
||||||
self.pos = end;
|
self.pos = end;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(CodecErr::NotEnoughBytes)
|
Err(CodecErr::NotEnoughBytes)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a slice reference overlaid atop the data buffer
|
/// Create a slice reference overlaid atop the data buffer
|
||||||
/// such that the slice's members' contents directly reflect the buffer.
|
/// such that the slice's members' contents directly reflect the buffer.
|
||||||
/// Advances the `pos` index by the size of the slice contents in bytes.
|
/// Advances the `pos` index by the size of the slice contents in bytes.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_slice<T>(&mut self, count: usize, bytes_per_item: usize) -> CodecResult<&'d [T]> {
|
fn read_slice<T>(&mut self, count: usize, bytes_per_item: usize) -> CodecResult<&'d [T]> {
|
||||||
let num_bytes = bytes_per_item * count;
|
let num_bytes = bytes_per_item * count;
|
||||||
let end = self.pos + num_bytes;
|
let end = self.pos + num_bytes;
|
||||||
if end <= self.data.len() {
|
if end <= self.data.len() {
|
||||||
let v: &'d [T] = unsafe {
|
let v: &'d [T] = unsafe {
|
||||||
core::slice::from_raw_parts(self.data[self.pos..end].as_ptr() as *const T, count)
|
core::slice::from_raw_parts(self.data[self.pos..end].as_ptr() as *const T, count)
|
||||||
};
|
};
|
||||||
self.pos = end;
|
self.pos = end;
|
||||||
Ok(v)
|
Ok(v)
|
||||||
} else {
|
} else {
|
||||||
Err(CodecErr::NotEnoughBytes)
|
Err(CodecErr::NotEnoughBytes)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scratch Encoder Data Wrapper - codec internal use only
|
/// Scratch Encoder Data Wrapper - codec internal use only
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ScratchEncoderData<'d> {
|
pub struct ScratchEncoderData<'d> {
|
||||||
data: &'d mut [u8],
|
data: &'d mut [u8],
|
||||||
pos: usize,
|
pos: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> ScratchEncoderData<'d> {
|
impl<'d> ScratchEncoderData<'d> {
|
||||||
/// Copy the bytes of a value into the data buffer
|
/// Copy the bytes of a value into the data buffer
|
||||||
/// Advances the `pos` index to after the newly-written bytes.
|
/// Advances the `pos` index to after the newly-written bytes.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_type<T>(&mut self, t: &T, num_bytes: usize) -> CodecResult<()> {
|
fn write_type<T>(&mut self, t: &T, num_bytes: usize) -> CodecResult<()> {
|
||||||
let end = self.pos + num_bytes;
|
let end = self.pos + num_bytes;
|
||||||
if end <= self.data.len() {
|
if end <= self.data.len() {
|
||||||
let source_bytes: &[u8] = unsafe {
|
let source_bytes: &[u8] = unsafe {
|
||||||
core::slice::from_raw_parts(t as *const T as *const u8, num_bytes)
|
core::slice::from_raw_parts(t as *const T as *const u8, num_bytes)
|
||||||
};
|
};
|
||||||
(&mut self.data[self.pos..end]).copy_from_slice(source_bytes);
|
(&mut self.data[self.pos..end]).copy_from_slice(source_bytes);
|
||||||
self.pos = end;
|
self.pos = end;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(CodecErr::NotEnoughBytes)
|
Err(CodecErr::NotEnoughBytes)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Advances the `pos` index by a set number of bytes.
|
/// Advances the `pos` index by a set number of bytes.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn skip_bytes(&mut self, num_bytes: usize) -> CodecResult<()> {
|
fn skip_bytes(&mut self, num_bytes: usize) -> CodecResult<()> {
|
||||||
let end = self.pos + num_bytes;
|
let end = self.pos + num_bytes;
|
||||||
if end <= self.data.len() {
|
if end <= self.data.len() {
|
||||||
self.pos = end;
|
self.pos = end;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(CodecErr::NotEnoughBytes)
|
Err(CodecErr::NotEnoughBytes)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a struct reference overlaid atop the data buffer
|
/// Create a struct reference overlaid atop the data buffer
|
||||||
/// such that changes to the struct directly edit the buffer.
|
/// such that changes to the struct directly edit the buffer.
|
||||||
/// Note that the initial content of the struct's fields may be garbage.
|
/// Note that the initial content of the struct's fields may be garbage.
|
||||||
/// Advances the `pos` index to after the newly-written bytes.
|
/// Advances the `pos` index to after the newly-written bytes.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn writable_overlay<T>(&mut self, num_bytes: usize) -> CodecResult<&'d mut T> {
|
fn writable_overlay<T>(&mut self, num_bytes: usize) -> CodecResult<&'d mut T> {
|
||||||
let end = self.pos + num_bytes;
|
let end = self.pos + num_bytes;
|
||||||
if end <= self.data.len() {
|
if end <= self.data.len() {
|
||||||
let v: &'d mut T = unsafe {
|
let v: &'d mut T = unsafe {
|
||||||
let s = self.data.as_ptr().offset(self.pos as isize) as *mut T;
|
let s = self.data.as_ptr().offset(self.pos as isize) as *mut T;
|
||||||
&mut *s
|
&mut *s
|
||||||
};
|
};
|
||||||
self.pos = end;
|
self.pos = end;
|
||||||
Ok(v)
|
Ok(v)
|
||||||
} else {
|
} else {
|
||||||
Err(CodecErr::NotEnoughBytes)
|
Err(CodecErr::NotEnoughBytes)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Copy the bytes of a value into the data buffer at a specific position
|
/// Copy the bytes of a value into the data buffer at a specific position
|
||||||
/// Does **not** alter the `pos` index.
|
/// Does **not** alter the `pos` index.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_at_position<T>(&mut self, position: usize, t: &T, num_bytes: usize) -> CodecResult<()> {
|
fn write_at_position<T>(&mut self, position: usize, t: &T, num_bytes: usize) -> CodecResult<()> {
|
||||||
let end = position + num_bytes;
|
let end = position + num_bytes;
|
||||||
if end <= self.data.len() {
|
if end <= self.data.len() {
|
||||||
let source_bytes: &[u8] = unsafe {
|
let source_bytes: &[u8] = unsafe {
|
||||||
core::slice::from_raw_parts(t as *const T as *const u8, num_bytes)
|
core::slice::from_raw_parts(t as *const T as *const u8, num_bytes)
|
||||||
};
|
};
|
||||||
(&mut self.data[position..end]).copy_from_slice(source_bytes);
|
(&mut self.data[position..end]).copy_from_slice(source_bytes);
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(CodecErr::NotEnoughBytes)
|
Err(CodecErr::NotEnoughBytes)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// Create a mutable slice overlaid atop the data buffer directly
|
}
|
||||||
/// such that changes to the slice contents directly edit the buffer
|
/// Create a mutable slice overlaid atop the data buffer directly
|
||||||
/// Note that the initial content of the slice's members' fields may be garbage.
|
/// such that changes to the slice contents directly edit the buffer
|
||||||
/// Advances the `pos` index to after the region representing the slice.
|
/// Note that the initial content of the slice's members' fields may be garbage.
|
||||||
#[inline]
|
/// Advances the `pos` index to after the region representing the slice.
|
||||||
fn writable_slice<T>(&mut self, count: usize, bytes_per_item: usize) -> CodecResult<&'d mut [T]> {
|
#[inline]
|
||||||
let end = self.pos + (count * bytes_per_item);
|
fn writable_slice<T>(&mut self, count: usize, bytes_per_item: usize) -> CodecResult<&'d mut [T]> {
|
||||||
if end <= self.data.len() {
|
let end = self.pos + (count * bytes_per_item);
|
||||||
let v: &'d mut [T] = unsafe {
|
if end <= self.data.len() {
|
||||||
core::slice::from_raw_parts_mut(self.data[self.pos..end].as_mut_ptr() as *mut T, count)
|
let v: &'d mut [T] = unsafe {
|
||||||
};
|
core::slice::from_raw_parts_mut(self.data[self.pos..end].as_mut_ptr() as *mut T, count)
|
||||||
self.pos = end;
|
};
|
||||||
Ok(v)
|
self.pos = end;
|
||||||
} else {
|
Ok(v)
|
||||||
Err(CodecErr::NotEnoughBytes)
|
} else {
|
||||||
}
|
Err(CodecErr::NotEnoughBytes)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Copy the raw bytes of a slice's contents into the data buffer
|
/// Copy the raw bytes of a slice's contents into the data buffer
|
||||||
/// Does **not** encode the length of the slice explicitly into the buffer.
|
/// Does **not** encode the length of the slice explicitly into the buffer.
|
||||||
/// Advances the `pos` index to after the newly-written slice bytes.
|
/// Advances the `pos` index to after the newly-written slice bytes.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write_slice_without_count<T>(&mut self, t: &[T], bytes_per_item: usize) -> CodecResult<()> {
|
fn write_slice_without_count<T>(&mut self, t: &[T], bytes_per_item: usize) -> CodecResult<()> {
|
||||||
let content_bytes_size = bytes_per_item * t.len();
|
let content_bytes_size = bytes_per_item * t.len();
|
||||||
let end = self.pos + content_bytes_size;
|
let end = self.pos + content_bytes_size;
|
||||||
if end <= self.data.len() {
|
if end <= self.data.len() {
|
||||||
let source_bytes: &[u8] = unsafe {
|
let source_bytes: &[u8] = unsafe {
|
||||||
core::slice::from_raw_parts(t.as_ptr() as *const u8, content_bytes_size)
|
core::slice::from_raw_parts(t.as_ptr() as *const u8, content_bytes_size)
|
||||||
};
|
};
|
||||||
(&mut self.data[self.pos..end]).copy_from_slice(source_bytes);
|
(&mut self.data[self.pos..end]).copy_from_slice(source_bytes);
|
||||||
self.pos = end;
|
self.pos = end;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(CodecErr::NotEnoughBytes)
|
Err(CodecErr::NotEnoughBytes)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience Either enum
|
/// Convenience Either enum
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
pub enum Either<L, R> {
|
pub enum Either<L, R> {
|
||||||
Left(L),
|
Left(L),
|
||||||
Right(R),
|
Right(R)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enum Side
|
/// Enum Side
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Side {
|
pub enum Side {
|
||||||
Buy = 0u8,
|
Buy = 0u8,
|
||||||
Sell = 1u8,
|
Sell = 1u8,
|
||||||
NullVal = 255u8,
|
NullVal = 255u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Side {
|
impl Default for Side {
|
||||||
fn default() -> Self { Side::NullVal }
|
fn default() -> Self { Side::NullVal }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enum MsgType
|
/// Enum MsgType
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum MsgType {
|
pub enum MsgType {
|
||||||
Trade = 0u8,
|
Trade = 0u8,
|
||||||
Quote = 1u8,
|
Quote = 1u8,
|
||||||
NullVal = 255u8,
|
NullVal = 255u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MsgType {
|
impl Default for MsgType {
|
||||||
fn default() -> Self { MsgType::NullVal }
|
fn default() -> Self { MsgType::NullVal }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Quote
|
/// Quote
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Quote {
|
pub struct Quote {
|
||||||
pub price: u64,
|
pub price: u64,
|
||||||
pub size: u32,
|
pub size: u32,
|
||||||
pub flags: u8,
|
pub flags: u8,
|
||||||
pub side: Side,
|
pub side: Side,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Quote {}
|
impl Quote {}
|
||||||
@ -233,8 +231,8 @@ impl Quote {}
|
|||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Trade {
|
pub struct Trade {
|
||||||
pub price: u64,
|
pub price: u64,
|
||||||
pub size: u32,
|
pub size: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Trade {}
|
impl Trade {}
|
||||||
@ -243,10 +241,10 @@ impl Trade {}
|
|||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MessageHeader {
|
pub struct MessageHeader {
|
||||||
pub block_length: u16,
|
pub block_length: u16,
|
||||||
pub template_id: u16,
|
pub template_id: u16,
|
||||||
pub schema_id: u16,
|
pub schema_id: u16,
|
||||||
pub version: u16,
|
pub version: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageHeader {}
|
impl MessageHeader {}
|
||||||
@ -255,8 +253,8 @@ impl MessageHeader {}
|
|||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct GroupSizeEncoding {
|
pub struct GroupSizeEncoding {
|
||||||
pub block_length: u16,
|
pub block_length: u16,
|
||||||
pub num_in_group: u16,
|
pub num_in_group: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GroupSizeEncoding {}
|
impl GroupSizeEncoding {}
|
||||||
@ -265,24 +263,24 @@ impl GroupSizeEncoding {}
|
|||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct VarAsciiEncoding {
|
pub struct VarAsciiEncoding {
|
||||||
pub length: u32,
|
pub length: u32,
|
||||||
pub var_data: u8,
|
pub var_data: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VarAsciiEncoding {}
|
impl VarAsciiEncoding {}
|
||||||
|
|
||||||
/// MessageHeader Decoder entry point
|
/// MessageHeader Decoder entry point
|
||||||
pub fn start_decoding_message_header<'d>(data: &'d [u8]) -> CodecResult<(&'d MessageHeader, ScratchDecoderData<'d>)> {
|
pub fn start_decoding_message_header<'d>(data: &'d [u8]) -> CodecResult<(&'d MessageHeader, ScratchDecoderData<'d>)> {
|
||||||
let mut scratch = ScratchDecoderData { data: data, pos: 0 };
|
let mut scratch = ScratchDecoderData { data: data, pos: 0 };
|
||||||
let v = scratch.read_type::<MessageHeader>(8)?;
|
let v = scratch.read_type::<MessageHeader>(8)?;
|
||||||
Ok((v, scratch))
|
Ok((v, scratch))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MultiMessage Fixed-size Fields (8 bytes)
|
/// MultiMessage Fixed-size Fields (8 bytes)
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MultiMessageFields {
|
pub struct MultiMessageFields {
|
||||||
pub sequence_number: u64,
|
pub sequence_number: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -291,293 +289,279 @@ impl MultiMessageFields {}
|
|||||||
/// MultiMessage specific Message Header
|
/// MultiMessage specific Message Header
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub struct MultiMessageMessageHeader {
|
pub struct MultiMessageMessageHeader {
|
||||||
pub message_header: MessageHeader
|
pub message_header: MessageHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MultiMessageMessageHeader {
|
impl MultiMessageMessageHeader {
|
||||||
pub const BLOCK_LENGTH: u16 = 8;
|
pub const BLOCK_LENGTH: u16 = 8;
|
||||||
pub const TEMPLATE_ID: u16 = 1;
|
pub const TEMPLATE_ID: u16 = 1;
|
||||||
pub const SCHEMA_ID: u16 = 1;
|
pub const SCHEMA_ID: u16 = 1;
|
||||||
pub const VERSION: u16 = 0;
|
pub const VERSION: u16 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MultiMessageMessageHeader {
|
impl Default for MultiMessageMessageHeader {
|
||||||
fn default() -> MultiMessageMessageHeader {
|
fn default() -> MultiMessageMessageHeader {
|
||||||
MultiMessageMessageHeader {
|
MultiMessageMessageHeader {
|
||||||
message_header: MessageHeader {
|
message_header: MessageHeader {
|
||||||
block_length: 8u16,
|
block_length: 8u16,
|
||||||
template_id: 1u16,
|
template_id: 1u16,
|
||||||
schema_id: 1u16,
|
schema_id: 1u16,
|
||||||
version: 0u16,
|
version: 0u16,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Group fixed-field member representations
|
/// Group fixed-field member representations
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MultiMessageMessagesMember {
|
pub struct MultiMessageMessagesMember {
|
||||||
pub timestamp: i64,
|
pub timestamp: i64,
|
||||||
pub msg_type: MsgType,
|
pub msg_type: MsgType,
|
||||||
pub trade: Trade,
|
pub trade: Trade,
|
||||||
pub quote: Quote,
|
pub quote: Quote,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MultiMessageMessagesMember {}
|
impl MultiMessageMessagesMember {}
|
||||||
|
|
||||||
/// MultiMessageDecoderDone
|
/// MultiMessageDecoderDone
|
||||||
pub struct MultiMessageDecoderDone<'d> {
|
pub struct MultiMessageDecoderDone<'d> {
|
||||||
scratch: ScratchDecoderData<'d>,
|
scratch: ScratchDecoderData<'d>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> MultiMessageDecoderDone<'d> {
|
impl<'d> MultiMessageDecoderDone<'d> {
|
||||||
/// Returns the number of bytes decoded
|
/// Returns the number of bytes decoded
|
||||||
pub fn unwrap(self) -> usize {
|
pub fn unwrap(self) -> usize {
|
||||||
self.scratch.pos
|
self.scratch.pos
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrap(scratch: ScratchDecoderData<'d>) -> MultiMessageDecoderDone<'d> {
|
pub fn wrap(scratch: ScratchDecoderData<'d>) -> MultiMessageDecoderDone<'d> {
|
||||||
MultiMessageDecoderDone { scratch: scratch }
|
MultiMessageDecoderDone { scratch: scratch }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// symbol variable-length data
|
/// symbol variable-length data
|
||||||
pub struct MultiMessageMessagesSymbolDecoder<'d> {
|
pub struct MultiMessageMessagesSymbolDecoder<'d> {
|
||||||
parent: MultiMessageMessagesMemberDecoder<'d>,
|
parent: MultiMessageMessagesMemberDecoder<'d>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> MultiMessageMessagesSymbolDecoder<'d> {
|
impl<'d> MultiMessageMessagesSymbolDecoder<'d> {
|
||||||
fn wrap(parent: MultiMessageMessagesMemberDecoder<'d>) -> Self {
|
fn wrap(parent: MultiMessageMessagesMemberDecoder<'d>) -> Self {
|
||||||
MultiMessageMessagesSymbolDecoder { parent: parent }
|
MultiMessageMessagesSymbolDecoder { parent: parent }
|
||||||
}
|
}
|
||||||
pub fn symbol(mut self) -> CodecResult<(&'d [u8], Either<MultiMessageMessagesMemberDecoder<'d>, MultiMessageDecoderDone<'d>>)> {
|
pub fn symbol(mut self) -> CodecResult<(&'d [u8], Either<MultiMessageMessagesMemberDecoder<'d>, MultiMessageDecoderDone<'d>>)> {
|
||||||
let count = *self.parent.scratch.read_type::<u32>(4)?;
|
let count = *self.parent.scratch.read_type::<u32>(4)?;
|
||||||
Ok((self.parent.scratch.read_slice::<u8>(count as usize, 1)?, self.parent.after_member()))
|
Ok((self.parent.scratch.read_slice::<u8>(count as usize, 1)?, self.parent.after_member()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MultiMessageMessages Decoder for fields and header
|
/// MultiMessageMessages Decoder for fields and header
|
||||||
pub struct MultiMessageMessagesMemberDecoder<'d> {
|
pub struct MultiMessageMessagesMemberDecoder<'d> {
|
||||||
scratch: ScratchDecoderData<'d>,
|
scratch: ScratchDecoderData<'d>,
|
||||||
max_index: u16,
|
max_index: u16,
|
||||||
index: u16,
|
index: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> MultiMessageMessagesMemberDecoder<'d> {
|
impl<'d> MultiMessageMessagesMemberDecoder<'d> {
|
||||||
fn new(scratch: ScratchDecoderData<'d>, count: u16) -> Self {
|
fn new(scratch: ScratchDecoderData<'d>, count: u16) -> Self {
|
||||||
assert!(count > 0u16);
|
assert!(count > 0u16);
|
||||||
MultiMessageMessagesMemberDecoder {
|
MultiMessageMessagesMemberDecoder {
|
||||||
scratch: scratch,
|
scratch: scratch,
|
||||||
max_index: count - 1,
|
max_index: count - 1,
|
||||||
index: 0,
|
index: 0,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn next_messages_member(mut self) -> CodecResult<(&'d MultiMessageMessagesMember, MultiMessageMessagesSymbolDecoder<'d>)> {
|
pub fn next_messages_member(mut self) -> CodecResult<(&'d MultiMessageMessagesMember, MultiMessageMessagesSymbolDecoder<'d>)> {
|
||||||
let v = self.scratch.read_type::<MultiMessageMessagesMember>(35)?;
|
let v = self.scratch.read_type::<MultiMessageMessagesMember>(35)?;
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
Ok((v, MultiMessageMessagesSymbolDecoder::wrap(self)))
|
Ok((v, MultiMessageMessagesSymbolDecoder::wrap(self)))
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn after_member(self) -> Either<MultiMessageMessagesMemberDecoder<'d>, MultiMessageDecoderDone<'d>> {
|
fn after_member(self) -> Either<MultiMessageMessagesMemberDecoder<'d>, MultiMessageDecoderDone<'d>> {
|
||||||
if self.index <= self.max_index {
|
if self.index <= self.max_index {
|
||||||
Either::Left(self)
|
Either::Left(self)
|
||||||
} else {
|
} else {
|
||||||
Either::Right(MultiMessageDecoderDone::wrap(self.scratch))
|
Either::Right(MultiMessageDecoderDone::wrap(self.scratch))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MultiMessageMessagesHeaderDecoder<'d> {
|
pub struct MultiMessageMessagesHeaderDecoder<'d> {
|
||||||
scratch: ScratchDecoderData<'d>,
|
scratch: ScratchDecoderData<'d>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> MultiMessageMessagesHeaderDecoder<'d> {
|
impl<'d> MultiMessageMessagesHeaderDecoder<'d> {
|
||||||
fn wrap(scratch: ScratchDecoderData<'d>) -> Self {
|
fn wrap(scratch: ScratchDecoderData<'d>) -> Self {
|
||||||
MultiMessageMessagesHeaderDecoder { scratch: scratch }
|
MultiMessageMessagesHeaderDecoder { scratch: scratch }
|
||||||
}
|
}
|
||||||
pub fn messages_individually(mut self) -> CodecResult<Either<MultiMessageMessagesMemberDecoder<'d>, MultiMessageDecoderDone<'d>>> {
|
pub fn messages_individually(mut self) -> CodecResult<Either<MultiMessageMessagesMemberDecoder<'d>, MultiMessageDecoderDone<'d>>> {
|
||||||
let dim = self.scratch.read_type::<GroupSizeEncoding>(4)?;
|
let dim = self.scratch.read_type::<GroupSizeEncoding>(4)?;
|
||||||
if dim.num_in_group > 0 {
|
if dim.num_in_group > 0 {
|
||||||
Ok(Either::Left(MultiMessageMessagesMemberDecoder::new(self.scratch, dim.num_in_group)))
|
Ok(Either::Left(MultiMessageMessagesMemberDecoder::new(self.scratch, dim.num_in_group)))
|
||||||
} else {
|
} else {
|
||||||
Ok(Either::Right(MultiMessageDecoderDone::wrap(self.scratch)))
|
Ok(Either::Right(MultiMessageDecoderDone::wrap(self.scratch)))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MultiMessage Fixed fields Decoder
|
/// MultiMessage Fixed fields Decoder
|
||||||
pub struct MultiMessageFieldsDecoder<'d> {
|
pub struct MultiMessageFieldsDecoder<'d> {
|
||||||
scratch: ScratchDecoderData<'d>,
|
scratch: ScratchDecoderData<'d>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> MultiMessageFieldsDecoder<'d> {
|
impl<'d> MultiMessageFieldsDecoder<'d> {
|
||||||
pub fn wrap(scratch: ScratchDecoderData<'d>) -> MultiMessageFieldsDecoder<'d> {
|
pub fn wrap(scratch: ScratchDecoderData<'d>) -> MultiMessageFieldsDecoder<'d> {
|
||||||
MultiMessageFieldsDecoder { scratch: scratch }
|
MultiMessageFieldsDecoder { scratch: scratch }
|
||||||
}
|
}
|
||||||
pub fn multi_message_fields(mut self) -> CodecResult<(&'d MultiMessageFields, MultiMessageMessagesHeaderDecoder<'d>)> {
|
pub fn multi_message_fields(mut self) -> CodecResult<(&'d MultiMessageFields, MultiMessageMessagesHeaderDecoder<'d>)> {
|
||||||
let v = self.scratch.read_type::<MultiMessageFields>(8)?;
|
let v = self.scratch.read_type::<MultiMessageFields>(8)?;
|
||||||
Ok((v, MultiMessageMessagesHeaderDecoder::wrap(self.scratch)))
|
Ok((v, MultiMessageMessagesHeaderDecoder::wrap(self.scratch)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MultiMessageMessageHeaderDecoder
|
/// MultiMessageMessageHeaderDecoder
|
||||||
pub struct MultiMessageMessageHeaderDecoder<'d> {
|
pub struct MultiMessageMessageHeaderDecoder<'d> {
|
||||||
scratch: ScratchDecoderData<'d>,
|
scratch: ScratchDecoderData<'d>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> MultiMessageMessageHeaderDecoder<'d> {
|
impl<'d> MultiMessageMessageHeaderDecoder<'d> {
|
||||||
pub fn wrap(scratch: ScratchDecoderData<'d>) -> MultiMessageMessageHeaderDecoder<'d> {
|
pub fn wrap(scratch: ScratchDecoderData<'d>) -> MultiMessageMessageHeaderDecoder<'d> {
|
||||||
MultiMessageMessageHeaderDecoder { scratch: scratch }
|
MultiMessageMessageHeaderDecoder { scratch: scratch }
|
||||||
}
|
}
|
||||||
pub fn header(mut self) -> CodecResult<(&'d MessageHeader, MultiMessageFieldsDecoder<'d>)> {
|
pub fn header(mut self) -> CodecResult<(&'d MessageHeader, MultiMessageFieldsDecoder<'d>)> {
|
||||||
let v = self.scratch.read_type::<MessageHeader>(8)?;
|
let v = self.scratch.read_type::<MessageHeader>(8)?;
|
||||||
Ok((v, MultiMessageFieldsDecoder::wrap(self.scratch)))
|
Ok((v, MultiMessageFieldsDecoder::wrap(self.scratch)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MultiMessage Decoder entry point
|
/// MultiMessage Decoder entry point
|
||||||
pub fn start_decoding_multi_message<'d>(data: &'d [u8]) -> MultiMessageMessageHeaderDecoder<'d> {
|
pub fn start_decoding_multi_message<'d>(data: &'d [u8]) -> MultiMessageMessageHeaderDecoder<'d> {
|
||||||
MultiMessageMessageHeaderDecoder::wrap(ScratchDecoderData { data: data, pos: 0 })
|
MultiMessageMessageHeaderDecoder::wrap(ScratchDecoderData { data: data, pos: 0 })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MultiMessageEncoderDone
|
/// MultiMessageEncoderDone
|
||||||
pub struct MultiMessageEncoderDone<'d> {
|
pub struct MultiMessageEncoderDone<'d> {
|
||||||
scratch: ScratchEncoderData<'d>,
|
scratch: ScratchEncoderData<'d>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> MultiMessageEncoderDone<'d> {
|
impl<'d> MultiMessageEncoderDone<'d> {
|
||||||
/// Returns the number of bytes encoded
|
/// Returns the number of bytes encoded
|
||||||
pub fn unwrap(self) -> usize {
|
pub fn unwrap(self) -> usize {
|
||||||
self.scratch.pos
|
self.scratch.pos
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrap(scratch: ScratchEncoderData<'d>) -> MultiMessageEncoderDone<'d> {
|
pub fn wrap(scratch: ScratchEncoderData<'d>) -> MultiMessageEncoderDone<'d> {
|
||||||
MultiMessageEncoderDone { scratch: scratch }
|
MultiMessageEncoderDone { scratch: scratch }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// symbol variable-length data
|
/// symbol variable-length data
|
||||||
pub struct MultiMessageMessagesSymbolEncoder<'d> {
|
pub struct MultiMessageMessagesSymbolEncoder<'d> {
|
||||||
parent: MultiMessageMessagesMemberEncoder<'d>,
|
parent: MultiMessageMessagesMemberEncoder<'d>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> MultiMessageMessagesSymbolEncoder<'d> {
|
impl<'d> MultiMessageMessagesSymbolEncoder<'d> {
|
||||||
fn wrap(parent: MultiMessageMessagesMemberEncoder<'d>) -> Self {
|
fn wrap(parent: MultiMessageMessagesMemberEncoder<'d>) -> Self {
|
||||||
MultiMessageMessagesSymbolEncoder { parent: parent }
|
MultiMessageMessagesSymbolEncoder { parent: parent }
|
||||||
}
|
}
|
||||||
pub fn symbol(mut self, s: &'d [u8]) -> CodecResult<MultiMessageMessagesMemberEncoder> {
|
pub fn symbol(mut self, s: &'d [u8]) -> CodecResult<MultiMessageMessagesMemberEncoder> {
|
||||||
let l = s.len();
|
let l = s.len();
|
||||||
if l > 4294967294 {
|
if l > 4294967294 {
|
||||||
return Err(CodecErr::SliceIsLongerThanAllowedBySchema);
|
return Err(CodecErr::SliceIsLongerThanAllowedBySchema)
|
||||||
}
|
|
||||||
// Write data length
|
|
||||||
self.parent.scratch.write_type::<u32>(&(l as u32), 4)?; // group length
|
|
||||||
self.parent.scratch.write_slice_without_count::<u8>(s, 1)?;
|
|
||||||
Ok(self.parent)
|
|
||||||
}
|
}
|
||||||
|
// Write data length
|
||||||
|
self.parent.scratch.write_type::<u32>(&(l as u32), 4)?; // group length
|
||||||
|
self.parent.scratch.write_slice_without_count::<u8>(s, 1)?;
|
||||||
|
Ok(self.parent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MultiMessageMessages Encoder for fields and header
|
/// MultiMessageMessages Encoder for fields and header
|
||||||
pub struct MultiMessageMessagesMemberEncoder<'d> {
|
pub struct MultiMessageMessagesMemberEncoder<'d> {
|
||||||
scratch: ScratchEncoderData<'d>,
|
scratch: ScratchEncoderData<'d>,
|
||||||
count_write_pos: usize,
|
count_write_pos: usize,
|
||||||
count: u16,
|
count: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> MultiMessageMessagesMemberEncoder<'d> {
|
impl<'d> MultiMessageMessagesMemberEncoder<'d> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn new(scratch: ScratchEncoderData<'d>, count_write_pos: usize) -> Self {
|
fn new(scratch: ScratchEncoderData<'d>, count_write_pos: usize) -> Self {
|
||||||
MultiMessageMessagesMemberEncoder {
|
MultiMessageMessagesMemberEncoder {
|
||||||
scratch: scratch,
|
scratch: scratch,
|
||||||
count_write_pos: count_write_pos,
|
count_write_pos: count_write_pos,
|
||||||
count: 0,
|
count: 0,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_messages_member(mut self, fields: &MultiMessageMessagesMember) -> CodecResult<MultiMessageMessagesSymbolEncoder<'d>> {
|
pub fn next_messages_member(mut self, fields: &MultiMessageMessagesMember) -> CodecResult<MultiMessageMessagesSymbolEncoder<'d>> {
|
||||||
self.scratch.write_type::<MultiMessageMessagesMember>(fields, 35)?; // block length
|
self.scratch.write_type::<MultiMessageMessagesMember>(fields, 35)?; // block length
|
||||||
self.count += 1;
|
self.count += 1;
|
||||||
Ok(MultiMessageMessagesSymbolEncoder::wrap(self))
|
Ok(MultiMessageMessagesSymbolEncoder::wrap(self))
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn done_with_messages(mut self) -> CodecResult<MultiMessageEncoderDone<'d>> {
|
pub fn done_with_messages(mut self) -> CodecResult<MultiMessageEncoderDone<'d>> {
|
||||||
self.scratch.write_at_position::<u16>(self.count_write_pos, &self.count, 2)?;
|
self.scratch.write_at_position::<u16>(self.count_write_pos, &self.count, 2)?;
|
||||||
Ok(MultiMessageEncoderDone::wrap(self.scratch))
|
Ok(MultiMessageEncoderDone::wrap(self.scratch))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MultiMessageMessagesHeaderEncoder<'d> {
|
pub struct MultiMessageMessagesHeaderEncoder<'d> {
|
||||||
scratch: ScratchEncoderData<'d>,
|
scratch: ScratchEncoderData<'d>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> MultiMessageMessagesHeaderEncoder<'d> {
|
impl<'d> MultiMessageMessagesHeaderEncoder<'d> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn wrap(scratch: ScratchEncoderData<'d>) -> Self {
|
fn wrap(scratch: ScratchEncoderData<'d>) -> Self {
|
||||||
MultiMessageMessagesHeaderEncoder { scratch: scratch }
|
MultiMessageMessagesHeaderEncoder { scratch: scratch }
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn messages_individually(mut self) -> CodecResult<MultiMessageMessagesMemberEncoder<'d>> {
|
pub fn messages_individually(mut self) -> CodecResult<MultiMessageMessagesMemberEncoder<'d>> {
|
||||||
self.scratch.write_type::<u16>(&35u16, 2)?; // block length
|
self.scratch.write_type::<u16>(&35u16, 2)?; // block length
|
||||||
let count_pos = self.scratch.pos;
|
let count_pos = self.scratch.pos;
|
||||||
self.scratch.write_type::<u16>(&0, 2)?; // preliminary group member count
|
self.scratch.write_type::<u16>(&0, 2)?; // preliminary group member count
|
||||||
Ok(MultiMessageMessagesMemberEncoder::new(self.scratch, count_pos))
|
Ok(MultiMessageMessagesMemberEncoder::new(self.scratch, count_pos))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MultiMessage Fixed fields Encoder
|
/// MultiMessage Fixed fields Encoder
|
||||||
pub struct MultiMessageFieldsEncoder<'d> {
|
pub struct MultiMessageFieldsEncoder<'d> {
|
||||||
scratch: ScratchEncoderData<'d>,
|
scratch: ScratchEncoderData<'d>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> MultiMessageFieldsEncoder<'d> {
|
impl<'d> MultiMessageFieldsEncoder<'d> {
|
||||||
pub fn wrap(scratch: ScratchEncoderData<'d>) -> MultiMessageFieldsEncoder<'d> {
|
pub fn wrap(scratch: ScratchEncoderData<'d>) -> MultiMessageFieldsEncoder<'d> {
|
||||||
MultiMessageFieldsEncoder { scratch: scratch }
|
MultiMessageFieldsEncoder { scratch: scratch }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a mutable struct reference overlaid atop the data buffer
|
/// Create a mutable struct reference overlaid atop the data buffer
|
||||||
/// such that changes to the struct directly edit the buffer.
|
/// such that changes to the struct directly edit the buffer.
|
||||||
/// Note that the initial content of the struct's fields may be garbage.
|
/// Note that the initial content of the struct's fields may be garbage.
|
||||||
pub fn multi_message_fields(mut self) -> CodecResult<(&'d mut MultiMessageFields, MultiMessageMessagesHeaderEncoder<'d>)> {
|
pub fn multi_message_fields(mut self) -> CodecResult<(&'d mut MultiMessageFields, MultiMessageMessagesHeaderEncoder<'d>)> {
|
||||||
let v = self.scratch.writable_overlay::<MultiMessageFields>(8 + 0)?;
|
let v = self.scratch.writable_overlay::<MultiMessageFields>(8 + 0)?;
|
||||||
Ok((v, MultiMessageMessagesHeaderEncoder::wrap(self.scratch)))
|
Ok((v, MultiMessageMessagesHeaderEncoder::wrap(self.scratch)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copy the bytes of a value into the data buffer
|
/// Copy the bytes of a value into the data buffer
|
||||||
pub fn multi_message_fields_copy(mut self, t: &MultiMessageFields) -> CodecResult<MultiMessageMessagesHeaderEncoder<'d>> {
|
pub fn multi_message_fields_copy(mut self, t: &MultiMessageFields) -> CodecResult<MultiMessageMessagesHeaderEncoder<'d>> {
|
||||||
self.scratch.write_type::<MultiMessageFields>(t, 8)?;
|
self.scratch.write_type::<MultiMessageFields>(t, 8)?;
|
||||||
Ok(MultiMessageMessagesHeaderEncoder::wrap(self.scratch))
|
Ok(MultiMessageMessagesHeaderEncoder::wrap(self.scratch))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MultiMessageMessageHeaderEncoder
|
/// MultiMessageMessageHeaderEncoder
|
||||||
pub struct MultiMessageMessageHeaderEncoder<'d> {
|
pub struct MultiMessageMessageHeaderEncoder<'d> {
|
||||||
scratch: ScratchEncoderData<'d>,
|
scratch: ScratchEncoderData<'d>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> MultiMessageMessageHeaderEncoder<'d> {
|
impl<'d> MultiMessageMessageHeaderEncoder<'d> {
|
||||||
pub fn wrap(scratch: ScratchEncoderData<'d>) -> MultiMessageMessageHeaderEncoder<'d> {
|
pub fn wrap(scratch: ScratchEncoderData<'d>) -> MultiMessageMessageHeaderEncoder<'d> {
|
||||||
MultiMessageMessageHeaderEncoder { scratch: scratch }
|
MultiMessageMessageHeaderEncoder { scratch: scratch }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a mutable struct reference overlaid atop the data buffer
|
/// Create a mutable struct reference overlaid atop the data buffer
|
||||||
/// such that changes to the struct directly edit the buffer.
|
/// such that changes to the struct directly edit the buffer.
|
||||||
/// Note that the initial content of the struct's fields may be garbage.
|
/// Note that the initial content of the struct's fields may be garbage.
|
||||||
pub fn header(mut self) -> CodecResult<(&'d mut MessageHeader, MultiMessageFieldsEncoder<'d>)> {
|
pub fn header(mut self) -> CodecResult<(&'d mut MessageHeader, MultiMessageFieldsEncoder<'d>)> {
|
||||||
let v = self.scratch.writable_overlay::<MessageHeader>(8 + 0)?;
|
let v = self.scratch.writable_overlay::<MessageHeader>(8 + 0)?;
|
||||||
Ok((v, MultiMessageFieldsEncoder::wrap(self.scratch)))
|
Ok((v, MultiMessageFieldsEncoder::wrap(self.scratch)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copy the bytes of a value into the data buffer
|
/// Copy the bytes of a value into the data buffer
|
||||||
pub fn header_copy(mut self, t: &MessageHeader) -> CodecResult<MultiMessageFieldsEncoder<'d>> {
|
pub fn header_copy(mut self, t: &MessageHeader) -> CodecResult<MultiMessageFieldsEncoder<'d>> {
|
||||||
self.scratch.write_type::<MessageHeader>(t, 8)?;
|
self.scratch.write_type::<MessageHeader>(t, 8)?;
|
||||||
Ok(MultiMessageFieldsEncoder::wrap(self.scratch))
|
Ok(MultiMessageFieldsEncoder::wrap(self.scratch))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MultiMessage Encoder entry point
|
/// MultiMessage Encoder entry point
|
||||||
pub fn start_encoding_multi_message<'d>(data: &'d mut [u8]) -> MultiMessageMessageHeaderEncoder<'d> {
|
pub fn start_encoding_multi_message<'d>(data: &'d mut [u8]) -> MultiMessageMessageHeaderEncoder<'d> {
|
||||||
MultiMessageMessageHeaderEncoder::wrap(ScratchEncoderData { data: data, pos: 0 })
|
MultiMessageMessageHeaderEncoder::wrap(ScratchEncoderData { data: data, pos: 0 })
|
||||||
}
|
}
|
||||||
|
144
src/sbe_runner.rs
Normal file
144
src/sbe_runner.rs
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
use std::io::{BufRead, Write};
|
||||||
|
use std::str::from_utf8_unchecked;
|
||||||
|
|
||||||
|
use nom::bytes::complete::take_until;
|
||||||
|
use nom::IResult;
|
||||||
|
|
||||||
|
use crate::{marketdata_sbe, StreamVec, Summarizer};
|
||||||
|
use crate::iex::{IexMessage, IexPayload};
|
||||||
|
use crate::marketdata_sbe::{Either, MultiMessageFields, MultiMessageMessageHeader, MultiMessageMessagesMember, MultiMessageMessagesMemberEncoder, MultiMessageMessagesSymbolEncoder, Side, start_decoding_multi_message, start_encoding_multi_message};
|
||||||
|
|
||||||
|
fn __take_until<'a>(tag: &'static str, input: &'a [u8]) -> IResult<&'a [u8], &'a [u8]> {
|
||||||
|
take_until(tag)(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_symbol(sym: &[u8; 8]) -> &str {
|
||||||
|
// TODO: Use the `jetscii` library for all that SIMD goodness
|
||||||
|
// IEX guarantees ASCII, so we're fine using an unsafe conversion
|
||||||
|
let (_, sym_bytes) = __take_until(" ", &sym[..]).unwrap();
|
||||||
|
unsafe { from_utf8_unchecked(sym_bytes) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SBEWriter {
|
||||||
|
/// Buffer to construct messages before copying. While SBE benefits
|
||||||
|
/// from easily being able to create messages directly in output buffer,
|
||||||
|
/// we'll construct in a scratch buffer and then copy to more fairly
|
||||||
|
/// benchmark against Cap'n Proto and Flatbuffers.
|
||||||
|
scratch_buffer: Vec<u8>,
|
||||||
|
default_header: MultiMessageMessageHeader,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SBEWriter {
|
||||||
|
pub fn new() -> SBEWriter {
|
||||||
|
SBEWriter {
|
||||||
|
// 8K scratch buffer is *way* more than necessary,
|
||||||
|
// but we don't want to run into issues with not enough
|
||||||
|
// data to encode messages
|
||||||
|
scratch_buffer: vec![0; 1024 * 8],
|
||||||
|
default_header: MultiMessageMessageHeader::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize(&mut self, payload: &IexPayload, output: &mut Vec<u8>) {
|
||||||
|
let (fields, encoder) = start_encoding_multi_message(&mut self.scratch_buffer[..])
|
||||||
|
.header_copy(&self.default_header.message_header).unwrap()
|
||||||
|
.multi_message_fields().unwrap();
|
||||||
|
fields.sequence_number = payload.first_seq_no;
|
||||||
|
|
||||||
|
let mut encoder = encoder.messages_individually().unwrap();
|
||||||
|
let mut encoder: MultiMessageMessagesMemberEncoder = payload.messages.iter().fold(encoder, |enc, m| {
|
||||||
|
match m {
|
||||||
|
IexMessage::TradeReport(tr) => {
|
||||||
|
let fields = MultiMessageMessagesMember {
|
||||||
|
msg_type: marketdata_sbe::MsgType::Trade,
|
||||||
|
timestamp: tr.timestamp,
|
||||||
|
trade: marketdata_sbe::Trade {
|
||||||
|
size: tr.size,
|
||||||
|
price: tr.price,
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let sym_enc: MultiMessageMessagesSymbolEncoder = enc.next_messages_member(&fields).unwrap();
|
||||||
|
sym_enc.symbol(parse_symbol(&tr.symbol).as_bytes()).unwrap()
|
||||||
|
}
|
||||||
|
IexMessage::PriceLevelUpdate(plu) => {
|
||||||
|
let fields = MultiMessageMessagesMember {
|
||||||
|
msg_type: marketdata_sbe::MsgType::Quote,
|
||||||
|
timestamp: plu.timestamp,
|
||||||
|
quote: marketdata_sbe::Quote {
|
||||||
|
price: plu.price,
|
||||||
|
size: plu.size,
|
||||||
|
flags: plu.event_flags,
|
||||||
|
side: if plu.msg_type == 0x38 { Side::Buy } else { Side::Sell },
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let sym_enc: MultiMessageMessagesSymbolEncoder = enc.next_messages_member(&fields).unwrap();
|
||||||
|
sym_enc.symbol(parse_symbol(&plu.symbol).as_bytes()).unwrap()
|
||||||
|
}
|
||||||
|
_ => enc
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let finished = encoder.done_with_messages().unwrap();
|
||||||
|
let data_len = finished.unwrap();
|
||||||
|
|
||||||
|
output.write(&self.scratch_buffer[..data_len]).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SBEReader;
|
||||||
|
|
||||||
|
impl SBEReader {
|
||||||
|
pub fn new() -> SBEReader {
|
||||||
|
SBEReader {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'a>(&self, buf: &'a mut StreamVec, stats: &mut Summarizer) -> Result<(), ()> {
|
||||||
|
let data = buf.fill_buf().unwrap();
|
||||||
|
if data.len() == 0 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let (header, decoder) = start_decoding_multi_message(data)
|
||||||
|
.header().unwrap();
|
||||||
|
|
||||||
|
let (fields, decoder) = decoder.multi_message_fields().unwrap();
|
||||||
|
let mut msg_decoder = decoder.messages_individually().unwrap();
|
||||||
|
while let Either::Left(msg) = msg_decoder {
|
||||||
|
let (member, sym_dec) = msg.next_messages_member().unwrap();
|
||||||
|
let (sym, next_msg_dec) = sym_dec.symbol().unwrap();
|
||||||
|
match member.msg_type {
|
||||||
|
marketdata_sbe::MsgType::Trade => stats.append_trade_volume(
|
||||||
|
unsafe { from_utf8_unchecked(sym) },
|
||||||
|
member.trade.size as u64,
|
||||||
|
),
|
||||||
|
marketdata_sbe::MsgType::Quote => stats.update_quote_prices(
|
||||||
|
unsafe { from_utf8_unchecked(sym) },
|
||||||
|
member.quote.price,
|
||||||
|
match member.quote.side {
|
||||||
|
Side::Buy => true,
|
||||||
|
_ => false
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
msg_decoder = next_msg_dec;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We now have a `Right`, which is a finished messages block
|
||||||
|
let msg_decoder = match msg_decoder {
|
||||||
|
Either::Right(r) => r,
|
||||||
|
_ => panic!("Didn't parse all messages")
|
||||||
|
};
|
||||||
|
|
||||||
|
// Interestingly enough, `buf.consume(msg_decoder.unwrap())` isn't OK,
|
||||||
|
// presumably something to do with when *precisely* the drop of `self`
|
||||||
|
// happens for `msg_decoder`. Leave it as two statments so that
|
||||||
|
// Rust is able to prove our immutable borrow of `data` ends in time
|
||||||
|
// to consume the buffer
|
||||||
|
let msg_len = msg_decoder.unwrap();
|
||||||
|
buf.consume(msg_len);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,50 +0,0 @@
|
|||||||
use alloc_counter::{AllocCounterSystem, count_alloc, deny_alloc};
|
|
||||||
|
|
||||||
use md_shootout::marketdata_capnp::multi_message;
|
|
||||||
|
|
||||||
#[global_allocator]
|
|
||||||
static A: AllocCounterSystem = AllocCounterSystem;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn reinit_memory_check() {
|
|
||||||
// Setting up the builder doesn't reserve any heap memory
|
|
||||||
let mut msg_block = deny_alloc(|| {
|
|
||||||
capnp::message::Builder::new_default()
|
|
||||||
});
|
|
||||||
|
|
||||||
// Setting up the root object, however, does reserve a first segment
|
|
||||||
let (stats, result) = count_alloc(|| {
|
|
||||||
let multimsg = msg_block.init_root::<multi_message::Builder>();
|
|
||||||
multimsg.init_messages(32);
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_eq!(stats.0, 4);
|
|
||||||
assert_eq!(stats.1, 0);
|
|
||||||
assert_eq!(stats.2, 0);
|
|
||||||
|
|
||||||
// If we reinitialize an object on that original builder, we re-use memory
|
|
||||||
deny_alloc(|| {
|
|
||||||
let multimsg = msg_block.init_root::<multi_message::Builder>();
|
|
||||||
multimsg.init_messages(32);
|
|
||||||
|
|
||||||
// Even if we down-size and up-size the message list size, we don't need
|
|
||||||
// to re-allocate
|
|
||||||
let multimsg = msg_block.init_root::<multi_message::Builder>();
|
|
||||||
multimsg.init_messages(16);
|
|
||||||
|
|
||||||
let multimsg = msg_block.init_root::<multi_message::Builder>();
|
|
||||||
multimsg.init_messages(32);
|
|
||||||
});
|
|
||||||
|
|
||||||
// It's only when we init a larger message count that a fresh allocation occurs
|
|
||||||
let (stats, _) = count_alloc(|| {
|
|
||||||
let multimsg = msg_block.init_root::<multi_message::Builder>();
|
|
||||||
// Note: calling `init_messages(33)` doesn't force allocation because
|
|
||||||
// the Capnproto builder reserved extra memory the first time around
|
|
||||||
multimsg.init_messages(256);
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_eq!(stats.0, 1);
|
|
||||||
assert_eq!(stats.1, 3);
|
|
||||||
assert_eq!(stats.2, 0);
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user