230 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			230 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
//! Traits to better express parsing semantics.
 | 
						|
 | 
						|
pub mod static_pair;
 | 
						|
 | 
						|
use crate::rcore::*;
 | 
						|
 | 
						|
use super::{
 | 
						|
    atomic::{atomic_object::*, *},
 | 
						|
    point::*,
 | 
						|
    *,
 | 
						|
};
 | 
						|
 | 
						|
/// This factory should return an error on EOF.
 | 
						|
pub trait InlineableFactory<'a, Ctx: Context<'a>>: Factory<'a, Ctx> {}
 | 
						|
 | 
						|
/// 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>>: 'a {}
 | 
						|
 | 
						|
/// 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;
 | 
						|
}
 | 
						|
 | 
						|
/// Atomic analogue of [`InlineableFactory`]/[`InlineableObject`].
 | 
						|
pub trait InlineableAtomic: Atomic {}
 | 
						|
 | 
						|
/// 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: ConstSizeAtomic> InlineableAtomic for A {}
 | 
						|
 | 
						|
impl<'a, Ctx: Context<'a>, A: InlineableAtomic> InlineableFactory<'a, Ctx> for AtomicFactory<A> {}
 | 
						|
 | 
						|
impl<'a, Ctx: Context<'a>, A: ConstSizeAtomic> FixedSizeFactory<'a, Ctx> for AtomicFactory<A> {
 | 
						|
    fn size(&self) -> usize {
 | 
						|
        A::SIZE
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
impl<'a, Ctx: Context<'a>, A: ConstSizeAtomic> ConstSizeFactory<'a, Ctx> for AtomicFactory<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`]/[`Factory::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)
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/// Returned by [`CheckedParse::deserialize_checked`].
 | 
						|
pub type CheckedParseResult<'a, Ctx, F> =
 | 
						|
    Result<Mtbl<'a, Ctx, F>, CheckedParseError<ParseError<'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,
 | 
						|
        deserializer: &mut dyn Deserializer,
 | 
						|
        resolver: Rc<dyn Resolver<'a, Ctx>>,
 | 
						|
        addresses: &mut Addresses,
 | 
						|
    ) -> CheckedParseResult<'a, Ctx, Self>;
 | 
						|
}
 | 
						|
 | 
						|
/// 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>;
 | 
						|
}
 | 
						|
 | 
						|
impl<'a, Ctx: Context<'a>, F: FixedSizeFactory<'a, Ctx>> CheckedParse<'a, Ctx> for F {
 | 
						|
    fn deserialize_checked(
 | 
						|
        &self,
 | 
						|
        deserializer: &mut dyn Deserializer,
 | 
						|
        resolver: Rc<dyn Resolver<'a, Ctx>>,
 | 
						|
        addresses: &mut Addresses,
 | 
						|
    ) -> CheckedParseResult<'a, Ctx, Self> {
 | 
						|
        let expected_size = self.size();
 | 
						|
        let start = deserializer.tell();
 | 
						|
        let result = self.deserialize(deserializer, resolver, addresses)?;
 | 
						|
        let end = deserializer.tell();
 | 
						|
        let received_size = end - start;
 | 
						|
        if received_size == expected_size {
 | 
						|
            Ok(result)
 | 
						|
        } else {
 | 
						|
            Err(CheckedParseError::Size(SizeError {
 | 
						|
                expected: expected_size,
 | 
						|
                received: received_size,
 | 
						|
            }))
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
impl<'a, Ctx: Context<'a>, A: Mentionable<'a, Ctx> + FixedSizeObject<'a, Ctx>>
 | 
						|
    CheckedSerialize<'a, Ctx> for A
 | 
						|
{
 | 
						|
    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,
 | 
						|
            })
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/// 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 + Factory<'a, Ctx>> InlineableFactory<'a, Ctx> for F {}
 | 
						|
 | 
						|
impl<'a, Ctx: Context<'a>, F: AlwaysFixedSize + Factory<'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 + Factory<'a, Ctx>> ConstSizeFactory<'a, Ctx> for F {
 | 
						|
    const SIZE: usize = Self::_SIZE;
 | 
						|
}
 | 
						|
 | 
						|
impl<F> AlwaysConstSize for PointFactory<F> {
 | 
						|
    const _SIZE: usize = HASH_SIZE;
 | 
						|
}
 |