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}