packet/util.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use core::ops::Deref;
use zerocopy::SplitByteSlice;
use crate::BufferView;
/// A packet that can be created from a raw form.
///
/// `FromRaw` provides a common interface for packets that can be created from
/// an "unchecked" form - that is, that are parsed raw without any higher-order
/// validation.
///
/// The type parameter `R` is the raw type that the `FromRaw` type can be
/// converted from, given some arguments of type `A`.
pub trait FromRaw<R, A>: Sized {
/// The type of error that may happen during validation.
type Error;
/// Attempts to create `Self` from the raw form in `raw` with `args`.
fn try_from_raw_with(raw: R, args: A) -> Result<Self, Self::Error>;
/// Attempts to create `Self` from the raw form in `raw`.
fn try_from_raw(raw: R) -> Result<Self, <Self as FromRaw<R, A>>::Error>
where
Self: FromRaw<R, (), Error = <Self as FromRaw<R, A>>::Error>,
{
Self::try_from_raw_with(raw, ())
}
}
/// A type that encapsulates the result of a complete or incomplete parsing
/// operation.
///
/// The type parameters `C` and `I` are the types for a "complete" and
/// "incomplete" parsing result, respectively.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum MaybeParsed<C, I> {
Complete(C),
Incomplete(I),
}
impl<T> MaybeParsed<T, T> {
/// Creates a `MaybeParsed` instance with `bytes` observing a minimum
/// length `min_len`.
///
/// Returns [`MaybeParsed::Complete`] if `bytes` is at least `min_len` long,
/// otherwise returns [`MaybeParsed::Incomplete`]. In both cases, `bytes`
/// is moved into one of the two `MaybeParsed` variants.
pub fn new_with_min_len(bytes: T, min_len: usize) -> Self
where
T: SplitByteSlice,
{
if bytes.len() >= min_len {
MaybeParsed::Complete(bytes)
} else {
MaybeParsed::Incomplete(bytes)
}
}
/// Consumes this `MaybeParsed` and returns its contained value if both the
/// `Complete` and `Incomplete` variants contain the same type.
pub fn into_inner(self) -> T {
match self {
MaybeParsed::Complete(c) => c,
MaybeParsed::Incomplete(i) => i,
}
}
}
impl<C, I> MaybeParsed<C, I> {
/// Creates a `MaybeParsed` instance taking `n` bytes from the front of
/// `buf` and mapping with `map`.
///
/// If `buf` contains at least `n` bytes, then `n` bytes are consumed from
/// the beginning of `buf`, those `n` bytes are passed to `map`, and the
/// result is returned as [`MaybeParsed::Complete`]. Otherwise, all bytes
/// are consumed from `buf` and returned as [`MaybeParsed::Incomplete`].
pub fn take_from_buffer_with<BV: BufferView<I>, F>(buf: &mut BV, n: usize, map: F) -> Self
where
F: FnOnce(I) -> C,
I: SplitByteSlice,
{
if let Some(v) = buf.take_front(n) {
MaybeParsed::Complete(map(v))
} else {
MaybeParsed::Incomplete(buf.take_rest_front())
}
}
/// Maps a [`MaybeParsed::Complete`] variant to another type.
///
/// If `self` is [`MaybeParsed::Incomplete`], it is left as-is.
pub fn map<M, F>(self, f: F) -> MaybeParsed<M, I>
where
F: FnOnce(C) -> M,
{
match self {
MaybeParsed::Incomplete(v) => MaybeParsed::Incomplete(v),
MaybeParsed::Complete(v) => MaybeParsed::Complete(f(v)),
}
}
/// Maps a [`MaybeParsed::Incomplete`] variant to another type.
///
/// If `self` is [`MaybeParsed::Complete`], it is left as-is.
pub fn map_incomplete<M, F>(self, f: F) -> MaybeParsed<C, M>
where
F: FnOnce(I) -> M,
{
match self {
MaybeParsed::Incomplete(v) => MaybeParsed::Incomplete(f(v)),
MaybeParsed::Complete(v) => MaybeParsed::Complete(v),
}
}
/// Converts from `&MaybeParsed<C, I>` to `MaybeParsed<&C, &I>`.
pub fn as_ref(&self) -> MaybeParsed<&C, &I> {
match self {
MaybeParsed::Incomplete(v) => MaybeParsed::Incomplete(v),
MaybeParsed::Complete(v) => MaybeParsed::Complete(v),
}
}
/// Transforms `self` into a [`Result`], mapping the [`Complete`] variant
/// into [`Ok`].
///
/// [`Complete`]: Self::Complete
/// [`Ok`]: Result::Ok
pub fn complete(self) -> Result<C, I> {
match self {
MaybeParsed::Complete(v) => Ok(v),
MaybeParsed::Incomplete(v) => Err(v),
}
}
/// Transforms `self` into a [`Result`], mapping the [`Incomplete`] variant
/// into [`Ok`].
///
/// [`Incomplete`]: Self::Incomplete
/// [`Ok`]: Result::Ok
pub fn incomplete(self) -> Result<I, C> {
match self {
MaybeParsed::Complete(v) => Err(v),
MaybeParsed::Incomplete(v) => Ok(v),
}
}
/// Transforms this `MaybeIncomplete` into a [`Result`] where the
/// [`Complete`] variant becomes [`Ok`] and the [`Incomplete`] variant is
/// passed through `f` and mapped to [`Err`].
///
/// [`Complete`]: Self::Complete
/// [`Incomplete`]: Self::Incomplete
/// [`Ok`]: Result::Ok
/// [`Err`]: Result::Err
pub fn ok_or_else<F, E>(self, f: F) -> Result<C, E>
where
F: FnOnce(I) -> E,
{
match self {
MaybeParsed::Complete(v) => Ok(v),
MaybeParsed::Incomplete(v) => Err(f(v)),
}
}
}
impl<C, I> MaybeParsed<C, I>
where
C: Deref<Target = [u8]>,
I: Deref<Target = [u8]>,
{
/// Returns the length in bytes of the contained data.
pub fn len(&self) -> usize {
match self {
MaybeParsed::Incomplete(v) => v.deref().len(),
MaybeParsed::Complete(v) => v.deref().len(),
}
}
/// Returns whether the contained data is empty - zero bytes long.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
impl<T> MaybeParsed<T, T> {
/// Creates a `MaybeParsed` instance taking `n` bytes from the front of
/// `buff`.
///
/// Returns [`MaybeParsed::Complete`] with `n` bytes if `buff` contains at
/// least `n` bytes. Otherwise returns [`MaybeParsed::Incomplete`] greedily
/// taking all the remaining bytes from `buff`
#[cfg(test)]
pub fn take_from_buffer<BV: BufferView<T>>(buff: &mut BV, n: usize) -> Self
where
T: SplitByteSlice,
{
if let Some(v) = buff.take_front(n) {
MaybeParsed::Complete(v)
} else {
MaybeParsed::Incomplete(buff.take_rest_front())
}
}
}
#[test]
fn test_maybe_parsed_take_from_buffer() {
let buff = [1_u8, 2, 3, 4];
let mut bv = &mut &buff[..];
assert_eq!(MaybeParsed::take_from_buffer(&mut bv, 2), MaybeParsed::Complete(&buff[..2]));
assert_eq!(MaybeParsed::take_from_buffer(&mut bv, 3), MaybeParsed::Incomplete(&buff[2..]));
}
#[test]
fn test_maybe_parsed_min_len() {
let buff = [1_u8, 2, 3, 4];
assert_eq!(MaybeParsed::new_with_min_len(&buff[..], 3), MaybeParsed::Complete(&buff[..]));
assert_eq!(MaybeParsed::new_with_min_len(&buff[..], 5), MaybeParsed::Incomplete(&buff[..]));
}
#[test]
fn test_maybe_parsed_take_from_buffer_with() {
let buff = [1_u8, 2, 3, 4];
let mut bv = &mut &buff[..];
assert_eq!(
MaybeParsed::take_from_buffer_with(&mut bv, 2, |x| Some(usize::from(x[0] + x[1]))),
MaybeParsed::Complete(Some(3)),
);
assert_eq!(
MaybeParsed::take_from_buffer_with(&mut bv, 3, |_| panic!("map shouldn't be called")),
MaybeParsed::Incomplete(&buff[2..]),
);
}
#[test]
fn test_maybe_parsed_map() {
assert_eq!(
MaybeParsed::<&str, ()>::Complete("hello").map(|x| format!("{} you", x)),
MaybeParsed::Complete("hello you".to_string()),
);
assert_eq!(
MaybeParsed::<(), &str>::Incomplete("hello").map(|_| panic!("map shouldn't be called")),
MaybeParsed::Incomplete("hello"),
);
}
#[test]
fn test_maybe_parsed_len() {
let buff = [1_u8, 2, 3, 4];
let mp1 = MaybeParsed::new_with_min_len(&buff[..], 2);
let mp2 = MaybeParsed::new_with_min_len(&buff[..], 10);
assert_eq!(mp1.len(), 4);
assert_eq!(mp2.len(), 4);
}
}