radn-rs/src/rstd/inlining.rs
2023-07-29 20:14:00 +00:00

251 lines
7.4 KiB
Rust

//! Traits to better express parsing semantics.
mod modes;
pub mod static_pair;
use crate::rcore::*;
use super::{
atomic::{atomic_object::*, *},
*,
};
pub use self::modes::InliningMode;
pub type IParseResult<'a, Ctx, F, I> = Result<(Mtbl<'a, Ctx, F>, I), ParseError<'a, Ctx, F>>;
/// This factory should return an error on EOF.
pub trait InlineableFactory<'a, Ctx: Context<'a>>:
FactoryBase<'a, Ctx> + ParseMode<Mode = InliningMode>
{
fn extension_error(&self, tail: &[u8]) -> Self::ParseError;
fn ideserialize<I: InCtx<'a, Ctx>>(&self, inctx: I) -> IParseResult<'a, Ctx, Self, I>;
}
/// This factory always reads the same amount of bytes or returns error.
pub trait FixedSizeFactory<'a, Ctx: Context<'a>>: InlineableFactory<'a, Ctx> {
/// For [`ConstSizeFactory`] this must return [`ConstSizeFactory::SIZE`].
fn size(&self) -> usize;
}
/// Compile-time analogue of [`FixedSizeFactory`].
pub trait ConstSizeFactory<'a, Ctx: Context<'a>>: FixedSizeFactory<'a, Ctx> {
/// Must be equal to [`FixedSizeFactory::size()`].
const SIZE: usize;
}
/// Object analogue of [`InlineableFactory`].
pub trait InlineableObject<'a, Ctx: Context<'a>>: Mentionable<'a, Ctx> {}
/// Object analogue of [`FixedSizeFactory`].
pub trait FixedSizeObject<'a, Ctx: Context<'a>>: InlineableObject<'a, Ctx> {
/// For [`ConstSizeObject`] this must return [`ConstSizeObject::SIZE`].
fn size(&self) -> usize;
}
/// Object analogue of [`ConstSizeFactory`].
pub trait ConstSizeObject<'a, Ctx: Context<'a>>: FixedSizeObject<'a, Ctx> {
/// Must be equal to [`FixedSizeObject::size()`].
const SIZE: usize;
}
pub type ADParseResult<A, D> = Result<(A, D), AParseError<A>>;
/// Atomic analogue of [`InlineableFactory`]/[`InlineableObject`].
pub trait InlineableAtomic: AtomicBase + ImplMode<Mode = InliningMode> {
fn a_extension_error(tail: &[u8]) -> Self::AParseError;
fn a_ideserialize<D: Inlining>(inlining: D) -> ADParseResult<Self, D>;
}
/// Atomic analogue of [`ConstSizeFactory`]/[`ConstSizeObject`].
///
/// Note: `FixedSizeAtomic` doesn't exist because it would
/// either be const anyway
/// or have a very undesireable implementation.
pub trait ConstSizeAtomic: InlineableAtomic {
const SIZE: usize;
}
impl<'a, Ctx: Context<'a>, A: InlineableAtomic> InlineableFactory<'a, Ctx> for AtomicFactory<A>
where
<A as WithParseMode>::WithMode: AoProxy<'a, Ctx, Fctr = Self, A = A>,
{
fn extension_error(&self, tail: &[u8]) -> Self::ParseError {
A::a_extension_error(tail)
}
fn ideserialize<I: InCtx<'a, Ctx>>(&self, inctx: I) -> IParseResult<'a, Ctx, Self, I> {
let (a, inctx) = A::a_ideserialize(inctx)?;
Ok((
<<A as WithParseMode>::WithMode as AoProxy<'a, Ctx>>::wrap(a),
inctx,
))
}
}
impl<'a, Ctx: Context<'a>, A: ConstSizeAtomic> FixedSizeFactory<'a, Ctx> for AtomicFactory<A>
where
<A as WithParseMode>::WithMode: AoProxy<'a, Ctx, Fctr = Self, A = A>,
{
fn size(&self) -> usize {
A::SIZE
}
}
impl<'a, Ctx: Context<'a>, A: ConstSizeAtomic> ConstSizeFactory<'a, Ctx> for AtomicFactory<A>
where
<A as WithParseMode>::WithMode: AoProxy<'a, Ctx, Fctr = Self, A = A>,
{
const SIZE: usize = A::SIZE;
}
impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> InlineableObject<'a, Ctx> for A where
A::Fctr: InlineableFactory<'a, Ctx>
{
}
impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> FixedSizeObject<'a, Ctx> for A
where
A::Fctr: FixedSizeFactory<'a, Ctx>,
{
fn size(&self) -> usize {
self.factory().size()
}
}
impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx>> ConstSizeObject<'a, Ctx> for A
where
A::Fctr: ConstSizeFactory<'a, Ctx>,
{
const SIZE: usize = A::Fctr::SIZE;
}
/// Error returned by [`CheckedParse::deserialize_checked`]/[`CheckedSerialize::serialize_checked`].
#[derive(Debug)]
pub struct SizeError {
expected: usize,
received: usize,
}
impl Display for SizeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"expected {} bytes, read/wrote {} instead",
self.expected, self.received
)
}
}
impl Error for SizeError {}
/// Wrapper for [`SizeError`]/[`FactoryBase::ParseError`].
#[derive(Debug)]
pub enum CheckedParseError<P: Error> {
Parse(P),
Size(SizeError),
}
impl<P: Error> Display for CheckedParseError<P> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Parse(parse_error) => write!(f, "{}", parse_error),
Self::Size(size_error) => {
write!(f, "size check failed: {}", size_error)
}
}
}
}
impl<P: Error> From<P> for CheckedParseError<P> {
fn from(value: P) -> Self {
Self::Parse(value)
}
}
pub type CheckedParseFailure<'a, Ctx, F> = CheckedParseError<ParseError<'a, Ctx, F>>;
/// Returned by [`CheckedParse::deserialize_checked`].
pub type CheckedParseResult<'a, Ctx, F> = Result<Mtbl<'a, Ctx, F>, CheckedParseFailure<'a, Ctx, F>>;
/// Extension trait for factories that ensures fixed size read.
pub trait CheckedParse<'a, Ctx: Context<'a>>: FixedSizeFactory<'a, Ctx> {
/// Verify proper read length using [`Deserializer::tell`].
fn deserialize_checked(&self, inctx: impl InCtx<'a, Ctx>) -> CheckedParseResult<'a, Ctx, Self> {
let expected_size = self.size();
let start = inctx.itell();
let (result, inctx) = self.ideserialize(inctx)?;
let end = inctx.itell();
let received_size = end - start;
if received_size == expected_size {
Ok(result)
} else {
Err(CheckedParseError::Size(SizeError {
expected: expected_size,
received: received_size,
}))
}
}
}
/// Extension trait for factories that ensures fixed size write.
pub trait CheckedSerialize<'a, Ctx: Context<'a>>: Serializable + FixedSizeObject<'a, Ctx> {
/// Verify proper write length using [`Serializer::tell`].
fn serialize_checked(&self, serializer: &mut dyn Serializer) -> Result<(), SizeError> {
let expected_size = self.size();
let start = serializer.tell();
self.serialize(serializer);
let end = serializer.tell();
let received_size = end - start;
if received_size == expected_size {
Ok(())
} else {
Err(SizeError {
expected: expected_size,
received: received_size,
})
}
}
}
impl<'a, Ctx: Context<'a>, F: FixedSizeFactory<'a, Ctx>> CheckedParse<'a, Ctx> for F {}
impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx> + FixedSizeObject<'a, Ctx>>
CheckedSerialize<'a, Ctx> for A
{
}
/// Trait useful for objects which aren't influenced by
/// whether some other type (for example, a generic parameter type)
/// is fixed-size or not.
pub trait AlwaysFixedSize {
fn _size(&self) -> usize;
}
impl<'a, Ctx: Context<'a>, F: AlwaysFixedSize + InlineableFactory<'a, Ctx>>
FixedSizeFactory<'a, Ctx> for F
{
fn size(&self) -> usize {
self._size()
}
}
/// Compile-time analogue of [`AlwaysFixedSize`].
pub trait AlwaysConstSize {
const _SIZE: usize;
}
impl<F: AlwaysConstSize> AlwaysFixedSize for F {
fn _size(&self) -> usize {
Self::_SIZE
}
}
impl<'a, Ctx: Context<'a>, F: AlwaysConstSize + InlineableFactory<'a, Ctx>>
ConstSizeFactory<'a, Ctx> for F
{
const SIZE: usize = Self::_SIZE;
}