use core::marker::PhantomData;
use crate::encoding::Encoding;
pub trait Payload<E: Encoding>: Sized {
const MAX_SIZE: usize;
type Buffer: AsRef<[u8]> + AsMut<[u8]> + Copy;
const BUFFER: Self::Buffer;
type View<'a>;
fn encode<'o>(&self, out: &'o mut [u8]) -> Result<Encoded<'o, Self::View<'o>>, EncodeError>;
fn verify(buf: &[u8]) -> Result<Self::View<'_>, VerifyError>;
fn decode(r: Ref<'_, Self, E>) -> Self;
}
pub trait View<'a> {
fn bytes(&self) -> &'a [u8];
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Encoded<'a, V> {
pub bytes: &'a [u8],
pub view: V,
}
pub struct Ref<'a, T: Payload<E>, E: Encoding> {
bytes: &'a [u8],
view: T::View<'a>,
_e: PhantomData<E>,
}
impl<'a, T: Payload<E>, E: Encoding> Ref<'a, T, E> {
pub fn verify(buf: &'a [u8]) -> Result<Self, VerifyError> {
let view = T::verify(buf)?;
Ok(Ref {
bytes: buf,
view,
_e: PhantomData,
})
}
pub fn encode(value: &T, out: &'a mut [u8]) -> Result<Self, EncodeError> {
let Encoded { bytes, view } = value.encode(out)?;
Ok(Ref {
bytes,
view,
_e: PhantomData,
})
}
pub fn bytes(&self) -> &'a [u8] {
self.bytes
}
pub fn view(&self) -> &T::View<'a> {
&self.view
}
pub fn into_view(self) -> T::View<'a> {
self.view
}
pub fn decode(self) -> T {
T::decode(self)
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum EncodeError {
Capacity {
needed: usize,
available: usize,
},
Contract(Violation),
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum VerifyError {
Structure(Malformed),
Contract(Violation),
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Malformed {
OutOfBounds,
Unaligned,
MissingRequired,
Utf8,
Union,
TooDeep,
TooManyTables,
TooLarge,
Varint,
WireType,
Duplicate,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Violation {
pub type_name: Option<&'static str>,
pub rule: Rule,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Rule {
Range,
Step,
Length,
Pattern,
Variant,
Unique,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TranscodeError {
Verify(VerifyError),
Encode(EncodeError),
}
impl From<VerifyError> for TranscodeError {
fn from(e: VerifyError) -> Self {
TranscodeError::Verify(e)
}
}
impl From<EncodeError> for TranscodeError {
fn from(e: EncodeError) -> Self {
TranscodeError::Encode(e)
}
}
pub fn transcode<'o, T, S, D>(
bytes: &[u8],
out: &'o mut [u8],
) -> Result<Ref<'o, T, D>, TranscodeError>
where
T: Payload<S> + Payload<D>,
S: Encoding,
D: Encoding,
{
let value: T = Ref::<T, S>::verify(bytes)?.decode();
Ok(Ref::<T, D>::encode(&value, out)?)
}
#[cfg(test)]
mod tests {
use core::marker::PhantomData;
use super::{
transcode, EncodeError, Encoded, Malformed, Payload, Ref, Rule, TranscodeError,
VerifyError, Violation,
};
use crate::encoding::{Proto3, ReprC};
struct Raw;
impl Payload<ReprC> for Raw {
const MAX_SIZE: usize = 0;
type Buffer = [u8; 0];
const BUFFER: [u8; 0] = [];
type View<'a> = &'a [u8];
fn encode<'o>(&self, out: &'o mut [u8]) -> Result<Encoded<'o, &'o [u8]>, EncodeError> {
let bytes: &'o [u8] = &out[..0];
Ok(Encoded { bytes, view: bytes })
}
fn verify(buf: &[u8]) -> Result<&[u8], VerifyError> {
Ok(buf)
}
fn decode(_: Ref<'_, Self, ReprC>) -> Self {
Raw
}
}
#[test]
fn a_ref_is_built_from_exactly_its_three_fields() {
let bytes: &[u8] = &[1, 2];
let proof: Ref<'_, Raw, ReprC> = Ref {
bytes,
view: bytes,
_e: PhantomData,
};
assert_eq!(proof.bytes(), bytes);
}
#[test]
fn rule_is_exactly_these_six_variants() {
fn all(r: Rule) {
match r {
Rule::Range => {}
Rule::Step => {}
Rule::Length => {}
Rule::Pattern => {}
Rule::Variant => {}
Rule::Unique => {}
}
}
all(Rule::Range);
all(Rule::Unique);
}
#[test]
fn malformed_has_the_three_proto3_variants() {
let added = [Malformed::Varint, Malformed::WireType, Malformed::Duplicate];
for (i, a) in added.iter().enumerate() {
for (j, b) in added.iter().enumerate() {
assert_eq!(a == b, i == j);
}
assert_ne!(*a, Malformed::OutOfBounds);
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct Speed(u16);
const SPEED_MAX: u16 = 300;
impl Payload<ReprC> for Speed {
const MAX_SIZE: usize = 2;
type Buffer = [u8; 2];
const BUFFER: [u8; 2] = [0; 2];
type View<'a> = &'a [u8];
fn encode<'o>(&self, out: &'o mut [u8]) -> Result<Encoded<'o, &'o [u8]>, EncodeError> {
if out.len() < 2 {
return Err(EncodeError::Capacity {
needed: 2,
available: out.len(),
});
}
out[..2].copy_from_slice(&self.0.to_le_bytes());
let bytes: &'o [u8] = &out[..2];
Ok(Encoded { bytes, view: bytes })
}
fn verify(buf: &[u8]) -> Result<&[u8], VerifyError> {
if buf.len() == 2 {
Ok(buf)
} else {
Err(VerifyError::Structure(Malformed::OutOfBounds))
}
}
fn decode(r: Ref<'_, Self, ReprC>) -> Self {
Speed(u16::from_le_bytes([r.bytes()[0], r.bytes()[1]]))
}
}
impl Payload<Proto3> for Speed {
const MAX_SIZE: usize = 3;
type Buffer = [u8; 3];
const BUFFER: [u8; 3] = [0; 3];
type View<'a> = &'a [u8];
fn encode<'o>(&self, out: &'o mut [u8]) -> Result<Encoded<'o, &'o [u8]>, EncodeError> {
if self.0 > SPEED_MAX {
return Err(EncodeError::Contract(Violation {
type_name: Some("Speed"),
rule: Rule::Range,
}));
}
let len = out.len();
if len < 3 {
return Err(EncodeError::Capacity {
needed: 3,
available: len,
});
}
let le = self.0.to_le_bytes();
out[len - 3..].copy_from_slice(&[0x08, le[0], le[1]]);
let bytes: &'o [u8] = &out[len - 3..];
Ok(Encoded { bytes, view: bytes })
}
fn verify(buf: &[u8]) -> Result<&[u8], VerifyError> {
if buf.len() != 3 || buf[0] != 0x08 {
return Err(VerifyError::Structure(Malformed::WireType));
}
if u16::from_le_bytes([buf[1], buf[2]]) > SPEED_MAX {
return Err(VerifyError::Contract(Violation {
type_name: Some("Speed"),
rule: Rule::Range,
}));
}
Ok(buf)
}
fn decode(r: Ref<'_, Self, Proto3>) -> Self {
Speed(u16::from_le_bytes([r.bytes()[1], r.bytes()[2]]))
}
}
#[test]
fn transcode_matches_direct_encode() {
let mut source = [0u8; 8];
let source = Ref::<Speed, ReprC>::encode(&Speed(120), &mut source).unwrap();
let mut out = [0u8; 8];
let moved = transcode::<Speed, ReprC, Proto3>(source.bytes(), &mut out).unwrap();
let mut direct = [0u8; 8];
let direct = Ref::<Speed, Proto3>::encode(&Speed(120), &mut direct).unwrap();
assert_eq!(moved.bytes(), direct.bytes());
assert_eq!(moved.decode(), Speed(120));
}
#[test]
fn transcode_reverse_direction() {
let mut source = [0u8; 8];
let source = Ref::<Speed, Proto3>::encode(&Speed(250), &mut source).unwrap();
let mut out = [0u8; 8];
let moved = transcode::<Speed, Proto3, ReprC>(source.bytes(), &mut out).unwrap();
let mut direct = [0u8; 8];
let direct = Ref::<Speed, ReprC>::encode(&Speed(250), &mut direct).unwrap();
assert_eq!(moved.bytes(), direct.bytes());
assert_eq!(moved.decode(), Speed(250));
}
#[test]
fn transcode_same_encoding_is_identity() {
let mut source = [0u8; 8];
let source = Ref::<Speed, Proto3>::encode(&Speed(77), &mut source).unwrap();
let mut out = [0u8; 8];
let moved = transcode::<Speed, Proto3, Proto3>(source.bytes(), &mut out).unwrap();
assert_eq!(moved.bytes(), source.bytes());
}
#[test]
fn transcode_refuses_bad_input() {
let mut out = [0u8; 8];
let result = transcode::<Speed, ReprC, Proto3>(&[1, 2, 3], &mut out).map(|r| r.bytes());
assert!(matches!(result, Err(TranscodeError::Verify(_))));
}
#[test]
fn transcode_reports_contract() {
let mut out = [0u8; 8];
let result =
transcode::<Speed, ReprC, Proto3>(&301u16.to_le_bytes(), &mut out).map(|r| r.bytes());
assert!(matches!(
result,
Err(TranscodeError::Encode(EncodeError::Contract(_)))
));
}
#[test]
fn transcode_reports_capacity() {
let mut out = [0u8; 1];
let result =
transcode::<Speed, ReprC, Proto3>(&120u16.to_le_bytes(), &mut out).map(|r| r.bytes());
assert!(matches!(
result,
Err(TranscodeError::Encode(EncodeError::Capacity { .. }))
));
}
}