1use bytemuck::{Pod, Zeroable};
24
25#[derive(Debug, Clone, Copy, Pod, Zeroable)]
30#[repr(C)]
31pub struct ZeroCopyPriceLevel {
32 pub price: u64,
34 pub quantity: u64,
36}
37
38#[derive(Debug, Clone, Copy)]
47#[repr(C)]
48pub struct ZeroCopyOrderBookUpdate<const N: usize = 10> {
49 pub symbol_id: u32,
51 #[allow(clippy::pub_underscore_fields)]
53 pub _padding1: u32,
54 pub sequence: u64,
56 pub timestamp_ns: u64,
58 pub bid_count: u8,
60 pub ask_count: u8,
62 #[allow(clippy::pub_underscore_fields)]
64 pub _padding2: [u8; 6],
65 pub bids: [ZeroCopyPriceLevel; N],
67 pub asks: [ZeroCopyPriceLevel; N],
69}
70
71#[derive(Debug, Clone, Copy, Pod, Zeroable)]
75#[repr(C)]
76pub struct ZeroCopyTrade {
77 pub symbol_id: u32,
79 pub side: u32,
81 pub trade_id: u64,
83 pub timestamp_ns: u64,
85 pub price: u64,
87 pub quantity: u64,
89}
90
91#[derive(Debug, Clone, Copy, Pod, Zeroable)]
93#[repr(C)]
94pub struct MessageHeader {
95 pub length: u32,
97 pub msg_type: u16,
99 pub version: u8,
101 pub flags: u8,
103}
104
105impl ZeroCopyPriceLevel {
106 #[inline]
108 #[must_use]
109 pub fn to_decimal_price(&self, decimals: u32) -> rust_decimal::Decimal {
110 rust_decimal::Decimal::from_i128_with_scale(i128::from(self.price), decimals)
111 }
112
113 #[inline]
115 #[must_use]
116 pub fn to_decimal_quantity(&self, decimals: u32) -> rust_decimal::Decimal {
117 rust_decimal::Decimal::from_i128_with_scale(i128::from(self.quantity), decimals)
118 }
119}
120
121impl<const N: usize> ZeroCopyOrderBookUpdate<N> {
122 #[inline]
124 #[must_use]
125 pub fn valid_bids(&self) -> &[ZeroCopyPriceLevel] {
126 &self.bids[..(self.bid_count as usize).min(N)]
127 }
128
129 #[inline]
131 #[must_use]
132 pub fn valid_asks(&self) -> &[ZeroCopyPriceLevel] {
133 &self.asks[..(self.ask_count as usize).min(N)]
134 }
135}
136
137#[inline]
142#[must_use]
143pub fn parse_message<T>(buffer: &[u8]) -> Option<&T>
144where
145 T: Pod,
146{
147 bytemuck::try_from_bytes(buffer).ok()
148}
149
150#[inline]
155#[must_use]
156pub fn parse_message_mut<T>(buffer: &mut [u8]) -> Option<&mut T>
157where
158 T: Pod,
159{
160 bytemuck::try_from_bytes_mut(buffer).ok()
161}
162
163#[inline]
165fn is_aligned_for_type<T>(buffer: &[u8]) -> bool {
166 let ptr_addr = buffer.as_ptr() as usize;
167 let required_alignment = std::mem::align_of::<T>();
168 ptr_addr.is_multiple_of(required_alignment)
169}
170
171#[inline]
180#[must_use]
181pub fn parse_message_generic<T>(buffer: &[u8]) -> Option<&T>
182where
183 T: Copy,
184{
185 if buffer.len() < std::mem::size_of::<T>() {
186 return None;
187 }
188
189 if !is_aligned_for_type::<T>(buffer) {
191 return None;
192 }
193
194 unsafe {
197 let ptr = buffer.as_ptr() as *const T;
198 Some(&*ptr)
199 }
200}
201
202impl ZeroCopyPriceLevel {
203 #[inline]
205 #[must_use]
206 pub fn as_bytes(&self) -> &[u8] {
207 bytemuck::bytes_of(self)
208 }
209
210 #[inline]
212 #[must_use]
213 pub const fn size() -> usize {
214 std::mem::size_of::<Self>()
215 }
216
217 #[inline]
219 #[must_use]
220 pub const fn new(price: u64, quantity: u64) -> Self {
221 Self { price, quantity }
222 }
223
224 #[inline]
226 #[must_use]
227 pub const fn is_empty(&self) -> bool {
228 self.quantity == 0
229 }
230
231 #[inline]
233 #[must_use]
234 pub const fn is_valid(&self) -> bool {
235 self.price > 0 && self.quantity > 0
236 }
237}
238
239impl<const N: usize> ZeroCopyOrderBookUpdate<N> {
240 #[inline]
252 #[must_use]
253 pub const fn as_bytes(&self) -> &[u8] {
254 unsafe {
255 std::slice::from_raw_parts(self as *const _ as *const u8, std::mem::size_of::<Self>())
256 }
257 }
258
259 #[inline]
271 #[must_use]
272 pub const fn new_zeroed() -> Self {
273 unsafe { std::mem::zeroed() }
274 }
275
276 #[inline]
278 #[must_use]
279 pub const fn size() -> usize {
280 std::mem::size_of::<Self>()
281 }
282
283 #[inline]
285 #[must_use]
286 pub const fn max_levels() -> usize {
287 N
288 }
289
290 #[inline]
292 #[must_use]
293 pub const fn buffer_size_for_levels(levels: usize) -> usize {
294 std::mem::size_of::<ZeroCopyOrderBookUpdate<1>>()
295 + (levels.saturating_sub(1)) * std::mem::size_of::<ZeroCopyPriceLevel>() * 2
296 }
297
298 #[inline]
300 #[must_use]
301 pub const fn has_valid_bid_count(&self) -> bool {
302 (self.bid_count as usize) <= N
303 }
304
305 #[inline]
307 #[must_use]
308 pub const fn has_valid_ask_count(&self) -> bool {
309 (self.ask_count as usize) <= N
310 }
311
312 #[inline]
317 #[must_use]
318 pub fn from_bytes(bytes: &[u8]) -> Option<&Self> {
319 parse_message_generic(bytes)
320 }
321}
322
323impl ZeroCopyTrade {
324 #[inline]
326 #[must_use]
327 pub fn as_bytes(&self) -> &[u8] {
328 bytemuck::bytes_of(self)
329 }
330
331 #[inline]
333 #[must_use]
334 pub const fn size() -> usize {
335 std::mem::size_of::<Self>()
336 }
337
338 #[inline]
340 #[must_use]
341 pub const fn new(
342 symbol_id: u32,
343 side: u32,
344 trade_id: u64,
345 timestamp_ns: u64,
346 price: u64,
347 quantity: u64,
348 ) -> Self {
349 Self {
350 symbol_id,
351 side,
352 trade_id,
353 timestamp_ns,
354 price,
355 quantity,
356 }
357 }
358
359 #[inline]
361 #[must_use]
362 pub const fn is_buy(&self) -> bool {
363 self.side == 0
364 }
365
366 #[inline]
368 #[must_use]
369 pub const fn is_sell(&self) -> bool {
370 self.side == 1
371 }
372
373 #[inline]
375 #[must_use]
376 pub const fn is_valid(&self) -> bool {
377 self.price > 0 && self.quantity > 0 && self.trade_id > 0
378 }
379
380 #[inline]
382 #[must_use]
383 pub fn from_bytes(bytes: &[u8]) -> Option<&Self> {
384 parse_message(bytes)
385 }
386}
387
388impl MessageHeader {
389 #[inline]
391 #[must_use]
392 pub fn as_bytes(&self) -> &[u8] {
393 bytemuck::bytes_of(self)
394 }
395
396 #[inline]
398 #[must_use]
399 pub const fn size() -> usize {
400 std::mem::size_of::<Self>()
401 }
402
403 #[inline]
405 #[must_use]
406 pub const fn new(length: u32, msg_type: u16, version: u8, flags: u8) -> Self {
407 Self {
408 length,
409 msg_type,
410 version,
411 flags,
412 }
413 }
414
415 #[inline]
417 #[must_use]
418 pub const fn payload_length(&self) -> u32 {
419 self.length.saturating_sub(Self::size() as u32)
420 }
421
422 #[inline]
424 #[must_use]
425 pub const fn has_valid_length(&self) -> bool {
426 self.length >= Self::size() as u32
427 }
428
429 #[inline]
431 #[must_use]
432 pub const fn is_message_type(&self, msg_type: u16) -> bool {
433 self.msg_type == msg_type
434 }
435}
436
437pub type ZeroCopyOrderBookUpdate10 = ZeroCopyOrderBookUpdate<10>;
439pub type ZeroCopyOrderBookUpdate5 = ZeroCopyOrderBookUpdate<5>;
441pub type ZeroCopyOrderBookUpdate20 = ZeroCopyOrderBookUpdate<20>;
443
444pub use ZeroCopyOrderBookUpdate10 as DefaultZeroCopyOrderBookUpdate;
446
447impl ZeroCopyPriceLevel {
448 #[inline]
450 #[must_use]
451 pub fn from_bytes(bytes: &[u8]) -> Option<&Self> {
452 parse_message(bytes)
453 }
454}
455
456impl MessageHeader {
457 #[inline]
459 #[must_use]
460 pub fn from_bytes(bytes: &[u8]) -> Option<&Self> {
461 parse_message(bytes)
462 }
463}
464
465#[cfg(test)]
466mod tests {
467 #[allow(unused_imports)]
468 use super::*;
469
470 const MISALIGNMENT_TEST_BUFFER_SIZE: usize = 64;
477
478 #[test]
479 fn test_zero_copy_price_level() {
480 let level = ZeroCopyPriceLevel {
481 price: 1_234_500_000_000, quantity: 10_000_000_000, };
484
485 let bytes = level.as_bytes();
486 assert_eq!(bytes.len(), 16); let parsed = ZeroCopyPriceLevel::from_bytes(bytes).unwrap();
489 assert_eq!(parsed.price, level.price);
490 assert_eq!(parsed.quantity, level.quantity);
491 }
492
493 #[test]
494 fn test_zero_copy_orderbook_update() {
495 let mut update = ZeroCopyOrderBookUpdate::<10>::new_zeroed();
496 update.symbol_id = 42;
497 update.sequence = 12345;
498 update.timestamp_ns = 1_000_000_000;
499 update.bid_count = 2;
500 update.ask_count = 3;
501
502 update.bids[0] = ZeroCopyPriceLevel {
503 price: 10_000_000_000,
504 quantity: 1_000_000_000,
505 };
506 update.bids[1] = ZeroCopyPriceLevel {
507 price: 9_900_000_000,
508 quantity: 2_000_000_000,
509 };
510
511 let bytes = update.as_bytes();
512 let parsed = ZeroCopyOrderBookUpdate::<10>::from_bytes(bytes).unwrap();
513
514 assert_eq!(parsed.symbol_id, 42);
515 assert_eq!(parsed.sequence, 12345);
516 assert_eq!(parsed.valid_bids().len(), 2);
517 assert_eq!(parsed.valid_asks().len(), 3);
518 }
519
520 #[test]
521 fn test_zero_copy_orderbook_update_different_sizes() {
522 let mut update5 = ZeroCopyOrderBookUpdate::<5>::new_zeroed();
524 update5.symbol_id = 100;
525 update5.bid_count = 5;
526 update5.ask_count = 5;
527
528 let mut update20 = ZeroCopyOrderBookUpdate::<20>::new_zeroed();
529 update20.symbol_id = 200;
530 update20.bid_count = 15;
531 update20.ask_count = 18;
532
533 assert_eq!(update5.valid_bids().len(), 5);
534 assert_eq!(update5.valid_asks().len(), 5);
535 assert_eq!(update20.valid_bids().len(), 15);
536 assert_eq!(update20.valid_asks().len(), 18);
537 }
538
539 #[test]
540 fn test_message_header() {
541 let header = MessageHeader {
542 length: 256,
543 msg_type: 1001,
544 version: 1,
545 flags: 0,
546 };
547
548 let bytes = header.as_bytes();
549 assert_eq!(bytes.len(), 8);
550
551 let parsed = MessageHeader::from_bytes(bytes).unwrap();
552 assert_eq!(parsed.length, 256);
553 assert_eq!(parsed.msg_type, 1001);
554 }
555
556 #[test]
557 fn test_decimal_conversion() {
558 let level = ZeroCopyPriceLevel {
559 price: 1_234_567_890_000, quantity: 10_050_000_000, };
562
563 let price_decimal = level.to_decimal_price(8);
564 assert_eq!(price_decimal.to_string(), "12345.67890000");
565
566 let qty_decimal = level.to_decimal_quantity(8);
567 assert_eq!(qty_decimal.to_string(), "100.50000000");
568 }
569
570 #[test]
571 fn test_large_n_order_book_truncation_fix() {
572 let mut update = ZeroCopyOrderBookUpdate::<300>::new_zeroed();
575 update.symbol_id = 999;
576 update.bid_count = 250;
577 update.ask_count = 255; assert_eq!(update.valid_bids().len(), 250);
583 assert_eq!(update.valid_asks().len(), 255);
584
585 let mut update2 = ZeroCopyOrderBookUpdate::<100>::new_zeroed();
587 update2.bid_count = 255; update2.ask_count = 255; assert_eq!(update2.valid_bids().len(), 100);
592 assert_eq!(update2.valid_asks().len(), 100);
593
594 let mut update3 = ZeroCopyOrderBookUpdate::<512>::new_zeroed();
596 update3.bid_count = 200;
597 update3.ask_count = 200;
598
599 assert_eq!(update3.valid_bids().len(), 200);
602 assert_eq!(update3.valid_asks().len(), 200);
603 }
604
605 #[test]
606 fn test_parse_message_mut_valid_parsing() {
607 let mut level = ZeroCopyPriceLevel {
609 price: 5_000_000_000, quantity: 15_000_000_000, };
612
613 let bytes = bytemuck::bytes_of_mut(&mut level);
614 let parsed = parse_message_mut::<ZeroCopyPriceLevel>(bytes).unwrap();
615
616 assert_eq!(parsed.price, 5_000_000_000);
618 assert_eq!(parsed.quantity, 15_000_000_000);
619
620 parsed.price = 6_000_000_000;
622 parsed.quantity = 20_000_000_000;
623
624 assert_eq!(level.price, 6_000_000_000);
626 assert_eq!(level.quantity, 20_000_000_000);
627 }
628
629 #[test]
630 fn test_parse_message_mut_buffer_too_small() {
631 let mut small_buffer = vec![0u8; 4]; let result = parse_message_mut::<ZeroCopyPriceLevel>(&mut small_buffer);
634
635 assert!(result.is_none());
637 }
638
639 #[test]
640 fn test_parse_message_mut_misaligned_buffer() {
641 let mut data = [0u8; MISALIGNMENT_TEST_BUFFER_SIZE]; let misaligned_slice = &mut data[1..17]; let result = parse_message_mut::<ZeroCopyPriceLevel>(misaligned_slice);
647
648 assert!(result.is_none());
650 }
651
652 #[test]
653 fn test_parse_message_mut_edge_cases() {
654 let mut buffer = vec![0u8; std::mem::size_of::<ZeroCopyPriceLevel>()];
656 let result = parse_message_mut::<ZeroCopyPriceLevel>(&mut buffer);
657
658 assert!(result.is_some());
660
661 let empty: &mut [u8] = &mut [];
663 let result = parse_message_mut::<ZeroCopyPriceLevel>(empty);
664 assert!(result.is_none());
665 }
666
667 #[test]
668 fn test_parse_message_mut_message_header() {
669 let mut header = MessageHeader {
671 length: 1024,
672 msg_type: 2001,
673 version: 2,
674 flags: 1,
675 };
676
677 let bytes = bytemuck::bytes_of_mut(&mut header);
678 let parsed = parse_message_mut::<MessageHeader>(bytes).unwrap();
679
680 assert_eq!(parsed.length, 1024);
682 assert_eq!(parsed.msg_type, 2001);
683 assert_eq!(parsed.version, 2);
684 assert_eq!(parsed.flags, 1);
685
686 parsed.length = 2048;
688 parsed.msg_type = 3001;
689 parsed.version = 3;
690 parsed.flags = 2;
691
692 assert_eq!(header.length, 2048);
694 assert_eq!(header.msg_type, 3001);
695 assert_eq!(header.version, 3);
696 assert_eq!(header.flags, 2);
697 }
698
699 #[test]
700 fn test_parse_message_mut_trade_data() {
701 let mut trade = ZeroCopyTrade {
703 symbol_id: 42,
704 side: 0, trade_id: 98765,
706 timestamp_ns: 1_600_000_000_000_000_000,
707 price: 4_500_000_000, quantity: 25_000_000_000, };
710
711 let bytes = bytemuck::bytes_of_mut(&mut trade);
712 let parsed = parse_message_mut::<ZeroCopyTrade>(bytes).unwrap();
713
714 assert!(parsed.is_buy());
716 assert!(parsed.is_valid());
717 assert_eq!(parsed.symbol_id, 42);
718 assert_eq!(parsed.trade_id, 98765);
719
720 parsed.side = 1; parsed.price = 4_600_000_000;
723 parsed.quantity = 30_000_000_000;
724
725 assert!(trade.is_sell());
727 assert_eq!(trade.price, 4_600_000_000);
728 assert_eq!(trade.quantity, 30_000_000_000);
729 }
730
731 #[test]
732 fn test_parse_message_mut_concurrent_safety() {
733 let mut level1 = ZeroCopyPriceLevel::new(1000, 2000);
736 let mut level2 = ZeroCopyPriceLevel::new(3000, 4000);
737
738 let bytes1 = bytemuck::bytes_of_mut(&mut level1);
739 let bytes2 = bytemuck::bytes_of_mut(&mut level2);
740
741 let parsed1 = parse_message_mut::<ZeroCopyPriceLevel>(bytes1).unwrap();
742 let parsed2 = parse_message_mut::<ZeroCopyPriceLevel>(bytes2).unwrap();
743
744 parsed1.price = 1500;
746 parsed2.quantity = 5000;
747
748 assert_eq!(level1.price, 1500);
750 assert_eq!(level1.quantity, 2000); assert_eq!(level2.price, 3000); assert_eq!(level2.quantity, 5000);
753 }
754}