Skip to main content

zencodec/traits/
incremental.rs

1//! Incremental (sans-I/O) decoding: the contract between a resumable
2//! decoder and whatever holds the bytes.
3//!
4//! A decoder performs no I/O. A driver offers it bytes it already holds and
5//! steps it; each step reports how many of those bytes the decoder took and
6//! one [`Event`]. The driver owns the input buffer: bytes the decoder did not
7//! take are still the driver's, to offer again (with more appended) or to
8//! drop. Rows go straight into the driver's [`DecodeRowSink`]. The same
9//! decoder runs under a synchronous file loop, an async network task, an FFI
10//! host that pushes buffers, or a single-threaded WASM event loop.
11//!
12//! ```text
13//! let mut dec = job.dyn_incremental_decoder(&[])?;
14//! let mut input = Input::empty();
15//! loop {
16//!     let p = dec.step(input, &mut sink, Some(&pulse))?;
17//!     // drop p.consumed bytes from the front of what was offered
18//!     match p.event {
19//!         Event::Need => { /* satisfy one of dec.requested(), build the next Input */ }
20//!         Event::Done(info) => break,
21//!         _ => { /* Rows, Metadata, or a newer informational event: step again */ }
22//!     }
23//! }
24//! ```
25//!
26//! # The rules
27//!
28//! **Input.** [`Input`] is a run of bytes at an absolute `offset`, possibly
29//! empty, plus the total source length once the driver knows it. A decoder
30//! takes a prefix of the input and reports its length in
31//! [`Progress::consumed`]: the bytes it decoded in place or copied into its
32//! own state, plus any bytes before them it does not need (a gap the driver
33//! read across to batch two ranges). Consumed bytes are never offered again.
34//! A decoder never fails because it was offered bytes it did not ask for: it
35//! takes none of them, or skips them as part of the prefix.
36//!
37//! **Requests.** After [`Event::Need`], [`IncrementalDecode::requested`]
38//! lists what the decoder needs, never empty. The driver satisfies any one
39//! request and steps again:
40//! - [`InputRequest::AtLeast`]: an input starting at `offset` holding at least
41//!   `min_len` bytes, or every remaining byte with `total_len` set if the
42//!   source ends first. More is welcome.
43//! - [`InputRequest::Range`]: an input holding exactly that span (or a larger
44//!   input starting at its offset).
45//! - [`InputRequest::Len`]: any input (even empty) carrying `total_len`.
46//!
47//! A decoder never requests bytes it knows do not exist: once `total_len` is
48//! known, a request that cannot be met is a [`StepError::Codec`] error
49//! (truncated input), not another `Need`. A decoder makes progress whenever
50//! its input satisfies one of its requests; returning `Need` with nothing
51//! consumed for an input that satisfied a request is a protocol violation,
52//! and drivers report it instead of looping.
53//!
54//! **Output.** [`begin`](DecodeRowSink::begin) comes before the first rows
55//! and [`finish`](DecodeRowSink::finish) before [`Event::Done`]. Every row is
56//! written once in its final form through
57//! [`provide_next_buffer`](DecodeRowSink::provide_next_buffer), in
58//! increasing `y`. A progressive decoder may also write provisional rows
59//! (a pass a later one refines) through
60//! [`provide_pass_buffer`](DecodeRowSink::provide_pass_buffer), only when
61//! the sink's [`accepts_provisional`](DecodeRowSink::accepts_provisional)
62//! returned `true` at `begin`. A decoder with no provisional output ignores
63//! both.
64//!
65//! **Termination.** After [`Event::Done`], every step returns `Done` again
66//! with nothing consumed. After any error, every step returns
67//! [`StepError::Finished`]. `pulse` is checked at least once per step; a
68//! tripped pulse is [`StepError::Cancelled`], and terminal. [`Fused`] enforces
69//! all three for any decoder, and
70//! [`erase_incremental`](crate::decode::erase_incremental) applies it.
71//!
72//! **Events.** [`Event`] is non-exhaustive. Variants added later are
73//! informational: a driver that does not recognise one steps again. Anything
74//! that needs the driver to act is expressed as `Need` plus a [`InputRequest`].
75
76use alloc::boxed::Box;
77use core::fmt;
78
79use enough::StopReason;
80
81use super::control::{
82    BoundaryAction, BoundaryId, ControlError, ControlledIncrementalDecode, ControlledProgress,
83    DecodeBoundary, GuardedSink, InterpretationFacts,
84};
85use crate::sink::{DecodeRowSink, SinkError};
86use crate::{ImageInfo, OutputInfo};
87
88/// A byte span within an image file or stream.
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
90#[non_exhaustive]
91pub struct ByteRange {
92    /// Starting byte offset from the beginning of the stream.
93    pub offset: u64,
94    /// Number of bytes in this range.
95    pub length: u64,
96}
97
98impl ByteRange {
99    /// Create a new byte range.
100    #[inline]
101    pub const fn new(offset: u64, length: u64) -> Self {
102        Self { offset, length }
103    }
104
105    /// The ending byte offset (exclusive), saturating.
106    #[inline]
107    pub const fn end(&self) -> u64 {
108        self.offset.saturating_add(self.length)
109    }
110
111    /// Returns `true` if this range has zero length.
112    #[inline]
113    pub const fn is_empty(&self) -> bool {
114        self.length == 0
115    }
116
117    /// Returns `true` if this range contains `target_offset`.
118    #[inline]
119    pub const fn contains(&self, target_offset: u64) -> bool {
120        target_offset >= self.offset && target_offset < self.end()
121    }
122}
123
124/// What an incremental decoder needs before it can continue.
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
126#[non_exhaustive]
127pub enum InputRequest {
128    /// Bytes from `offset` on: an input starting there with at least
129    /// `min_len` bytes, or every remaining byte (with `total_len` set) when
130    /// the source ends first. More is welcome. A sequential decoder asks
131    /// this way; `min_len` is the contiguous span it needs to make progress
132    /// without copying (one worst-case row, one marker segment).
133    AtLeast {
134        /// Absolute offset of the first byte needed.
135        offset: u64,
136        /// Minimum contiguous bytes needed in one input.
137        min_len: usize,
138    },
139    /// Exactly this span.
140    Range(ByteRange),
141    /// The total source length, supplied as [`Input::total_len`].
142    Len,
143}
144
145/// Legacy ambiguous name for a byte acquisition request.
146#[deprecated(note = "use InputRequest")]
147pub type Request = InputRequest;
148
149impl InputRequest {
150    /// A request for at least `min_len` bytes from `offset`.
151    #[inline]
152    pub const fn at_least(offset: u64, min_len: usize) -> Self {
153        Self::AtLeast { offset, min_len }
154    }
155
156    /// A request for exactly `length` bytes at `offset`.
157    #[inline]
158    pub const fn range(offset: u64, length: u64) -> Self {
159        Self::Range(ByteRange::new(offset, length))
160    }
161
162    /// The offset this request starts at, if it asks for bytes.
163    #[inline]
164    pub const fn offset(&self) -> Option<u64> {
165        match self {
166            Self::AtLeast { offset, .. } => Some(*offset),
167            Self::Range(r) => Some(r.offset),
168            Self::Len => None,
169        }
170    }
171
172    /// Whether `input` satisfies this request.
173    pub fn is_satisfied_by(&self, input: &Input<'_>) -> bool {
174        match *self {
175            Self::AtLeast { offset, min_len } => {
176                input.offset == offset
177                    && (input.bytes.len() >= min_len || input.total_len == Some(input.end()))
178            }
179            Self::Range(r) => {
180                input.offset == r.offset
181                    && (input.bytes.len() as u64 >= r.length
182                        || input.total_len == Some(input.end()))
183            }
184            Self::Len => input.total_len.is_some(),
185        }
186    }
187}
188
189/// Bytes offered to one [`step`](IncrementalDecode::step).
190#[derive(Debug, Clone, Copy, PartialEq, Eq)]
191#[non_exhaustive]
192pub struct Input<'a> {
193    /// Absolute offset of `bytes[0]` in the source.
194    pub offset: u64,
195    /// The bytes. May be empty.
196    pub bytes: &'a [u8],
197    /// Total source length, once the driver knows it.
198    pub total_len: Option<u64>,
199}
200
201impl<'a> Input<'a> {
202    /// `bytes` located at `offset`, total length unknown.
203    #[inline]
204    pub const fn new(offset: u64, bytes: &'a [u8]) -> Self {
205        Self {
206            offset,
207            bytes,
208            total_len: None,
209        }
210    }
211
212    /// No bytes: step without new input.
213    #[inline]
214    pub const fn empty() -> Self {
215        Self {
216            offset: 0,
217            bytes: &[],
218            total_len: None,
219        }
220    }
221
222    /// The same input, carrying the total source length.
223    #[inline]
224    pub const fn with_total_len(mut self, total_len: u64) -> Self {
225        self.total_len = Some(total_len);
226        self
227    }
228
229    /// The same input, carrying the total source length if known.
230    #[inline]
231    pub const fn with_total_len_opt(mut self, total_len: Option<u64>) -> Self {
232        self.total_len = total_len;
233        self
234    }
235
236    /// Absolute offset one past the last byte.
237    #[inline]
238    pub const fn end(&self) -> u64 {
239        self.offset.saturating_add(self.bytes.len() as u64)
240    }
241
242    /// Whether these bytes run to the end of the source.
243    #[inline]
244    pub fn reaches_end(&self) -> bool {
245        self.total_len == Some(self.end())
246    }
247}
248
249/// Metadata update reported by [`Event::Metadata`].
250///
251/// Image containers deliver metadata across stages: the dimensions header,
252/// then colour signalling, then supplementary or trailing payloads.
253#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
254#[non_exhaustive]
255pub enum MetadataUpdate {
256    /// Dimensions and format header are available in
257    /// [`info()`](IncrementalDecode::info).
258    HeaderReady,
259    /// Colour profile or primaries (ICC / CICP) are available or refined.
260    ColorReady,
261    /// Supplementary or trailing metadata (EXIF, XMP, gain map) was
262    /// discovered or updated.
263    Updated,
264}
265
266impl MetadataUpdate {
267    /// Returns `true` if basic image dimensions and format are ready.
268    #[inline]
269    pub const fn is_header_ready(&self) -> bool {
270        matches!(self, Self::HeaderReady | Self::ColorReady | Self::Updated)
271    }
272}
273
274/// Metadata describing a batch of decoded scanlines.
275#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
276#[non_exhaustive]
277pub struct BatchMeta {
278    /// Starting row index (top row of this batch).
279    pub y: u32,
280    /// Progressive pass index (0 for baseline / non-progressive decode).
281    pub pass: u16,
282    /// Whether this batch is the final form of these scanlines.
283    pub is_final: bool,
284}
285
286impl BatchMeta {
287    /// Batch metadata with baseline defaults (`pass = 0`).
288    #[inline]
289    pub const fn new(y: u32, is_final: bool) -> Self {
290        Self {
291            y,
292            pass: 0,
293            is_final,
294        }
295    }
296
297    /// Final rows starting at `y` (`pass = 0`, `is_final = true`).
298    #[inline]
299    pub const fn final_rows(y: u32) -> Self {
300        Self::new(y, true)
301    }
302
303    /// Specify the progressive pass index.
304    #[inline]
305    pub const fn with_pass(mut self, pass: u16) -> Self {
306        self.pass = pass;
307        self
308    }
309}
310
311/// What one [`step`](IncrementalDecode::step) produced.
312#[derive(Debug, Clone, PartialEq, Eq)]
313#[non_exhaustive]
314pub enum Event {
315    /// The decoder needs bytes or the source length; see
316    /// [`requested`](IncrementalDecode::requested).
317    Need,
318    /// `rows` rows starting at `meta.y` were written into the sink.
319    Rows {
320        /// Row position, pass and finality of the rows just written.
321        meta: BatchMeta,
322        /// Number of rows written.
323        rows: u32,
324    },
325    /// Metadata became available or was refined.
326    Metadata(MetadataUpdate),
327    /// Decoding finished; the sink has been told to `finish`.
328    Done(OutputInfo),
329}
330
331impl Event {
332    /// Returns `true` for [`Event::Done`].
333    #[inline]
334    pub const fn is_done(&self) -> bool {
335        matches!(self, Self::Done(_))
336    }
337}
338
339/// The result of one [`step`](IncrementalDecode::step).
340#[derive(Debug, Clone, PartialEq, Eq)]
341#[non_exhaustive]
342pub struct Progress {
343    /// Bytes taken from the front of the offered input.
344    pub consumed: usize,
345    /// What happened.
346    pub event: Event,
347}
348
349impl Progress {
350    /// A step that took `consumed` bytes and produced `event`.
351    #[inline]
352    pub const fn new(consumed: usize, event: Event) -> Self {
353        Self { consumed, event }
354    }
355
356    /// A step that needs more input and took `consumed` bytes.
357    #[inline]
358    pub const fn need(consumed: usize) -> Self {
359        Self::new(consumed, Event::Need)
360    }
361}
362
363/// Why a [`step`](IncrementalDecode::step) failed. Every error is terminal.
364#[derive(Debug)]
365#[non_exhaustive]
366pub enum StepError<E> {
367    /// The decoder rejected the input (malformed, truncated, unsupported,
368    /// over a limit).
369    Codec(E),
370    /// The sink refused rows or failed.
371    Sink(SinkError),
372    /// The pulse token tripped.
373    Cancelled(StopReason),
374    /// The decoder was stepped again after an error.
375    Finished,
376    /// A mandatory control boundary was unhandled or violated.
377    Control(ControlError),
378}
379
380impl<E> StepError<E> {
381    /// Map the codec error, keeping the other variants.
382    pub fn map_codec<F>(self, f: impl FnOnce(E) -> F) -> StepError<F> {
383        match self {
384            Self::Codec(e) => StepError::Codec(f(e)),
385            Self::Sink(e) => StepError::Sink(e),
386            Self::Cancelled(r) => StepError::Cancelled(r),
387            Self::Finished => StepError::Finished,
388            Self::Control(e) => StepError::Control(e),
389        }
390    }
391}
392
393impl<E> From<StopReason> for StepError<E> {
394    fn from(r: StopReason) -> Self {
395        Self::Cancelled(r)
396    }
397}
398
399impl<E: fmt::Display> fmt::Display for StepError<E> {
400    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401        match self {
402            Self::Codec(e) => write!(f, "{e}"),
403            Self::Sink(e) => write!(f, "row sink: {e}"),
404            Self::Cancelled(r) => write!(f, "decode cancelled: {r:?}"),
405            Self::Finished => write!(f, "decoder stepped after it failed"),
406            Self::Control(e) => e.fmt(f),
407        }
408    }
409}
410
411impl<E: core::error::Error + 'static> core::error::Error for StepError<E> {
412    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
413        match self {
414            Self::Codec(e) => Some(e),
415            Self::Sink(e) => Some(e.as_ref()),
416            Self::Control(e) => Some(e),
417            _ => None,
418        }
419    }
420}
421
422/// A resumable decoder driven by whoever holds the bytes. See the
423/// contract in `docs/range-decoding-contract.md` for the rules.
424///
425/// The trait is object-safe: [`DynIncrementalDecoder`] is
426/// `dyn IncrementalDecode<Error = ErasedError> + Send`.
427pub trait IncrementalDecode {
428    /// The codec-specific error type.
429    type Error: core::error::Error + Send + Sync + 'static;
430
431    /// Take bytes from the front of `input` and advance by one event.
432    ///
433    /// `None` adds no per-call control. A decoder must still honor its retained
434    /// operation pulse. Forwarding adapters preserve absence; they must not
435    /// replace it with a new no-op control that overrides the retained pulse.
436    fn step(
437        &mut self,
438        input: Input<'_>,
439        sink: &mut dyn DecodeRowSink,
440        pulse: Option<&dyn crate::Pulse>,
441    ) -> Result<Progress, StepError<Self::Error>>;
442
443    /// What the decoder needs, valid after a step returned [`Event::Need`]
444    /// (never empty then). Empty otherwise.
445    fn requested(&self) -> &[InputRequest];
446
447    /// Image metadata, once the header has been parsed.
448    fn info(&self) -> Option<&ImageInfo>;
449
450    /// Take the owned details of a successfully completed decode, at most once.
451    ///
452    /// Before `Done`, after failure/cancellation, after a previous take, or when
453    /// unsupported, return `None`. A decoder returning `Some` must include all
454    /// result details produced by this configured operation, including typed
455    /// extras and source-encoding details; a header snapshot is not a substitute.
456    /// This does not certify complete container inventory or absence of PII.
457    ///
458    /// Consumers requiring owned details must reject `None`, not manufacture an
459    /// empty result. The default preserves compatibility with existing decoders.
460    fn take_details(&mut self) -> Option<crate::decode::DecodeDetails> {
461        None
462    }
463
464    /// Output pixel descriptor, once the header has been parsed.
465    fn output_descriptor(&self) -> Option<zenpixels::PixelDescriptor> {
466        None
467    }
468
469    /// How this decoder reads its input, so a driver can choose a buffering
470    /// strategy. May change once the format is known.
471    fn access_pattern(&self) -> crate::source::AccessPattern {
472        crate::source::AccessPattern::Sequential
473    }
474}
475
476/// A codec error with its type erased: the error type of
477/// [`DynIncrementalDecoder`].
478///
479/// `Box<dyn Error>` does not itself implement `Error`, so the erased
480/// decoder needs this wrapper. It displays as the inner error and reports it
481/// as its [`source`](core::error::Error::source), so cause-chain lookups
482/// (such as [`CodecErrorExt`](crate::CodecErrorExt)) reach the original.
483pub struct ErasedError(crate::traits::BoxedError);
484
485impl ErasedError {
486    /// Wrap a boxed error.
487    pub fn new(err: crate::traits::BoxedError) -> Self {
488        Self(err)
489    }
490
491    /// The boxed error.
492    pub fn into_inner(self) -> crate::traits::BoxedError {
493        self.0
494    }
495
496    /// The error, borrowed.
497    pub fn get_ref(&self) -> &(dyn core::error::Error + Send + Sync + 'static) {
498        &*self.0
499    }
500}
501
502impl fmt::Debug for ErasedError {
503    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
504        fmt::Debug::fmt(&self.0, f)
505    }
506}
507
508impl fmt::Display for ErasedError {
509    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510        fmt::Display::fmt(&self.0, f)
511    }
512}
513
514impl core::error::Error for ErasedError {
515    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
516        Some(&*self.0)
517    }
518}
519
520impl StepError<ErasedError> {
521    /// Flatten into a boxed error: a codec error unwraps to the original,
522    /// anything else is boxed as is.
523    pub fn into_boxed(self) -> crate::traits::BoxedError {
524        match self {
525            StepError::Codec(c) => c.0,
526            other => Box::new(other),
527        }
528    }
529}
530
531/// The object-safe form every erased incremental decoder takes.
532pub type DynIncrementalDecoder = dyn IncrementalDecode<Error = ErasedError> + Send;
533
534impl<D: IncrementalDecode + ?Sized> IncrementalDecode for Box<D> {
535    type Error = D::Error;
536
537    fn step(
538        &mut self,
539        input: Input<'_>,
540        sink: &mut dyn DecodeRowSink,
541        pulse: Option<&dyn crate::Pulse>,
542    ) -> Result<Progress, StepError<D::Error>> {
543        (**self).step(input, sink, pulse)
544    }
545
546    fn requested(&self) -> &[InputRequest] {
547        (**self).requested()
548    }
549
550    fn info(&self) -> Option<&ImageInfo> {
551        (**self).info()
552    }
553
554    fn take_details(&mut self) -> Option<crate::decode::DecodeDetails> {
555        (**self).take_details()
556    }
557
558    fn output_descriptor(&self) -> Option<zenpixels::PixelDescriptor> {
559        (**self).output_descriptor()
560    }
561
562    fn access_pattern(&self) -> crate::source::AccessPattern {
563        (**self).access_pattern()
564    }
565}
566
567impl<D: IncrementalDecode + ?Sized> IncrementalDecode for &mut D {
568    type Error = D::Error;
569
570    fn step(
571        &mut self,
572        input: Input<'_>,
573        sink: &mut dyn DecodeRowSink,
574        pulse: Option<&dyn crate::Pulse>,
575    ) -> Result<Progress, StepError<D::Error>> {
576        (**self).step(input, sink, pulse)
577    }
578
579    fn requested(&self) -> &[InputRequest] {
580        (**self).requested()
581    }
582
583    fn info(&self) -> Option<&ImageInfo> {
584        (**self).info()
585    }
586
587    fn take_details(&mut self) -> Option<crate::decode::DecodeDetails> {
588        (**self).take_details()
589    }
590
591    fn output_descriptor(&self) -> Option<zenpixels::PixelDescriptor> {
592        (**self).output_descriptor()
593    }
594
595    fn access_pattern(&self) -> crate::source::AccessPattern {
596        (**self).access_pattern()
597    }
598}
599
600/// Enforces the termination rules for any decoder: steps after `Done`
601/// return `Done` again, steps after an error return
602/// [`StepError::Finished`], and `pulse` is checked before every step, a
603/// tripped pulse being terminal.
604///
605/// Wrap a hand-written decoder in `Fused` and its own state machine need
606/// not track any of this.
607#[derive(Debug)]
608pub struct Fused<D> {
609    inner: D,
610    state: FusedState,
611    controlled: bool,
612    interpretation: Option<InterpretationFacts>,
613    details_taken: bool,
614}
615
616#[derive(Debug)]
617enum FusedState {
618    Running,
619    Suspended(DecodeBoundary),
620    Done(OutputInfo),
621    Failed,
622}
623
624impl<D> Fused<D> {
625    /// Wrap `inner`.
626    pub const fn new(inner: D) -> Self {
627        Self {
628            inner,
629            state: FusedState::Running,
630            controlled: false,
631            interpretation: None,
632            details_taken: false,
633        }
634    }
635
636    pub(super) fn controlled(inner: D) -> Self {
637        Self {
638            controlled: true,
639            ..Self::new(inner)
640        }
641    }
642
643    /// The wrapped decoder.
644    pub fn get_ref(&self) -> &D {
645        &self.inner
646    }
647
648    /// Unwrap.
649    pub fn into_inner(self) -> D {
650        self.inner
651    }
652}
653
654impl<D: IncrementalDecode> IncrementalDecode for Fused<D> {
655    type Error = D::Error;
656
657    fn step(
658        &mut self,
659        input: Input<'_>,
660        sink: &mut dyn DecodeRowSink,
661        pulse: Option<&dyn crate::Pulse>,
662    ) -> Result<Progress, StepError<D::Error>> {
663        match &self.state {
664            FusedState::Done(info) => return Ok(Progress::new(0, Event::Done(info.clone()))),
665            FusedState::Failed => return Err(StepError::Finished),
666            FusedState::Suspended(_) => {
667                self.state = FusedState::Failed;
668                return Err(StepError::Control(ControlError::UnhandledBoundary));
669            }
670            FusedState::Running => {}
671        }
672        if self.controlled {
673            self.state = FusedState::Failed;
674            return Err(StepError::Control(ControlError::UnhandledBoundary));
675        }
676        if let Err(reason) = pulse.unwrap_or(&crate::NoPulse).check() {
677            self.state = FusedState::Failed;
678            return Err(StepError::Cancelled(reason));
679        }
680        self.state = FusedState::Failed;
681        match self.inner.step(input, sink, pulse) {
682            Ok(p) => {
683                if let Event::Done(info) = &p.event {
684                    self.state = FusedState::Done(info.clone());
685                } else {
686                    self.state = FusedState::Running;
687                }
688                Ok(p)
689            }
690            Err(e) => {
691                self.state = FusedState::Failed;
692                Err(e)
693            }
694        }
695    }
696
697    fn requested(&self) -> &[InputRequest] {
698        match self.state {
699            FusedState::Running => self.inner.requested(),
700            _ => &[],
701        }
702    }
703
704    fn info(&self) -> Option<&ImageInfo> {
705        self.inner.info()
706    }
707
708    fn take_details(&mut self) -> Option<crate::decode::DecodeDetails> {
709        if !matches!(self.state, FusedState::Done(_)) || self.details_taken {
710            return None;
711        }
712        // Mark before delegation, including a native panic or unavailable result.
713        self.details_taken = true;
714        self.inner.take_details()
715    }
716
717    fn output_descriptor(&self) -> Option<zenpixels::PixelDescriptor> {
718        self.inner.output_descriptor()
719    }
720
721    fn access_pattern(&self) -> crate::source::AccessPattern {
722        self.inner.access_pattern()
723    }
724}
725
726impl<D: ControlledIncrementalDecode> ControlledIncrementalDecode for Fused<D> {
727    fn step_controlled(
728        &mut self,
729        input: Input<'_>,
730        sink: &mut dyn DecodeRowSink,
731        pulse: Option<&dyn crate::Pulse>,
732    ) -> Result<ControlledProgress, StepError<D::Error>> {
733        self.controlled = true;
734        match &self.state {
735            FusedState::Done(info) => return Ok(Progress::new(0, Event::Done(info.clone())).into()),
736            FusedState::Failed => return Err(StepError::Finished),
737            _ => {}
738        }
739        if let Err(reason) = pulse.unwrap_or(&crate::NoPulse).check() {
740            self.state = FusedState::Failed;
741            return Err(StepError::Cancelled(reason));
742        }
743        if let FusedState::Suspended(boundary) = self.state {
744            return Ok(ControlledProgress::Suspended {
745                consumed: 0,
746                boundary,
747            });
748        }
749        // Caught unwinds cannot resume a half-mutated native decoder.
750        self.state = FusedState::Failed;
751        let mut guarded = GuardedSink::new(sink, self.interpretation);
752        let result = self.inner.step_controlled(input, &mut guarded, pulse);
753        if let Some(error) = guarded.refused {
754            return Err(StepError::Control(error));
755        }
756        if let Some(error) = guarded.take_sink_error() {
757            return Err(StepError::Sink(error));
758        }
759        let progress = result?;
760        if self.interpretation.is_none()
761            && matches!(
762                &progress,
763                ControlledProgress::Decode(Progress {
764                    event: Event::Done(_) | Event::Rows { .. },
765                    ..
766                })
767            )
768        {
769            return Err(StepError::Control(ControlError::OutputBeforeInterpretation));
770        }
771        if let ControlledProgress::Suspended { boundary, .. } = &progress
772            && self
773                .interpretation
774                .is_some_and(|facts| facts != boundary.facts())
775        {
776            return Err(StepError::Control(ControlError::FinalInterpretationChanged));
777        }
778        self.state = match &progress {
779            ControlledProgress::Decode(Progress {
780                event: Event::Done(info),
781                ..
782            }) => {
783                if self.interpretation.is_some_and(|facts| {
784                    facts.width != info.width
785                        || facts.height != info.height
786                        || facts.descriptor != Some(info.native_format)
787                }) {
788                    return Err(StepError::Control(ControlError::InconsistentFacts));
789                }
790                FusedState::Done(info.clone())
791            }
792            ControlledProgress::Suspended { boundary, .. } => FusedState::Suspended(*boundary),
793            _ => FusedState::Running,
794        };
795        Ok(progress)
796    }
797
798    fn resume(
799        &mut self,
800        id: BoundaryId,
801        action: BoundaryAction,
802        pulse: Option<&dyn crate::Pulse>,
803    ) -> Result<(), StepError<D::Error>> {
804        let state = core::mem::replace(&mut self.state, FusedState::Failed);
805        let FusedState::Suspended(boundary) = state else {
806            return Err(StepError::Finished);
807        };
808        pulse.unwrap_or(&crate::NoPulse).check()?;
809        if boundary.id() != id || !action.permits(boundary.facts()) {
810            return Err(StepError::Control(ControlError::InvalidReply));
811        }
812        self.inner.resume(id, action, pulse)?;
813        if action == BoundaryAction::AdoptInterpretation {
814            self.interpretation = Some(boundary.facts());
815        }
816        self.state = FusedState::Running;
817        Ok(())
818    }
819}
820
821/// Shim erasing a decoder's error type to [`ErasedError`].
822pub struct IncrementalDecoderShim<D>(pub D);
823
824impl<D: IncrementalDecode> IncrementalDecode for IncrementalDecoderShim<D> {
825    type Error = ErasedError;
826
827    fn step(
828        &mut self,
829        input: Input<'_>,
830        sink: &mut dyn DecodeRowSink,
831        pulse: Option<&dyn crate::Pulse>,
832    ) -> Result<Progress, StepError<ErasedError>> {
833        self.0
834            .step(input, sink, pulse)
835            .map_err(|e| e.map_codec(|c| ErasedError(Box::new(c))))
836    }
837
838    fn requested(&self) -> &[InputRequest] {
839        self.0.requested()
840    }
841
842    fn info(&self) -> Option<&ImageInfo> {
843        self.0.info()
844    }
845
846    fn take_details(&mut self) -> Option<crate::decode::DecodeDetails> {
847        self.0.take_details()
848    }
849
850    fn output_descriptor(&self) -> Option<zenpixels::PixelDescriptor> {
851        self.0.output_descriptor()
852    }
853
854    fn access_pattern(&self) -> crate::source::AccessPattern {
855        self.0.access_pattern()
856    }
857}
858
859/// Erase `decoder` for [`DecodeJob::dyn_incremental_decoder`](crate::decode::DecodeJob::dyn_incremental_decoder),
860/// wrapped in [`Fused`] so the termination rules hold by construction.
861pub fn erase_incremental<D>(decoder: D) -> Box<DynIncrementalDecoder>
862where
863    D: IncrementalDecode + Send + 'static,
864{
865    Box::new(IncrementalDecoderShim(Fused::new(decoder)))
866}
867
868#[cfg(test)]
869mod tests {
870    use super::*;
871    use zenpixels::{PixelDescriptor, PixelSliceMut};
872
873    struct NullSink;
874    impl DecodeRowSink for NullSink {
875        fn provide_next_buffer(
876            &mut self,
877            _y: u32,
878            _h: u32,
879            _w: u32,
880            _d: PixelDescriptor,
881        ) -> Result<PixelSliceMut<'_>, SinkError> {
882            Err("null sink".into())
883        }
884    }
885
886    /// Fails on its second step, then would happily continue.
887    struct Flaky(u32);
888    impl IncrementalDecode for Flaky {
889        type Error = core::fmt::Error;
890        fn step(
891            &mut self,
892            _input: Input<'_>,
893            _sink: &mut dyn DecodeRowSink,
894            _pulse: Option<&dyn crate::Pulse>,
895        ) -> Result<Progress, StepError<core::fmt::Error>> {
896            self.0 += 1;
897            if self.0 == 2 {
898                Err(StepError::Codec(core::fmt::Error))
899            } else {
900                Ok(Progress::need(0))
901            }
902        }
903        fn requested(&self) -> &[InputRequest] {
904            &[InputRequest::Len]
905        }
906        fn info(&self) -> Option<&ImageInfo> {
907            None
908        }
909    }
910
911    #[test]
912    fn fused_makes_errors_and_cancellation_terminal() {
913        let mut d = Fused::new(Flaky(0));
914        let pulse = &crate::how_far::NoPulse;
915        assert!(d.step(Input::empty(), &mut NullSink, Some(&pulse)).is_ok());
916        assert!(matches!(
917            d.step(Input::empty(), &mut NullSink, Some(&pulse)),
918            Err(StepError::Codec(_))
919        ));
920        assert!(matches!(
921            d.step(Input::empty(), &mut NullSink, Some(&pulse)),
922            Err(StepError::Finished)
923        ));
924        assert!(d.requested().is_empty());
925
926        struct Tripped;
927        impl enough::Stop for Tripped {
928            fn check(&self) -> Result<(), StopReason> {
929                Err(StopReason::Cancelled)
930            }
931        }
932        let mut d = Fused::new(Flaky(0));
933        assert!(matches!(
934            d.step(
935                Input::empty(),
936                &mut NullSink,
937                Some(&how_far::StopOnly::borrowed(&Tripped))
938            ),
939            Err(StepError::Cancelled(StopReason::Cancelled))
940        ));
941        assert!(matches!(
942            d.step(Input::empty(), &mut NullSink, Some(&pulse)),
943            Err(StepError::Finished)
944        ));
945    }
946
947    struct DetailSource {
948        calls: alloc::sync::Arc<core::sync::atomic::AtomicUsize>,
949        fail: bool,
950    }
951    impl IncrementalDecode for DetailSource {
952        type Error = core::fmt::Error;
953        fn step(
954            &mut self,
955            _: Input<'_>,
956            _: &mut dyn DecodeRowSink,
957            _: Option<&dyn crate::Pulse>,
958        ) -> Result<Progress, StepError<Self::Error>> {
959            if self.fail {
960                return Err(StepError::Codec(core::fmt::Error));
961            }
962            Ok(Progress::new(
963                0,
964                Event::Done(OutputInfo::full_decode(1, 1, PixelDescriptor::GRAY8_SRGB)),
965            ))
966        }
967        fn requested(&self) -> &[InputRequest] {
968            &[]
969        }
970        fn info(&self) -> Option<&ImageInfo> {
971            None
972        }
973        // Deliberately eager and repeatable: Fused must enforce the protocol.
974        fn take_details(&mut self) -> Option<crate::decode::DecodeDetails> {
975            self.calls
976                .fetch_add(1, core::sync::atomic::Ordering::SeqCst);
977            Some(crate::decode::DecodeDetails::new(ImageInfo::new(
978                1,
979                1,
980                crate::ImageFormat::Jpeg,
981            )))
982        }
983    }
984
985    #[test]
986    fn fused_details_require_success_and_allow_one_take_through_erasure() {
987        use core::sync::atomic::{AtomicUsize, Ordering};
988        let calls = alloc::sync::Arc::new(AtomicUsize::new(0));
989        let mut decoder = erase_incremental(DetailSource {
990            calls: calls.clone(),
991            fail: false,
992        });
993        assert!(decoder.take_details().is_none());
994        assert_eq!(calls.load(Ordering::SeqCst), 0);
995        decoder.step(Input::empty(), &mut NullSink, None).unwrap();
996        // Exercise Box forwarding, &mut forwarding, erasure and Fused together.
997        let mut borrowed = &mut decoder;
998        assert_eq!(
999            IncrementalDecode::take_details(&mut borrowed)
1000                .unwrap()
1001                .info
1002                .width,
1003            1
1004        );
1005        assert!(decoder.take_details().is_none());
1006        decoder.step(Input::empty(), &mut NullSink, None).unwrap();
1007        assert!(decoder.take_details().is_none());
1008        assert_eq!(calls.load(Ordering::SeqCst), 1);
1009    }
1010
1011    #[test]
1012    fn fused_details_never_call_native_after_failure_or_cancellation() {
1013        use core::sync::atomic::{AtomicUsize, Ordering};
1014        struct Cancel;
1015        impl enough::Stop for Cancel {
1016            fn check(&self) -> Result<(), StopReason> {
1017                Err(StopReason::Cancelled)
1018            }
1019        }
1020        for cancel in [false, true] {
1021            let calls = alloc::sync::Arc::new(AtomicUsize::new(0));
1022            let mut decoder = erase_incremental(DetailSource {
1023                calls: calls.clone(),
1024                fail: !cancel,
1025            });
1026            let pulse = how_far::StopOnly::borrowed(&Cancel);
1027            assert!(
1028                decoder
1029                    .step(
1030                        Input::empty(),
1031                        &mut NullSink,
1032                        cancel.then_some(&pulse as &dyn crate::Pulse)
1033                    )
1034                    .is_err()
1035            );
1036            assert!(decoder.take_details().is_none());
1037            assert_eq!(calls.load(Ordering::SeqCst), 0);
1038        }
1039    }
1040
1041    #[test]
1042    fn requests_know_what_satisfies_them() {
1043        let bytes = [0u8; 10];
1044        let r = InputRequest::at_least(4, 8);
1045        assert!(!r.is_satisfied_by(&Input::new(4, &bytes[..6])));
1046        assert!(r.is_satisfied_by(&Input::new(4, &bytes[..8])));
1047        assert!(r.is_satisfied_by(&Input::new(4, &bytes[..6]).with_total_len(10)));
1048        assert!(!r.is_satisfied_by(&Input::new(3, &bytes)));
1049        let r = InputRequest::range(2, 3);
1050        assert!(r.is_satisfied_by(&Input::new(2, &bytes[..3])));
1051        assert!(!r.is_satisfied_by(&Input::new(2, &bytes[..2])));
1052        assert!(InputRequest::Len.is_satisfied_by(&Input::empty().with_total_len(10)));
1053        assert!(!InputRequest::Len.is_satisfied_by(&Input::empty()));
1054    }
1055}
1056
1057#[cfg(test)]
1058mod optional_pulse_tests;