channels_packet/
frame.rs

1//! [`Frame`] and helper types.
2
3use core::fmt;
4use core::marker::PhantomData;
5use core::ops::Range;
6
7use channels_io::buf::{Buf, Chain, Cursor};
8
9use crate::flags::Flags;
10use crate::header::{Header, HeaderBytes, HeaderError};
11use crate::payload::Payload;
12use crate::seq::{FrameNum, FrameNumSequence};
13use crate::util::Error;
14use crate::wants::Wants;
15
16/// A protocol frame.
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct Frame<T> {
19	/// Frame flags.
20	pub flags: Flags,
21	/// Frame number.
22	pub frame_num: FrameNum,
23	/// Frame payload data.
24	pub payload: T,
25}
26
27impl<T> Frame<T> {
28	/// Create a new [`Builder`].
29	#[inline]
30	pub const fn builder() -> Builder<T> {
31		Builder::new()
32	}
33
34	/// Convert a `Frame<T>` to a `Frame<U>` via a function _f_.
35	#[inline]
36	pub fn map_payload<U, F>(self, f: F) -> Frame<U>
37	where
38		F: FnOnce(T) -> U,
39	{
40		Frame {
41			flags: self.flags,
42			frame_num: self.frame_num,
43			payload: f(self.payload),
44		}
45	}
46
47	fn get_header(&self, data_len: u32) -> Header {
48		Header {
49			flags: self.flags,
50			frame_num: self.frame_num,
51			data_len,
52		}
53	}
54}
55
56/// Errors when parsing a frame.
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
58pub enum FrameError {
59	/// There was an error while parsing the header.
60	Header(HeaderError),
61	/// The frame is too large to fit in memory.
62	TooLarge,
63}
64
65impl fmt::Display for FrameError {
66	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67		match self {
68			Self::Header(e) => e.fmt(f),
69			Self::TooLarge => f.write_str("frame too large"),
70		}
71	}
72}
73
74impl Error for FrameError {}
75
76impl Frame<Range<usize>> {
77	/// Try to parse a frame from `bytes` returning an indice to the payload.
78	///
79	/// Returns a [`Frame`] whose payload is a [`Range`] inside `bytes`. If `bytes` does not
80	/// have a complete frame, this method returns `Ok(Err(Wants))`, the provided [`Wants`]
81	/// type can be used as an optimization hint for outside code. If `bytes` has a complete
82	/// frame but that frame contains errors or cannot be parsed, it returns `Err(...)`.
83	/// Otherwise, it returns `Ok(Ok(frame))`.
84	///
85	/// The implementation makes no assumptions about the platform's pointer width and thus
86	/// it is possible for parsing to fail if the entire frame is larger than the address
87	/// space of the platform. Typically this is only a concern on 16-bit and 32-bit targets.
88	pub fn try_parse_range(
89		bytes: &[u8],
90	) -> Result<Result<Self, Wants>, FrameError> {
91		let header = match Header::try_parse(bytes) {
92			Ok(Ok(x)) => x,
93			Ok(Err(wants)) => return Ok(Err(wants)),
94			Err(e) => return Err(FrameError::Header(e)),
95		};
96
97		let data_len = header
98			.data_len
99			.try_into()
100			.map_err(|_| FrameError::TooLarge)?;
101
102		let frame_len = Header::SIZE
103			.checked_add(data_len)
104			.ok_or(FrameError::TooLarge)?;
105
106		if bytes.len() < frame_len {
107			return Ok(Err(Wants(frame_len - bytes.len())));
108		}
109
110		Ok(Ok(Frame {
111			flags: header.flags,
112			frame_num: header.frame_num,
113			payload: Header::SIZE..frame_len,
114		}))
115	}
116
117	/// Get the length of the frame in bytes.
118	///
119	/// Returns `None` if the sum of the lengths of the header and payload cannot be
120	/// represented in a `usize`. Otherwise, it returns `Some(length)`.
121	#[inline]
122	#[must_use]
123	pub fn length(&self) -> Option<usize> {
124		calculate_frame_len(self.payload.len())
125	}
126
127	/// Get the header of the frame.
128	///
129	/// Returns `None` if the range of the payload does not fit inside a [`u32`].
130	///
131	/// # Example
132	///
133	/// ```
134	/// # use core::ops::Range;
135	/// # use channels_packet::{Frame, Header, FrameNum, Flags};
136	/// let frame = Frame {
137	///     flags: Flags::empty(),
138	///     frame_num: FrameNum::new(13),
139	///     payload: 8..42,
140	/// };
141	///
142	/// assert_eq!(frame.header().unwrap(), Header {
143	///     flags: Flags::empty(),
144	///     frame_num: FrameNum::new(13),
145	///     data_len: 34,
146	/// });
147	/// ```
148	#[inline]
149	#[must_use]
150	pub fn header(&self) -> Option<Header> {
151		let len = self.payload.len().try_into().ok()?;
152		Some(self.get_header(len))
153	}
154}
155
156impl<'a> Frame<Payload<&'a [u8]>> {
157	/// Try to parse a frame from `bytes` returning the payload as a slice.
158	///
159	/// Returns a [`Frame`] whose payload is a slice of `bytes`. If `bytes` does not
160	/// have a complete frame, this method returns `Ok(Err(Wants))`, the provided [`Wants`]
161	/// type can be used as an optimization hint for outside code. If `bytes` has a complete
162	/// frame but that frame contains errors or cannot be parsed, it returns `Err(...)`.
163	/// Otherwise, it returns `Ok(Ok(frame))`.
164	///
165	/// The implementation makes no assumptions about the platform's pointer width and thus
166	/// it is possible for parsing to fail if the entire frame is larger than the address
167	/// space of the platform. Typically this is only a concern on 16-bit and 32-bit targets.
168	#[allow(clippy::missing_panics_doc)]
169	pub fn try_parse(
170		bytes: &'a [u8],
171	) -> Result<Result<Self, Wants>, FrameError> {
172		let frame = match Frame::try_parse_range(bytes) {
173			Ok(Ok(x)) => x,
174			Ok(Err(wants)) => return Ok(Err(wants)),
175			Err(e) => return Err(e),
176		};
177
178		Ok(Ok(frame.map_payload(|x| {
179			// SAFETY: `try_parse_range` returns a range to the payload of the frame, so
180			//         so the length of the slice represented by that range is always a
181			//         32 bit number. If this `u32` number cannot be represented using the
182			//         platform's `usize`, then `try_parse_range` will fail. If `try_parse_range`
183			//         succeeds, then we know that the length of the payload fits inside
184			//         a `usize`. We also know that it is a valid `u32` number. `Payload::new`
185			//         fails if the length of the payload cannot fit inside a `u32` or a
186			//         `usize`, whichever is smaller.
187			Payload::new(&bytes[x]).expect(
188				"try_parse_range returned an invalid payload range",
189			)
190		})))
191	}
192}
193
194impl<T: AsRef<[u8]>> Frame<Payload<T>> {
195	/// Get the length of the frame in bytes.
196	///
197	/// Returns `None` if the sum of the lengths of the header and payload cannot be
198	/// represented in a `usize`. Otherwise, it returns `Some(length)`.
199	#[inline]
200	#[must_use]
201	pub fn length(&self) -> Option<usize> {
202		calculate_frame_len(self.payload.get().as_ref().len())
203	}
204
205	/// Get the header of the frame.
206	///
207	/// # Example
208	///
209	/// ```
210	/// # use channels_packet::{Frame, Payload, Header, FrameNum, Flags};
211	/// let frame = Frame {
212	///     flags: Flags::empty(),
213	///     frame_num: FrameNum::new(13),
214	///     payload: Payload::new([1, 2, 3, 4]).unwrap(),
215	/// };
216	///
217	/// assert_eq!(frame.header(), Header {
218	///     flags: Flags::empty(),
219	///     frame_num: FrameNum::new(13),
220	///     data_len: 4,
221	/// });
222	/// ```
223	#[inline]
224	#[must_use]
225	pub fn header(&self) -> Header {
226		self.get_header(self.payload.length())
227	}
228
229	/// Encode the frame.
230	///
231	/// Returns a [`Buf`] that contains the entire frame.
232	///
233	/// # Example
234	///
235	/// ```rust
236	/// # use channels_packet::{Frame, Payload, FrameNum, Flags};
237	/// let frame = Frame {
238	///    flags: Flags::empty(),
239	///    frame_num: FrameNum::new(5),
240	///    payload: Payload::new([1, 2, 3, 4]).unwrap()
241	/// };
242	///
243	/// // encode by borrowing the payload
244	/// let bytes = frame.as_ref().encode();
245	/// # let _ = bytes;
246	///
247	/// // or, take ownership of the payload
248	/// let bytes = frame.encode();
249	/// # let _ = bytes;
250	/// ```
251	#[inline]
252	#[must_use]
253	pub fn encode(self) -> EncodedFrame<T> {
254		EncodedFrame::new(self)
255	}
256}
257
258impl<T> Frame<Payload<T>> {
259	/// Convert a `&Frame<Payload<T>>` to a `Frame<Payload<&T>>`.
260	#[inline]
261	#[must_use]
262	pub fn as_ref(&self) -> Frame<Payload<&T>> {
263		Frame {
264			flags: self.flags,
265			frame_num: self.frame_num,
266			payload: self.payload.as_ref(),
267		}
268	}
269
270	/// Convert a `&mut Frame<Payload<T>>` to a `Frame<Payload<&mut T>>`.
271	///
272	/// [`&mut Frame<T>]: Frame
273	/// [`Frame<&mut T>`]: Frame
274	#[inline]
275	#[must_use]
276	pub fn as_mut(&mut self) -> Frame<Payload<&mut T>> {
277		Frame {
278			flags: self.flags,
279			frame_num: self.frame_num,
280			payload: self.payload.as_mut(),
281		}
282	}
283}
284
285/// An encoded frame exposed as a [`Buf`].
286#[derive(Debug, Clone)]
287pub struct EncodedFrame<T> {
288	inner: Chain<Cursor<HeaderBytes>, Cursor<T>>,
289}
290
291impl<T> EncodedFrame<T>
292where
293	T: AsRef<[u8]>,
294{
295	#[inline]
296	fn new(frame: Frame<Payload<T>>) -> Self {
297		let header = Cursor::new(frame.header().to_bytes());
298		let payload = Cursor::new(frame.payload.into_inner());
299
300		Self { inner: Buf::chain(header, payload) }
301	}
302}
303
304impl<T: AsRef<[u8]>> Buf for EncodedFrame<T> {
305	fn remaining(&self) -> usize {
306		self.inner.remaining()
307	}
308
309	fn chunk(&self) -> &[u8] {
310		self.inner.chunk()
311	}
312
313	fn advance(&mut self, n: usize) {
314		self.inner.advance(n);
315	}
316}
317
318/// [`Frame`] builder.
319///
320/// # Example
321///
322/// ```no_run
323/// # use channels_packet::{frame::{Builder, Frame}, Payload, FrameNum};
324/// let payload = Payload::new([1u8, 1, 1, 1]).unwrap();
325///
326/// let mut frame = Builder::new()
327///     .frame_num(FrameNum::new(0))
328///     .payload(payload);
329/// ```
330#[allow(missing_debug_implementations)]
331#[must_use = "builders don't do anything unless you build them"]
332pub struct Builder<T> {
333	_marker: PhantomData<T>,
334	flags: Flags,
335	frame_num: FrameNum,
336}
337
338impl<T> Builder<T> {
339	/// Create a new [`Builder`].
340	#[inline]
341	pub const fn new() -> Self {
342		Self {
343			_marker: PhantomData,
344			flags: Flags::empty(),
345			frame_num: FrameNum::new(0),
346		}
347	}
348
349	/// Set the frame flags.
350	///
351	/// # Example
352	///
353	/// ```no_run
354	/// # use channels_packet::{frame::Builder, Payload, Flags};
355	/// let frame = Builder::new()
356	///     // ...
357	///     .flags(Flags::empty())
358	///     // ...
359	/// #   .payload(Payload::new([]).unwrap());
360	/// ```
361	#[inline]
362	pub const fn flags(mut self, flags: Flags) -> Self {
363		self.flags = flags;
364		self
365	}
366
367	/// Set the frame number.
368	///
369	/// # Example
370	///
371	/// ```no_run
372	/// # use channels_packet::{frame::Builder, Payload, FrameNum};
373	/// let frame = Builder::new()
374	///     // ...
375	///     .frame_num(FrameNum::new(23))
376	///     // ...
377	/// #   .payload(Payload::new([]).unwrap());
378	/// ```
379	#[inline]
380	pub const fn frame_num(mut self, frame_num: FrameNum) -> Self {
381		self.frame_num = frame_num;
382		self
383	}
384
385	/// Set the frame number from the next one in `seq`.
386	///
387	/// This method will advance `seq`.
388	///
389	/// # Example
390	///
391	/// ```no_run
392	/// # use channels_packet::{frame::Builder, Payload, FrameNumSequence};
393	/// let mut seq = FrameNumSequence::new();
394	///
395	/// let frame = Builder::new()
396	///     // ...
397	///     .frame_num_from_seq(&mut seq)
398	///     // ...
399	/// #   .payload(Payload::new([]).unwrap());
400	/// ```
401	#[inline]
402	pub fn frame_num_from_seq(
403		self,
404		seq: &mut FrameNumSequence,
405	) -> Self {
406		self.frame_num(seq.advance())
407	}
408
409	/// Set the payload of the frame
410	///
411	/// # Example
412	///
413	/// ```
414	/// # use channels_packet::{frame::Builder, Payload};
415	/// let buf: [u8; 6] = [1, 2, 3, 4, 5, 6];
416	///
417	/// let frame = Builder::new()
418	///     // ...
419	///     .payload(Payload::new(buf).unwrap());
420	///
421	/// assert_eq!(frame.payload.as_slice(), &[1, 2, 3, 4, 5, 6]);
422	/// ```
423	#[inline]
424	pub const fn payload(self, payload: T) -> Frame<T> {
425		let Self { _marker, flags, frame_num } = self;
426
427		Frame { flags, frame_num, payload }
428	}
429}
430
431impl<T> Default for Builder<T> {
432	#[inline]
433	fn default() -> Self {
434		Self::new()
435	}
436}
437
438const fn calculate_frame_len(payload_len: usize) -> Option<usize> {
439	Header::SIZE.checked_add(payload_len)
440}