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 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 const ORDER_BOOK_DELTA: u16 = 0;
37 pub const ORDER_BOOK_DELTAS: u16 = 1;
38 pub const ORDER_BOOK_DEPTH10: u16 = 2;
39 pub const QUOTE: u16 = 3;
40 pub const TRADE: u16 = 4;
41 pub const BAR: u16 = 5;
42 pub const MARK_PRICE: u16 = 6;
43 pub const INDEX_PRICE: u16 = 7;
44 pub const FUNDING_RATE: u16 = 8;
45 pub const INSTRUMENT_STATUS: u16 = 9;
46 pub const INSTRUMENT_CLOSE: u16 = 10;
47}
48
49pub(super) mod template_id {
50 pub const BOOK_ORDER: u16 = 30_001;
51 pub const ORDER_BOOK_DELTA: u16 = 30_002;
52 pub const ORDER_BOOK_DELTAS: u16 = 30_003;
53 pub const ORDER_BOOK_DEPTH10: u16 = 30_004;
54 pub const QUOTE_TICK: u16 = 30_005;
55 pub const TRADE_TICK: u16 = 30_006;
56 pub const BAR_TYPE: u16 = 30_007;
57 pub const BAR: u16 = 30_008;
58 pub const MARK_PRICE_UPDATE: u16 = 30_009;
59 pub const INDEX_PRICE_UPDATE: u16 = 30_010;
60 pub const FUNDING_RATE_UPDATE: u16 = 30_011;
61 pub const INSTRUMENT_STATUS: u16 = 30_012;
62 pub const INSTRUMENT_CLOSE: u16 = 30_013;
63 pub const DATA_ANY: u16 = 30_014;
64}
65
66#[expect(clippy::large_enum_variant)]
67#[derive(Debug, Clone, PartialEq)]
68pub enum DataAny {
69 OrderBookDelta(OrderBookDelta),
70 OrderBookDeltas(OrderBookDeltas),
71 OrderBookDepth10(OrderBookDepth10),
72 Quote(QuoteTick),
73 Trade(TradeTick),
74 Bar(Bar),
75 MarkPrice(MarkPriceUpdate),
76 IndexPrice(IndexPriceUpdate),
77 FundingRate(FundingRateUpdate),
78 InstrumentStatus(InstrumentStatus),
79 InstrumentClose(InstrumentClose),
80}
81
82pub trait ToSbe {
83 fn to_sbe(&self) -> Result<Vec<u8>, SbeEncodeError>;
89
90 fn to_sbe_into(&self, buf: &mut Vec<u8>) -> Result<(), SbeEncodeError> {
98 let bytes = self.to_sbe()?;
99 buf.clear();
100 buf.extend_from_slice(&bytes);
101 Ok(())
102 }
103}
104
105pub trait FromSbe: Sized {
106 fn from_sbe(bytes: &[u8]) -> Result<Self, SbeDecodeError>;
112}
113
114pub trait FromSbeReuse: FromSbe {
121 type Scratch;
123
124 fn from_sbe_reuse(bytes: &[u8], scratch: &mut Self::Scratch) -> Result<Self, SbeDecodeError>;
134}
135
136pub(super) trait MarketSbeMessage: Sized {
137 const TEMPLATE_ID: u16;
138 const BLOCK_LENGTH: u16;
139
140 fn encode_body(&self, writer: &mut SbeWriter<'_>) -> Result<(), SbeEncodeError>;
141
142 fn decode_body(cursor: &mut SbeCursor<'_>) -> Result<Self, SbeDecodeError>;
143
144 fn encoded_body_size(&self) -> usize {
145 usize::from(Self::BLOCK_LENGTH)
146 }
147}
148
149impl<T> ToSbe for T
150where
151 T: MarketSbeMessage,
152{
153 #[inline]
154 fn to_sbe(&self) -> Result<Vec<u8>, SbeEncodeError> {
155 let encoded_size = HEADER_LENGTH + self.encoded_body_size();
156 let mut buf = Vec::with_capacity(encoded_size);
157 encode_into_uninit(self, &mut buf, encoded_size)?;
158 Ok(buf)
159 }
160
161 #[inline]
162 fn to_sbe_into(&self, buf: &mut Vec<u8>) -> Result<(), SbeEncodeError> {
163 let encoded_size = HEADER_LENGTH + self.encoded_body_size();
164 buf.clear();
165 buf.reserve(encoded_size);
166 encode_into_uninit(self, buf, encoded_size)
167 }
168}
169
170impl<T> FromSbe for T
171where
172 T: MarketSbeMessage,
173{
174 #[inline]
175 fn from_sbe(bytes: &[u8]) -> Result<Self, SbeDecodeError> {
176 let mut cursor = SbeCursor::new(bytes);
177 let header = decode_header(&mut cursor)?;
178 validate_header(header, T::TEMPLATE_ID, T::BLOCK_LENGTH)?;
179 T::decode_body(&mut cursor)
180 }
181}
182
183#[inline]
187#[allow(
188 unsafe_code,
189 reason = "set_len commits writes the SbeWriter has already made into spare capacity"
190)]
191#[allow(
192 clippy::panic_in_result_fn,
193 reason = "load-bearing safety check for the unsafe set_len; panic is the right outcome"
194)]
195fn encode_into_uninit<T>(
196 value: &T,
197 buf: &mut Vec<u8>,
198 encoded_size: usize,
199) -> Result<(), SbeEncodeError>
200where
201 T: MarketSbeMessage,
202{
203 debug_assert_eq!(buf.len(), 0);
204 debug_assert!(buf.capacity() >= encoded_size);
205
206 let spare = &mut buf.spare_capacity_mut()[..encoded_size];
207 let mut writer = SbeWriter::new_uninit(spare);
208 encode_header(
209 &mut writer,
210 T::BLOCK_LENGTH,
211 T::TEMPLATE_ID,
212 MARKET_SCHEMA_ID,
213 MARKET_SCHEMA_VERSION,
214 );
215 value.encode_body(&mut writer)?;
216
217 assert_eq!(
222 writer.pos(),
223 encoded_size,
224 "SBE encode_body wrote {} bytes but encoded_body_size reported {}",
225 writer.pos(),
226 encoded_size,
227 );
228
229 unsafe {
234 buf.set_len(encoded_size);
235 }
236 Ok(())
237}