diff --git a/build.rs b/build.rs index 42af504..eb448b9 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,7 @@ extern crate capnpc; use std::path::Path; +use std::process::Command; fn main() { capnpc::CompilerCommand::new() @@ -14,6 +15,18 @@ fn main() { inputs: &[Path::new("marketdata.fbs")], out_dir: Path::new("src/"), ..Default::default() - }) - .expect("Unable to compile flatc"); + }).expect("Unable to compile flatc"); + + // There's no Rust-style builder crate for SBE, + // so we need to run the command by hand. + // TODO: Automatically download the SBE JAR? + let _output = Command::new("java") + .arg("-Dsbe.output.dir=src") + .arg("-Dsbe.xinclude.aware=true") + .arg("-Dsbe.target.language=uk.co.real_logic.sbe.generation.rust.Rust") + .arg("-Dsbe.target.namespace=marketdata_sbe") + .arg("-jar").arg("sbe-all-1.13.2-all.jar") + .arg("marketdata.xml") + .output() + .expect("Unable to execute SBE compiler"); } diff --git a/marketdata.xml b/marketdata.xml new file mode 100644 index 0000000..038e9d8 --- /dev/null +++ b/marketdata.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 1 + + + 0 + 1 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sbe-all-1.13.2-all.jar b/sbe-all-1.13.2-all.jar new file mode 100644 index 0000000..00ad10a Binary files /dev/null and b/sbe-all-1.13.2-all.jar differ diff --git a/sbe.xsd b/sbe.xsd new file mode 100644 index 0000000..cce09d0 --- /dev/null +++ b/sbe.xsd @@ -0,0 +1,390 @@ + + + + + Message schema for FIX Simple Binary Encoding + Version: 1.0 Draft Standard + © Copyright 2014-2016 FIX Protocol Limited + License: Creative Commons Attribution-NoDerivatives 4.0 International Public License + + + + + + Root of XML document, holds all message templates + and their elements + + + + + + + + More than one set of types may be provided. + Names must be unique across all encoding + types. + Encoding types may appear in any order. + + + + + + + + + + + + + + + + + Unique ID of a message schema + + + + + + The version of a message schema. Initial version + is 0. + + + + + + Application layer specification version, such as + FIX version 'FIX.5.0SP2' + + + + + + + + + + + + + + + + Name of the encoding type of the message header, + which is the same for all messages in a schema. The name has a + default, but an encoding of that name must be present under a + 'types' element. + + + + + + + + + A message type, also known as a message template + + + + + + Base type of message and repeating group entry + + + + + + Fixed-length fields + + + + + + Variable-length fields + + + + + + + Unique ID of a message template + + + + + + Space reserved for root level of message, not + include groups or variable-length + data elements. + + + + + + + + + + A repeating group contains an array of entries + + + + + + + + + + + + Simple wire encoding consisting of a primitive type + or array of primitives + + + + + + + + Override of default null indicator for the data + type in SBE specification, + as a string. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A wire encoding composed of multiple parts + + + + + + + + + + + + + + + + + + An enumeration of valid values + + + + + + + + + + + + + + + Valid value as a string + + + + + + + + + + + + + + A reference to any existing encoding type (simple type, enum or set) + to reuse as a member of a composite type + + + + + + + + + + + A multi value choice (encoded as a bitset) + + + + + + + + + + + + + + + A choice within a multi value set. Value is the + position within a bitset (zero-based index). + + + + + + + + + + + + + + A field of a message of a specified dataType + + + + + + + Must match the name of an encoding contained by + 'types' element + + + + + + + Deprecated - only for back compatibility with RC2 + + + + + + + + + + + + + Application layer class. Maps a field or encoding + to a FIX data type. + + + + + + + + + Schema versioning supports message extension + + + + + + The schema version in which an element was added + + + + + + + The version of the schema in which an element was + deprecated. It is retained for back compatibility but should no + longer be used by updated applications. + + + + + + + + Offset from start of a composite type or block + as a zero-based index. + + + + + + + + + + + The value must always be populated + + + + + + Value may be set to nullValue for its data type + + + + + + Value does not vary so it need not be + serialized on the wire + + + + + + + + + A constant value as valid value of an enum + in the form enum-name.valid-value-name + + + + + + + + + + + + + + + + + diff --git a/src/marketdata_sbe.rs b/src/marketdata_sbe.rs new file mode 100644 index 0000000..4d2ea13 --- /dev/null +++ b/src/marketdata_sbe.rs @@ -0,0 +1,583 @@ +/// Generated code for SBE package marketdata_sbe + + +/// Imports core rather than std to broaden usable environments. +extern crate core; + +/// Result types for error handling + +/// Errors that may occur during the course of encoding or decoding. +#[derive(Debug)] +pub enum CodecErr { + /// Too few bytes in the byte-slice to read or write the data structure relevant + /// to the current state of the codec + NotEnoughBytes, + + /// Groups and vardata are constrained by the numeric type chosen to represent their + /// length as well as optional maxima imposed by the schema + SliceIsLongerThanAllowedBySchema, +} + +pub type CodecResult = core::result::Result; + +/// Scratch Decoder Data Wrapper - codec internal use only +#[derive(Debug)] +pub struct ScratchDecoderData<'d> { + data: &'d [u8], + pos: usize, +} + +impl<'d> ScratchDecoderData<'d> { + /// Create a struct reference overlaid atop the data buffer + /// such that the struct's contents directly reflect the buffer. + /// Advances the `pos` index by the size of the struct in bytes. + #[inline] + fn read_type(&mut self, num_bytes: usize) -> CodecResult<&'d T> { + let end = self.pos + num_bytes; + if end <= self.data.len() { + let s = self.data[self.pos..end].as_ptr() as *mut T; + let v: &'d T = unsafe { &*s }; + self.pos = end; + Ok(v) + } else { + Err(CodecErr::NotEnoughBytes) + } + } + + /// Advances the `pos` index by a set number of bytes. + #[inline] + fn skip_bytes(&mut self, num_bytes: usize) -> CodecResult<()> { + let end = self.pos + num_bytes; + if end <= self.data.len() { + self.pos = end; + Ok(()) + } else { + Err(CodecErr::NotEnoughBytes) + } + } + + /// Create a slice reference overlaid atop the data 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. + #[inline] + fn read_slice(&mut self, count: usize, bytes_per_item: usize) -> CodecResult<&'d [T]> { + let num_bytes = bytes_per_item * count; + let end = self.pos + num_bytes; + if end <= self.data.len() { + let v: &'d [T] = unsafe { + core::slice::from_raw_parts(self.data[self.pos..end].as_ptr() as *const T, count) + }; + self.pos = end; + Ok(v) + } else { + Err(CodecErr::NotEnoughBytes) + } + } +} + +/// Scratch Encoder Data Wrapper - codec internal use only +#[derive(Debug)] +pub struct ScratchEncoderData<'d> { + data: &'d mut [u8], + pos: usize, +} + +impl<'d> ScratchEncoderData<'d> { + /// Copy the bytes of a value into the data buffer + /// Advances the `pos` index to after the newly-written bytes. + #[inline] + fn write_type(&mut self, t: &T, num_bytes: usize) -> CodecResult<()> { + let end = self.pos + num_bytes; + if end <= self.data.len() { + let source_bytes: &[u8] = unsafe { + 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); + self.pos = end; + Ok(()) + } else { + Err(CodecErr::NotEnoughBytes) + } + } + + /// Advances the `pos` index by a set number of bytes. + #[inline] + fn skip_bytes(&mut self, num_bytes: usize) -> CodecResult<()> { + let end = self.pos + num_bytes; + if end <= self.data.len() { + self.pos = end; + Ok(()) + } else { + Err(CodecErr::NotEnoughBytes) + } + } + + /// Create a struct reference overlaid atop the data buffer + /// such that changes to the struct directly edit the buffer. + /// Note that the initial content of the struct's fields may be garbage. + /// Advances the `pos` index to after the newly-written bytes. + #[inline] + fn writable_overlay(&mut self, num_bytes: usize) -> CodecResult<&'d mut T> { + let end = self.pos + num_bytes; + if end <= self.data.len() { + let v: &'d mut T = unsafe { + let s = self.data.as_ptr().offset(self.pos as isize) as *mut T; + &mut *s + }; + self.pos = end; + Ok(v) + } else { + Err(CodecErr::NotEnoughBytes) + } + } + + /// Copy the bytes of a value into the data buffer at a specific position + /// Does **not** alter the `pos` index. + #[inline] + fn write_at_position(&mut self, position: usize, t: &T, num_bytes: usize) -> CodecResult<()> { + let end = position + num_bytes; + if end <= self.data.len() { + let source_bytes: &[u8] = unsafe { + core::slice::from_raw_parts(t as *const T as *const u8, num_bytes) + }; + (&mut self.data[position..end]).copy_from_slice(source_bytes); + Ok(()) + } else { + Err(CodecErr::NotEnoughBytes) + } + } + /// Create a mutable slice overlaid atop the data buffer directly + /// such that changes to the slice contents directly edit the buffer + /// Note that the initial content of the slice's members' fields may be garbage. + /// Advances the `pos` index to after the region representing the slice. + #[inline] + fn writable_slice(&mut self, count: usize, bytes_per_item: usize) -> CodecResult<&'d mut [T]> { + let end = self.pos + (count * bytes_per_item); + if end <= self.data.len() { + 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) + } else { + Err(CodecErr::NotEnoughBytes) + } + } + + /// 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. + /// Advances the `pos` index to after the newly-written slice bytes. + #[inline] + fn write_slice_without_count(&mut self, t: &[T], bytes_per_item: usize) -> CodecResult<()> { + let content_bytes_size = bytes_per_item * t.len(); + let end = self.pos + content_bytes_size; + if end <= self.data.len() { + let source_bytes: &[u8] = unsafe { + 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); + self.pos = end; + Ok(()) + } else { + Err(CodecErr::NotEnoughBytes) + } + } +} + +/// Convenience Either enum +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum Either { + Left(L), + Right(R), +} + +/// Enum Side +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(u8)] +pub enum Side { + Buy = 0u8, + Sell = 1u8, + NullVal = 255u8, +} + +impl Default for Side { + fn default() -> Self { Side::NullVal } +} + +/// Enum MsgType +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(u8)] +pub enum MsgType { + Trade = 0u8, + Quote = 1u8, + NullVal = 255u8, +} + +impl Default for MsgType { + fn default() -> Self { MsgType::NullVal } +} + +/// Quote +#[repr(C, packed)] +#[derive(Default)] +pub struct Quote { + pub price: u64, + pub size: u32, + pub flags: u8, + pub side: Side, +} + +impl Quote {} + +/// Trade +#[repr(C, packed)] +#[derive(Default)] +pub struct Trade { + pub price: u64, + pub size: u32, +} + +impl Trade {} + +/// MessageHeader +#[repr(C, packed)] +#[derive(Default)] +pub struct MessageHeader { + pub block_length: u16, + pub template_id: u16, + pub schema_id: u16, + pub version: u16, +} + +impl MessageHeader {} + +/// GroupSizeEncoding +#[repr(C, packed)] +#[derive(Default)] +pub struct GroupSizeEncoding { + pub block_length: u16, + pub num_in_group: u16, +} + +impl GroupSizeEncoding {} + +/// VarAsciiEncoding +#[repr(C, packed)] +#[derive(Default)] +pub struct VarAsciiEncoding { + pub length: u32, + pub var_data: u8, +} + +impl VarAsciiEncoding {} + +/// MessageHeader Decoder entry point +pub fn start_decoding_message_header<'d>(data: &'d [u8]) -> CodecResult<(&'d MessageHeader, ScratchDecoderData<'d>)> { + let mut scratch = ScratchDecoderData { data: data, pos: 0 }; + let v = scratch.read_type::(8)?; + Ok((v, scratch)) +} + +/// MultiMessage Fixed-size Fields (8 bytes) +#[repr(C, packed)] +#[derive(Default)] +pub struct MultiMessageFields { + pub sequence_number: u64, +} + + +impl MultiMessageFields {} + +/// MultiMessage specific Message Header +#[repr(C, packed)] +pub struct MultiMessageMessageHeader { + pub message_header: MessageHeader +} + +impl MultiMessageMessageHeader { + pub const BLOCK_LENGTH: u16 = 8; + pub const TEMPLATE_ID: u16 = 1; + pub const SCHEMA_ID: u16 = 1; + pub const VERSION: u16 = 0; +} + +impl Default for MultiMessageMessageHeader { + fn default() -> MultiMessageMessageHeader { + MultiMessageMessageHeader { + message_header: MessageHeader { + block_length: 8u16, + template_id: 1u16, + schema_id: 1u16, + version: 0u16, + } + } + } +} + +/// Group fixed-field member representations +#[repr(C, packed)] +#[derive(Default)] +pub struct MultiMessageMessagesMember { + pub timestamp: i64, + pub msg_type: MsgType, + pub trade: Trade, + pub quote: Quote, +} + +impl MultiMessageMessagesMember {} + +/// MultiMessageDecoderDone +pub struct MultiMessageDecoderDone<'d> { + scratch: ScratchDecoderData<'d>, +} + +impl<'d> MultiMessageDecoderDone<'d> { + /// Returns the number of bytes decoded + pub fn unwrap(self) -> usize { + self.scratch.pos + } + + pub fn wrap(scratch: ScratchDecoderData<'d>) -> MultiMessageDecoderDone<'d> { + MultiMessageDecoderDone { scratch: scratch } + } +} + +/// symbol variable-length data +pub struct MultiMessageMessagesSymbolDecoder<'d> { + parent: MultiMessageMessagesMemberDecoder<'d>, +} + +impl<'d> MultiMessageMessagesSymbolDecoder<'d> { + fn wrap(parent: MultiMessageMessagesMemberDecoder<'d>) -> Self { + MultiMessageMessagesSymbolDecoder { parent: parent } + } + pub fn symbol(mut self) -> CodecResult<(&'d [u8], Either, MultiMessageDecoderDone<'d>>)> { + let count = *self.parent.scratch.read_type::(4)?; + Ok((self.parent.scratch.read_slice::(count as usize, 1)?, self.parent.after_member())) + } +} + +/// MultiMessageMessages Decoder for fields and header +pub struct MultiMessageMessagesMemberDecoder<'d> { + scratch: ScratchDecoderData<'d>, + max_index: u16, + index: u16, +} + +impl<'d> MultiMessageMessagesMemberDecoder<'d> { + fn new(scratch: ScratchDecoderData<'d>, count: u16) -> Self { + assert!(count > 0u16); + MultiMessageMessagesMemberDecoder { + scratch: scratch, + max_index: count - 1, + index: 0, + } + } + + pub fn next_messages_member(mut self) -> CodecResult<(&'d MultiMessageMessagesMember, MultiMessageMessagesSymbolDecoder<'d>)> { + let v = self.scratch.read_type::(35)?; + self.index += 1; + Ok((v, MultiMessageMessagesSymbolDecoder::wrap(self))) + } + #[inline] + fn after_member(self) -> Either, MultiMessageDecoderDone<'d>> { + if self.index <= self.max_index { + Either::Left(self) + } else { + Either::Right(MultiMessageDecoderDone::wrap(self.scratch)) + } + } +} + +pub struct MultiMessageMessagesHeaderDecoder<'d> { + scratch: ScratchDecoderData<'d>, +} + +impl<'d> MultiMessageMessagesHeaderDecoder<'d> { + fn wrap(scratch: ScratchDecoderData<'d>) -> Self { + MultiMessageMessagesHeaderDecoder { scratch: scratch } + } + pub fn messages_individually(mut self) -> CodecResult, MultiMessageDecoderDone<'d>>> { + let dim = self.scratch.read_type::(4)?; + if dim.num_in_group > 0 { + Ok(Either::Left(MultiMessageMessagesMemberDecoder::new(self.scratch, dim.num_in_group))) + } else { + Ok(Either::Right(MultiMessageDecoderDone::wrap(self.scratch))) + } + } +} + +/// MultiMessage Fixed fields Decoder +pub struct MultiMessageFieldsDecoder<'d> { + scratch: ScratchDecoderData<'d>, +} + +impl<'d> MultiMessageFieldsDecoder<'d> { + pub fn wrap(scratch: ScratchDecoderData<'d>) -> MultiMessageFieldsDecoder<'d> { + MultiMessageFieldsDecoder { scratch: scratch } + } + pub fn multi_message_fields(mut self) -> CodecResult<(&'d MultiMessageFields, MultiMessageMessagesHeaderDecoder<'d>)> { + let v = self.scratch.read_type::(8)?; + Ok((v, MultiMessageMessagesHeaderDecoder::wrap(self.scratch))) + } +} + +/// MultiMessageMessageHeaderDecoder +pub struct MultiMessageMessageHeaderDecoder<'d> { + scratch: ScratchDecoderData<'d>, +} + +impl<'d> MultiMessageMessageHeaderDecoder<'d> { + pub fn wrap(scratch: ScratchDecoderData<'d>) -> MultiMessageMessageHeaderDecoder<'d> { + MultiMessageMessageHeaderDecoder { scratch: scratch } + } + pub fn header(mut self) -> CodecResult<(&'d MessageHeader, MultiMessageFieldsDecoder<'d>)> { + let v = self.scratch.read_type::(8)?; + Ok((v, MultiMessageFieldsDecoder::wrap(self.scratch))) + } +} + +/// MultiMessage Decoder entry point +pub fn start_decoding_multi_message<'d>(data: &'d [u8]) -> MultiMessageMessageHeaderDecoder<'d> { + MultiMessageMessageHeaderDecoder::wrap(ScratchDecoderData { data: data, pos: 0 }) +} + +/// MultiMessageEncoderDone +pub struct MultiMessageEncoderDone<'d> { + scratch: ScratchEncoderData<'d>, +} + +impl<'d> MultiMessageEncoderDone<'d> { + /// Returns the number of bytes encoded + pub fn unwrap(self) -> usize { + self.scratch.pos + } + + pub fn wrap(scratch: ScratchEncoderData<'d>) -> MultiMessageEncoderDone<'d> { + MultiMessageEncoderDone { scratch: scratch } + } +} + +/// symbol variable-length data +pub struct MultiMessageMessagesSymbolEncoder<'d> { + parent: MultiMessageMessagesMemberEncoder<'d>, +} + +impl<'d> MultiMessageMessagesSymbolEncoder<'d> { + fn wrap(parent: MultiMessageMessagesMemberEncoder<'d>) -> Self { + MultiMessageMessagesSymbolEncoder { parent: parent } + } + pub fn symbol(mut self, s: &'d [u8]) -> CodecResult { + let l = s.len(); + if l > 4294967294 { + return Err(CodecErr::SliceIsLongerThanAllowedBySchema); + } + // Write data length + self.parent.scratch.write_type::(&(l as u32), 4)?; // group length + self.parent.scratch.write_slice_without_count::(s, 1)?; + Ok(self.parent) + } +} + +/// MultiMessageMessages Encoder for fields and header +pub struct MultiMessageMessagesMemberEncoder<'d> { + scratch: ScratchEncoderData<'d>, + count_write_pos: usize, + count: u16, +} + +impl<'d> MultiMessageMessagesMemberEncoder<'d> { + #[inline] + fn new(scratch: ScratchEncoderData<'d>, count_write_pos: usize) -> Self { + MultiMessageMessagesMemberEncoder { + scratch: scratch, + count_write_pos: count_write_pos, + count: 0, + } + } + + #[inline] + pub fn next_messages_member(mut self, fields: &MultiMessageMessagesMember) -> CodecResult> { + self.scratch.write_type::(fields, 35)?; // block length + self.count += 1; + Ok(MultiMessageMessagesSymbolEncoder::wrap(self)) + } + #[inline] + pub fn done_with_messages(mut self) -> CodecResult> { + self.scratch.write_at_position::(self.count_write_pos, &self.count, 2)?; + Ok(MultiMessageEncoderDone::wrap(self.scratch)) + } +} + +pub struct MultiMessageMessagesHeaderEncoder<'d> { + scratch: ScratchEncoderData<'d>, +} + +impl<'d> MultiMessageMessagesHeaderEncoder<'d> { + #[inline] + fn wrap(scratch: ScratchEncoderData<'d>) -> Self { + MultiMessageMessagesHeaderEncoder { scratch: scratch } + } + #[inline] + pub fn messages_individually(mut self) -> CodecResult> { + self.scratch.write_type::(&35u16, 2)?; // block length + let count_pos = self.scratch.pos; + self.scratch.write_type::(&0, 2)?; // preliminary group member count + Ok(MultiMessageMessagesMemberEncoder::new(self.scratch, count_pos)) + } +} + +/// MultiMessage Fixed fields Encoder +pub struct MultiMessageFieldsEncoder<'d> { + scratch: ScratchEncoderData<'d>, +} + +impl<'d> MultiMessageFieldsEncoder<'d> { + pub fn wrap(scratch: ScratchEncoderData<'d>) -> MultiMessageFieldsEncoder<'d> { + MultiMessageFieldsEncoder { scratch: scratch } + } + + /// Create a mutable struct reference overlaid atop the data buffer + /// such that changes to the struct directly edit the buffer. + /// 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>)> { + let v = self.scratch.writable_overlay::(8 + 0)?; + Ok((v, MultiMessageMessagesHeaderEncoder::wrap(self.scratch))) + } + + /// Copy the bytes of a value into the data buffer + pub fn multi_message_fields_copy(mut self, t: &MultiMessageFields) -> CodecResult> { + self.scratch.write_type::(t, 8)?; + Ok(MultiMessageMessagesHeaderEncoder::wrap(self.scratch)) + } +} + +/// MultiMessageMessageHeaderEncoder +pub struct MultiMessageMessageHeaderEncoder<'d> { + scratch: ScratchEncoderData<'d>, +} + +impl<'d> MultiMessageMessageHeaderEncoder<'d> { + pub fn wrap(scratch: ScratchEncoderData<'d>) -> MultiMessageMessageHeaderEncoder<'d> { + MultiMessageMessageHeaderEncoder { scratch: scratch } + } + + /// Create a mutable struct reference overlaid atop the data buffer + /// such that changes to the struct directly edit the buffer. + /// Note that the initial content of the struct's fields may be garbage. + pub fn header(mut self) -> CodecResult<(&'d mut MessageHeader, MultiMessageFieldsEncoder<'d>)> { + let v = self.scratch.writable_overlay::(8 + 0)?; + Ok((v, MultiMessageFieldsEncoder::wrap(self.scratch))) + } + + /// Copy the bytes of a value into the data buffer + pub fn header_copy(mut self, t: &MessageHeader) -> CodecResult> { + self.scratch.write_type::(t, 8)?; + Ok(MultiMessageFieldsEncoder::wrap(self.scratch)) + } +} + +/// MultiMessage Encoder entry point +pub fn start_encoding_multi_message<'d>(data: &'d mut [u8]) -> MultiMessageMessageHeaderEncoder<'d> { + MultiMessageMessageHeaderEncoder::wrap(ScratchEncoderData { data: data, pos: 0 }) +}