rusty_model/data/
zero_copy.rs

1//! Zero-copy market data structures for ultra-fast binary parsing
2//!
3//! This module provides zerocopy-enabled structures for direct memory mapping
4//! of binary market data from exchanges, eliminating parsing overhead.
5//!
6//! # Bytemuck Usage Strategy
7//!
8//! We use a hybrid approach with bytemuck for maximum safety:
9//!
10//! 1. **Non-generic structs** (ZeroCopyPriceLevel, ZeroCopyTrade, MessageHeader):
11//!    - Use bytemuck's Pod and Zeroable derives for safe zero-copy operations
12//!    - All methods use bytemuck's safe APIs (bytes_of, try_from_bytes)
13//!
14//! 2. **Generic structs** (ZeroCopyOrderBookUpdate<N>):
15//!    - Cannot derive Pod/Zeroable due to const generic limitations
16//!    - Bytemuck doesn't support arrays [T; N] with generic N
17//!    - Maintain minimal unsafe code with comprehensive safety documentation
18//!    - Use parse_message_generic() instead of parse_message()
19//!
20//! This approach maximizes safety while maintaining the flexibility of const generics
21//! for compile-time configurable buffer sizes.
22
23use bytemuck::{Pod, Zeroable};
24
25/// Zero-copy price level structure
26///
27/// Represents a single price level in an order book with fixed-size fields
28/// suitable for direct binary parsing.
29#[derive(Debug, Clone, Copy, Pod, Zeroable)]
30#[repr(C)]
31pub struct ZeroCopyPriceLevel {
32    /// Price as fixed-point integer (e.g., price * 100000000 for 8 decimals)
33    pub price: u64,
34    /// Quantity as fixed-point integer
35    pub quantity: u64,
36}
37
38/// Zero-copy order book update with const generic array sizing
39///
40/// Fixed-size structure for receiving binary order book updates from exchanges.
41/// Supports up to N levels per side where N is compile-time configurable.
42///
43/// NOTE: Cannot derive Pod/Zeroable due to const generic parameter N.
44/// Bytemuck doesn't support generic arrays [T; N] for arbitrary N.
45/// We maintain manual unsafe implementations for this struct only.
46#[derive(Debug, Clone, Copy)]
47#[repr(C)]
48pub struct ZeroCopyOrderBookUpdate<const N: usize = 10> {
49    /// Symbol identifier (exchange-specific)
50    pub symbol_id: u32,
51    /// Padding for alignment
52    #[allow(clippy::pub_underscore_fields)]
53    pub _padding1: u32,
54    /// Sequence number for detecting gaps
55    pub sequence: u64,
56    /// Exchange timestamp in nanoseconds
57    pub timestamp_ns: u64,
58    /// Number of valid bid levels (0-N)
59    pub bid_count: u8,
60    /// Number of valid ask levels (0-N)
61    pub ask_count: u8,
62    /// Padding for alignment
63    #[allow(clippy::pub_underscore_fields)]
64    pub _padding2: [u8; 6],
65    /// Bid levels (only first `bid_count` are valid)
66    pub bids: [ZeroCopyPriceLevel; N],
67    /// Ask levels (only first `ask_count` are valid)
68    pub asks: [ZeroCopyPriceLevel; N],
69}
70
71/// Zero-copy trade message
72///
73/// Fixed-size structure for receiving binary trade data from exchanges.
74#[derive(Debug, Clone, Copy, Pod, Zeroable)]
75#[repr(C)]
76pub struct ZeroCopyTrade {
77    /// Symbol identifier (exchange-specific)
78    pub symbol_id: u32,
79    /// Trade side: 0 = Buy, 1 = Sell
80    pub side: u32,
81    /// Trade ID from exchange
82    pub trade_id: u64,
83    /// Exchange timestamp in nanoseconds
84    pub timestamp_ns: u64,
85    /// Price as fixed-point integer
86    pub price: u64,
87    /// Quantity as fixed-point integer
88    pub quantity: u64,
89}
90
91/// Zero-copy message header for length-prefixed protocols
92#[derive(Debug, Clone, Copy, Pod, Zeroable)]
93#[repr(C)]
94pub struct MessageHeader {
95    /// Total message length including header
96    pub length: u32,
97    /// Message type identifier
98    pub msg_type: u16,
99    /// Protocol version
100    pub version: u8,
101    /// Flags/reserved
102    pub flags: u8,
103}
104
105impl ZeroCopyPriceLevel {
106    /// Convert from fixed-point to `rust_decimal::Decimal`
107    #[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    /// Convert from fixed-point to `rust_decimal::Decimal`
114    #[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    /// Get valid bid levels as a slice
123    #[inline]
124    #[must_use]
125    pub fn valid_bids(&self) -> &[ZeroCopyPriceLevel] {
126        &self.bids[..(self.bid_count as usize).min(N)]
127    }
128
129    /// Get valid ask levels as a slice
130    #[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/// Parse a message from a buffer with proper alignment checks
138///
139/// This function safely parses a message from a byte buffer using bytemuck.
140/// Returns None if the buffer is too small or improperly aligned.
141#[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/// Parse a mutable message from a buffer with proper alignment checks
151///
152/// This function safely parses a mutable message from a byte buffer using bytemuck.
153/// Returns None if the buffer is too small or improperly aligned.
154#[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/// Check if a buffer pointer is properly aligned for type T
164#[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/// Parse a message from a buffer for types that can't implement Pod
172///
173/// This is needed for types with const generic parameters like ZeroCopyOrderBookUpdate<N>
174/// where bytemuck can't verify padding requirements at compile time.
175///
176/// # Safety
177/// The caller must ensure the buffer contains valid data for type T.
178/// This function validates both size and alignment requirements.
179#[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    // Check alignment - this is critical for avoiding undefined behavior
190    if !is_aligned_for_type::<T>(buffer) {
191        return None;
192    }
193
194    // Safety: We checked both buffer size and alignment above
195    // The type must be Copy which ensures it's safe to read from raw bytes
196    unsafe {
197        let ptr = buffer.as_ptr() as *const T;
198        Some(&*ptr)
199    }
200}
201
202impl ZeroCopyPriceLevel {
203    /// Convert this structure to bytes
204    #[inline]
205    #[must_use]
206    pub fn as_bytes(&self) -> &[u8] {
207        bytemuck::bytes_of(self)
208    }
209
210    /// Get the size of this structure at compile time
211    #[inline]
212    #[must_use]
213    pub const fn size() -> usize {
214        std::mem::size_of::<Self>()
215    }
216
217    /// Create a new price level with given values
218    #[inline]
219    #[must_use]
220    pub const fn new(price: u64, quantity: u64) -> Self {
221        Self { price, quantity }
222    }
223
224    /// Check if this price level is empty (zero quantity)
225    #[inline]
226    #[must_use]
227    pub const fn is_empty(&self) -> bool {
228        self.quantity == 0
229    }
230
231    /// Check if this price level is valid (non-zero price and quantity)
232    #[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    /// Convert this structure to bytes
241    ///
242    /// # Safety
243    /// This uses unsafe to convert the struct to a byte slice.
244    /// Safe because:
245    /// - The struct is #[repr(C)] with known layout
246    /// - All fields are Pod types (primitives and arrays of Pod)
247    /// - No padding issues due to explicit padding fields
248    ///
249    /// NOTE: We can't use bytemuck::bytes_of here because this struct
250    /// can't implement Pod due to the const generic parameter N.
251    #[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    /// Create a new zeroed instance
260    ///
261    /// # Safety
262    /// This uses unsafe to create a zeroed instance.
263    /// Safe because:
264    /// - All fields are primitives or arrays of primitives
265    /// - Zero is a valid bit pattern for all our fields
266    /// - No references or other types that could be invalid when zeroed
267    ///
268    /// NOTE: We can't use bytemuck::Zeroable::zeroed() here because
269    /// this struct can't implement Zeroable due to the const generic parameter N.
270    #[inline]
271    #[must_use]
272    pub const fn new_zeroed() -> Self {
273        unsafe { std::mem::zeroed() }
274    }
275
276    /// Get the size of this structure at compile time
277    #[inline]
278    #[must_use]
279    pub const fn size() -> usize {
280        std::mem::size_of::<Self>()
281    }
282
283    /// Get the maximum number of levels per side
284    #[inline]
285    #[must_use]
286    pub const fn max_levels() -> usize {
287        N
288    }
289
290    /// Calculate total buffer size needed for N levels
291    #[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    /// Check if the update has valid bid count
299    #[inline]
300    #[must_use]
301    pub const fn has_valid_bid_count(&self) -> bool {
302        (self.bid_count as usize) <= N
303    }
304
305    /// Check if the update has valid ask count
306    #[inline]
307    #[must_use]
308    pub const fn has_valid_ask_count(&self) -> bool {
309        (self.ask_count as usize) <= N
310    }
311
312    /// Create from bytes
313    ///
314    /// Uses the generic parse_message_generic function which handles
315    /// types that can't implement Pod due to const generics.
316    #[inline]
317    #[must_use]
318    pub fn from_bytes(bytes: &[u8]) -> Option<&Self> {
319        parse_message_generic(bytes)
320    }
321}
322
323impl ZeroCopyTrade {
324    /// Convert this structure to bytes
325    #[inline]
326    #[must_use]
327    pub fn as_bytes(&self) -> &[u8] {
328        bytemuck::bytes_of(self)
329    }
330
331    /// Get the size of this structure at compile time
332    #[inline]
333    #[must_use]
334    pub const fn size() -> usize {
335        std::mem::size_of::<Self>()
336    }
337
338    /// Create a new trade with given values
339    #[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    /// Check if this is a buy trade (side == 0)
360    #[inline]
361    #[must_use]
362    pub const fn is_buy(&self) -> bool {
363        self.side == 0
364    }
365
366    /// Check if this is a sell trade (side == 1)
367    #[inline]
368    #[must_use]
369    pub const fn is_sell(&self) -> bool {
370        self.side == 1
371    }
372
373    /// Check if the trade has valid data
374    #[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    /// Create from bytes
381    #[inline]
382    #[must_use]
383    pub fn from_bytes(bytes: &[u8]) -> Option<&Self> {
384        parse_message(bytes)
385    }
386}
387
388impl MessageHeader {
389    /// Convert this structure to bytes
390    #[inline]
391    #[must_use]
392    pub fn as_bytes(&self) -> &[u8] {
393        bytemuck::bytes_of(self)
394    }
395
396    /// Get the size of this structure at compile time
397    #[inline]
398    #[must_use]
399    pub const fn size() -> usize {
400        std::mem::size_of::<Self>()
401    }
402
403    /// Create a new message header
404    #[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    /// Get the payload length (total length minus header size)
416    #[inline]
417    #[must_use]
418    pub const fn payload_length(&self) -> u32 {
419        self.length.saturating_sub(Self::size() as u32)
420    }
421
422    /// Check if the header has valid length
423    #[inline]
424    #[must_use]
425    pub const fn has_valid_length(&self) -> bool {
426        self.length >= Self::size() as u32
427    }
428
429    /// Check if this is a specific message type
430    #[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
437/// Type alias for a zero-copy order book update with 10 levels per side.
438pub type ZeroCopyOrderBookUpdate10 = ZeroCopyOrderBookUpdate<10>;
439/// Type alias for a zero-copy order book update with 5 levels per side.
440pub type ZeroCopyOrderBookUpdate5 = ZeroCopyOrderBookUpdate<5>;
441/// Type alias for a zero-copy order book update with 20 levels per side.
442pub type ZeroCopyOrderBookUpdate20 = ZeroCopyOrderBookUpdate<20>;
443
444// Default type alias for seamless migration
445pub use ZeroCopyOrderBookUpdate10 as DefaultZeroCopyOrderBookUpdate;
446
447impl ZeroCopyPriceLevel {
448    /// Create from bytes
449    #[inline]
450    #[must_use]
451    pub fn from_bytes(bytes: &[u8]) -> Option<&Self> {
452        parse_message(bytes)
453    }
454}
455
456impl MessageHeader {
457    /// Create from bytes
458    #[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    /// Buffer size for alignment testing scenarios.
471    /// Must be large enough to accommodate:
472    /// - Target structure size (e.g., 16 bytes for ZeroCopyPriceLevel)
473    /// - Offset for creating misalignment (at least 1 byte)
474    /// - Safety margin for various alignment tests
475    ///   64 bytes provides ample space for testing alignment edge cases.
476    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, // $12345 with 8 decimals
482            quantity: 10_000_000_000, // 100 units with 8 decimals
483        };
484
485        let bytes = level.as_bytes();
486        assert_eq!(bytes.len(), 16); // 2 * u64
487
488        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        // Test with different const generic sizes
523        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, // $12345.6789 with 8 decimals
560            quantity: 10_050_000_000, // 100.5 units with 8 decimals
561        };
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        // Test with N = 300 (larger than u8::MAX = 255)
573        // This tests that the fix properly handles N > 255
574        let mut update = ZeroCopyOrderBookUpdate::<300>::new_zeroed();
575        update.symbol_id = 999;
576        update.bid_count = 250;
577        update.ask_count = 255; // Max value for u8
578
579        // The fix ensures we get the correct min between bid_count and N
580        // Without the fix, N would be truncated to 44 (300 & 0xFF)
581        // and we'd get min(250, 44) = 44 instead of 250
582        assert_eq!(update.valid_bids().len(), 250);
583        assert_eq!(update.valid_asks().len(), 255);
584
585        // Test edge case where count exceeds N
586        let mut update2 = ZeroCopyOrderBookUpdate::<100>::new_zeroed();
587        update2.bid_count = 255; // Max u8 value
588        update2.ask_count = 255; // Max u8 value
589
590        // Should be capped at N (100), not at some truncated value
591        assert_eq!(update2.valid_bids().len(), 100);
592        assert_eq!(update2.valid_asks().len(), 100);
593
594        // Test with N = 512 to really show the truncation issue would happen
595        let mut update3 = ZeroCopyOrderBookUpdate::<512>::new_zeroed();
596        update3.bid_count = 200;
597        update3.ask_count = 200;
598
599        // Without fix: 512 & 0xFF = 0, so min(200, 0) = 0
600        // With fix: min(200, 512) = 200
601        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        // Test successful mutable parsing with properly aligned data
608        let mut level = ZeroCopyPriceLevel {
609            price: 5_000_000_000,     // $50.00 with 8 decimals
610            quantity: 15_000_000_000, // 150 units with 8 decimals
611        };
612
613        let bytes = bytemuck::bytes_of_mut(&mut level);
614        let parsed = parse_message_mut::<ZeroCopyPriceLevel>(bytes).unwrap();
615
616        // Verify we can read the original values
617        assert_eq!(parsed.price, 5_000_000_000);
618        assert_eq!(parsed.quantity, 15_000_000_000);
619
620        // Verify we can mutate through the parsed reference
621        parsed.price = 6_000_000_000;
622        parsed.quantity = 20_000_000_000;
623
624        // Verify the original struct was modified
625        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        // Test with buffer smaller than required type size
632        let mut small_buffer = vec![0u8; 4]; // Only 4 bytes
633        let result = parse_message_mut::<ZeroCopyPriceLevel>(&mut small_buffer);
634
635        // Should return None due to insufficient buffer size
636        assert!(result.is_none());
637    }
638
639    #[test]
640    fn test_parse_message_mut_misaligned_buffer() {
641        // Create a properly sized buffer but with misaligned data
642        let mut data = [0u8; MISALIGNMENT_TEST_BUFFER_SIZE]; // Large enough buffer
643
644        // Use an offset to create misalignment for u64 types (8-byte alignment required)
645        let misaligned_slice = &mut data[1..17]; // 16 bytes but starting at offset 1
646        let result = parse_message_mut::<ZeroCopyPriceLevel>(misaligned_slice);
647
648        // Should return None due to alignment issues
649        assert!(result.is_none());
650    }
651
652    #[test]
653    fn test_parse_message_mut_edge_cases() {
654        // Test with exactly the right size buffer
655        let mut buffer = vec![0u8; std::mem::size_of::<ZeroCopyPriceLevel>()];
656        let result = parse_message_mut::<ZeroCopyPriceLevel>(&mut buffer);
657
658        // Should succeed with exact size and proper alignment
659        assert!(result.is_some());
660
661        // Test with empty buffer
662        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        // Test mutable parsing with MessageHeader
670        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        // Verify original values
681        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        // Test mutation
687        parsed.length = 2048;
688        parsed.msg_type = 3001;
689        parsed.version = 3;
690        parsed.flags = 2;
691
692        // Verify original was modified
693        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        // Test mutable parsing with ZeroCopyTrade
702        let mut trade = ZeroCopyTrade {
703            symbol_id: 42,
704            side: 0, // Buy
705            trade_id: 98765,
706            timestamp_ns: 1_600_000_000_000_000_000,
707            price: 4_500_000_000,     // $45.00 with 8 decimals
708            quantity: 25_000_000_000, // 250 units with 8 decimals
709        };
710
711        let bytes = bytemuck::bytes_of_mut(&mut trade);
712        let parsed = parse_message_mut::<ZeroCopyTrade>(bytes).unwrap();
713
714        // Verify we can read and validate
715        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        // Test mutation capabilities
721        parsed.side = 1; // Change to sell
722        parsed.price = 4_600_000_000;
723        parsed.quantity = 30_000_000_000;
724
725        // Verify changes propagated to original
726        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        // Test that mutable parsing is safe for concurrent scenarios
734        // Note: This tests memory safety, not thread safety
735        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        // Modify both independently
745        parsed1.price = 1500;
746        parsed2.quantity = 5000;
747
748        // Verify independent modifications
749        assert_eq!(level1.price, 1500);
750        assert_eq!(level1.quantity, 2000); // Unchanged
751        assert_eq!(level2.price, 3000); // Unchanged
752        assert_eq!(level2.quantity, 5000);
753    }
754}