sark-h2 0.26.3

Fiber-free HTTP/2 protocol primitives and manifold server for sark
use core::{marker::PhantomData, mem};

use decode::mode::{self, Active, Facts, Field, Flow, Kind, Name, NameRoute, Route, Value};
use sark_http::field::metadata::{block, strings::Cursor};

use crate::hpack::decode::{self, limit, sink, strings};

#[derive(Clone, Copy)]
enum State<R, D> {
    PlainSkip,
    PlainBuffered(R),
    PlainDirect(D),
    IndexedSkip,
    IndexedBuffered(R),
}

#[derive(Clone, Copy)]
#[repr(transparent)]
pub(crate) struct Store<R, D>(PhantomData<fn() -> (R, D)>);

#[derive(Clone, Copy)]
pub(crate) struct Storage<R, D> {
    facts: Facts,
    state: State<R, D>,
}

#[derive(Clone, Copy)]
pub(crate) struct Direct(());

const _: () = assert!(mem::size_of::<Store<usize, ()>>() == 0);
const _: () = assert!(mem::size_of::<Direct>() == 0);
const _: () = assert!(mem::size_of::<Storage<usize, ()>>() == 4 * mem::size_of::<usize>());

impl<R: Copy, D: Copy> Store<R, D> {
    fn skip<S: sink::Store>(
        mut sink: S,
        budget: &mut limit::Budget,
        name_len: usize,
        value: block::Valued<'_>,
    ) -> Result<S, (S, S::Error)> {
        let result = sink.validate(value.validation());
        let ((), sink) = mode::carry(sink, result)?;
        budget.admit(name_len, value.span().len());
        Ok(sink)
    }

    fn buffered<S>(
        mut sink: S,
        budget: &mut limit::Budget,
        rule: R,
        facts: Facts,
        name: block::Named<'_>,
        value: block::Valued<'_>,
    ) -> Result<S, (S, S::Error)>
    where
        S: sink::Store<Rule = R, Completion = D>,
    {
        match budget.admit(facts.len, value.span().len()) {
            limit::Admission::Admitted => sink.field(rule, value, facts.index.indexing(), name),
            limit::Admission::Exceeded => {
                sink.rollback();
                Ok(sink)
            }
        }
    }
}

impl<R: Copy, D: Copy> Kind for Store<R, D> {
    type Direct = Direct;
    type Active = Storage<R, D>;

    fn new() -> Self {
        Self(PhantomData)
    }

    fn name(self) -> NameRoute {
        NameRoute::Retain
    }
}

impl<R: Copy, D: Copy> Active for Storage<R, D> {
    type Direct = Direct;

    fn route(self) -> Route<Self::Direct> {
        match self.state {
            State::PlainSkip => Route::Validate,
            State::PlainBuffered(_) | State::IndexedSkip | State::IndexedBuffered(_) => {
                Route::Retain
            }
            State::PlainDirect(_) => Route::Direct(Direct(())),
        }
    }

    fn facts(self) -> Facts {
        self.facts
    }
}

impl<S: sink::Direct> mode::Direct<S> for Direct {
    type Error = S::Error;

    fn literal(
        self,
        sink: &mut S,
        input: strings::Encoded<'_>,
    ) -> Result<strings::Decoded, Self::Error> {
        let Self(()) = self;
        input.decode_to(sink)
    }

    fn string(self, sink: &mut S, string: &mut Cursor, input: &[u8]) -> Result<(), Self::Error> {
        let Self(()) = self;
        sink.string(string, input)
    }
}

impl<R, D, S> Flow<S> for Store<R, D>
where
    R: Copy,
    D: Copy,
    S: sink::Store<Rule = R, Completion = D>,
{
    type Error = S::Error;

    fn validate(sink: &mut S, value: block::Validation) -> Result<(), Self::Error> {
        match value {
            block::Validation::Valid => Ok(()),
            block::Validation::Malformed | block::Validation::RejectPush => sink.validate(value),
        }
    }

    fn begin(self, sink: S, name: Name<'_>) -> Result<(Self::Active, S), (S, Self::Error)> {
        let Name { span, facts } = name;
        let (state, sink) = match facts.index {
            mode::Index::Indexed => {
                let (sink, indexed) = sink.indexed(span)?;
                (
                    match indexed {
                        sink::Indexed::Skip => State::IndexedSkip,
                        sink::Indexed::Buffered(rule) => State::IndexedBuffered(rule),
                    },
                    sink,
                )
            }
            mode::Index::Plain | mode::Index::Never => {
                let (sink, begin) = sink.begin(span, facts.index.indexing())?;
                (
                    match begin {
                        sink::Begin::Skip => State::PlainSkip,
                        sink::Begin::Buffered(rule) => State::PlainBuffered(rule),
                        sink::Begin::Direct(done) => State::PlainDirect(done),
                    },
                    sink,
                )
            }
        };
        Ok((Storage { facts, state }, sink))
    }

    fn field(
        sink: S,
        budget: &mut limit::Budget,
        name: block::Named<'_>,
        value: block::Valued<'_>,
    ) -> Result<S, (S, Self::Error)> {
        let facts = Facts {
            len: name.span().len(),
            index: mode::Index::Indexed,
        };
        let (sink, indexed) = sink.indexed(Some(name))?;
        match indexed {
            sink::Indexed::Skip => Self::skip(sink, budget, facts.len, value),
            sink::Indexed::Buffered(rule) => Self::buffered(sink, budget, rule, facts, name, value),
        }
    }

    fn finish(
        active: Self::Active,
        mut sink: S,
        budget: &mut limit::Budget,
        field: Field<'_>,
    ) -> Result<S, (S, Self::Error)> {
        let facts = active.facts;
        match active.state {
            State::PlainSkip | State::IndexedSkip => match field.value {
                Value::Retained(value) => return Self::skip(sink, budget, facts.len, value),
                Value::Counted(len) => {
                    budget.admit(facts.len, len);
                }
            },
            State::PlainBuffered(rule) | State::IndexedBuffered(rule) => match field.value {
                Value::Retained(value) => {
                    return Self::buffered(sink, budget, rule, facts, field.name, value);
                }
                Value::Counted(len) => {
                    budget.exceed(facts.len, len);
                    sink.rollback();
                }
            },
            State::PlainDirect(done) => match budget.admit(facts.len, field.value.len()) {
                limit::Admission::Admitted => return sink.direct(done),
                limit::Admission::Exceeded => sink.rollback(),
            },
        }
        Ok(sink)
    }
}