nautilus_serialization/sbe/
market.rs1mod bars;
19mod book;
20mod common;
21mod data_any;
22mod ticks;
23
24use nautilus_model::data::{
25 Bar, FundingRateUpdate, IndexPriceUpdate, InstrumentClose, InstrumentStatus, MarkPriceUpdate,
26 OptionGreeks, OrderBookDelta, OrderBookDeltas, OrderBookDepth10, QuoteTick, TradeTick,
27};
28
29use self::common::{HEADER_LENGTH, decode_header, encode_header, validate_header};
30use super::{SbeCursor, SbeDecodeError, SbeEncodeError, SbeWriter};
31
32pub const MARKET_SCHEMA_ID: u16 = 1;
33pub const MARKET_SCHEMA_VERSION: u16 = 0;
34
35pub(super) mod data_any_variant {
36 pub(crate) const ORDER_BOOK_DELTA: u16 = 0;
37 pub(crate) const ORDER_BOOK_DELTAS: u16 = 1;
38 pub(crate) const ORDER_BOOK_DEPTH10: u16 = 2;
39 pub(crate) const QUOTE: u16 = 3;
40 pub(crate) const TRADE: u16 = 4;
41 pub(crate) const BAR: u16 = 5;
42 pub(crate) const MARK_PRICE: u16 = 6;
43 pub(crate) const INDEX_PRICE: u16 = 7;
44 pub(crate) const FUNDING_RATE: u16 = 8;
45 pub(crate) const OPTION_GREEKS: u16 = 9;
46 pub(crate) const INSTRUMENT_STATUS: u16 = 10;
47 pub(crate) const INSTRUMENT_CLOSE: u16 = 11;
48}
49
50pub(super) mod template_id {
51 pub(crate) const BOOK_ORDER: u16 = 30_001;
52 pub(crate) const ORDER_BOOK_DELTA: u16 = 30_002;
53 pub(crate) const ORDER_BOOK_DELTAS: u16 = 30_003;
54 pub(crate) const ORDER_BOOK_DEPTH10: u16 = 30_004;
55 pub(crate) const QUOTE_TICK: u16 = 30_005;
56 pub(crate) const TRADE_TICK: u16 = 30_006;
57 pub(crate) const BAR_TYPE: u16 = 30_007;
58 pub(crate) const BAR: u16 = 30_008;
59 pub(crate) const MARK_PRICE_UPDATE: u16 = 30_009;
60 pub(crate) const INDEX_PRICE_UPDATE: u16 = 30_010;
61 pub(crate) const FUNDING_RATE_UPDATE: u16 = 30_011;
62 pub(crate) const OPTION_GREEKS: u16 = 30_012;
63 pub(crate) const INSTRUMENT_STATUS: u16 = 30_013;
64 pub(crate) const INSTRUMENT_CLOSE: u16 = 30_014;
65 pub(crate) const DATA_ANY: u16 = 30_015;
66}
67
68#[expect(clippy::large_enum_variant)]
69#[derive(Debug, Clone, PartialEq)]
70pub enum DataAny {
71 OrderBookDelta(OrderBookDelta),
72 OrderBookDeltas(OrderBookDeltas),
73 OrderBookDepth10(OrderBookDepth10),
74 Quote(QuoteTick),
75 Trade(TradeTick),
76 Bar(Bar),
77 MarkPrice(MarkPriceUpdate),
78 IndexPrice(IndexPriceUpdate),
79 FundingRate(FundingRateUpdate),
80 OptionGreeks(OptionGreeks),
81 InstrumentStatus(InstrumentStatus),
82 InstrumentClose(InstrumentClose),
83}
84
85pub trait ToSbe {
86 fn to_sbe(&self) -> Result<Vec<u8>, SbeEncodeError>;
92
93 fn to_sbe_into(&self, buf: &mut Vec<u8>) -> Result<(), SbeEncodeError> {
101 let bytes = self.to_sbe()?;
102 buf.clear();
103 buf.extend_from_slice(&bytes);
104 Ok(())
105 }
106}
107
108pub trait FromSbe: Sized {
109 fn from_sbe(bytes: &[u8]) -> Result<Self, SbeDecodeError>;
115}
116
117pub trait FromSbeReuse: FromSbe {
124 type Scratch;
126
127 fn from_sbe_reuse(bytes: &[u8], scratch: &mut Self::Scratch) -> Result<Self, SbeDecodeError>;
137}
138
139pub(super) trait MarketSbeMessage: Sized {
140 const TEMPLATE_ID: u16;
141 const BLOCK_LENGTH: u16;
142
143 fn encode_body(&self, writer: &mut SbeWriter<'_>) -> Result<(), SbeEncodeError>;
144
145 fn decode_body(cursor: &mut SbeCursor<'_>) -> Result<Self, SbeDecodeError>;
146
147 fn encoded_body_size(&self) -> usize {
148 usize::from(Self::BLOCK_LENGTH)
149 }
150}
151
152impl<T> ToSbe for T
153where
154 T: MarketSbeMessage,
155{
156 #[inline]
157 fn to_sbe(&self) -> Result<Vec<u8>, SbeEncodeError> {
158 let encoded_size = HEADER_LENGTH + self.encoded_body_size();
159 let mut buf = Vec::with_capacity(encoded_size);
160 encode_into_uninit(self, &mut buf, encoded_size)?;
161 Ok(buf)
162 }
163
164 #[inline]
165 fn to_sbe_into(&self, buf: &mut Vec<u8>) -> Result<(), SbeEncodeError> {
166 let encoded_size = HEADER_LENGTH + self.encoded_body_size();
167 buf.clear();
168 buf.reserve(encoded_size);
169 encode_into_uninit(self, buf, encoded_size)
170 }
171}
172
173impl<T> FromSbe for T
174where
175 T: MarketSbeMessage,
176{
177 #[inline]
178 fn from_sbe(bytes: &[u8]) -> Result<Self, SbeDecodeError> {
179 let mut cursor = SbeCursor::new(bytes);
180 let header = decode_header(&mut cursor)?;
181 validate_header(header, T::TEMPLATE_ID, T::BLOCK_LENGTH)?;
182 T::decode_body(&mut cursor)
183 }
184}
185
186#[inline]
190#[allow(
191 unsafe_code,
192 reason = "set_len commits writes the SbeWriter has already made into spare capacity"
193)]
194#[allow(
195 clippy::panic_in_result_fn,
196 reason = "load-bearing safety check for the unsafe set_len; panic is the right outcome"
197)]
198fn encode_into_uninit<T>(
199 value: &T,
200 buf: &mut Vec<u8>,
201 encoded_size: usize,
202) -> Result<(), SbeEncodeError>
203where
204 T: MarketSbeMessage,
205{
206 debug_assert_eq!(buf.len(), 0);
207 debug_assert!(buf.capacity() >= encoded_size);
208
209 let spare = &mut buf.spare_capacity_mut()[..encoded_size];
210 let mut writer = SbeWriter::new_uninit(spare);
211 encode_header(
212 &mut writer,
213 T::BLOCK_LENGTH,
214 T::TEMPLATE_ID,
215 MARKET_SCHEMA_ID,
216 MARKET_SCHEMA_VERSION,
217 );
218 value.encode_body(&mut writer)?;
219
220 assert_eq!(
225 writer.pos(),
226 encoded_size,
227 "SBE encode_body wrote {} bytes but encoded_body_size reported {}",
228 writer.pos(),
229 encoded_size,
230 );
231
232 unsafe {
237 buf.set_len(encoded_size);
238 }
239 Ok(())
240}