Skip to main content

ddp_rs/
packet.rs

1//! Packet parsing for receiving data from DDP displays.
2//!
3//! This module provides the [`Packet`] type for parsing incoming DDP packets,
4//! typically used when receiving responses from displays.
5
6use crate::protocol::Header;
7#[cfg(feature = "std")]
8use crate::protocol::message::Message;
9#[cfg(all(feature = "alloc", not(feature = "std")))]
10use alloc::vec::Vec;
11
12/// A borrowed, zero-copy view of a received DDP packet.
13///
14/// This is the allocation-free receive path: it parses the header and borrows the payload
15/// directly from the input buffer, so it works on bare-metal `no_std` targets with no
16/// allocator. For an owned packet (and JSON message parsing), use [`Packet`] (requires the
17/// `std` feature).
18///
19/// # Examples
20///
21/// ```
22/// use ddp_rs::packet::PacketRef;
23///
24/// let bytes = [
25///     0x41, 0x01, 0x0D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
26///     0xFF, 0x00, 0x00, // 1 RGB pixel: red
27/// ];
28/// let packet = PacketRef::from_bytes(&bytes).unwrap();
29///
30/// assert_eq!(packet.header.sequence_number, 1);
31/// assert_eq!(packet.data, &[0xFF, 0x00, 0x00]);
32/// ```
33#[derive(Debug, PartialEq, Eq, Clone, Copy)]
34pub struct PacketRef<'a> {
35    /// The parsed packet header with metadata.
36    pub header: Header,
37
38    /// The packet payload, borrowed from the input buffer.
39    pub data: &'a [u8],
40}
41
42impl<'a> PacketRef<'a> {
43    /// Parses a DDP packet, borrowing the payload from `bytes`.
44    ///
45    /// Handles both 10-byte and 14-byte (timecode) headers. Returns `None` if `bytes` is too
46    /// short to contain a complete header.
47    pub fn from_bytes(bytes: &'a [u8]) -> Option<Self> {
48        if bytes.len() < 10 {
49            return None;
50        }
51
52        // Check the timecode flag to learn the header size before parsing it.
53        let has_timecode = (bytes[0] & 0b00010000) != 0;
54        let header_size = if has_timecode { 14 } else { 10 };
55
56        if bytes.len() < header_size {
57            return None;
58        }
59
60        Some(PacketRef {
61            header: Header::from(&bytes[0..header_size]),
62            data: &bytes[header_size..],
63        })
64    }
65}
66
67/// A parsed DDP packet received from a display.
68///
69/// This struct represents packets sent back by displays, such as status updates,
70/// configuration responses, or acknowledgments.
71///
72/// # Examples
73///
74/// ```
75/// use ddp_rs::packet::Packet;
76///
77/// // Parse a packet from raw bytes
78/// let bytes = vec![
79///     0x41, 0x01, 0x0D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
80///     0xFF, 0x00, 0x00  // 1 RGB pixel: red
81/// ];
82/// let packet = Packet::from_bytes(&bytes);
83///
84/// assert_eq!(packet.header.sequence_number, 1);
85/// assert_eq!(packet.data, vec![0xFF, 0x00, 0x00]);
86/// ```
87#[cfg(feature = "alloc")]
88#[derive(Debug, PartialEq, Clone)]
89pub struct Packet {
90    /// The parsed packet header with metadata
91    pub header: Header,
92
93    /// Raw pixel data (if this packet contains pixels)
94    pub data: Vec<u8>,
95
96    /// Parsed JSON message (if this packet contains a message).
97    ///
98    /// Only present with the `std` feature, since JSON parsing requires `serde_json`.
99    #[cfg(feature = "std")]
100    pub parsed: Option<Message>,
101}
102
103#[cfg(feature = "alloc")]
104impl Packet {
105    /// Creates a packet from a header and data slice (without parsing).
106    pub fn from_data(h: Header, d: &[u8]) -> Packet {
107        Packet {
108            header: h,
109            data: d.to_vec(),
110            #[cfg(feature = "std")]
111            parsed: None,
112        }
113    }
114
115    /// Parses a DDP packet from raw bytes.
116    ///
117    /// This method handles both 10-byte and 14-byte headers (with timecode),
118    /// and attempts to parse JSON messages if the packet is a reply/query.
119    ///
120    /// # Arguments
121    ///
122    /// * `bytes` - Raw packet bytes including header and data
123    ///
124    /// # Returns
125    ///
126    /// A parsed `Packet`. If parsing fails, returns a default packet with empty data.
127    ///
128    /// # Examples
129    ///
130    /// ```
131    /// use ddp_rs::packet::Packet;
132    ///
133    /// let bytes = vec![
134    ///     0x41, 0x01, 0x0D, 0x01,           // Packet type, seq, config, id
135    ///     0x00, 0x00, 0x00, 0x00,           // Offset
136    ///     0x00, 0x06,                        // Length = 6
137    ///     0xFF, 0x00, 0x00,                 // Pixel 1: Red
138    ///     0x00, 0xFF, 0x00,                 // Pixel 2: Green
139    /// ];
140    /// let packet = Packet::from_bytes(&bytes);
141    /// assert_eq!(packet.data.len(), 6);
142    /// ```
143    pub fn from_bytes(bytes: &[u8]) -> Self {
144        // Reuse the zero-copy parser for header + payload slicing.
145        let (header, data): (Header, &[u8]) = match PacketRef::from_bytes(bytes) {
146            Some(p) => (p.header, p.data),
147            // Too short for a complete header: return a default, empty packet.
148            None => (Header::default(), &[]),
149        };
150
151        Packet {
152            header,
153            data: data.to_vec(),
154            #[cfg(feature = "std")]
155            parsed: Self::parse_message(&header, data),
156        }
157    }
158
159    /// Attempts to parse the payload of a reply packet into a JSON [`Message`].
160    #[cfg(feature = "std")]
161    fn parse_message(header: &Header, data: &[u8]) -> Option<Message> {
162        if !header.packet_type.reply {
163            return None;
164        }
165
166        // Try to parse the data into typed structs in the spec
167        match match header.id {
168            crate::protocol::ID::Control => match serde_json::from_slice(data) {
169                Ok(v) => Some(Message::Control(v)),
170                Err(_) => None,
171            },
172            crate::protocol::ID::Config => match serde_json::from_slice(data) {
173                Ok(v) => Some(Message::Config(v)),
174                Err(_) => None,
175            },
176            crate::protocol::ID::Status => match serde_json::from_slice(data) {
177                Ok(v) => Some(Message::Status(v)),
178                Err(_) => None,
179            },
180            _ => None,
181        } {
182            // Worked, return the typed struct
183            Some(v) => Some(v),
184
185            // OK, no bueno, lets try just untyped JSON
186            None => match header.id {
187                crate::protocol::ID::Control
188                | crate::protocol::ID::Config
189                | crate::protocol::ID::Status => match serde_json::from_slice(data) {
190                    // JSON Value it is
191                    Ok(v) => Some(Message::Parsed((header.id, v))),
192                    // Ok we're really screwed, lets just return the raw data as a string
193                    Err(_) => match std::str::from_utf8(data) {
194                        Ok(v) => Some(Message::Unparsed((header.id, v.to_string()))),
195                        // I guess it's... just bytes?
196                        Err(_) => None,
197                    },
198                },
199                _ => None,
200            },
201        }
202    }
203}
204
205#[cfg(test)]
206mod packet_ref_tests {
207    use super::*;
208
209    #[test]
210    fn parses_header_and_borrows_data() {
211        let bytes = [
212            0x41, 0x01, 0x0D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xFF, 0x00, 0x00, 0x00,
213            0xFF, 0x00,
214        ];
215        let p = PacketRef::from_bytes(&bytes).unwrap();
216        assert_eq!(p.header.sequence_number, 1);
217        assert_eq!(p.header.length, 6);
218        assert_eq!(p.data, &[0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00]);
219    }
220
221    #[test]
222    fn handles_timecode_header() {
223        // timecode bit set -> 14 byte header
224        let mut bytes = vec![0x51, 0x01, 0x0D, 0x01, 0, 0, 0, 0, 0, 3];
225        bytes.extend_from_slice(&[0x00, 0x00, 0x30, 0x39]); // timecode
226        bytes.extend_from_slice(&[0xAB, 0xCD, 0xEF]); // payload
227        let p = PacketRef::from_bytes(&bytes).unwrap();
228        assert_eq!(p.header.time_code.0, Some(12345));
229        assert_eq!(p.data, &[0xAB, 0xCD, 0xEF]);
230    }
231
232    #[test]
233    fn too_short_returns_none() {
234        assert!(PacketRef::from_bytes(&[0u8; 4]).is_none());
235    }
236}
237
238#[cfg(all(test, feature = "std"))]
239mod tests {
240    use super::*;
241
242    #[test]
243    fn test_json() {
244        {
245            let data = vec![
246                0x44, 0x00, 0x0D, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8E, 0x7b, 0x0a, 0x20, 0x20,
247                0x20, 0x20, 0x22, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x3a, 0x0a, 0x20, 0x20,
248                0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x67,
249                0x77, 0x22, 0x3a, 0x20, 0x22, 0x61, 0x2e, 0x62, 0x2e, 0x63, 0x2e, 0x64, 0x22, 0x2c,
250                0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x69, 0x70, 0x22, 0x3a,
251                0x20, 0x22, 0x61, 0x2e, 0x62, 0x2e, 0x63, 0x2e, 0x64, 0x22, 0x2c, 0x0a, 0x20, 0x20,
252                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6e, 0x6d, 0x22, 0x3a, 0x20, 0x22, 0x61,
253                0x2e, 0x62, 0x2e, 0x63, 0x2e, 0x64, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
254                0x20, 0x20, 0x20, 0x22, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x22, 0x3a, 0x0a, 0x20, 0x20,
255                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
256                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
257                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6c, 0x22, 0x3a,
258                0x20, 0x33, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
259                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x3a, 0x20,
260                0x31, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
261                0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x73, 0x22, 0x3a, 0x20, 0x34, 0x2c, 0x0a,
262                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
263                0x20, 0x20, 0x22, 0x74, 0x73, 0x22, 0x3a, 0x20, 0x32, 0x0a, 0x20, 0x20, 0x20, 0x20,
264                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20,
265                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
266                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22,
267                0x6c, 0x22, 0x3a, 0x20, 0x37, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
268                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x70, 0x6f, 0x72, 0x74,
269                0x22, 0x3a, 0x20, 0x35, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
270                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x73, 0x22, 0x3a, 0x20,
271                0x38, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
272                0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x73, 0x22, 0x3a, 0x20, 0x36, 0x0a, 0x20,
273                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20,
274                0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d,
275                0x0a, 0x7d,
276            ];
277            let packet = Packet::from_bytes(&data);
278
279            assert_eq!(packet.header.length, 398);
280
281            match packet.parsed {
282                Some(p) => match p {
283                    Message::Config(c) => {
284                        assert_eq!(c.config.gw.unwrap(), "a.b.c.d");
285                        assert_eq!(c.config.nm.unwrap(), "a.b.c.d");
286                        assert_eq!(c.config.ports.len(), 2);
287                    }
288                    _ => panic!("not the right packet parsed"),
289                },
290                None => panic!("Packet parsing failed"),
291            }
292        }
293    }
294
295    #[test]
296    fn test_untyped() {
297        {
298            let data = vec![
299                0x44, 0x00, 0x0D, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x7B, 0x22, 0x68, 0x65,
300                0x6C, 0x6C, 0x6F, 0x22, 0x3A, 0x20, 0x22, 0x6F, 0x6B, 0x22, 0x7D,
301            ];
302            let packet = Packet::from_bytes(&data);
303
304            match packet.parsed {
305                Some(p) => match p {
306                    Message::Parsed((_, p)) => {
307                        assert_eq!(p["hello"], "ok");
308                    }
309                    _ => panic!("not the right packet parsed"),
310                },
311                None => panic!("Packet parsing failed"),
312            }
313        }
314    }
315
316    #[test]
317    fn test_unparsed() {
318        {
319            let data = vec![
320                0x44, 0x00, 0x0D, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x53, 0x4C, 0x49, 0x43,
321                0x4B, 0x44, 0x45, 0x4E, 0x49, 0x53, 0x34, 0x30, 0x30, 0x30,
322            ];
323            let packet = Packet::from_bytes(&data);
324
325            match packet.parsed {
326                Some(p) => match p {
327                    Message::Unparsed((_, p)) => {
328                        assert_eq!(p, "SLICKDENIS4000");
329                    }
330                    _ => panic!("not the right packet parsed"),
331                },
332                None => panic!("Packet parsing failed"),
333            }
334        }
335    }
336
337    // Property-based tests using proptest
338    use proptest::prelude::*;
339
340    proptest! {
341        #[test]
342        fn test_packet_header_roundtrip(
343            version in 0u8..4,
344            seq_num in 0u8..=15,
345            pixel_config in 0u8..=255,
346            id in 0u8..=255,
347            offset in 0u32..1000000,
348            length in 0u16..1500,
349        ) {
350            // Create a header with random but valid values
351            let mut packet_bytes = vec![
352                (version << 6) | 0b00000001, // packet_type with push bit set
353                seq_num,
354                pixel_config,
355                id,
356            ];
357            packet_bytes.extend_from_slice(&offset.to_be_bytes());
358            packet_bytes.extend_from_slice(&length.to_be_bytes());
359
360            // Add some data
361            let data = vec![0u8; length.min(100) as usize];
362            packet_bytes.extend_from_slice(&data);
363
364            // Parse the packet
365            let packet = Packet::from_bytes(&packet_bytes);
366
367            // Verify the header fields were parsed correctly
368            prop_assert_eq!(packet.header.sequence_number, seq_num);
369            prop_assert_eq!(packet.header.offset, offset);
370            prop_assert_eq!(packet.header.length, length);
371        }
372
373        #[test]
374        fn test_packet_with_arbitrary_data(
375            data_len in 0usize..500,
376            seq_num in 1u8..=15,
377        ) {
378            // Generate random pixel data
379            let data: Vec<u8> = (0..data_len).map(|i| (i % 256) as u8).collect();
380
381            // Create a minimal valid header
382            let mut packet_bytes = vec![
383                0x41, // version 1, push bit set
384                seq_num,
385                0x00, // pixel config
386                0x01, // ID
387                0x00, 0x00, 0x00, 0x00, // offset
388            ];
389            let length = data.len() as u16;
390            packet_bytes.extend_from_slice(&length.to_be_bytes());
391            packet_bytes.extend_from_slice(&data);
392
393            // Parse it
394            let packet = Packet::from_bytes(&packet_bytes);
395
396            // Verify
397            prop_assert_eq!(packet.data, data);
398            prop_assert_eq!(packet.header.sequence_number, seq_num);
399        }
400
401        #[test]
402        fn test_packet_parsing_never_panics(
403            bytes in prop::collection::vec(any::<u8>(), 10..1500)
404        ) {
405            // This test ensures that parsing arbitrary bytes never panics
406            // Even with completely random data, we should handle it gracefully
407            let _ = Packet::from_bytes(&bytes);
408        }
409
410        #[test]
411        fn test_packet_with_timecode_roundtrip(
412            timecode in any::<u32>(),
413            seq_num in 1u8..=15,
414            data_len in 0usize..100,
415        ) {
416            // Create header with timecode bit set
417            let mut packet_bytes = vec![
418                0x51, // version 1, push bit set, timecode bit set (0b01010001)
419                seq_num,
420                0x00, // pixel config
421                0x01, // ID
422                0x00, 0x00, 0x00, 0x00, // offset
423            ];
424
425            let data: Vec<u8> = (0..data_len).map(|i| (i % 256) as u8).collect();
426            let length = data.len() as u16;
427            packet_bytes.extend_from_slice(&length.to_be_bytes());
428            packet_bytes.extend_from_slice(&timecode.to_be_bytes());
429            packet_bytes.extend_from_slice(&data);
430
431            let packet = Packet::from_bytes(&packet_bytes);
432
433            prop_assert_eq!(packet.header.time_code.0, Some(timecode));
434            prop_assert_eq!(packet.data, data);
435        }
436
437        #[test]
438        fn test_offset_values_preserved(
439            offset in 0u32..4000000,
440        ) {
441            let mut packet_bytes = vec![
442                0x41, 1, 0x00, 0x01,
443            ];
444            packet_bytes.extend_from_slice(&offset.to_be_bytes());
445            packet_bytes.extend_from_slice(&[0x00, 0x03]); // length = 3
446            packet_bytes.extend_from_slice(&[255, 0, 0]); // 1 pixel
447
448            let packet = Packet::from_bytes(&packet_bytes);
449            prop_assert_eq!(packet.header.offset, offset);
450        }
451    }
452
453    // Integration tests for full packet roundtrips
454    #[test]
455    fn test_full_packet_roundtrip() {
456        use crate::protocol::{Header, PacketType, PixelConfig, ID, timecode::TimeCode};
457
458        // Create a header
459        let header = Header {
460            packet_type: PacketType {
461                version: 1,
462                timecode: false,
463                storage: false,
464                reply: false,
465                query: false,
466                push: true,
467            },
468            sequence_number: 5,
469            pixel_config: PixelConfig::default(),
470            id: ID::default(),
471            offset: 0,
472            length: 9,
473            time_code: TimeCode(None),
474        };
475
476        // Create RGB data
477        let data = vec![255, 0, 0, 0, 255, 0, 0, 0, 255];
478
479        // Convert header to bytes
480        let header_bytes: [u8; 10] = header.into();
481
482        // Create full packet
483        let mut packet_bytes = header_bytes.to_vec();
484        packet_bytes.extend_from_slice(&data);
485
486        // Parse it back
487        let parsed_packet = Packet::from_bytes(&packet_bytes);
488
489        // Verify
490        assert_eq!(parsed_packet.header.sequence_number, 5);
491        assert_eq!(parsed_packet.header.length, 9);
492        assert_eq!(parsed_packet.data, data);
493    }
494
495    #[test]
496    fn test_packet_with_timecode_integration() {
497        use crate::protocol::{Header, PacketType, PixelConfig, ID, timecode::TimeCode};
498
499        // Create a header with timecode
500        let header = Header {
501            packet_type: PacketType {
502                version: 1,
503                timecode: true,
504                storage: false,
505                reply: false,
506                query: false,
507                push: true,
508            },
509            sequence_number: 3,
510            pixel_config: PixelConfig::default(),
511            id: ID::default(),
512            offset: 100,
513            length: 6,
514            time_code: TimeCode(Some(12345)),
515        };
516
517        // Create RGB data
518        let data = vec![128, 128, 128, 64, 64, 64];
519
520        // Convert header to bytes (14 bytes with timecode)
521        let header_bytes: [u8; 14] = header.into();
522
523        // Create full packet
524        let mut packet_bytes = header_bytes.to_vec();
525        packet_bytes.extend_from_slice(&data);
526
527        // Parse it back
528        let parsed_packet = Packet::from_bytes(&packet_bytes);
529
530        // Verify
531        assert_eq!(parsed_packet.header.sequence_number, 3);
532        assert_eq!(parsed_packet.header.length, 6);
533        assert_eq!(parsed_packet.header.offset, 100);
534        assert_eq!(parsed_packet.header.time_code.0, Some(12345));
535        assert_eq!(parsed_packet.data, data);
536    }
537
538    #[test]
539    fn test_packet_with_config_message_integration() {
540        use crate::protocol::{Header, PacketType, PixelConfig, ID, timecode::TimeCode};
541
542        let json = r#"{"config":{"gw":"192.168.1.1","ip":"192.168.1.100"}}"#;
543
544        let header = Header {
545            packet_type: PacketType {
546                version: 1,
547                timecode: false,
548                storage: false,
549                reply: true,
550                query: false,
551                push: false,
552            },
553            sequence_number: 1,
554            pixel_config: PixelConfig::default(),
555            id: ID::Config,
556            offset: 0,
557            length: json.len() as u16,
558            time_code: TimeCode(None),
559        };
560
561        let header_bytes: [u8; 10] = header.into();
562        let mut packet_bytes = header_bytes.to_vec();
563        packet_bytes.extend_from_slice(json.as_bytes());
564
565        let parsed_packet = Packet::from_bytes(&packet_bytes);
566
567        assert_eq!(parsed_packet.header.id, ID::Config);
568        assert!(parsed_packet.parsed.is_some());
569    }
570
571    #[test]
572    fn test_multiple_packets_different_sequences_integration() {
573        use crate::protocol::{Header, PacketType, PixelConfig, ID, timecode::TimeCode};
574
575        // Test that we can parse multiple packets with different sequence numbers
576        let test_cases = vec![
577            (1, vec![255, 0, 0]),
578            (5, vec![0, 255, 0]),
579            (10, vec![0, 0, 255]),
580            (15, vec![128, 128, 128]),
581        ];
582
583        for (seq_num, data) in test_cases {
584            let header = Header {
585                packet_type: PacketType {
586                    version: 1,
587                    timecode: false,
588                    storage: false,
589                    reply: false,
590                    query: false,
591                    push: true,
592                },
593                sequence_number: seq_num,
594                pixel_config: PixelConfig::default(),
595                id: ID::default(),
596                offset: 0,
597                length: data.len() as u16,
598                time_code: TimeCode(None),
599            };
600
601            let header_bytes: [u8; 10] = header.into();
602            let mut packet_bytes = header_bytes.to_vec();
603            packet_bytes.extend_from_slice(&data);
604
605            let parsed = Packet::from_bytes(&packet_bytes);
606            assert_eq!(parsed.header.sequence_number, seq_num);
607            assert_eq!(parsed.data, data);
608        }
609    }
610
611    #[test]
612    fn test_large_pixel_data_integration() {
613        use crate::protocol::{Header, PacketType, PixelConfig, ID, timecode::TimeCode};
614
615        // Test with a large number of pixels
616        let num_pixels = 480; // Max size in connection
617        let mut data = Vec::with_capacity(num_pixels * 3);
618
619        for i in 0..num_pixels {
620            data.push((i % 256) as u8);
621            data.push(((i * 2) % 256) as u8);
622            data.push(((i * 3) % 256) as u8);
623        }
624
625        let header = Header {
626            packet_type: PacketType {
627                version: 1,
628                timecode: false,
629                storage: false,
630                reply: false,
631                query: false,
632                push: true,
633            },
634            sequence_number: 1,
635            pixel_config: PixelConfig::default(),
636            id: ID::default(),
637            offset: 0,
638            length: data.len() as u16,
639            time_code: TimeCode(None),
640        };
641
642        let header_bytes: [u8; 10] = header.into();
643        let mut packet_bytes = header_bytes.to_vec();
644        packet_bytes.extend_from_slice(&data);
645
646        let parsed = Packet::from_bytes(&packet_bytes);
647        assert_eq!(parsed.data.len(), data.len());
648        assert_eq!(parsed.data, data);
649    }
650}