use super::EncodePlan;
pub trait FixedCodec {
type Value<'wire>
where
Self: 'wire;
type EncodeError: core::fmt::Debug;
type Plan<'value>: EncodePlan
where
Self: 'value;
const WIDTH: usize;
fn decode<'wire>(bytes: &'wire [u8]) -> Self::Value<'wire>;
fn plan<'value>(value: Self::Value<'value>) -> Result<Self::Plan<'value>, Self::EncodeError>;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ExactWidthError {
expected: usize,
actual: usize,
}
impl ExactWidthError {
#[must_use]
pub const fn new(expected: usize, actual: usize) -> Self {
Self { expected, actual }
}
#[must_use]
pub const fn expected(&self) -> usize {
self.expected
}
#[must_use]
pub const fn actual(&self) -> usize {
self.actual
}
}
impl core::fmt::Display for ExactWidthError {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
formatter,
"fixed codec expected {} bytes, got {}",
self.expected, self.actual
)
}
}
impl core::error::Error for ExactWidthError {}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct Bytes<const N: usize>;
impl<const N: usize> FixedCodec for Bytes<N> {
type Value<'wire>
= &'wire [u8]
where
Self: 'wire;
type EncodeError = ExactWidthError;
type Plan<'value>
= &'value [u8]
where
Self: 'value;
const WIDTH: usize = {
assert!(N != 0, "Bytes<N> requires a nonzero width");
N
};
#[inline]
fn decode<'wire>(bytes: &'wire [u8]) -> Self::Value<'wire> {
const { assert!(N != 0, "Bytes<N> requires a nonzero width") };
bytes
}
#[inline]
fn plan<'value>(value: Self::Value<'value>) -> Result<Self::Plan<'value>, Self::EncodeError> {
const { assert!(N != 0, "Bytes<N> requires a nonzero width") };
if value.len() == N {
Ok(value)
} else {
Err(ExactWidthError::new(N, value.len()))
}
}
}
#[doc(hidden)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct OwnedBytes<const N: usize>;
impl<const N: usize> FixedCodec for OwnedBytes<N> {
type Value<'wire>
= [u8; N]
where
Self: 'wire;
type EncodeError = core::convert::Infallible;
type Plan<'value>
= [u8; N]
where
Self: 'value;
const WIDTH: usize = {
assert!(N != 0, "OwnedBytes<N> requires a nonzero width");
N
};
#[inline]
fn decode<'wire>(bytes: &'wire [u8]) -> Self::Value<'wire> {
const { assert!(N != 0, "OwnedBytes<N> requires a nonzero width") };
let mut value = [0_u8; N];
value.copy_from_slice(bytes);
value
}
#[inline]
fn plan<'value>(value: Self::Value<'value>) -> Result<Self::Plan<'value>, Self::EncodeError> {
const { assert!(N != 0, "OwnedBytes<N> requires a nonzero width") };
Ok(value)
}
}
#[cfg(test)]
mod tests {
use super::{FixedCodec, OwnedBytes};
use crate::codec::EncodePlan;
#[test]
fn owned_bytes_decode_copies_exact_input() {
let mut source = [1, 2, 3, 4];
let decoded = <OwnedBytes<4> as FixedCodec>::decode(&source);
source[0] = 9;
assert_eq!(decoded, [1, 2, 3, 4]);
}
#[test]
fn owned_bytes_plan_is_infallible_and_writes_exact_bytes() {
let plan = <OwnedBytes<4> as FixedCodec>::plan([1, 2, 3, 4]).unwrap();
let mut output = [0; 4];
assert_eq!(plan.encoded_len(), 4);
plan.write_into(&mut output);
assert_eq!(output, [1, 2, 3, 4]);
}
#[test]
fn owned_bytes_width_is_nonzero() {
assert_eq!(<OwnedBytes<1> as FixedCodec>::WIDTH, 1);
}
}