capsules_extra/net/
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 handle offsets easily for the
127/// 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::{enc_try, stream_done};
142/// # use capsules::net::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::net::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/// Unwrapping macro that only returns the offset.
195///
196/// With this, it can be simpler to programmatically chain multiple
197/// headers together when the outputs do not have to be collated.
198#[macro_export]
199macro_rules! enc_consume {
200    ($($tts:tt)*) => { {
201        let (offset, _) = enc_try!($($tts)*);
202        offset
203    } };
204}
205
206/// The decoding equivalent of `enc_try`. The only difference is that only an
207/// immutable borrow of the buffer is required each time.
208#[macro_export]
209macro_rules! dec_try {
210    ($result:expr, $offset:expr) => {
211        match $result {
212            SResult::Done(offset, out) => ($offset + offset, out),
213            SResult::Needed(bytes) => { return SResult::Needed($offset + bytes); }
214            SResult::Error(error) => { return SResult::Error(error); }
215        }
216    };
217    ($result:expr)
218        => { dec_try!($result, 0) };
219    ($buf:expr, $offset:expr; $fun:expr)
220        => { dec_try!($fun(&$buf[$offset..]), $offset) };
221    ($buf:expr, $offset:expr; $fun:expr, $($args:expr),+)
222        => { dec_try!($fun(&$buf[$offset..], $($args),+), $offset) };
223    ($buf:expr, $offset:expr; $object:expr; $fun:ident)
224        => { dec_try!($object.$fun(&$buf[$offset..]), $offset) };
225    ($buf:expr, $offset:expr; $object:expr; $fun:ident, $($args:expr),+)
226        => { dec_try!($object.$fun(&$buf[$offset..], $($args),+), $offset) };
227    ($buf:expr; $($tts:tt)+)
228        => { dec_try!($buf, 0; $($tts)+) };
229}
230
231/// The decoding equivalent of `enc_consume`
232#[macro_export]
233macro_rules! dec_consume {
234    ($($tts:tt)*) => { {
235        let (offset, _) = dec_try!($($tts)*);
236        offset
237    } };
238}
239
240pub fn encode_u8(buf: &mut [u8], b: u8) -> SResult {
241    stream_len_cond!(buf, 1);
242    buf[0] = b;
243    stream_done!(1);
244}
245
246pub fn encode_u16(buf: &mut [u8], b: u16) -> SResult {
247    stream_len_cond!(buf, 2);
248    buf[0] = (b >> 8) as u8;
249    buf[1] = b as u8;
250    stream_done!(2);
251}
252
253pub fn encode_u32(buf: &mut [u8], b: u32) -> SResult {
254    stream_len_cond!(buf, 4);
255    buf[0] = (b >> 24) as u8;
256    buf[1] = (b >> 16) as u8;
257    buf[2] = (b >> 8) as u8;
258    buf[3] = b as u8;
259    stream_done!(4);
260}
261
262pub fn encode_bytes(buf: &mut [u8], bs: &[u8]) -> SResult {
263    stream_len_cond!(buf, bs.len());
264    buf[..bs.len()].copy_from_slice(bs);
265    stream_done!(bs.len());
266}
267
268// This function assumes that the host is little-endian
269pub fn encode_bytes_be(buf: &mut [u8], bs: &[u8]) -> SResult {
270    stream_len_cond!(buf, bs.len());
271    for (i, b) in bs.iter().rev().enumerate() {
272        buf[i] = *b;
273    }
274    stream_done!(bs.len());
275}
276
277pub fn decode_u8(buf: &[u8]) -> SResult<u8> {
278    stream_len_cond!(buf, 1);
279    stream_done!(1, buf[0]);
280}
281
282pub fn decode_u16(buf: &[u8]) -> SResult<u16> {
283    stream_len_cond!(buf, 2);
284    stream_done!(2, (buf[0] as u16) << 8 | (buf[1] as u16));
285}
286
287pub fn decode_u32(buf: &[u8]) -> SResult<u32> {
288    stream_len_cond!(buf, 4);
289    let b = (buf[0] as u32) << 24 | (buf[1] as u32) << 16 | (buf[2] as u32) << 8 | (buf[3] as u32);
290    stream_done!(4, b);
291}
292
293pub fn decode_bytes(buf: &[u8], out: &mut [u8]) -> SResult {
294    stream_len_cond!(buf, out.len());
295    let len = out.len();
296    out.copy_from_slice(&buf[..len]);
297    stream_done!(out.len());
298}
299
300// This function assumes that the host is little-endian
301pub fn decode_bytes_be(buf: &[u8], out: &mut [u8]) -> SResult {
302    stream_len_cond!(buf, out.len());
303    for (i, b) in buf[..out.len()].iter().rev().enumerate() {
304        out[i] = *b;
305    }
306    stream_done!(out.len());
307}