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}