344 lines
9.9 KiB
Rust
344 lines
9.9 KiB
Rust
//! Traits to better express parsing semantics.
|
|
|
|
pub mod static_pair;
|
|
|
|
use crate::rcore::*;
|
|
|
|
use super::{
|
|
atomic::{atomic_object::*, *},
|
|
point::*,
|
|
*,
|
|
};
|
|
|
|
impl<D: ?Sized + Deserializer> Inlining for &mut D {
|
|
fn iread_n<A, E>(
|
|
self,
|
|
n: usize,
|
|
ok: impl FnOnce(&[u8]) -> A,
|
|
err: impl FnOnce(&[u8]) -> E,
|
|
) -> Result<(A, Self), E> {
|
|
let slice = self.read_n(n);
|
|
if slice.len() == n {
|
|
Ok((ok(slice), self))
|
|
} else {
|
|
Err(err(slice))
|
|
}
|
|
}
|
|
|
|
fn iread_all<A>(self, ok: impl FnOnce(&[u8]) -> A) -> A {
|
|
ok(self.read_all())
|
|
}
|
|
|
|
fn itell(&self) -> usize {
|
|
self.tell()
|
|
}
|
|
}
|
|
|
|
pub type ResultDyn<A, I> = Result<(A, I), Vec<u8>>;
|
|
|
|
pub trait InCtxDyn<'a: 'c, 'c, Ctx: Context<'a>>: 'c {
|
|
fn idread_n(self, n: usize) -> ResultDyn<Vec<u8>, Box<dyn InCtxDyn<'a, 'c, Ctx>>>;
|
|
|
|
fn idread_all(self) -> Vec<u8>;
|
|
|
|
fn idtell(&self) -> usize;
|
|
|
|
fn idnext_address(self) -> ResultDyn<Address, Box<dyn InCtxDyn<'a, 'c, Ctx>>>;
|
|
|
|
fn idresolver(&self) -> Rc<dyn Resolver<'a, Ctx>>;
|
|
}
|
|
|
|
impl<'a: 'c, 'c, Ctx: Context<'a>, I: 'c + InCtx<'a, Ctx>> InCtxDyn<'a, 'c, Ctx> for I {
|
|
fn idread_n(self, n: usize) -> ResultDyn<Vec<u8>, Box<dyn InCtxDyn<'a, 'c, Ctx>>> {
|
|
let (vec, i) = self.iread_n(n, |slice| Vec::from(slice), |slice| Vec::from(slice))?;
|
|
Ok((vec, Box::new(i)))
|
|
}
|
|
|
|
fn idread_all(self) -> Vec<u8> {
|
|
self.iread_all(|slice| Vec::from(slice))
|
|
}
|
|
|
|
fn idtell(&self) -> usize {
|
|
self.itell()
|
|
}
|
|
|
|
fn idnext_address(self) -> ResultDyn<Address, Box<dyn InCtxDyn<'a, 'c, Ctx>>> {
|
|
let (address, i) = self.icnext_address(|slice| Vec::from(slice))?;
|
|
Ok((address, Box::new(i)))
|
|
}
|
|
|
|
fn idresolver(&self) -> Rc<dyn Resolver<'a, Ctx>> {
|
|
self.iresolver()
|
|
}
|
|
}
|
|
|
|
impl<'a: 'c, 'c, Ctx: Context<'a>> Inlining for Box<dyn InCtxDyn<'a, 'c, Ctx>> {
|
|
fn iread_n<A, E>(
|
|
self,
|
|
n: usize,
|
|
ok: impl FnOnce(&[u8]) -> A,
|
|
err: impl FnOnce(&[u8]) -> E,
|
|
) -> Result<(A, Self), E> {
|
|
let (vec, i) = self.idread_n(n).map_err(|vec| err(&vec))?;
|
|
Ok((ok(&vec), i))
|
|
}
|
|
|
|
fn iread_all<A>(self, ok: impl FnOnce(&[u8]) -> A) -> A {
|
|
ok(&self.idread_all())
|
|
}
|
|
|
|
fn itell(&self) -> usize {
|
|
self.idtell()
|
|
}
|
|
}
|
|
|
|
impl<'a: 'c, 'c, Ctx: Context<'a>> InCtx<'a, Ctx> for Box<dyn InCtxDyn<'a, 'c, Ctx>> {
|
|
fn icnext_address<E>(self, err: impl FnOnce(&[u8]) -> E) -> Result<(Address, Self), E> {
|
|
self.idnext_address().map_err(|vec| err(&vec))
|
|
}
|
|
|
|
fn iresolver(&self) -> Rc<dyn Resolver<'a, Ctx>> {
|
|
self.idresolver()
|
|
}
|
|
}
|
|
|
|
pub type IParseResult<'a, Ctx, F, I> =
|
|
Result<(Mtbl<'a, Ctx, F>, I), <F as Factory<'a, Ctx>>::ParseError>;
|
|
|
|
/// This factory should return an error on EOF.
|
|
pub trait InlineableFactory<'a, Ctx: Context<'a>>: Factory<'a, Ctx> {
|
|
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), <A as Atomic>::AParseError>;
|
|
|
|
/// Atomic analogue of [`InlineableFactory`]/[`InlineableObject`].
|
|
pub trait InlineableAtomic: Atomic {
|
|
fn a_extension_error(tail: &[u8]) -> Self::AParseError;
|
|
|
|
fn a_ideserialize<D: Inlining>(deserializer: 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> {
|
|
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.into(), inctx))
|
|
}
|
|
}
|
|
|
|
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,
|
|
dectx: &mut dyn DeCtx<'a, Ctx>,
|
|
) -> CheckedParseResult<'a, Ctx, Self> {
|
|
let expected_size = self.size();
|
|
let start = dectx.deserializer().tell();
|
|
let result = self.deserialize(dectx)?;
|
|
let end = dectx.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,
|
|
}))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 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;
|
|
}
|
|
|
|
impl<'a, Ctx: Context<'a>, F: Factory<'a, Ctx>> InlineableFactory<'a, Ctx> for PointFactory<F> {
|
|
fn extension_error(&self, tail: &[u8]) -> Self::ParseError {
|
|
PointParseError::WrongLength(HASH_SIZE + tail.len())
|
|
}
|
|
|
|
fn ideserialize<I: InCtx<'a, Ctx>>(&self, inctx: I) -> IParseResult<'a, Ctx, Self, I> {
|
|
inctx.icnext_point(self.inner(), |slice| PointParseError::from(slice))
|
|
}
|
|
}
|
|
|
|
impl<F> AlwaysConstSize for PointFactory<F> {
|
|
const _SIZE: usize = HASH_SIZE;
|
|
}
|