// Copyright 2024 Gabriel Bjørnager Jensen.
//
// This file is part of bzipper.
//
// bzipper is free software: you can redistribute
// it and/or modify it under the terms of the GNU
// Lesser General Public License as published by
// the Free Software Foundation, either version 3
// of the License, or (at your option) any later
// version.
//
// bzipper is distributed in the hope that it will
// be useful, but WITHOUT ANY WARRANTY; without
// even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Less-
// er General Public License along with bzipper. If
// not, see .
#[cfg(test)]
mod test;
use crate::{Dstream, Error, Result, Serialise};
use core::convert::Infallible;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::num::NonZero;
mod tuple;
/// Types capable of being deserialised.
///
/// This trait requires [`Serialise`] also being implemented as it relies on the [`SERIALISED_SIZE`](crate::Serialise::SERIALISED_SIZE) constant.
pub trait Deserialise: Serialise + Sized {
/// Deserialises a slice into an object.
///
/// This function must **never** take more bytes than specified by [`SERIALISED_SIZE`](crate::Serialise::SERIALISED_SIZE).
/// Doing so is considered a logic error.
/// Likewise, providing more than this amount is also disfavoured.
///
/// # Errors
///
/// If deserialisation failed, e.g. by an illegal byte being found, an error is returned.
///
/// # Panics
///
/// This method will usually panic if the provided slice has a length *less* than the value of `SERIALISED_SIZE`.
/// Official implementations of this trait (including those that are derived) always panic in debug mode if the provided slice has a length that is different at all.
fn deserialise(data: &[u8]) -> Result;
}
macro_rules! impl_numeric {
($ty:ty) => {
impl ::bzipper::Deserialise for $ty {
fn deserialise(data: &[u8]) -> ::bzipper::Result {
::core::debug_assert_eq!(data.len(), ::SERIALISED_SIZE);
const SIZE: usize = ::core::mem::size_of::<$ty>();
let data = data
.get(0x0..SIZE)
.ok_or(::bzipper::Error::EndOfStream { req: SIZE, rem: data.len() })?
.try_into()
.unwrap();
Ok(Self::from_be_bytes(data))
}
}
};
}
macro_rules! impl_non_zero {
($ty:ty) => {
impl ::bzipper::Deserialise for NonZero<$ty> {
fn deserialise(data: &[u8]) -> ::bzipper::Result {
::core::debug_assert_eq!(data.len(), ::SERIALISED_SIZE);
let value = <$ty as ::bzipper::Deserialise>::deserialise(data)?;
NonZero::new(value)
.ok_or(Error::NullInteger)
}
}
};
}
impl Deserialise for [T; N]
where
T: Deserialise {
fn deserialise(data: &[u8]) -> Result {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
// Initialise the array incrementally.
let mut buf: [MaybeUninit; N] = unsafe { MaybeUninit::uninit().assume_init() };
let mut pos = 0x0;
for item in &mut buf {
let range = pos..pos + T::SERIALISED_SIZE;
pos = range.end;
item.write(Deserialise::deserialise(&data[range])?);
}
// This should be safe as `MaybeUninit` is
// transparent to `T`, and we have initialised
// every element. The original buffer is NOT
// dropped automatically, so we can just forget
// about it from this point on. `transmute` cannot
// be used here, and `transmute_unchecked` is re-
// served for the greedy rustc devs.
let value: [T; N] = unsafe { buf.as_ptr().cast::<[T; N]>().read() };
Ok(value)
}
}
impl Deserialise for bool {
fn deserialise(data: &[u8]) -> Result {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
let value = u8::deserialise(data)?;
match value {
0x00 => Ok(false),
0x01 => Ok(true),
_ => Err(Error::InvalidBoolean { value })
}
}
}
impl Deserialise for char {
fn deserialise(data: &[u8]) -> Result {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
let value = u32::deserialise(data)?;
Self::from_u32(value)
.ok_or(Error::InvalidCodePoint { value })
}
}
impl Deserialise for Infallible {
#[allow(clippy::panic_in_result_fn)]
#[inline(always)]
fn deserialise(_data: &[u8]) -> Result { panic!("cannot deserialise `Infallible` as it cannot be serialised to begin with") }
}
impl Deserialise for isize {
fn deserialise(data: &[u8]) -> Result {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
let value = i32::deserialise(data)?
.try_into().expect("unable to convert from `i32` to `isize`");
Ok(value)
}
}
impl Deserialise for Option {
fn deserialise(data: &[u8]) -> Result {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
let stream = Dstream::new(data);
let sign = stream.take::()?;
if sign {
Ok(Some(stream.take::()?))
} else {
Ok(None)
}
}
}
impl Deserialise for PhantomData {
fn deserialise(data: &[u8]) -> Result {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
Ok(Self)
}
}
impl Deserialise for core::result::Result {
fn deserialise(data: &[u8]) -> Result {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
let stream = Dstream::new(data);
let sign = stream.take::()?;
let value = if sign {
Err(stream.take::()?)
} else {
Ok(stream.take::()?)
};
Ok(value)
}
}
impl Deserialise for () {
fn deserialise(_data: &[u8]) -> Result { Ok(()) }
}
impl Deserialise for usize {
fn deserialise(data: &[u8]) -> Result {
debug_assert_eq!(data.len(), Self::SERIALISED_SIZE);
let value = u32::deserialise(data)?
.try_into().expect("unable to convert from `u32` to `usize`");
Ok(value)
}
}
//impl_numeric!(f128);
//impl_numeric!(f16);
impl_numeric!(f32);
impl_numeric!(f64);
impl_numeric!(i128);
impl_numeric!(i16);
impl_numeric!(i32);
impl_numeric!(i64);
impl_numeric!(i8);
impl_numeric!(u128);
impl_numeric!(u16);
impl_numeric!(u32);
impl_numeric!(u64);
impl_numeric!(u8);
impl_non_zero!(i128);
impl_non_zero!(i16);
impl_non_zero!(i32);
impl_non_zero!(i64);
impl_non_zero!(i8);
impl_non_zero!(isize);
impl_non_zero!(u128);
impl_non_zero!(u16);
impl_non_zero!(u32);
impl_non_zero!(u64);
impl_non_zero!(u8);
impl_non_zero!(usize);