bbqueue_sync/
framed.rs

1//! A Framed flavor of BBQueue, useful for variable length packets
2//!
3//! This module allows for a `Framed` mode of operation,
4//! where a size header is included in each grant, allowing for
5//! "chunks" of data to be passed through a BBQueue, rather than
6//! just a stream of bytes. This is convenient when receiving
7//! packets of variable sizes.
8//!
9//! ## Example
10//!
11//! ```rust
12//! # // bbqueue test shim!
13//! # fn bbqtest() {
14//! use bbqueue::BBBuffer;
15//!
16//! let bb: BBBuffer<1000> = BBBuffer::new();
17//! let (mut prod, mut cons) = bb.try_split_framed().unwrap();
18//!
19//! // One frame in, one frame out
20//! let mut wgrant = prod.grant(128).unwrap();
21//! assert_eq!(wgrant.len(), 128);
22//! for (idx, i) in wgrant.iter_mut().enumerate() {
23//!     *i = idx as u8;
24//! }
25//! wgrant.commit(128);
26//!
27//! let rgrant = cons.read().unwrap();
28//! assert_eq!(rgrant.len(), 128);
29//! for (idx, i) in rgrant.iter().enumerate() {
30//!     assert_eq!(*i, idx as u8);
31//! }
32//! rgrant.release();
33//! # // bbqueue test shim!
34//! # }
35//! #
36//! # fn main() {
37//! # #[cfg(not(feature = "thumbv6"))]
38//! # bbqtest();
39//! # }
40//! ```
41//!
42//! ## Frame header
43//!
44//! An internal header is required for each frame stored
45//! inside of the `BBQueue`. This header is never exposed to end
46//! users of the bbqueue library.
47//!
48//! A variable sized integer is used for the header size, and the
49//! size of this header is based on the max size requested for the grant.
50//! This header size must be factored in when calculating an appropriate
51//! total size of your buffer.
52//!
53//! Even if a smaller portion of the grant is committed, the original
54//! requested grant size will be used for the header size calculation.
55//!
56//! For example, if you request a 128 byte grant, the header size will
57//! be two bytes. If you then only commit 32 bytes, two bytes will still
58//! be used for the header of that grant.
59//!
60//! | Grant Size (bytes)    | Header size (bytes)  |
61//! | :---                  | :---                 |
62//! | 1..(2^7)              | 1                    |
63//! | (2^7)..(2^14)         | 2                    |
64//! | (2^14)..(2^21)        | 3                    |
65//! | (2^21)..(2^28)        | 4                    |
66//! | (2^28)..(2^35)        | 5                    |
67//! | (2^35)..(2^42)        | 6                    |
68//! | (2^42)..(2^49)        | 7                    |
69//! | (2^49)..(2^56)        | 8                    |
70//! | (2^56)..(2^64)        | 9                    |
71//!
72
73use crate::{Consumer, GrantR, GrantW, Producer};
74
75use crate::{
76    vusize::{decode_usize, decoded_len, encode_usize_to_slice, encoded_len},
77    Result,
78};
79
80use core::{
81    cmp::min,
82    ops::{Deref, DerefMut},
83};
84
85/// A producer of Framed data
86pub struct FrameProducer<'a, const N: usize> {
87    pub(crate) producer: Producer<'a, N>,
88}
89
90impl<'a, const N: usize> FrameProducer<'a, N> {
91    /// Receive a grant for a frame with a maximum size of `max_sz` in bytes.
92    ///
93    /// This size does not include the size of the frame header. The exact size
94    /// of the frame can be set on `commit`.
95    pub fn grant(&self, max_sz: usize) -> Result<FrameGrantW<'a, N>> {
96        let hdr_len = encoded_len(max_sz);
97        Ok(FrameGrantW {
98            grant_w: self.producer.grant_exact(max_sz + hdr_len)?,
99            hdr_len: hdr_len as u8,
100        })
101    }
102}
103
104/// A consumer of Framed data
105pub struct FrameConsumer<'a, const N: usize> {
106    pub(crate) consumer: Consumer<'a, N>,
107}
108
109impl<'a, const N: usize> FrameConsumer<'a, N> {
110    /// Obtain the next available frame, if any
111    pub fn read(&self) -> Option<FrameGrantR<'a, N>> {
112        // Get all available bytes. We never wrap a frame around,
113        // so if a header is available, the whole frame will be.
114        let mut grant_r = self.consumer.read().ok()?;
115
116        // Additionally, we never commit less than a full frame with
117        // a header, so if we have ANY data, we'll have a full header
118        // and frame. `Consumer::read` will return an Error when
119        // there are 0 bytes available.
120
121        // The header consists of a single usize, encoded in native
122        // endianess order
123        let frame_len = decode_usize(&grant_r);
124        let hdr_len = decoded_len(grant_r[0]);
125        let total_len = frame_len + hdr_len;
126        let hdr_len = hdr_len as u8;
127
128        debug_assert!(grant_r.len() >= total_len);
129
130        // Reduce the grant down to the size of the frame with a header
131        grant_r.shrink(total_len);
132
133        Some(FrameGrantR { grant_r, hdr_len })
134    }
135}
136
137/// A write grant for a single frame
138///
139/// NOTE: If the grant is dropped without explicitly commiting
140/// the contents without first calling `to_commit()`, then no
141/// frame will be comitted for writing.
142#[derive(Debug, PartialEq)]
143pub struct FrameGrantW<'a, const N: usize> {
144    grant_w: GrantW<'a, N>,
145    hdr_len: u8,
146}
147
148/// A read grant for a single frame
149///
150/// NOTE: If the grant is dropped without explicitly releasing
151/// the contents, then no frame will be released.
152#[derive(Debug, PartialEq)]
153pub struct FrameGrantR<'a, const N: usize> {
154    grant_r: GrantR<'a, N>,
155    hdr_len: u8,
156}
157
158impl<'a, const N: usize> Deref for FrameGrantW<'a, N> {
159    type Target = [u8];
160
161    fn deref(&self) -> &Self::Target {
162        &self.grant_w.buf[self.hdr_len.into()..]
163    }
164}
165
166impl<'a, const N: usize> DerefMut for FrameGrantW<'a, N> {
167    fn deref_mut(&mut self) -> &mut [u8] {
168        &mut self.grant_w.buf[self.hdr_len.into()..]
169    }
170}
171
172impl<'a, const N: usize> Deref for FrameGrantR<'a, N> {
173    type Target = [u8];
174
175    fn deref(&self) -> &Self::Target {
176        &self.grant_r.buf[self.hdr_len.into()..]
177    }
178}
179
180impl<'a, const N: usize> DerefMut for FrameGrantR<'a, N> {
181    fn deref_mut(&mut self) -> &mut [u8] {
182        &mut self.grant_r.buf[self.hdr_len.into()..]
183    }
184}
185
186impl<'a, const N: usize> FrameGrantW<'a, N> {
187    /// Commit a frame to make it available to the Consumer half.
188    ///
189    /// `used` is the size of the payload, in bytes, not
190    /// including the frame header
191    pub fn commit(mut self, used: usize) {
192        let total_len = self.set_header(used);
193
194        // Commit the header + frame
195        self.grant_w.commit(total_len);
196    }
197
198    /// Set the header and return the total size
199    fn set_header(&mut self, used: usize) -> usize {
200        // Saturate the commit size to the available frame size
201        let grant_len = self.grant_w.len();
202        let hdr_len: usize = self.hdr_len.into();
203        let frame_len = min(used, grant_len - hdr_len);
204        let total_len = frame_len + hdr_len;
205
206        // Write the actual frame length to the header
207        encode_usize_to_slice(frame_len, hdr_len, &mut self.grant_w[..hdr_len]);
208
209        total_len
210    }
211
212    /// Configures the amount of bytes to be commited on drop.
213    pub fn to_commit(&mut self, amt: usize) {
214        if amt == 0 {
215            self.grant_w.to_commit(0);
216        } else {
217            let size = self.set_header(amt);
218            self.grant_w.to_commit(size);
219        }
220    }
221}
222
223impl<'a, const N: usize> FrameGrantR<'a, N> {
224    /// Release a frame to make the space available for future writing
225    ///
226    /// Note: The full frame is always released
227    pub fn release(mut self) {
228        // For a read grant, we have already shrunk the grant
229        // size down to the correct size
230        let len = self.grant_r.len();
231        self.grant_r.release_inner(len);
232    }
233
234    /// Set whether the read fram should be automatically released
235    pub fn auto_release(&mut self, is_auto: bool) {
236        self.grant_r
237            .to_release(if is_auto { self.grant_r.len() } else { 0 });
238    }
239}