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, OrderBookDepth, QuoteTick, TradeTick,
27};
28
29use self::common::{HEADER_LENGTH, MessageHeader, 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_DEPTH: 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 pub(crate) const ORDER_BOOK_DEPTH_FULL: u16 = 12;
49}
50
51pub(super) mod template_id {
52 pub(crate) const BOOK_ORDER: u16 = 30_001;
53 pub(crate) const ORDER_BOOK_DELTA: u16 = 30_002;
54 pub(crate) const ORDER_BOOK_DELTAS: u16 = 30_003;
55 pub(crate) const ORDER_BOOK_DEPTH: u16 = 30_004;
56 pub(crate) const QUOTE_TICK: u16 = 30_005;
57 pub(crate) const TRADE_TICK: u16 = 30_006;
58 pub(crate) const BAR_TYPE: u16 = 30_007;
59 pub(crate) const BAR: u16 = 30_008;
60 pub(crate) const MARK_PRICE_UPDATE: u16 = 30_009;
61 pub(crate) const INDEX_PRICE_UPDATE: u16 = 30_010;
62 pub(crate) const FUNDING_RATE_UPDATE: u16 = 30_011;
63 pub(crate) const OPTION_GREEKS: u16 = 30_012;
64 pub(crate) const INSTRUMENT_STATUS: u16 = 30_013;
65 pub(crate) const INSTRUMENT_CLOSE: u16 = 30_014;
66 pub(crate) const DATA_ANY: u16 = 30_015;
67 pub(crate) const ORDER_BOOK_DEPTH_FULL: u16 = 30_016;
68}
69
70#[expect(clippy::large_enum_variant)]
71#[derive(Debug, Clone, PartialEq)]
72pub enum DataAny {
73 BookDelta(OrderBookDelta),
74 BookDeltas(OrderBookDeltas),
75 BookDepth(OrderBookDepth),
76 Quote(QuoteTick),
77 Trade(TradeTick),
78 Bar(Bar),
79 MarkPrice(MarkPriceUpdate),
80 IndexPrice(IndexPriceUpdate),
81 FundingRate(FundingRateUpdate),
82 OptionGreeks(OptionGreeks),
83 InstrumentStatus(InstrumentStatus),
84 InstrumentClose(InstrumentClose),
85}
86
87pub trait ToSbe {
88 fn to_sbe(&self) -> Result<Vec<u8>, SbeEncodeError>;
94
95 fn to_sbe_into(&self, buf: &mut Vec<u8>) -> Result<(), SbeEncodeError> {
103 let bytes = self.to_sbe()?;
104 buf.clear();
105 buf.extend_from_slice(&bytes);
106 Ok(())
107 }
108}
109
110pub trait FromSbe: Sized {
111 fn from_sbe(bytes: &[u8]) -> Result<Self, SbeDecodeError>;
117}
118
119pub trait FromSbeReuse: FromSbe {
126 type Scratch;
128
129 fn from_sbe_reuse(bytes: &[u8], scratch: &mut Self::Scratch) -> Result<Self, SbeDecodeError>;
139}
140
141pub(super) trait MarketSbeMessage: Sized {
142 const TEMPLATE_ID: u16;
143 const BLOCK_LENGTH: u16;
144
145 fn template_id(&self) -> u16 {
146 Self::TEMPLATE_ID
147 }
148
149 fn block_length(&self) -> u16 {
150 Self::BLOCK_LENGTH
151 }
152
153 fn decode_with_header(
154 header: MessageHeader,
155 cursor: &mut SbeCursor<'_>,
156 ) -> Result<Self, SbeDecodeError> {
157 validate_header(header, Self::TEMPLATE_ID, Self::BLOCK_LENGTH)?;
158 Self::decode_body(cursor)
159 }
160
161 fn encode_body(&self, writer: &mut SbeWriter<'_>) -> Result<(), SbeEncodeError>;
162
163 fn decode_body(cursor: &mut SbeCursor<'_>) -> Result<Self, SbeDecodeError>;
164
165 fn encoded_body_size(&self) -> usize {
166 usize::from(Self::BLOCK_LENGTH)
167 }
168}
169
170impl<T> ToSbe for T
171where
172 T: MarketSbeMessage,
173{
174 #[inline]
175 fn to_sbe(&self) -> Result<Vec<u8>, SbeEncodeError> {
176 let encoded_size = HEADER_LENGTH + self.encoded_body_size();
177 let mut buf = Vec::with_capacity(encoded_size);
178 encode_into_uninit(self, &mut buf, encoded_size)?;
179 Ok(buf)
180 }
181
182 #[inline]
183 fn to_sbe_into(&self, buf: &mut Vec<u8>) -> Result<(), SbeEncodeError> {
184 let encoded_size = HEADER_LENGTH + self.encoded_body_size();
185 buf.clear();
186 buf.reserve(encoded_size);
187 encode_into_uninit(self, buf, encoded_size)
188 }
189}
190
191impl<T> FromSbe for T
192where
193 T: MarketSbeMessage,
194{
195 #[inline]
196 fn from_sbe(bytes: &[u8]) -> Result<Self, SbeDecodeError> {
197 let mut cursor = SbeCursor::new(bytes);
198 let header = decode_header(&mut cursor)?;
199 T::decode_with_header(header, &mut cursor)
200 }
201}
202
203#[inline]
207#[allow(
208 unsafe_code,
209 reason = "set_len commits writes the SbeWriter has already made into spare capacity"
210)]
211#[allow(
212 clippy::panic_in_result_fn,
213 reason = "load-bearing safety check for the unsafe set_len; panic is the right outcome"
214)]
215fn encode_into_uninit<T>(
216 value: &T,
217 buf: &mut Vec<u8>,
218 encoded_size: usize,
219) -> Result<(), SbeEncodeError>
220where
221 T: MarketSbeMessage,
222{
223 debug_assert_eq!(buf.len(), 0);
224 debug_assert!(buf.capacity() >= encoded_size);
225
226 let spare = &mut buf.spare_capacity_mut()[..encoded_size];
227 let mut writer = SbeWriter::new_uninit(spare);
228 encode_header(
229 &mut writer,
230 value.block_length(),
231 value.template_id(),
232 MARKET_SCHEMA_ID,
233 MARKET_SCHEMA_VERSION,
234 );
235 value.encode_body(&mut writer)?;
236
237 assert_eq!(
242 writer.pos(),
243 encoded_size,
244 "SBE encode_body wrote {} bytes but encoded_body_size reported {}",
245 writer.pos(),
246 encoded_size,
247 );
248
249 unsafe {
254 buf.set_len(encoded_size);
255 }
256 Ok(())
257}