blob: 49f3e63905adaa960ca4441315850712310c74ec [file] [log] [blame]
// Copyright 2023 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 thiserror::Error;
/// A decodable type can be created from a byte buffer.
/// The type returned is separate (copied) from the buffer once decoded.
pub trait Decodable: ::core::marker::Sized {
type Error;
/// Decodes into a new object or an error, and the number of bytes that
/// the decoding consumed. Should attempt to consume the entire item from
/// the buffer in the case of an error. If the item end cannot be determined,
/// return an error and consume the entirety of the bufer (`buf.len()`)
fn decode(buf: &[u8]) -> (::core::result::Result<Self, Self::Error>, usize);
/// Tries to decode a collection of this object concatenated in a buffer.
/// Returns a vector of items (or errors) and the number of bytes consumed to
/// decode them.
/// Continues to decode items until the buffer is consumed or the max items.
/// If None, will decode the entire buffer.
fn decode_multiple(
buf: &[u8],
max: Option<usize>,
) -> (Vec<::core::result::Result<Self, Self::Error>>, usize) {
let mut idx = 0;
let mut result = Vec::new();
while idx < buf.len() && Some(result.len()) != max {
let (one_result, consumed) = Self::decode(&buf[idx..]);
result.push(one_result);
idx += consumed;
}
(result, idx)
}
}
/// A decodable type that has an exact size in bytes.
pub trait FixedSizeDecodable: ::core::marker::Sized {
type Error;
const WRONG_SIZE_ERROR: Self::Error;
const BYTE_SIZE: usize;
/// Decodes the value. This function assumes that buf is of at least
/// BYTE_SIZE, and assumes that BYTE_SIZE bytes are consumed to decode.
fn decode_checked(buf: &[u8]) -> core::result::Result<Self, Self::Error>;
}
/// An encodable type can write itself into a byte buffer.
pub trait Encodable {
type Error;
/// Returns the number of bytes necessary to encode |self|.
fn encoded_len(&self) -> ::core::primitive::usize;
/// Writes the encoded version of |self| at the start of |buf|.
/// |buf| must be at least |self.encoded_len()| length.
fn encode(&self, buf: &mut [u8]) -> ::core::result::Result<(), Self::Error>;
}
/// Generates an enum value where each variant can be converted into a constant
/// in the given raw_type.
///
/// For example:
/// decodable_enum! {
/// pub(crate) enum Color<u8, MyError, Variant> {
/// Red = 1,
/// Blue = 2,
/// Green = 3,
/// }
/// }
///
/// Color::try_from(2) -> Color::Red
/// u8::from(&Color::Red) -> 1.
#[macro_export]
macro_rules! decodable_enum {
($(#[$meta:meta])* $visibility:vis enum $name:ident<
$raw_type:ty,
$error_type:ty,
$error_path:ident
> {
$($(#[$variant_meta:meta])* $variant:ident = $val:expr),*,
}) => {
$(#[$meta])*
#[derive(
::core::clone::Clone,
::core::marker::Copy,
::core::fmt::Debug,
::core::cmp::Eq,
::core::hash::Hash,
::core::cmp::PartialEq)]
$visibility enum $name {
$($(#[$variant_meta])* $variant = $val),*
}
impl $name {
pub const VALUES : &'static [$raw_type] = &[$($val),*,];
pub const VARIANTS : &'static [$name] = &[$($name::$variant),*,];
pub fn name(&self) -> &'static ::core::primitive::str {
match self {
$($name::$variant => ::core::stringify!($variant)),*
}
}
}
impl ::core::convert::From<$name> for $raw_type {
fn from(v: $name) -> $raw_type {
match v {
$($name::$variant => $val),*,
}
}
}
impl ::core::convert::TryFrom<$raw_type> for $name {
type Error = $error_type;
fn try_from(value: $raw_type) -> ::core::result::Result<Self, $error_type> {
match value {
$($val => ::core::result::Result::Ok($name::$variant)),*,
_ => ::core::result::Result::Err(<$error_type>::$error_path),
}
}
}
}
}
#[macro_export]
macro_rules! codable_as_bitmask {
($type:ty, $raw_type:ty) => {
impl $type {
pub fn from_bits(v: $raw_type) -> impl Iterator<Item = $type> {
(0..<$raw_type>::BITS)
.map(|bit| 1 << bit)
.filter(move |val| (v & val) != 0)
.filter_map(|val| val.try_into().ok())
}
pub fn to_bits<'a>(it: impl Iterator<Item = &'a $type>) -> $raw_type {
it.fold(0, |acc, item| acc | Into::<$raw_type>::into(*item))
}
}
};
}
#[derive(Error, Debug, PartialEq)]
pub enum Error {
#[error("Parameter is not valid: {0}")]
InvalidParameter(String),
#[error("Out-of-range enum value")]
OutOfRange,
#[error("Encoding buffer is too small")]
BufferTooSmall,
#[error("Buffer being decoded is invalid length")]
UnexpectedDataLength,
#[error("Unrecognized type for {0}: {1}")]
UnrecognizedType(String, u8),
#[error("Uuid parsing error: {0}")]
Uuid(uuid::Error),
}