1use super::{Header, PixelConfig, ID};
10
11pub const MAX_DATA_LENGTH: usize = 480 * 3;
16
17#[derive(Debug, Clone)]
38pub struct FrameBuilder {
39 pub pixel_config: PixelConfig,
41 pub id: ID,
43 sequence_number: u8,
44}
45
46impl FrameBuilder {
47 pub fn new(pixel_config: PixelConfig, id: ID) -> Self {
49 Self {
50 pixel_config,
51 id,
52 sequence_number: 1,
53 }
54 }
55
56 pub fn sequence_number(&self) -> u8 {
58 self.sequence_number
59 }
60
61 pub fn for_each_frame<F, E>(
72 &mut self,
73 data: &[u8],
74 offset: u32,
75 scratch: &mut [u8],
76 f: F,
77 ) -> Result<(), E>
78 where
79 F: FnMut(&[u8]) -> Result<(), E>,
80 {
81 let header = Header {
82 pixel_config: self.pixel_config,
83 id: self.id,
84 ..Default::default()
85 };
86 self.frames_with(header, data, offset, scratch, f)
87 }
88
89 pub fn frames_with<F, E>(
94 &mut self,
95 mut header: Header,
96 data: &[u8],
97 offset: u32,
98 scratch: &mut [u8],
99 mut f: F,
100 ) -> Result<(), E>
101 where
102 F: FnMut(&[u8]) -> Result<(), E>,
103 {
104 header.packet_type.push(false);
105
106 let total = data.len();
107 let num_iterations = total.div_ceil(MAX_DATA_LENGTH);
108 let mut chunk_index = 0usize;
109 let mut data_offset = 0usize;
110
111 while data_offset < total {
112 chunk_index += 1;
113
114 if chunk_index == num_iterations {
116 header.packet_type.push(true);
117 }
118
119 header.sequence_number = self.sequence_number;
120
121 let chunk_end = core::cmp::min(data_offset + MAX_DATA_LENGTH, total);
122 let chunk = &data[data_offset..chunk_end];
123 header.length = chunk.len() as u16;
124 header.offset = offset + data_offset as u32;
125
126 let header_len = header.write_into(scratch);
127 scratch[header_len..header_len + chunk.len()].copy_from_slice(chunk);
128 f(&scratch[..header_len + chunk.len()])?;
129
130 if self.sequence_number > 15 {
132 self.sequence_number = 1;
133 } else {
134 self.sequence_number += 1;
135 }
136
137 data_offset += MAX_DATA_LENGTH;
138 }
139
140 Ok(())
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use crate::protocol::PixelConfig;
148
149 #[test]
150 fn single_frame_matches_expected_bytes() {
151 let mut builder = FrameBuilder::new(PixelConfig::default(), ID::Default);
152 let mut scratch = [0u8; 1500];
153 let mut frames: Vec<Vec<u8>> = Vec::new();
154
155 builder
156 .for_each_frame(&[255, 0, 0, 255, 0, 0, 255, 0, 0], 0, &mut scratch, |frame| {
157 frames.push(frame.to_vec());
158 Ok::<(), ()>(())
159 })
160 .unwrap();
161
162 assert_eq!(frames.len(), 1);
163 assert_eq!(
165 frames[0],
166 vec![
167 0x41, 0x01, 0x0D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xFF, 0x00, 0x00,
168 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00
169 ]
170 );
171 }
172
173 #[test]
174 fn chunks_large_data_and_sets_push_on_last() {
175 let mut builder = FrameBuilder::new(PixelConfig::default(), ID::Default);
176 let mut scratch = [0u8; 1500];
177 let data = vec![7u8; MAX_DATA_LENGTH * 2 + 30];
178 let mut pushes: Vec<bool> = Vec::new();
179 let mut seqs: Vec<u8> = Vec::new();
180
181 builder
182 .for_each_frame(&data, 0, &mut scratch, |frame| {
183 let h = Header::from(frame);
184 pushes.push(h.packet_type.push);
185 seqs.push(h.sequence_number);
186 Ok::<(), ()>(())
187 })
188 .unwrap();
189
190 assert_eq!(pushes, vec![false, false, true]);
191 assert_eq!(seqs, vec![1, 2, 3]);
192 }
193
194 #[test]
195 fn frame_roundtrips_through_packet_ref() {
196 use crate::packet::PacketRef;
197
198 let mut builder = FrameBuilder::new(PixelConfig::default(), ID::Custom(42));
199 let mut scratch = [0u8; 1500];
200 let payload: Vec<u8> = (0..90u8).collect();
201
202 builder
203 .for_each_frame(&payload, 30, &mut scratch, |frame| {
204 let parsed = PacketRef::from_bytes(frame).unwrap();
205 assert_eq!(parsed.header.offset, 30);
206 assert_eq!(parsed.header.id, ID::Custom(42));
207 assert_eq!(parsed.header.length as usize, payload.len());
208 assert_eq!(parsed.data, &payload[..]);
209 Ok::<(), ()>(())
210 })
211 .unwrap();
212 }
213
214 #[test]
215 fn empty_data_produces_no_frames() {
216 let mut builder = FrameBuilder::new(PixelConfig::default(), ID::Default);
217 let mut scratch = [0u8; 1500];
218 let mut count = 0;
219 builder
220 .for_each_frame(&[], 0, &mut scratch, |_| {
221 count += 1;
222 Ok::<(), ()>(())
223 })
224 .unwrap();
225 assert_eq!(count, 0);
226 }
227}