Skip to main content

sark_json/segment/
mod.rs

1//! Cooperative JSON decoding over independently retained transport segments.
2
3use std::{iter, marker, mem, num::NonZero, ops::ControlFlow, task::Poll};
4
5use o3::{
6    alloc,
7    buffer::{resident, segments, storage},
8    mem::slot,
9};
10use sark_http::{payload::owned, request};
11
12mod atom;
13mod cursor;
14mod document;
15pub mod frames;
16mod input;
17mod nested;
18mod scanner;
19mod state;
20
21pub(crate) use atom::Atom;
22use cursor::Cursor;
23pub use document::{
24    Document, admitted::Admitted, project::Project, release::Release, value::Value,
25};
26pub use nested::Nested;
27use scanner::{Prepared, Scanner};
28pub(crate) use state::ValueKind;
29use state::{Capture, ScanProgress, State, Stored, is_delimiter, is_overlap};
30
31/// Bytes examined by one task poll before it yields to the application plane.
32pub const DEFAULT_STEP_BUDGET: NonZero<usize> = NonZero::<usize>::MIN.saturating_add(4 * 1024 - 1);
33
34#[derive(Clone, Copy, Eq, PartialEq)]
35enum End {
36    Allowed,
37    Forbidden,
38}
39
40#[derive(Clone, Copy, PartialEq, Eq)]
41pub enum Order {
42    Unordered,
43    Ordered,
44}
45
46#[derive(Clone, Copy, PartialEq, Eq)]
47pub enum Projection {
48    None,
49    Scalar,
50    Bytes,
51    Frame,
52    /// A compound value consumed by another segmented decoder. Its transport
53    /// owners must stay split instead of being coalesced at field boundaries.
54    Segments,
55}
56
57#[derive(Clone, Copy)]
58pub struct Field<'a> {
59    name: &'a [u8],
60    projection: Projection,
61}
62
63impl<'a> Field<'a> {
64    #[must_use]
65    pub const fn new(name: &'a [u8], mode: Projection) -> Self {
66        Self {
67            name,
68            projection: mode,
69        }
70    }
71}
72
73/// Implemented by generated owned JSON values.
74pub trait Decode<C: Copy = ()>: Sized {
75    type State<'d>: Materialize<'d, Self>;
76
77    const ORDER: Order;
78
79    fn fields<'d>() -> &'d [Field<'d>];
80
81    fn start<'d>(document: Document<'d>, context: C) -> Self::State<'d>;
82
83    /// Retires input-sized collections before this completed value is dropped.
84    /// Generated implementations visit array elements within the budget. A
85    /// fixed-schema value's scalar fields and user-defined Drop are not split.
86    fn release(&mut self, budget: &mut usize) -> Poll<()>;
87}
88
89/// Selects the generated value whose fields retain one exact request identity.
90pub trait IntoRequest: Decode {
91    type Request<'d>: Decode<request::Id<'d>>;
92}
93
94pub trait Materialize<'d, T>: Sized {
95    fn step(self, budget: NonZero<usize>) -> Outcome<'d, Self, T>;
96
97    /// Retains pending owners until their budgeted retirement is complete.
98    fn release(&mut self, budget: &mut usize) -> Poll<()>;
99}
100
101pub enum Outcome<'d, S, T> {
102    Pending(S),
103    Ready(T),
104    Failed(crate::parsing::Error<'d>),
105}
106
107/// A materialized value retained until its source owners finish bounded release.
108pub struct Ready<'d, T> {
109    value: Result<T, crate::parsing::Error<'d>>,
110    release: Release<'d>,
111}
112
113impl<'d, T> Ready<'d, T> {
114    #[must_use]
115    pub fn new(value: T, document: Document<'d>) -> Self {
116        Self {
117            value: Ok(value),
118            release: document.release(),
119        }
120    }
121
122    #[must_use]
123    pub fn try_new(value: Result<T, crate::parsing::Error<'d>>, document: Document<'d>) -> Self {
124        Self {
125            value,
126            release: document.release(),
127        }
128    }
129}
130
131impl<'d, T> Materialize<'d, T> for Ready<'d, T> {
132    fn step(self, budget: NonZero<usize>) -> Outcome<'d, Self, T> {
133        match self.release.step(budget) {
134            ControlFlow::Continue(release) => Outcome::Pending(Self {
135                value: self.value,
136                release,
137            }),
138            ControlFlow::Break(()) => match self.value {
139                Ok(value) => Outcome::Ready(value),
140                Err(error) => Outcome::Failed(error),
141            },
142        }
143    }
144
145    fn release(&mut self, budget: &mut usize) -> Poll<()> {
146        self.release.retire(budget)
147    }
148}
149
150/// Observable progress of a feedable decoder. `NeedInput` tells runtimes not
151/// to poll again until another request segment arrives.
152pub enum Progress<'d, T> {
153    NeedInput,
154    Pending,
155    Ready(T),
156    Failed(crate::parsing::Error<'d>),
157}
158
159/// Failure to allocate the initial decoder state.
160#[repr(u8)]
161#[derive(Debug)]
162pub enum Create {
163    Exhausted,
164    Overflow,
165}
166
167impl From<alloc::Error> for Create {
168    fn from(error: alloc::Error) -> Self {
169        match error {
170            alloc::Error::Exhausted => Self::Exhausted,
171            alloc::Error::Overflow => Self::Overflow,
172        }
173    }
174}
175
176const _: () = assert!(mem::size_of::<Create>() == 1);
177
178/// Failure to transfer one retained input owner into a streaming decoder.
179pub enum Push<'d> {
180    Complete(resident::Shared<'d>),
181    Length {
182        len: usize,
183        add: usize,
184        owner: resident::Shared<'d>,
185    },
186    Segments {
187        current: segments::Count,
188        add: segments::Count,
189        owner: resident::Shared<'d>,
190    },
191    Allocation {
192        error: alloc::Error,
193        owner: resident::Shared<'d>,
194    },
195}
196
197/// State ownership after one fallible transfer attempt.
198#[must_use = "the accepted or rejected state must be consumed"]
199pub enum Transfer<S, E> {
200    Accepted(S),
201    Rejected { state: S, error: E },
202}
203
204impl<S, E> Transfer<S, E> {
205    pub(crate) fn map<T, F>(
206        self,
207        state: impl FnOnce(S) -> T,
208        error: impl FnOnce(E) -> F,
209    ) -> Transfer<T, F> {
210        match self {
211            Self::Accepted(value) => Transfer::Accepted(state(value)),
212            Self::Rejected {
213                state: value,
214                error: cause,
215            } => Transfer::Rejected {
216                state: state(value),
217                error: error(cause),
218            },
219        }
220    }
221}
222
223/// Resumable decoder. Input, replay, field lookup, and owner release work are
224/// charged to the caller-provided budget. Its driver brand cannot be widened even
225/// when the scanner owns every input segment.
226///
227/// Error retirement charges input owners, field slots, and array elements.
228/// Fixed-schema scalar destruction and allocation metadata are constant work;
229/// an arbitrary user-provided materializer or destructor must uphold its own
230/// cooperative work contract. Dropping a decoder cancels it immediately.
231pub struct Decoder<'d, T: Decode<C>, C: Copy = ()> {
232    phase: slot::Storage<Phase<'d, T::State<'d>>>,
233    context: C,
234    bytes: usize,
235    chunks: segments::Count,
236    marker: marker::PhantomData<fn() -> T>,
237}
238
239enum Phase<'d, S> {
240    Scan(Scanner<'d>),
241    Start(Document<'d>),
242    Build(S),
243    Release {
244        release: Release<'d>,
245        error: Option<crate::parsing::Error<'d>>,
246    },
247    ReleaseBuild(S),
248}
249
250impl<'d, T: Decode> Decoder<'d, T> {
251    /// Creates a decoder whose input is still being received.
252    pub fn try_stream() -> Result<Self, Create> {
253        Self::try_feed(())
254    }
255
256    pub fn new(segments: Vec<storage::Shared>) -> crate::Result<Self> {
257        let scan = Scanner::new(segments, T::fields(), T::ORDER)?;
258        Ok(Self::with_scan(scan, ()))
259    }
260
261    pub fn one(segment: storage::Shared) -> crate::Result<Self> {
262        let scan = Scanner::new(iter::once(segment), T::fields(), T::ORDER)?;
263        Ok(Self::with_scan(scan, ()))
264    }
265
266    /// Creates a decoder over owners which remain tied to their driver.
267    pub fn from_scoped(segments: Vec<resident::Shared<'d>>) -> crate::Result<Self> {
268        let scan = Scanner::new(segments, T::fields(), T::ORDER)?;
269        Ok(Self::with_scan(scan, ()))
270    }
271}
272
273impl<'d, T: Decode<C>, C: Copy> Decoder<'d, T, C> {
274    fn with_scan(scan: Scanner<'d>, context: C) -> Self {
275        Self {
276            phase: slot::Storage::filled(Phase::Scan(scan), ()),
277            context,
278            bytes: 0,
279            chunks: segments::Count::EMPTY,
280            marker: marker::PhantomData,
281        }
282    }
283
284    /// Starts streaming directly into the final output representation.
285    pub fn try_feed(context: C) -> Result<Self, Create> {
286        let scan = Scanner::try_stream(T::fields(), T::ORDER)?;
287        Ok(Self::with_scan(scan, context))
288    }
289
290    /// Builds a recursive worker without reconstructing its admitted owner table.
291    /// Allocation failure leaves the input unchanged; success transfers it once.
292    #[doc(hidden)]
293    pub fn try_admitted(input: &mut Admitted<'d>, context: C) -> crate::Result<Box<Self>> {
294        let selection =
295            scanner::Selection::try_new(T::fields(), T::ORDER).map_err(crate::Error::Allocation)?;
296        let slot: Box<mem::MaybeUninit<Self>> =
297            alloc::BoxExt::try_new(mem::MaybeUninit::uninit()).map_err(crate::Error::Exhausted)?;
298        let input = Admitted {
299            input: mem::replace(&mut input.input, input::Input::empty()),
300        };
301        let scan = Scanner::admitted(input, selection);
302        Ok(Box::write(slot, Self::with_scan(scan, context)))
303    }
304
305    /// Builds directly from exact retained payload owners.
306    #[doc(hidden)]
307    pub fn try_scoped(segments: owned::Segments<'d>, context: C) -> Result<Self, Create> {
308        let scan = Scanner::try_new(segments, T::fields(), T::ORDER)?;
309        Ok(Self::with_scan(scan, context))
310    }
311
312    /// Transfers one retained transport owner into the decoder.
313    pub fn push(mut self, segment: resident::Shared<'d>) -> Transfer<Self, Push<'d>> {
314        let Some(phase) = self.phase.occupied() else {
315            return Transfer::Rejected {
316                state: self,
317                error: Push::Complete(segment),
318            };
319        };
320        let (phase, vacant) = phase.take();
321        let (phase, error) = match phase {
322            Phase::Scan(scanner) => match scanner.push(segment) {
323                Transfer::Accepted(scanner) => (Phase::Scan(scanner), None),
324                Transfer::Rejected { state, error } => (Phase::Scan(state), Some(error)),
325            },
326            phase => (phase, Some(Push::Complete(segment))),
327        };
328        vacant.insert(phase);
329        match error {
330            Some(error) => Transfer::Rejected { state: self, error },
331            None => Transfer::Accepted(self),
332        }
333    }
334
335    /// Declares that no more input segments can arrive.
336    pub fn close(mut self) -> Self {
337        if let Some(phase) = self.phase.occupied() {
338            let (phase, vacant) = phase.take();
339            vacant.insert(match phase {
340                Phase::Scan(scanner) => Phase::Scan(scanner.close()),
341                phase => phase,
342            });
343        }
344        self
345    }
346
347    /// Returns original input bytes retired by scanning or scanner-error cleanup
348    /// since the previous call. Materialization and cancellation retain the
349    /// caller's remaining transport credit until its terminal settlement.
350    pub fn take_bytes(&mut self) -> usize {
351        mem::replace(&mut self.bytes, 0)
352    }
353
354    /// Returns original transport owners retired by scanning or scanner-error
355    /// cleanup. Projected copies are never counted as new transport owners.
356    pub fn take_chunks(&mut self) -> segments::Count {
357        mem::replace(&mut self.chunks, segments::Count::EMPTY)
358    }
359
360    /// Cooperatively retires this decoder without materializing another value.
361    pub fn release(&mut self, budget: &mut usize) -> Poll<()> {
362        let Some(phase) = self.phase.occupied() else {
363            return Poll::Ready(());
364        };
365        let (phase, vacant) = phase.take();
366        let (mut release, error) = match phase {
367            Phase::Scan(scanner) => (scanner.release(), None),
368            Phase::Start(document) => (document.release(), None),
369            Phase::Build(mut state) | Phase::ReleaseBuild(mut state) => {
370                let done = Materialize::release(&mut state, budget);
371                if done.is_pending() {
372                    vacant.insert(Phase::ReleaseBuild(state));
373                }
374                return done;
375            }
376            Phase::Release { release, error } => (release, error),
377        };
378        let done = release.retire(budget);
379        if done.is_pending() {
380            vacant.insert(Phase::Release { release, error });
381        }
382        done
383    }
384
385    /// Performs bounded work while preserving whether the decoder needs
386    /// scheduling or another transport segment.
387    pub fn poll(&mut self, budget: NonZero<usize>) -> Progress<'d, T> {
388        let Some(phase) = self.phase.occupied() else {
389            return Progress::Pending;
390        };
391        let (phase, vacant) = phase.take();
392        let (state, budget, vacant) = match phase {
393            Phase::ReleaseBuild(mut state) => {
394                let mut left = budget.get();
395                if Materialize::release(&mut state, &mut left).is_pending() {
396                    vacant.insert(Phase::ReleaseBuild(state));
397                }
398                return Progress::Pending;
399            }
400            Phase::Release { mut release, error } => {
401                let mut left = budget.get();
402                let (done, released) = release.drain(&mut left);
403                // Every reported owner is a disjoint member of this decoder's
404                // admitted input, whose total length and count already fit.
405                (self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
406                if done.is_ready() {
407                    return match error {
408                        Some(error) => Progress::Failed(error),
409                        None => Progress::Pending,
410                    };
411                }
412                vacant.insert(Phase::Release { release, error });
413                return Progress::Pending;
414            }
415            Phase::Build(state) => (state, budget, vacant),
416            Phase::Start(document) => (T::start(document, self.context), budget, vacant),
417            Phase::Scan(scanner) => {
418                let (document, left, released) = {
419                    let mut prepared;
420                    let left = 'prepare: {
421                        let (mut scan, closed) = match scanner {
422                            Scanner::Open(scan) => (scan, false),
423                            Scanner::Closed(scan) => (scan, true),
424                            Scanner::Preparing(value) => {
425                                prepared = value;
426                                break 'prepare budget.get();
427                            }
428                        };
429                        let (progress, left) = match scan.step(budget, closed) {
430                            Ok(progress) => progress,
431                            Err(error) => {
432                                let released = scan.stream.drain();
433                                let release = scan.release();
434                                (self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
435                                vacant.insert(Phase::Release {
436                                    release,
437                                    error: Some(error.into()),
438                                });
439                                return Progress::Pending;
440                            }
441                        };
442                        let progress = match progress {
443                            ScanProgress::Ready => {
444                                prepared = Prepared::new(scan);
445                                break 'prepare left;
446                            }
447                            ScanProgress::NeedInput => Progress::NeedInput,
448                            ScanProgress::Pending => Progress::Pending,
449                        };
450                        let released = scan.stream.drain();
451                        vacant.insert(Phase::Scan(if closed {
452                            Scanner::Closed(scan)
453                        } else {
454                            Scanner::Open(scan)
455                        }));
456                        (self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
457                        return progress;
458                    };
459                    let progress = match prepared.step(left) {
460                        Ok(progress) => progress,
461                        Err(error) => {
462                            let released = prepared.stream.drain();
463                            let release = prepared.release();
464                            (self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
465                            vacant.insert(Phase::Release {
466                                release,
467                                error: Some(error.into()),
468                            });
469                            return Progress::Pending;
470                        }
471                    };
472                    let released = prepared.stream.drain();
473                    match progress {
474                        Poll::Ready(left) => (prepared.document, left, released),
475                        Poll::Pending => {
476                            vacant.insert(Phase::Scan(Scanner::Preparing(prepared)));
477                            (self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
478                            return Progress::Pending;
479                        }
480                    }
481                };
482                (self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
483                let Some(left) = NonZero::new(left) else {
484                    vacant.insert(Phase::Start(document));
485                    return Progress::Pending;
486                };
487                (T::start(document, self.context), left, vacant)
488            }
489        };
490        match Materialize::step(state, budget) {
491            Outcome::Ready(value) => Progress::Ready(value),
492            Outcome::Pending(state) => {
493                vacant.insert(Phase::Build(state));
494                Progress::Pending
495            }
496            Outcome::Failed(error) => Progress::Failed(error),
497        }
498    }
499}