use crate::region::Seed;
use crate::decode::{get, Decode, DecodeError, ReadError, Reader, StreamDecoder};
use crate::encode::{
write_to_uninit, Encode, EncodeError, StreamEncoder, WriteError, Writer,
};
use crate::region::AllocOrd;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::fmt::Display;
use core::ops::Deref;
#[derive(thiserror::Error, Debug)]
pub enum FrameError {
#[error("Malformed label")]
MalformedLabel,
#[error("Non-ASCII label contents")]
NonAsciiLabel,
#[error("Leading magic number not found at expected position in source")]
NoMagicNumber,
#[error("Mismatch between flags found in the metadata and the actual environment")]
EnvironmentMismatch,
#[error("Read error: \"{0}\"")]
ReadError(#[from] ReadError),
#[error("Write error: \"{0}\"")]
WriteError(#[from] WriteError),
}
pub const MAGIC_NUM: u32 = 0x96EBCEA9;
const fn magic_num_bytes() -> [u8; 4] {
MAGIC_NUM.to_le_bytes()
}
const fn is_magic_num_bytes(bytes: [u8; 4]) -> bool {
u32::from_le_bytes(bytes) == MAGIC_NUM
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Label {
buf: [u8; 255],
len: u8,
}
impl Label {
pub fn new(str: &str) -> Option<Self> {
if !str.is_ascii() || str.len() > 255 {
return None;
}
let mut buf = [0u8; 255];
buf[..str.len()].copy_from_slice(str.as_bytes());
Some(Self {
len: str.len() as u8,
buf,
})
}
#[inline]
pub fn as_str(&self) -> &str {
unsafe {
core::str::from_utf8_unchecked(self.as_bytes())
}
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
unsafe {
self.buf.get_unchecked(0..self.len as usize)
}
}
#[inline]
pub const fn len(&self) -> usize {
self.len as usize
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
}
impl Label {
fn decode_from_reader(src: &mut impl Reader) -> Result<Self, FrameError> {
let len = src.read_borrow_const::<1>()?[0] as usize;
src.advance(1);
let str = unsafe {
core::str::from_utf8_unchecked(
src.read_borrow(len)
.map_err(|_| FrameError::MalformedLabel)?,
)
};
let label = Label::new(str).ok_or(FrameError::NonAsciiLabel)?;
src.advance(len);
Ok(label)
}
fn encode_into_writer(&self, dst: &mut impl Writer) -> Result<(), FrameError> {
let len = self.len() + 1;
let arena = dst.allocate(self.len() + 1)?;
assert_eq!(arena.len(), len, "Invalid result of Writer implementation");
arena[0].write(self.len);
write_to_uninit(self.as_bytes(), &mut arena[1..]);
unsafe {
dst.commit(len);
}
Ok(())
}
}
impl Default for Label {
fn default() -> Self {
Self {
buf: [0u8; 255],
len: 0,
}
}
}
impl Display for Label {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.deref())
}
}
impl TryFrom<&str> for Label {
type Error = ();
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::new(value).ok_or(())
}
}
impl Deref for Label {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
#[derive(Debug, Clone, Default)]
struct FrameMetadata {
frame_descriptor: FrameDescriptor,
label: Label,
}
impl FrameMetadata {
fn decode_from_reader(src: &mut impl Reader) -> Result<Self, FrameError> {
let leading_bytes = src.read_borrow_const::<4>()?;
if !is_magic_num_bytes(leading_bytes) {
return Err(FrameError::NoMagicNumber);
}
src.advance(4 );
let frame_descriptor_bytes = src.read_borrow(FrameDescriptor::max_len())?;
let frame_descriptor = FrameDescriptor::from_bytes(frame_descriptor_bytes)
.expect("Invalid result of Reader implementation");
src.advance(frame_descriptor.len());
let label = Label::decode_from_reader(src)?;
Ok(Self {
frame_descriptor,
label,
})
}
fn encode_into_writer(self, dst: &mut impl Writer) -> Result<(), FrameError> {
let len = 4 + self.frame_descriptor.len();
let arena = dst.allocate(len)?;
assert_eq!(arena.len(), len, "Invalid result of Writer implementation");
write_to_uninit(&magic_num_bytes(), arena);
self.frame_descriptor
.into_bytes(&mut arena[4 ..])
.unwrap();
unsafe {
dst.commit(len);
}
self.label.encode_into_writer(dst)?;
Ok(())
}
}
const MASK_SEEDED: u8 = 0b10000000;
const MASK_TIMESTAMP: u8 = 0b01000000;
#[derive(Debug, Default, Clone)]
struct FrameDescriptor {
seeded: bool,
timestamp: Option<u64>,
}
impl FrameDescriptor {
fn from_bytes(src: &[u8]) -> Option<Self> {
let byte = src[0];
let timestamp = if byte & MASK_TIMESTAMP != 0 {
Some(u64::from_le_bytes(
src.get(1..=8 )?.try_into().unwrap(),
))
} else {
None
};
Some(Self {
seeded: byte & MASK_SEEDED != 0,
timestamp,
})
}
fn into_bytes(self, dst: &mut [MaybeUninit<u8>]) -> Option<()> {
let mut byte = 0u8;
if self.seeded {
byte |= MASK_SEEDED;
}
if let Some(timestamp) = self.timestamp {
byte |= MASK_TIMESTAMP;
if dst.len() < 8 + 1 {
return None;
}
write_to_uninit(×tamp.to_le_bytes(), &mut dst[1..=8]);
}
dst.get_mut(0)?.write(byte);
Some(())
}
const fn len(&self) -> usize {
1 + if self.timestamp.is_some() { 8 } else { 0 }
}
const fn max_len() -> usize {
1 + 8
}
}
pub struct Frame<V, B> {
_phantom: PhantomData<V>,
metadata: FrameMetadata,
seed: Seed,
base: B,
}
impl<T, W> Frame<T, W>
where
T: Encode,
W: Writer,
{
#[inline]
pub fn new(dst: W, seed: Seed) -> Self {
Self {
_phantom: Default::default(),
metadata: Default::default(),
base: dst,
seed,
}
}
#[inline]
pub fn with_label(mut self, label: Label) -> Frame<T, W> {
self.metadata.label = label;
self
}
#[inline]
pub fn with_timestamp(mut self, timestamp: u64) -> Frame<T, W> {
self.metadata.frame_descriptor.timestamp = Some(timestamp);
self
}
pub fn encode(mut self, value: T) -> Result<(), EncodeError> {
if !self.seed.is_empty() {
self.metadata.frame_descriptor.seeded = true;
}
self.metadata.encode_into_writer(&mut self.base)?;
let mut encoder = StreamEncoder::new(self.base, self.seed, AllocOrd::Auto(&value));
let result = T::encode(&mut encoder, &value);
encoder.flush()?;
result
}
}
impl<T, R> Frame<T, R>
where
T: Decode,
R: Reader,
{
#[inline]
pub fn decode(mut src: R, seed: Seed) -> Result<Self, FrameError> {
let metadata = FrameMetadata::decode_from_reader(&mut src)?;
Ok(Self {
_phantom: Default::default(),
base: src,
metadata,
seed,
})
}
#[inline]
pub fn get_label(&self) -> Option<&Label> {
(!self.metadata.label.is_empty()).then_some(&self.metadata.label)
}
#[inline]
pub fn get_timestamp(&self) -> Option<u64> {
self.metadata.frame_descriptor.timestamp
}
#[inline]
pub fn get_value(self, ord: AllocOrd) -> Result<T, DecodeError> {
if self.seed.is_empty() == self.metadata.frame_descriptor.seeded {
return Err(FrameError::EnvironmentMismatch.into());
}
let mut decoder = StreamDecoder::new(self.base, self.seed, ord)?;
get(&mut decoder)
}
}