1use core::iter::Chain;
2
3use crate::{Control, Crc16, Error, OneKData, Result, Sequence};
4
5#[repr(C)]
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub struct XmodemOneKPacket {
9 start: Control,
10 seq: Sequence,
11 cmpl_seq: Sequence,
12 data: OneKData,
13 csum: Crc16,
14}
15
16impl XmodemOneKPacket {
17 const START: usize = 0;
18 const SEQ: usize = 1;
19 const CMPL_SEQ: usize = 2;
20 const DATA_START: usize = 3;
21 const DATA_END: usize = 3 + OneKData::LEN;
22 const CSUM: usize = 1027;
23
24 const PRELUDE_LEN: usize = 3;
25
26 pub const LEN: usize = Self::CSUM + Crc16::LEN;
28
29 pub const fn new() -> Self {
31 Self {
32 start: Control::Stx,
33 seq: Sequence::new(),
34 cmpl_seq: Sequence::new().complement(),
35 data: OneKData::new(),
36 csum: Crc16::new(),
37 }
38 }
39
40 pub const fn start(&self) -> Control {
42 self.start
43 }
44
45 pub const fn sequence(&self) -> Sequence {
47 self.seq
48 }
49
50 pub fn set_sequence(&mut self, seq: Sequence) {
52 self.seq = seq;
53 self.cmpl_seq = seq.complement();
54 }
55
56 pub const fn with_sequence(self, seq: Sequence) -> Self {
58 Self {
59 start: self.start,
60 seq,
61 cmpl_seq: seq.complement(),
62 data: self.data,
63 csum: self.csum,
64 }
65 }
66
67 pub const fn complement_sequence(&self) -> Sequence {
69 self.cmpl_seq
70 }
71
72 pub const fn data(&self) -> &OneKData {
74 &self.data
75 }
76
77 pub fn set_data(&mut self, data: OneKData) {
79 self.csum = Crc16::calculate(data.as_ref());
80 self.data = data;
81 }
82
83 pub const fn with_data(self, data: OneKData) -> Self {
85 let csum = Crc16::calculate(data.inner());
86
87 Self {
88 start: self.start,
89 seq: self.seq,
90 cmpl_seq: self.cmpl_seq,
91 data,
92 csum,
93 }
94 }
95
96 pub const fn checksum(&self) -> Crc16 {
98 self.csum
99 }
100
101 pub const fn validate(self) -> Result<Self> {
107 let cmpl_seq = self.seq.complement();
108 let csum = Crc16::calculate(self.data.inner());
109
110 if !matches!(self.start, Control::Stx) {
111 Err(Error::InvalidStart((
112 self.start.into_u8(),
113 Control::Stx.into_u8(),
114 )))
115 } else if self.cmpl_seq.into_u8() != cmpl_seq.into_u8() {
116 Err(Error::InvalidComplementSequence((
117 self.cmpl_seq.into_u8(),
118 cmpl_seq.into_u8(),
119 )))
120 } else if self.csum.into_u16() != csum.into_u16() {
121 Err(Error::InvalidCrc16((self.csum.into_u16(), csum.into_u16())))
122 } else {
123 Ok(self)
124 }
125 }
126
127 pub fn into_bytes(self) -> [u8; Self::LEN] {
132 let mut out = [0u8; Self::LEN];
133
134 out.iter_mut().zip(self).for_each(|(dst, src)| *dst = src);
135
136 out
137 }
138}
139
140impl Default for XmodemOneKPacket {
141 fn default() -> Self {
142 Self::new()
143 }
144}
145
146type XmodemOneKPacketPreludeIntoIter = core::array::IntoIter<u8, { XmodemOneKPacket::PRELUDE_LEN }>;
148type XmodemOneKPacketDataIntoIter = core::array::IntoIter<u8, { OneKData::LEN }>;
150type XmodemOneKPacketCsumIntoIter = core::array::IntoIter<u8, { Crc16::LEN }>;
152
153type XmodemOneKPacketIntoIter = Chain<
155 Chain<XmodemOneKPacketPreludeIntoIter, XmodemOneKPacketDataIntoIter>,
156 XmodemOneKPacketCsumIntoIter,
157>;
158
159impl IntoIterator for XmodemOneKPacket {
160 type Item = u8;
161 type IntoIter = XmodemOneKPacketIntoIter;
162
163 fn into_iter(self) -> Self::IntoIter {
164 [
165 self.start.into_u8(),
166 self.seq.into_u8(),
167 self.cmpl_seq.into_u8(),
168 ]
169 .into_iter()
170 .chain(self.data)
171 .chain(self.csum)
172 }
173}
174
175impl TryFrom<&[u8]> for XmodemOneKPacket {
176 type Error = Error;
177
178 fn try_from(val: &[u8]) -> Result<Self> {
179 Self {
180 start: Control::try_from(
181 val.first()
182 .copied()
183 .ok_or(Error::InvalidPacketLen((Self::START, Self::LEN)))?,
184 )?,
185 seq: Sequence::from(
186 val.get(Self::SEQ)
187 .copied()
188 .ok_or(Error::InvalidPacketLen((Self::SEQ, Self::LEN)))?,
189 ),
190 cmpl_seq: Sequence::from(
191 val.get(Self::CMPL_SEQ)
192 .copied()
193 .ok_or(Error::InvalidPacketLen((Self::CMPL_SEQ, Self::LEN)))?,
194 ),
195 data: OneKData::try_from(
196 val.get(Self::DATA_START..Self::DATA_END)
197 .ok_or(Error::InvalidPacketLen((val.len(), Self::LEN)))?,
198 )?,
199 csum: Crc16::try_from(
200 val.get(Self::CSUM..Self::LEN)
201 .ok_or(Error::InvalidPacketLen((val.len(), Self::LEN)))?,
202 )?,
203 }
204 .validate()
205 }
206}
207
208impl<const N: usize> TryFrom<&[u8; N]> for XmodemOneKPacket {
209 type Error = Error;
210
211 fn try_from(val: &[u8; N]) -> Result<Self> {
212 val.as_ref().try_into()
213 }
214}
215
216impl<const N: usize> TryFrom<[u8; N]> for XmodemOneKPacket {
217 type Error = Error;
218
219 fn try_from(val: [u8; N]) -> Result<Self> {
220 val.as_ref().try_into()
221 }
222}
223
224impl TryFrom<XmodemOneKPacket> for [u8; XmodemOneKPacket::LEN] {
225 type Error = Error;
226
227 fn try_from(val: XmodemOneKPacket) -> Result<Self> {
228 Ok(val.validate()?.into_bytes())
229 }
230}
231
232impl TryFrom<&XmodemOneKPacket> for [u8; XmodemOneKPacket::LEN] {
233 type Error = Error;
234
235 fn try_from(val: &XmodemOneKPacket) -> Result<Self> {
236 (*val).try_into()
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243
244 #[test]
245 fn test_packet() {
246 let start = Control::Stx;
247 let seq = Sequence::new();
248 let cmpl_seq = seq.complement();
249 let data = OneKData::from_block([0x1a; OneKData::LEN].as_ref());
250 let csum = Crc16::calculate(data.as_ref());
251
252 let mut raw = [0u8; XmodemOneKPacket::LEN];
253 raw.iter_mut()
254 .zip(
255 [start.into_u8(), seq.into_u8(), cmpl_seq.into_u8()]
256 .into_iter()
257 .chain(data)
258 .chain(csum.into_bytes()),
259 )
260 .for_each(|(dst, src)| *dst = src);
261
262 let exp = XmodemOneKPacket::new().with_sequence(seq).with_data(data);
263
264 assert_eq!(XmodemOneKPacket::try_from(raw), Ok(exp));
265
266 assert_eq!(exp.start(), start);
267 assert_eq!(exp.sequence(), seq);
268 assert_eq!(exp.complement_sequence(), cmpl_seq);
269 assert_eq!(exp.data(), &data);
270 assert_eq!(exp.checksum(), csum);
271 }
272
273 #[test]
274 fn test_packet_invalid() {
275 let packet = XmodemOneKPacket::new();
276
277 let mut raw = packet.into_bytes();
278
279 let stx = packet.start().into_u8();
280
281 (0..XmodemOneKPacket::LEN).for_each(|invalid| {
283 assert_eq!(
284 XmodemOneKPacket::try_from(raw[..invalid].as_ref()),
285 Err(Error::InvalidPacketLen((invalid, XmodemOneKPacket::LEN)))
286 );
287 });
288
289 (0..=u8::MAX).filter(|r| r != &stx).for_each(|invalid| {
291 raw[XmodemOneKPacket::START] = invalid;
292
293 assert!(match XmodemOneKPacket::try_from(raw.as_ref()) {
294 Err(Error::InvalidControl(i)) => i == invalid,
295 Err(Error::InvalidStart((i, s))) => i == invalid && s == stx,
296 _ => false,
297 });
298 });
299
300 raw[XmodemOneKPacket::START] = stx;
301
302 let cmpl_seq = packet.complement_sequence().into_u8();
303
304 (0..=u8::MAX)
306 .filter(|r| r != &cmpl_seq)
307 .for_each(|invalid| {
308 raw[XmodemOneKPacket::CMPL_SEQ] = invalid;
309
310 assert_eq!(
311 XmodemOneKPacket::try_from(raw.as_ref()),
312 Err(Error::InvalidComplementSequence((invalid, cmpl_seq)))
313 );
314 });
315
316 raw[XmodemOneKPacket::CMPL_SEQ] = cmpl_seq;
317
318 let csum = Crc16::calculate(
319 raw[XmodemOneKPacket::DATA_START..XmodemOneKPacket::DATA_END].as_ref(),
320 )
321 .into_u16();
322
323 assert_eq!(
324 raw.get(XmodemOneKPacket::CSUM..XmodemOneKPacket::LEN),
325 Some(csum.to_be_bytes().as_ref())
326 );
327
328 (0..=u16::MAX).filter(|c| c != &csum).for_each(|invalid| {
330 raw[XmodemOneKPacket::CSUM..].copy_from_slice(invalid.to_be_bytes().as_ref());
331
332 assert_eq!(
333 XmodemOneKPacket::try_from(raw.as_ref()),
334 Err(Error::InvalidCrc16((invalid, csum)))
335 );
336 });
337 }
338}