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
204/// Resumable decoder. Input, replay, field lookup, and owner release work are
205/// charged to the caller-provided budget. Its driver brand cannot be widened even
206/// when the scanner owns every input segment.
207///
208/// Error retirement charges input owners, field slots, and array elements.
209/// Fixed-schema scalar destruction and allocation metadata are constant work;
210/// an arbitrary user-provided materializer or destructor must uphold its own
211/// cooperative work contract. Dropping a decoder cancels it immediately.
212pub struct Decoder<'d, T: Decode<C>, C: Copy = ()> {
213    phase: slot::Storage<Phase<'d, T::State<'d>>>,
214    context: C,
215    bytes: usize,
216    chunks: segments::Count,
217    marker: marker::PhantomData<fn() -> T>,
218}
219
220enum Phase<'d, S> {
221    Scan(Scanner<'d>),
222    Start(Document<'d>),
223    Build(S),
224    Release {
225        release: Release<'d>,
226        error: Option<crate::parsing::Error<'d>>,
227    },
228    ReleaseBuild(S),
229}
230
231impl<'d, T: Decode> Decoder<'d, T> {
232    /// Creates a decoder whose input is still being received.
233    pub fn try_stream() -> Result<Self, Create> {
234        Self::try_feed(())
235    }
236
237    pub fn new(segments: Vec<storage::Shared>) -> crate::Result<Self> {
238        let scan = Scanner::new(segments, T::fields(), T::ORDER)?;
239        Ok(Self::with_scan(scan, ()))
240    }
241
242    pub fn one(segment: storage::Shared) -> crate::Result<Self> {
243        let scan = Scanner::new(iter::once(segment), T::fields(), T::ORDER)?;
244        Ok(Self::with_scan(scan, ()))
245    }
246
247    /// Creates a decoder over owners which remain tied to their driver.
248    pub fn from_scoped(segments: Vec<resident::Shared<'d>>) -> crate::Result<Self> {
249        let scan = Scanner::new(segments, T::fields(), T::ORDER)?;
250        Ok(Self::with_scan(scan, ()))
251    }
252}
253
254impl<'d, T: Decode<C>, C: Copy> Decoder<'d, T, C> {
255    fn with_scan(scan: Scanner<'d>, context: C) -> Self {
256        Self {
257            phase: slot::Storage::filled(Phase::Scan(scan), ()),
258            context,
259            bytes: 0,
260            chunks: segments::Count::EMPTY,
261            marker: marker::PhantomData,
262        }
263    }
264
265    /// Starts streaming directly into the final output representation.
266    pub fn try_feed(context: C) -> Result<Self, Create> {
267        let scan = Scanner::try_stream(T::fields(), T::ORDER)?;
268        Ok(Self::with_scan(scan, context))
269    }
270
271    /// Builds a recursive worker without reconstructing its admitted owner table.
272    /// Allocation failure leaves the input unchanged; success transfers it once.
273    #[doc(hidden)]
274    pub fn try_admitted(input: &mut Admitted<'d>, context: C) -> crate::Result<Box<Self>> {
275        let selection =
276            scanner::Selection::try_new(T::fields(), T::ORDER).map_err(crate::Error::Allocation)?;
277        let slot: Box<mem::MaybeUninit<Self>> =
278            alloc::BoxExt::try_new(mem::MaybeUninit::uninit()).map_err(crate::Error::Exhausted)?;
279        let input = Admitted {
280            input: mem::replace(&mut input.input, input::Input::empty()),
281        };
282        let scan = Scanner::admitted(input, selection);
283        Ok(Box::write(slot, Self::with_scan(scan, context)))
284    }
285
286    /// Builds directly from exact retained payload owners.
287    #[doc(hidden)]
288    pub fn try_scoped(segments: owned::Segments<'d>, context: C) -> Result<Self, Create> {
289        let scan = Scanner::try_new(segments, T::fields(), T::ORDER)?;
290        Ok(Self::with_scan(scan, context))
291    }
292
293    /// Transfers one retained transport owner into the decoder.
294    pub fn push(mut self, segment: resident::Shared<'d>) -> Transfer<Self, Push<'d>> {
295        let result = match self.phase.get_mut() {
296            Some(Phase::Scan(scanner)) => scanner.push(segment),
297            _ => Err(Push::Complete(segment)),
298        };
299        match result {
300            Ok(()) => Transfer::Accepted(self),
301            Err(error) => Transfer::Rejected { state: self, error },
302        }
303    }
304
305    /// Declares that no more input segments can arrive.
306    pub fn close(mut self) -> Self {
307        if let Some(phase) = self.phase.occupied() {
308            let (phase, vacant) = phase.take();
309            vacant.insert(match phase {
310                Phase::Scan(scanner) => Phase::Scan(scanner.close()),
311                phase => phase,
312            });
313        }
314        self
315    }
316
317    /// Returns original input bytes retired by scanning or scanner-error cleanup
318    /// since the previous call. Materialization and cancellation retain the
319    /// caller's remaining transport credit until its terminal settlement.
320    pub fn take_bytes(&mut self) -> usize {
321        mem::replace(&mut self.bytes, 0)
322    }
323
324    /// Returns original transport owners retired by scanning or scanner-error
325    /// cleanup. Projected copies are never counted as new transport owners.
326    pub fn take_chunks(&mut self) -> segments::Count {
327        mem::replace(&mut self.chunks, segments::Count::EMPTY)
328    }
329
330    /// Cooperatively retires this decoder without materializing another value.
331    pub fn release(&mut self, budget: &mut usize) -> Poll<()> {
332        let Some(phase) = self.phase.occupied() else {
333            return Poll::Ready(());
334        };
335        let (phase, vacant) = phase.take();
336        let (mut release, error) = match phase {
337            Phase::Scan(scanner) => (scanner.release(), None),
338            Phase::Start(document) => (document.release(), None),
339            Phase::Build(mut state) | Phase::ReleaseBuild(mut state) => {
340                let done = Materialize::release(&mut state, budget);
341                if done.is_pending() {
342                    vacant.insert(Phase::ReleaseBuild(state));
343                }
344                return done;
345            }
346            Phase::Release { release, error } => (release, error),
347        };
348        let done = release.retire(budget);
349        if done.is_pending() {
350            vacant.insert(Phase::Release { release, error });
351        }
352        done
353    }
354
355    /// Performs bounded work while preserving whether the decoder needs
356    /// scheduling or another transport segment.
357    pub fn poll(&mut self, budget: NonZero<usize>) -> Progress<'d, T> {
358        let Some(phase) = self.phase.occupied() else {
359            return Progress::Pending;
360        };
361        let (phase, vacant) = phase.take();
362        let (state, budget, vacant) = match phase {
363            Phase::ReleaseBuild(mut state) => {
364                let mut left = budget.get();
365                if Materialize::release(&mut state, &mut left).is_pending() {
366                    vacant.insert(Phase::ReleaseBuild(state));
367                }
368                return Progress::Pending;
369            }
370            Phase::Release { mut release, error } => {
371                let mut left = budget.get();
372                let (done, released) = release.drain(&mut left);
373                // Every reported owner is a disjoint member of this decoder's
374                // admitted input, whose total length and count already fit.
375                (self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
376                if done.is_ready() {
377                    return match error {
378                        Some(error) => Progress::Failed(error),
379                        None => Progress::Pending,
380                    };
381                }
382                vacant.insert(Phase::Release { release, error });
383                return Progress::Pending;
384            }
385            Phase::Build(state) => (state, budget, vacant),
386            Phase::Start(document) => (T::start(document, self.context), budget, vacant),
387            Phase::Scan(scanner) => {
388                let (document, left, released) = {
389                    let mut prepared;
390                    let left = 'prepare: {
391                        let (mut scan, closed) = match scanner {
392                            Scanner::Open(scan) => (scan, false),
393                            Scanner::Closed(scan) => (scan, true),
394                            Scanner::Preparing(value) => {
395                                prepared = value;
396                                break 'prepare budget.get();
397                            }
398                        };
399                        let (progress, left) = match scan.step(budget, closed) {
400                            Ok(progress) => progress,
401                            Err(error) => {
402                                let released = scan.stream.drain();
403                                let release = scan.release();
404                                (self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
405                                vacant.insert(Phase::Release {
406                                    release,
407                                    error: Some(error.into()),
408                                });
409                                return Progress::Pending;
410                            }
411                        };
412                        let progress = match progress {
413                            ScanProgress::Ready => {
414                                prepared = Prepared::new(scan);
415                                break 'prepare left;
416                            }
417                            ScanProgress::NeedInput => Progress::NeedInput,
418                            ScanProgress::Pending => Progress::Pending,
419                        };
420                        let released = scan.stream.drain();
421                        vacant.insert(Phase::Scan(if closed {
422                            Scanner::Closed(scan)
423                        } else {
424                            Scanner::Open(scan)
425                        }));
426                        (self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
427                        return progress;
428                    };
429                    let progress = match prepared.step(left) {
430                        Ok(progress) => progress,
431                        Err(error) => {
432                            let released = prepared.stream.drain();
433                            let release = prepared.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 released = prepared.stream.drain();
443                    match progress {
444                        Poll::Ready(left) => (prepared.document, left, released),
445                        Poll::Pending => {
446                            vacant.insert(Phase::Scan(Scanner::Preparing(prepared)));
447                            (self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
448                            return Progress::Pending;
449                        }
450                    }
451                };
452                (self.bytes, self.chunks) = released.add(self.bytes, self.chunks);
453                let Some(left) = NonZero::new(left) else {
454                    vacant.insert(Phase::Start(document));
455                    return Progress::Pending;
456                };
457                (T::start(document, self.context), left, vacant)
458            }
459        };
460        match Materialize::step(state, budget) {
461            Outcome::Ready(value) => Progress::Ready(value),
462            Outcome::Pending(state) => {
463                vacant.insert(Phase::Build(state));
464                Progress::Pending
465            }
466            Outcome::Failed(error) => Progress::Failed(error),
467        }
468    }
469}