blob: 4adc3c96d5fffd435bd06506762cb8989f8d2cfb [file] [log] [blame]
// 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::ByteSlice;
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: ByteSlice,
{
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: ByteSlice,
{
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: ByteSlice,
{
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);
}
}