postcard_rpc/
accumulator.rs

1//! Accumulator tools
2//!
3//! These tools are useful for accumulating and decoding COBS encoded messages.
4//!
5//! Unlike the `CobsAccumulator` from `postcard`, these versions do not deserialize
6//! directly.
7
8/// Decode-only accumulator
9pub mod raw {
10    use cobs::decode_in_place;
11
12    /// A header-aware COBS accumulator
13    pub struct CobsAccumulator<const N: usize> {
14        buf: [u8; N],
15        idx: usize,
16    }
17
18    /// The result of feeding the accumulator.
19    pub enum FeedResult<'a, 'b> {
20        /// Consumed all data, still pending.
21        Consumed,
22
23        /// Buffer was filled. Contains remaining section of input, if any.
24        OverFull(&'a [u8]),
25
26        /// Reached end of chunk, but deserialization failed. Contains remaining section of input, if.
27        /// any
28        DeserError(&'a [u8]),
29
30        /// Deserialization complete. Contains deserialized data and remaining section of input, if any.
31        Success {
32            /// Deserialize data.
33            data: &'b [u8],
34
35            /// Remaining data left in the buffer after deserializing.
36            remaining: &'a [u8],
37        },
38    }
39
40    impl<const N: usize> CobsAccumulator<N> {
41        /// Create a new accumulator.
42        pub const fn new() -> Self {
43            CobsAccumulator {
44                buf: [0; N],
45                idx: 0,
46            }
47        }
48
49        /// Appends data to the internal buffer and attempts to deserialize the accumulated data into
50        /// `T`.
51        #[inline]
52        pub fn feed<'a, 'b>(&'b mut self, input: &'a [u8]) -> FeedResult<'a, 'b> {
53            self.feed_ref(input)
54        }
55
56        /// Appends data to the internal buffer and attempts to deserialize the accumulated data into
57        /// `T`.
58        ///
59        /// This differs from feed, as it allows the `T` to reference data within the internal buffer, but
60        /// mutably borrows the accumulator for the lifetime of the deserialization.
61        /// If `T` does not require the reference, the borrow of `self` ends at the end of the function.
62        pub fn feed_ref<'a, 'b>(&'b mut self, input: &'a [u8]) -> FeedResult<'a, 'b> {
63            if input.is_empty() {
64                return FeedResult::Consumed;
65            }
66
67            let zero_pos = input.iter().position(|&i| i == 0);
68
69            if let Some(n) = zero_pos {
70                // Yes! We have an end of message here.
71                // Add one to include the zero in the "take" portion
72                // of the buffer, rather than in "release".
73                let (take, release) = input.split_at(n + 1);
74
75                // Does it fit?
76                if (self.idx + take.len()) <= N {
77                    // Aw yiss - add to array
78                    self.extend_unchecked(take);
79
80                    let retval = match decode_in_place(&mut self.buf[..self.idx]) {
81                        Ok(used) => FeedResult::Success {
82                            data: &self.buf[..used],
83                            remaining: release,
84                        },
85                        Err(_) => FeedResult::DeserError(release),
86                    };
87                    self.idx = 0;
88                    retval
89                } else {
90                    self.idx = 0;
91                    FeedResult::OverFull(release)
92                }
93            } else {
94                // Does it fit?
95                if (self.idx + input.len()) > N {
96                    // nope
97                    let new_start = N - self.idx;
98                    self.idx = 0;
99                    FeedResult::OverFull(&input[new_start..])
100                } else {
101                    // yup!
102                    self.extend_unchecked(input);
103                    FeedResult::Consumed
104                }
105            }
106        }
107
108        /// Extend the internal buffer with the given input.
109        ///
110        /// # Panics
111        ///
112        /// Will panic if the input does not fit in the internal buffer.
113        fn extend_unchecked(&mut self, input: &[u8]) {
114            let new_end = self.idx + input.len();
115            self.buf[self.idx..new_end].copy_from_slice(input);
116            self.idx = new_end;
117        }
118    }
119}