use std::{iter, marker, mem, num::NonZero, ops::ControlFlow, task::Poll};
use o3::{
alloc,
buffer::{resident, segments, storage},
mem::slot,
};
use sark_http::{payload::owned, request};
mod atom;
mod cursor;
mod document;
pub mod frames;
mod input;
mod nested;
mod scanner;
mod state;
pub(crate) use atom::Atom;
use cursor::Cursor;
pub use document::{
Document, admitted::Admitted, project::Project, release::Release, value::Value,
};
pub use nested::Nested;
use scanner::{Prepared, Scanner};
pub(crate) use state::ValueKind;
use state::{Capture, ScanProgress, State, Stored, is_delimiter, is_overlap};
pub const DEFAULT_STEP_BUDGET: NonZero<usize> = NonZero::<usize>::MIN.saturating_add(4 * 1024 - 1);
#[derive(Clone, Copy, Eq, PartialEq)]
enum End {
Allowed,
Forbidden,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Order {
Unordered,
Ordered,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Projection {
None,
Scalar,
Bytes,
Frame,
Segments,
}
#[derive(Clone, Copy)]
pub struct Field<'a> {
name: &'a [u8],
projection: Projection,
}
impl<'a> Field<'a> {
#[must_use]
pub const fn new(name: &'a [u8], mode: Projection) -> Self {
Self {
name,
projection: mode,
}
}
}
pub trait Decode<C: Copy = ()>: Sized {
type State<'d>: Materialize<'d, Self>;
const ORDER: Order;
fn fields<'d>() -> &'d [Field<'d>];
fn start<'d>(document: Document<'d>, context: C) -> Self::State<'d>;
fn release(&mut self, budget: &mut usize) -> Poll<()>;
}
pub trait IntoRequest: Decode {
type Request<'d>: Decode<request::Id<'d>>;
}
pub trait Materialize<'d, T>: Sized {
fn step(self, budget: NonZero<usize>) -> Outcome<'d, Self, T>;
fn release(&mut self, budget: &mut usize) -> Poll<()>;
}
pub enum Outcome<'d, S, T> {
Pending(S),
Ready(T),
Failed(crate::parsing::Error<'d>),
}
pub struct Ready<'d, T> {
value: Result<T, crate::parsing::Error<'d>>,
release: Release<'d>,
}
impl<'d, T> Ready<'d, T> {
#[must_use]
pub fn new(value: T, document: Document<'d>) -> Self {
Self {
value: Ok(value),
release: document.release(),
}
}
#[must_use]
pub fn try_new(value: Result<T, crate::parsing::Error<'d>>, document: Document<'d>) -> Self {
Self {
value,
release: document.release(),
}
}
}
impl<'d, T> Materialize<'d, T> for Ready<'d, T> {
fn step(self, budget: NonZero<usize>) -> Outcome<'d, Self, T> {
match self.release.step(budget) {
ControlFlow::Continue(release) => Outcome::Pending(Self {
value: self.value,
release,
}),
ControlFlow::Break(()) => match self.value {
Ok(value) => Outcome::Ready(value),
Err(error) => Outcome::Failed(error),
},
}
}
fn release(&mut self, budget: &mut usize) -> Poll<()> {
self.release.retire(budget)
}
}
pub enum Progress<'d, T> {
NeedInput,
Pending,
Ready(T),
Failed(crate::parsing::Error<'d>),
}
#[repr(u8)]
#[derive(Debug)]
pub enum Create {
Exhausted,
Overflow,
}
impl From<alloc::Error> for Create {
fn from(error: alloc::Error) -> Self {
match error {
alloc::Error::Exhausted => Self::Exhausted,
alloc::Error::Overflow => Self::Overflow,
}
}
}
const _: () = assert!(mem::size_of::<Create>() == 1);
pub enum Push<'d> {
Complete(resident::Shared<'d>),
Length {
len: usize,
add: usize,
owner: resident::Shared<'d>,
},
Segments {
current: segments::Count,
add: segments::Count,
owner: resident::Shared<'d>,
},
Allocation {
error: alloc::Error,
owner: resident::Shared<'d>,
},
}
#[must_use = "the accepted or rejected state must be consumed"]
pub enum Transfer<S, E> {
Accepted(S),
Rejected { state: S, error: E },
}
pub struct Decoder<'d, T: Decode<C>, C: Copy = ()> {
phase: slot::Storage<Phase<'d, T::State<'d>>>,
context: C,
bytes: usize,
chunks: segments::Count,
marker: marker::PhantomData<fn() -> T>,
}
enum Phase<'d, S> {
Scan(Scanner<'d>),
Start(Document<'d>),
Build(S),
Release {
release: Release<'d>,
error: Option<crate::parsing::Error<'d>>,
},
ReleaseBuild(S),
}
impl<'d, T: Decode> Decoder<'d, T> {
pub fn try_stream() -> Result<Self, Create> {
Self::try_feed(())
}
pub fn new(segments: Vec<storage::Shared>) -> crate::Result<Self> {
let scan = Scanner::new(segments, T::fields(), T::ORDER)?;
Ok(Self::with_scan(scan, ()))
}
pub fn one(segment: storage::Shared) -> crate::Result<Self> {
let scan = Scanner::new(iter::once(segment), T::fields(), T::ORDER)?;
Ok(Self::with_scan(scan, ()))
}
pub fn from_scoped(segments: Vec<resident::Shared<'d>>) -> crate::Result<Self> {
let scan = Scanner::new(segments, T::fields(), T::ORDER)?;
Ok(Self::with_scan(scan, ()))
}
}
impl<'d, T: Decode<C>, C: Copy> Decoder<'d, T, C> {
fn with_scan(scan: Scanner<'d>, context: C) -> Self {
Self {
phase: slot::Storage::filled(Phase::Scan(scan), ()),
context,
bytes: 0,
chunks: segments::Count::EMPTY,
marker: marker::PhantomData,
}
}
pub fn try_feed(context: C) -> Result<Self, Create> {
let scan = Scanner::try_stream(T::fields(), T::ORDER)?;
Ok(Self::with_scan(scan, context))
}
#[doc(hidden)]
pub fn try_admitted(input: &mut Admitted<'d>, context: C) -> crate::Result<Box<Self>> {
let selection =
scanner::Selection::try_new(T::fields(), T::ORDER).map_err(crate::Error::Allocation)?;
let slot: Box<mem::MaybeUninit<Self>> =
alloc::BoxExt::try_new(mem::MaybeUninit::uninit()).map_err(crate::Error::Exhausted)?;
let input = Admitted {
input: mem::replace(&mut input.input, input::Input::empty()),
};
let scan = Scanner::admitted(input, selection);
Ok(Box::write(slot, Self::with_scan(scan, context)))
}
#[doc(hidden)]
pub fn try_scoped(segments: owned::Segments<'d>, context: C) -> Result<Self, Create> {
let scan = Scanner::try_new(segments, T::fields(), T::ORDER)?;
Ok(Self::with_scan(scan, context))
}
pub fn push(mut self, segment: resident::Shared<'d>) -> Transfer<Self, Push<'d>> {
let result = match self.phase.get_mut() {
Some(Phase::Scan(scanner)) => scanner.push(segment),
_ => Err(Push::Complete(segment)),
};
match result {
Ok(()) => Transfer::Accepted(self),
Err(error) => Transfer::Rejected { state: self, error },
}
}
pub fn close(mut self) -> Self {
if let Some(phase) = self.phase.occupied() {
let (phase, vacant) = phase.take();
vacant.insert(match phase {
Phase::Scan(scanner) => Phase::Scan(scanner.close()),
phase => phase,
});
}
self
}
pub fn take_bytes(&mut self) -> usize {
mem::replace(&mut self.bytes, 0)
}
pub fn take_chunks(&mut self) -> segments::Count {
mem::replace(&mut self.chunks, segments::Count::EMPTY)
}
pub fn release(&mut self, budget: &mut usize) -> Poll<()> {
let Some(phase) = self.phase.occupied() else {
return Poll::Ready(());
};
let (phase, vacant) = phase.take();
let (mut release, error) = match phase {
Phase::Scan(scanner) => (scanner.release(), None),
Phase::Start(document) => (document.release(), None),
Phase::Build(mut state) | Phase::ReleaseBuild(mut state) => {
let done = Materialize::release(&mut state, budget);
if done.is_pending() {
vacant.insert(Phase::ReleaseBuild(state));
}
return done;
}
Phase::Release { release, error } => (release, error),
};
let done = release.retire(budget);
if done.is_pending() {
vacant.insert(Phase::Release { release, error });
}
done
}
pub fn poll(&mut self, budget: NonZero<usize>) -> Progress<'d, T> {
let Some(phase) = self.phase.occupied() else {
return Progress::Pending;
};
let (phase, vacant) = phase.take();
let (state, budget, vacant) = match phase {
Phase::ReleaseBuild(mut state) => {
let mut left = budget.get();
if Materialize::release(&mut state, &mut left).is_pending() {
vacant.insert(Phase::ReleaseBuild(state));
}
return Progress::Pending;
}
Phase::Release { mut release, error } => {
let mut left = budget.get();
let (done, released) = release.drain(&mut left);
(self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
if done.is_ready() {
return match error {
Some(error) => Progress::Failed(error),
None => Progress::Pending,
};
}
vacant.insert(Phase::Release { release, error });
return Progress::Pending;
}
Phase::Build(state) => (state, budget, vacant),
Phase::Start(document) => (T::start(document, self.context), budget, vacant),
Phase::Scan(scanner) => {
let (document, left, released) = {
let mut prepared;
let left = 'prepare: {
let (mut scan, closed) = match scanner {
Scanner::Open(scan) => (scan, false),
Scanner::Closed(scan) => (scan, true),
Scanner::Preparing(value) => {
prepared = value;
break 'prepare budget.get();
}
};
let (progress, left) = match scan.step(budget, closed) {
Ok(progress) => progress,
Err(error) => {
let released = scan.stream.drain();
let release = scan.release();
(self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
vacant.insert(Phase::Release {
release,
error: Some(error.into()),
});
return Progress::Pending;
}
};
let progress = match progress {
ScanProgress::Ready => {
prepared = Prepared::new(scan);
break 'prepare left;
}
ScanProgress::NeedInput => Progress::NeedInput,
ScanProgress::Pending => Progress::Pending,
};
let released = scan.stream.drain();
vacant.insert(Phase::Scan(if closed {
Scanner::Closed(scan)
} else {
Scanner::Open(scan)
}));
(self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
return progress;
};
let progress = match prepared.step(left) {
Ok(progress) => progress,
Err(error) => {
let released = prepared.stream.drain();
let release = prepared.release();
(self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
vacant.insert(Phase::Release {
release,
error: Some(error.into()),
});
return Progress::Pending;
}
};
let released = prepared.stream.drain();
match progress {
Poll::Ready(left) => (prepared.document, left, released),
Poll::Pending => {
vacant.insert(Phase::Scan(Scanner::Preparing(prepared)));
(self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
return Progress::Pending;
}
}
};
(self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
let Some(left) = NonZero::new(left) else {
vacant.insert(Phase::Start(document));
return Progress::Pending;
};
(T::start(document, self.context), left, vacant)
}
};
match Materialize::step(state, budget) {
Outcome::Ready(value) => Progress::Ready(value),
Outcome::Pending(state) => {
vacant.insert(Phase::Build(state));
Progress::Pending
}
Outcome::Failed(error) => Progress::Failed(error),
}
}
}