| // 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), | |
| } |