capsules_core/
stream.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5#[derive(Debug)]
6pub enum SResult<Output = (), Error = ()> {
7    // `Done(off, out)`: No errors encountered. We are currently at `off` in the
8    // buffer, and the previous encoder/decoder produced output `out`.
9    Done(usize, Output),
10
11    // `Needed(bytes)`: Could not proceed because we needed to have `bytes`
12    // bytes in the buffer, but there weren't.
13    Needed(usize),
14
15    // `Error(err)`: Some other error occurred.
16    Error(Error),
17}
18
19impl<Output, Error> SResult<Output, Error> {
20    pub fn is_done(&self) -> bool {
21        match *self {
22            SResult::Done(_, _) => true,
23            _ => false,
24        }
25    }
26
27    pub fn is_needed(&self) -> bool {
28        match *self {
29            SResult::Needed(_) => true,
30            _ => false,
31        }
32    }
33
34    pub fn is_err(&self) -> bool {
35        match *self {
36            SResult::Error(_) => true,
37            _ => false,
38        }
39    }
40
41    pub fn done(self) -> Option<(usize, Output)> {
42        match self {
43            SResult::Done(offset, out) => Some((offset, out)),
44            _ => None,
45        }
46    }
47
48    pub fn needed(self) -> Option<usize> {
49        match self {
50            SResult::Needed(bytes) => Some(bytes),
51            _ => None,
52        }
53    }
54
55    pub fn err(self) -> Option<Error> {
56        match self {
57            SResult::Error(err) => Some(err),
58            _ => None,
59        }
60    }
61}
62
63/// Returns the result of encoding/decoding
64#[macro_export]
65macro_rules! stream_done {
66    ($bytes:expr, $out:expr) => {
67        return SResult::Done($bytes, $out)
68    };
69    ($bytes:expr) => {
70        stream_done!($bytes, ())
71    };
72}
73
74/// Returns a buffer length error if there are not enough bytes
75#[macro_export]
76macro_rules! stream_len_cond {
77    ($buf:expr, $bytes:expr) => {
78        if $buf.len() < $bytes {
79            return SResult::Needed($bytes);
80        }
81    };
82}
83
84/// Returns an error
85#[macro_export]
86macro_rules! stream_err {
87    ($err:expr) => {
88        return SResult::Error($err)
89    };
90    () => {
91        stream_err!(())
92    };
93}
94
95/// Returns an error if a condition is unmet
96#[macro_export]
97macro_rules! stream_cond {
98    ($cond:expr, $err:expr) => {
99        if !$cond {
100            return SResult::Error($err);
101        }
102    };
103    ($cond:expr) => {
104        stream_cond!($cond, ());
105    };
106}
107
108/// Gets the result of an `Option<T>`, throwing a stream error if it
109/// is `None`
110#[macro_export]
111macro_rules! stream_from_option {
112    ($opt:expr, $err:expr) => {
113        match $opt {
114            Some(opt) => opt,
115            None => stream_err!($err),
116        }
117    };
118    ($opt:expr) => {
119        stream_from_option!($opt, ())
120    };
121}
122
123/// Extracts the result of encoding/decoding (the new offset and the output) only
124/// if no errors were encountered in encoding.
125///
126/// This macro makes it possible to
127/// handle offsets easily for the following use cases:
128///
129/// `enc_try!(result, offset)`: Unwrap an already-provided result that
130/// represents starting from `offset` in the buffer.
131/// `enc_try!(buf, offset; encoder, args..)`: Use the encoder function, called with the
132/// optionally provided arguments, on the buffer starting from `offset`.
133/// `enc_try!(buf, offset; object; method, args..)`: Same as the above, but the
134/// encoder function is a member method of object.
135///
136/// Additionally, the offset can always be omitted from any of the above, which
137/// would result in it defaulting to 0. Idiomatically, the way to combine
138/// encoders is to define another encoder as follows:
139///
140/// ```rust,ignore
141/// # use capsules_core::{enc_try, stream_done};
142/// # use capsules_core::stream::{SResult};
143///
144/// // call a simple encoder
145/// let (bytes, out1) = enc_try!(buf; encoder1);
146/// /* Do something with out1 */
147///
148/// // call another encoder, but starting at the previous offset
149/// let (bytes, out2) = enc_try!(buf, bytes; encoder2);
150/// /* Note that bytes is shadowed. Alternatively you could mutably update a
151/// variable. */
152///
153/// // subsequently, encode a struct into the buffer, with some extra arguments
154/// let (bytes, out3) = enc_try!(buf, bytes; someheader; encode, 2, 5);
155///
156/// // report success without returning a meaningful output
157/// stream_done!(bytes);
158/// ```
159///
160/// Then, using an encoder can be done simply by:
161///
162/// ```rust,ignore
163/// # use capsules_core::stream::SResult;
164///
165/// match encoder(&mut buf) {
166///     SResult::Done(off, out) => { /* celebrate */ }
167///     SResult::Needed(off) => { /* get more memory? */ }
168///     SResult::Error(err) => { /* give up */ }
169/// }
170/// ```
171#[macro_export]
172macro_rules! enc_try {
173    ($result:expr, $offset:expr) => {
174        match $result {
175            SResult::Done(offset, out) => ($offset + offset, out),
176            SResult::Needed(bytes) => { return SResult::Needed($offset + bytes); }
177            SResult::Error(error) => { return SResult::Error(error); }
178        }
179    };
180    ($result:expr)
181        => { enc_try!($result, 0) };
182    ($buf:expr, $offset:expr; $fun:expr)
183        => { enc_try!($fun(&mut $buf[$offset..]), $offset) };
184    ($buf:expr, $offset:expr; $fun:expr, $($args:expr),+)
185        => { enc_try!($fun(&mut $buf[$offset..], $($args),+), $offset) };
186    ($buf:expr, $offset:expr; $object:expr; $fun:ident)
187        => { enc_try!($object.$fun(&mut $buf[$offset..]), $offset) };
188    ($buf:expr, $offset:expr; $object:expr; $fun:ident, $($args:expr),+)
189        => { enc_try!($object.$fun(&mut $buf[$offset..], $($args),+), $offset) };
190    ($buf:expr; $($tts:tt)+)
191        => { enc_try!($buf, 0; $($tts)+) };
192}
193
194/// This is the aforementioned version of the unwrapping macro that only returns
195/// the offset.
196///
197/// With this, it can be simpler to programmatically chain multiple
198/// headers together when the outputs do not have to be collated.
199#[macro_export]
200macro_rules! enc_consume {
201    ($($tts:tt)*) => { {
202        let (offset, _) = enc_try!($($tts)*);
203        offset
204    } };
205}
206
207/// The decoding equivalent of `enc_try`. The only difference is that only an
208/// immutable borrow of the buffer is required each time.
209#[macro_export]
210macro_rules! dec_try {
211    ($result:expr, $offset:expr) => {
212        match $result {
213            SResult::Done(offset, out) => ($offset + offset, out),
214            SResult::Needed(bytes) => { return SResult::Needed($offset + bytes); }
215            SResult::Error(error) => { return SResult::Error(error); }
216        }
217    };
218    ($result:expr)
219        => { dec_try!($result, 0) };
220    ($buf:expr, $offset:expr; $fun:expr)
221        => { dec_try!($fun(&$buf[$offset..]), $offset) };
222    ($buf:expr, $offset:expr; $fun:expr, $($args:expr),+)
223        => { dec_try!($fun(&$buf[$offset..], $($args),+), $offset) };
224    ($buf:expr, $offset:expr; $object:expr; $fun:ident)
225        => { dec_try!($object.$fun(&$buf[$offset..]), $offset) };
226    ($buf:expr, $offset:expr; $object:expr; $fun:ident, $($args:expr),+)
227        => { dec_try!($object.$fun(&$buf[$offset..], $($args),+), $offset) };
228    ($buf:expr; $($tts:tt)+)
229        => { dec_try!($buf, 0; $($tts)+) };
230}
231
232/// The decoding equivalent of `enc_consume`
233#[macro_export]
234macro_rules! dec_consume {
235    ($($tts:tt)*) => { {
236        let (offset, _) = dec_try!($($tts)*);
237        offset
238    } };
239}
240
241pub fn encode_u8(buf: &mut [u8], b: u8) -> SResult {
242    stream_len_cond!(buf, 1);
243    buf[0] = b;
244    stream_done!(1);
245}
246
247pub fn encode_u16(buf: &mut [u8], b: u16) -> SResult {
248    stream_len_cond!(buf, 2);
249    buf[0] = (b >> 8) as u8;
250    buf[1] = b as u8;
251    stream_done!(2);
252}
253
254pub fn encode_u32(buf: &mut [u8], b: u32) -> SResult {
255    stream_len_cond!(buf, 4);
256    buf[0] = (b >> 24) as u8;
257    buf[1] = (b >> 16) as u8;
258    buf[2] = (b >> 8) as u8;
259    buf[3] = b as u8;
260    stream_done!(4);
261}
262
263pub fn encode_bytes(buf: &mut [u8], bs: &[u8]) -> SResult {
264    stream_len_cond!(buf, bs.len());
265    buf[..bs.len()].copy_from_slice(bs);
266    stream_done!(bs.len());
267}
268
269// This function assumes that the host is little-endian
270pub fn encode_bytes_be(buf: &mut [u8], bs: &[u8]) -> SResult {
271    stream_len_cond!(buf, bs.len());
272    for (i, b) in bs.iter().rev().enumerate() {
273        buf[i] = *b;
274    }
275    stream_done!(bs.len());
276}
277
278pub fn decode_u8(buf: &[u8]) -> SResult<u8> {
279    stream_len_cond!(buf, 1);
280    stream_done!(1, buf[0]);
281}
282
283pub fn decode_u16(buf: &[u8]) -> SResult<u16> {
284    stream_len_cond!(buf, 2);
285    stream_done!(2, (buf[0] as u16) << 8 | (buf[1] as u16));
286}
287
288pub fn decode_u32(buf: &[u8]) -> SResult<u32> {
289    stream_len_cond!(buf, 4);
290    let b = (buf[0] as u32) << 24 | (buf[1] as u32) << 16 | (buf[2] as u32) << 8 | (buf[3] as u32);
291    stream_done!(4, b);
292}
293
294pub fn decode_bytes(buf: &[u8], out: &mut [u8]) -> SResult {
295    stream_len_cond!(buf, out.len());
296    let len = out.len();
297    out.copy_from_slice(&buf[..len]);
298    stream_done!(out.len());
299}
300
301// This function assumes that the host is little-endian
302pub fn decode_bytes_be(buf: &[u8], out: &mut [u8]) -> SResult {
303    stream_len_cond!(buf, out.len());
304    for (i, b) in buf[..out.len()].iter().rev().enumerate() {
305        out[i] = *b;
306    }
307    stream_done!(out.len());
308}