1use alloc::boxed::Box;
4use alloc::sync::Arc;
5use alloc::vec::Vec;
6use core::future::Future;
7use core::marker::PhantomData;
8use core::pin::Pin;
9use core::task::{Context, Poll, Waker};
10
11use zencodec::ImageInfo;
12use zencodec::decode::{
13 BoundaryAction, BoundaryId, ControlError, ControlledIncrementalDecode, ControlledProgress,
14 DecodeBoundary, DecodeRowSink, Event, IncrementalDecode, Input, InputRequest, MetadataUpdate,
15 OutputInfo, Progress, SinkError, StepError,
16};
17use zencodec::source::AccessPattern;
18use zenpixels::PixelDescriptor;
19
20use crate::context::{CoroutineContext, CoroutineError, Shared, SharedCell};
21
22pub const DEFAULT_INPUT_CAP: usize = 16 * 1024 * 1024;
24
25pub type BoxFuture<E> = Pin<Box<dyn Future<Output = Result<OutputInfo, E>> + Send + 'static>>;
28
29pub type BoxedCoroutineDecoder<E> = CoroutineDecoder<BoxFuture<E>, E>;
32
33enum State {
34 Running,
35 Finishing(OutputInfo),
39 Done(OutputInfo),
40 Failed,
41}
42
43pub struct CoroutineDecoder<F, E> {
50 shared: SharedCell,
51 future: Pin<Box<F>>,
52 state: State,
53 begun: bool,
54 accepts_provisional: bool,
57 access: AccessPattern,
58 need: Vec<InputRequest>,
59 cached_info: Option<ImageInfo>,
60 details: Option<zencodec::decode::DecodeDetails>,
61 cached_info_generation: u64,
62 pending_rows: Option<(crate::context::PendingRows, u32)>,
66 _err: PhantomData<fn() -> E>,
67}
68
69struct DetailsCleanup<'a, F, E>(&'a mut CoroutineDecoder<F, E>);
71impl<F, E> Drop for DetailsCleanup<'_, F, E> {
72 fn drop(&mut self) {
73 if matches!(self.0.state, State::Failed) {
74 let pending = self.0.shared.lock().details.take();
75 drop(pending);
76 self.0.details = None;
77 }
78 }
79}
80
81impl<F, E> CoroutineDecoder<F, E>
82where
83 F: Future<Output = Result<OutputInfo, E>> + Send + 'static,
84 E: core::error::Error + Send + Sync + From<CoroutineError> + 'static,
85{
86 pub fn new(init: impl FnOnce(CoroutineContext) -> F) -> Self {
89 Self::with_options(DEFAULT_INPUT_CAP, AccessPattern::RandomAccess, init)
90 }
91
92 pub fn controlled(
97 input_cap: usize,
98 access: AccessPattern,
99 init: impl FnOnce(CoroutineContext) -> F,
100 ) -> Self {
101 let decoder = Self::with_options(input_cap, access, init);
102 decoder.shared.lock().controlled = true;
103 decoder
104 }
105
106 pub fn controlled_with_storage(
113 input_cap: usize,
114 access: AccessPattern,
115 storage: zencodec::storage::StorageBudget,
116 init: impl FnOnce(CoroutineContext) -> F,
117 ) -> Result<Self, CoroutineError> {
118 let mut shared = Shared::with_storage(input_cap, storage)?;
119 shared.controlled = true;
120 Ok(Self::from_shared(shared, access, init))
121 }
122
123 pub fn with_options(
126 input_cap: usize,
127 access: AccessPattern,
128 init: impl FnOnce(CoroutineContext) -> F,
129 ) -> Self {
130 Self::from_shared(Shared::new(input_cap), access, init)
131 }
132
133 fn from_shared(
134 state: Shared,
135 access: AccessPattern,
136 init: impl FnOnce(CoroutineContext) -> F,
137 ) -> Self {
138 let shared = Arc::new(spin::Mutex::new(state));
139 let future = Box::pin(init(CoroutineContext::new(Arc::clone(&shared))));
140 Self {
141 shared,
142 future,
143 state: State::Running,
144 begun: false,
145 accepts_provisional: false,
146 access,
147 need: Vec::new(),
148 cached_info: None,
149 details: None,
150 cached_info_generation: 0,
151 pending_rows: None,
152 _err: PhantomData,
153 }
154 }
155
156 pub fn retained_input(&self) -> usize {
158 self.shared.lock().input.retained()
159 }
160}
161
162impl<E> CoroutineDecoder<BoxFuture<E>, E>
163where
164 E: core::error::Error + Send + Sync + From<CoroutineError> + 'static,
165{
166 pub fn boxed<Fut>(init: impl FnOnce(CoroutineContext) -> Fut) -> Self
169 where
170 Fut: Future<Output = Result<OutputInfo, E>> + Send + 'static,
171 {
172 Self::new(|ctx| Box::pin(init(ctx)) as BoxFuture<E>)
173 }
174
175 pub fn boxed_with_options<Fut>(
177 input_cap: usize,
178 access: AccessPattern,
179 init: impl FnOnce(CoroutineContext) -> Fut,
180 ) -> Self
181 where
182 Fut: Future<Output = Result<OutputInfo, E>> + Send + 'static,
183 {
184 Self::with_options(input_cap, access, |ctx| Box::pin(init(ctx)) as BoxFuture<E>)
185 }
186}
187
188impl<F, E> CoroutineDecoder<F, E>
189where
190 F: Future<Output = Result<OutputInfo, E>> + Send + 'static,
191 E: core::error::Error + Send + Sync + From<CoroutineError> + 'static,
192{
193 fn absorb(
197 shared: &mut Shared,
198 input: &Input<'_>,
199 from: usize,
200 ) -> Result<usize, CoroutineError> {
201 if let Some(t) = input.total_len {
202 shared.input.set_total(t);
203 }
204 let front = input
205 .offset
206 .checked_add(from as u64)
207 .ok_or(CoroutineError::InputRangeOverflow)?;
208 input
209 .offset
210 .checked_add(input.bytes.len() as u64)
211 .ok_or(CoroutineError::InputRangeOverflow)?;
212 let end = input.end();
213 if front >= end {
214 return Ok(from);
215 }
216 let mut taken_to = front;
217 let needs = core::mem::take(&mut shared.need);
218 for r in &needs {
219 let (lo, hi, sequential) = match *r {
220 InputRequest::AtLeast { offset, .. } => (offset, end, true),
221 InputRequest::Range(range) => (range.offset, range.end(), false),
222 _ => continue,
223 };
224 let lo = lo.max(front);
225 let hi = hi.min(end);
226 if lo >= hi {
227 continue;
228 }
229 let slice = &input.bytes[(lo - input.offset) as usize..(hi - input.offset) as usize];
230 let stored = shared.input.feed(lo, slice, false)?;
231 let reached = lo + stored as u64;
232 if stored == slice.len() || sequential {
233 taken_to = taken_to.max(reached);
234 } else {
235 taken_to = taken_to.max(reached);
237 break;
238 }
239 }
240 shared.need = needs;
241 Ok((taken_to - input.offset) as usize)
242 }
243
244 fn begin_if_ready(&mut self, sink: &mut dyn DecodeRowSink) -> Result<(), StepError<E>> {
245 self.check_interpretation()?;
246 if self.begun {
247 return Ok(());
248 }
249 let (dims, desc) = {
250 let shared = self.shared.lock();
251 (
252 shared.info.as_ref().map(|i| (i.width, i.height)),
253 shared.descriptor,
254 )
255 };
256 if let (Some((w, h)), Some(desc)) = (dims, desc) {
257 sink.begin(w, h, desc).map_err(StepError::Sink)?;
258 let accepts = sink.accepts_provisional();
259 self.shared.lock().accepts_provisional = accepts;
260 self.accepts_provisional = accepts;
261 self.begun = true;
262 }
263 Ok(())
264 }
265
266 fn check_interpretation(&self) -> Result<(), StepError<E>> {
267 let shared = self.shared.lock();
268 if let Some(error) = shared.control_failure {
269 return Err(StepError::Control(error));
270 }
271 if shared.controlled && (shared.boundary.is_some() || shared.adopted.is_none()) {
272 return Err(StepError::Control(ControlError::OutputBeforeInterpretation));
273 }
274 Ok(())
275 }
276}
277
278impl<F, E> CoroutineDecoder<F, E>
279where
280 F: Future<Output = Result<OutputInfo, E>> + Send + 'static,
281 E: core::error::Error + Send + Sync + From<CoroutineError> + 'static,
282{
283 fn with_details_cleanup<T>(&mut self, work: impl FnOnce(&mut Self) -> T) -> T {
284 let guard = DetailsCleanup(self);
285 work(&mut *guard.0)
286 }
287
288 fn advance(
289 &mut self,
290 input: Input<'_>,
291 sink: &mut dyn DecodeRowSink,
292 pulse: &dyn zencodec::Pulse,
293 ) -> Result<ControlledProgress, StepError<E>> {
294 match &self.state {
295 State::Done(info) => return Ok(Progress::new(0, Event::Done(info.clone())).into()),
296 State::Failed => return Err(StepError::Finished),
297 State::Running | State::Finishing(_) => {}
298 }
299 if let Err(reason) = pulse.check() {
300 self.pending_rows = None;
301 self.shared.lock().cancelled = Some(reason);
302 self.state = State::Failed;
303 return Err(StepError::Cancelled(reason));
304 }
305 if let Some(boundary) = self.shared.lock().boundary {
306 return Ok(ControlledProgress::Suspended {
307 consumed: 0,
308 boundary,
309 });
310 }
311 if let Some((rows, offset)) = self.pending_rows.take() {
312 return self.deliver_rows(rows, offset, 0, sink);
313 }
314 if let State::Finishing(info) = &self.state {
315 return self.finish(sink, info.clone(), 0).map(Into::into);
316 }
317 self.state = State::Failed;
318 let mut consumed = Self::absorb(&mut self.shared.lock(), &input, 0)
319 .map_err(|e| StepError::Codec(E::from(e)))?;
320 self.state = State::Running;
321 let waker = Waker::noop();
322 let mut cx = Context::from_waker(waker);
323 loop {
324 self.state = State::Failed;
326 let polled = self.future.as_mut().poll(&mut cx);
327 self.state = State::Running;
328 if let Err(reason) = pulse.check() {
329 self.shared.lock().cancelled = Some(reason);
330 self.state = State::Failed;
331 return Err(StepError::Cancelled(reason));
332 }
333 let event = {
334 let mut shared = self.shared.lock();
335 if shared.info_generation != self.cached_info_generation {
336 self.cached_info = shared.info.clone();
337 self.cached_info_generation = shared.info_generation;
338 }
339 if let Some(error) = shared.control_failure {
340 self.state = State::Failed;
341 return Err(StepError::Control(error));
342 }
343 if let Some(boundary) = shared.boundary {
344 if shared.rows.is_some() {
345 self.state = State::Failed;
346 return Err(StepError::Control(ControlError::OutputBeforeInterpretation));
347 }
348 Pending::Boundary(boundary)
349 } else if let Some(rows) = shared.rows.take() {
350 Pending::Rows(rows)
351 } else if let Some(update) = shared.metadata.take() {
352 Pending::Metadata(update)
353 } else if !shared.need.is_empty() {
354 Pending::Need
355 } else {
356 Pending::Stalled
357 }
358 };
359 match polled {
360 Poll::Ready(Ok(info)) => {
361 if matches!(event, Pending::Boundary(_)) {
362 self.state = State::Failed;
363 return Err(StepError::Control(ControlError::InconsistentFacts));
364 }
365 if matches!(event, Pending::Rows(_) | Pending::Metadata(_)) {
366 self.state = State::Finishing(info);
369 self.need.clear();
370 } else {
371 return self.finish(sink, info, consumed).map(Into::into);
372 }
373 }
374 Poll::Ready(Err(e)) => {
375 self.state = State::Failed;
376 self.need.clear();
377 return Err(StepError::Codec(e));
378 }
379 Poll::Pending => {}
380 }
381 match event {
382 Pending::Boundary(boundary) => {
383 self.need.clear();
384 return Ok(ControlledProgress::Suspended { consumed, boundary });
385 }
386 Pending::Rows(rows) => {
387 let inconsistent = {
388 let shared = self.shared.lock();
389 shared.controlled
390 && (shared.descriptor != Some(rows.descriptor)
391 || shared
392 .info
393 .as_ref()
394 .is_none_or(|info| info.width != rows.width))
395 };
396 if inconsistent {
397 self.state = State::Failed;
398 return Err(StepError::Control(ControlError::InconsistentFacts));
399 }
400 if let Err(e) = self.begin_if_ready(sink) {
401 self.state = State::Failed;
402 return Err(e);
403 }
404 if !self.begun {
405 self.state = State::Failed;
406 return Err(StepError::Codec(E::from(CoroutineError::RowsBeforeHeader)));
407 }
408 return self.deliver_rows(rows, 0, consumed, sink);
409 }
410 Pending::Metadata(update) => {
411 let controlled = self.shared.lock().controlled;
412 if !controlled
413 && update.is_header_ready()
414 && let Err(e) = self.begin_if_ready(sink)
415 {
416 self.state = State::Failed;
417 return Err(e);
418 }
419 return Ok(Progress::new(consumed, Event::Metadata(update)).into());
420 }
421 Pending::Need => {
422 let mut shared = self.shared.lock();
426 self.state = State::Failed;
427 let more = Self::absorb(&mut shared, &input, consumed)
428 .map_err(|e| StepError::Codec(E::from(e)))?;
429 self.state = State::Running;
430 if more > consumed {
431 consumed = more;
432 continue;
433 }
434 self.need.clear();
435 self.need.extend_from_slice(&shared.need);
436 return Ok(Progress::need(consumed).into());
437 }
438 Pending::Stalled => {
439 self.state = State::Failed;
440 return Err(StepError::Codec(E::from(CoroutineError::Stalled)));
441 }
442 }
443 }
444 }
445}
446
447impl<F, E> IncrementalDecode for CoroutineDecoder<F, E>
448where
449 F: Future<Output = Result<OutputInfo, E>> + Send + 'static,
450 E: core::error::Error + Send + Sync + From<CoroutineError> + 'static,
451{
452 type Error = E;
453
454 fn step(
455 &mut self,
456 input: Input<'_>,
457 sink: &mut dyn DecodeRowSink,
458 pulse: Option<&dyn zencodec::Pulse>,
459 ) -> Result<Progress, StepError<E>> {
460 self.with_details_cleanup(|this| {
461 let pulse = pulse.unwrap_or(&zencodec::NoPulse);
462 if matches!(this.state, State::Failed) {
463 return Err(StepError::Finished);
464 }
465 if this.shared.lock().controlled {
466 this.state = State::Failed;
467 return Err(StepError::Control(ControlError::UnhandledBoundary));
468 }
469 match this.advance(input, sink, pulse)? {
470 ControlledProgress::Decode(progress) => Ok(progress),
471 ControlledProgress::Suspended { .. } => {
472 this.state = State::Failed;
473 Err(StepError::Control(ControlError::UnhandledBoundary))
474 }
475 }
476 })
477 }
478
479 fn requested(&self) -> &[InputRequest] {
480 match self.state {
481 State::Running => &self.need,
482 _ => &[],
483 }
484 }
485
486 fn info(&self) -> Option<&ImageInfo> {
487 self.details
488 .as_ref()
489 .map(|d| &d.info)
490 .or(self.cached_info.as_ref())
491 }
492
493 fn take_details(&mut self) -> Option<zencodec::decode::DecodeDetails> {
494 if !matches!(self.state, State::Done(_)) {
495 return None;
496 }
497 let details = self.details.take();
498 if details.is_some() {
499 self.cached_info = None;
500 }
501 details
502 }
503
504 fn output_descriptor(&self) -> Option<PixelDescriptor> {
505 self.shared.lock().descriptor
506 }
507
508 fn access_pattern(&self) -> AccessPattern {
509 self.access
510 }
511}
512
513impl<F, E> ControlledIncrementalDecode for CoroutineDecoder<F, E>
514where
515 F: Future<Output = Result<OutputInfo, E>> + Send + 'static,
516 E: core::error::Error + Send + Sync + From<CoroutineError> + 'static,
517{
518 fn step_controlled(
519 &mut self,
520 input: Input<'_>,
521 sink: &mut dyn DecodeRowSink,
522 pulse: Option<&dyn zencodec::Pulse>,
523 ) -> Result<ControlledProgress, StepError<E>> {
524 self.with_details_cleanup(|this| {
525 let pulse = pulse.unwrap_or(&zencodec::NoPulse);
526 if matches!(this.state, State::Failed) {
527 return Err(StepError::Finished);
528 }
529 if !this.shared.lock().controlled {
530 this.state = State::Failed;
531 return Err(StepError::Control(ControlError::NotControlled));
532 }
533 this.advance(input, sink, pulse)
534 })
535 }
536
537 fn resume(
538 &mut self,
539 id: BoundaryId,
540 action: BoundaryAction,
541 pulse: Option<&dyn zencodec::Pulse>,
542 ) -> Result<(), StepError<E>> {
543 self.with_details_cleanup(|this| {
544 let pulse = pulse.unwrap_or(&zencodec::NoPulse);
545 let state = core::mem::replace(&mut this.state, State::Failed);
546 if !matches!(state, State::Running) {
547 return Err(StepError::Finished);
548 }
549 pulse.check()?;
550 let mut shared = this.shared.lock();
551 if !shared.controlled {
552 return Err(StepError::Control(ControlError::NotControlled));
553 }
554 if let Some(error) = shared.control_failure {
555 return Err(StepError::Control(error));
556 }
557 let Some(boundary) = shared.boundary.take() else {
558 return Err(StepError::Control(ControlError::InvalidReply));
559 };
560 if boundary.id() != id || !action.permits(boundary.facts()) || shared.reply.is_some() {
561 return Err(StepError::Control(ControlError::InvalidReply));
562 }
563 let facts = boundary.facts();
564 if facts.descriptor != shared.descriptor
565 || shared
566 .info
567 .as_ref()
568 .is_none_or(|info| info.width != facts.width || info.height != facts.height)
569 {
570 return Err(StepError::Control(ControlError::InconsistentFacts));
571 }
572 if action == BoundaryAction::AdoptInterpretation {
573 shared.adopted = Some(facts);
574 }
575 shared.reply = Some((id, action));
576 this.state = State::Running;
577 Ok(())
578 })
579 }
580}
581
582enum Pending {
583 Boundary(DecodeBoundary),
584 Rows(crate::context::PendingRows),
585 Metadata(MetadataUpdate),
586 Need,
587 Stalled,
588}
589
590fn copy_rows(
596 sink: &mut dyn DecodeRowSink,
597 rows: &crate::context::PendingRows,
598 offset: u32,
599 count: u32,
600 accepts_provisional: bool,
601) -> Result<(), SinkError> {
602 if !rows.meta.is_final && !accepts_provisional {
603 return Err("decoder wrote provisional rows to a sink that does not accept them".into());
604 }
605 let pixels = zenpixels::PixelSlice::new(
606 rows.data.as_slice(),
607 rows.width,
608 rows.rows,
609 rows.stride,
610 rows.descriptor,
611 )?;
612 rows.meta
613 .y
614 .checked_add(rows.rows)
615 .ok_or_else(|| SinkError::from("copied rows exceed the row coordinate range"))?;
616 if count == 0 {
617 return Ok(());
618 }
619 let mut meta = rows.meta;
620 meta.y += offset;
621 let mut destination = if meta.is_final {
622 sink.provide_next_buffer(meta.y, count, rows.width, rows.descriptor)?
623 } else {
624 sink.provide_pass_buffer(meta, count, rows.width, rows.descriptor)?
625 };
626 if destination.width() != rows.width
627 || destination.rows() != count
628 || destination.descriptor() != rows.descriptor
629 {
630 return Err("sink returned a buffer with a different geometry or descriptor".into());
631 }
632 for row in 0..count {
633 destination
634 .row_mut(row)
635 .copy_from_slice(pixels.row(offset + row));
636 }
637 Ok(())
638}
639
640impl<F, E> CoroutineDecoder<F, E>
641where
642 F: Future<Output = Result<OutputInfo, E>> + Send + 'static,
643 E: core::error::Error + Send + Sync + From<CoroutineError> + 'static,
644{
645 fn deliver_rows(
646 &mut self,
647 rows: crate::context::PendingRows,
648 offset: u32,
649 consumed: usize,
650 sink: &mut dyn DecodeRowSink,
651 ) -> Result<ControlledProgress, StepError<E>> {
652 let state = core::mem::replace(&mut self.state, State::Failed);
655 self.check_interpretation()?;
656 let count = sink.max_buffer_rows().get().min(rows.rows - offset);
657 copy_rows(sink, &rows, offset, count, self.accepts_provisional).map_err(StepError::Sink)?;
658 let mut meta = rows.meta;
659 meta.y += offset;
660 if offset + count < rows.rows {
661 self.pending_rows = Some((rows, offset + count));
662 } else {
663 self.shared.lock().spare = rows.data;
664 }
665 self.state = state;
666 self.need.clear();
667 Ok(Progress::new(consumed, Event::Rows { meta, rows: count }).into())
668 }
669
670 fn finish(
673 &mut self,
674 sink: &mut dyn DecodeRowSink,
675 info: OutputInfo,
676 consumed: usize,
677 ) -> Result<Progress, StepError<E>> {
678 self.state = State::Failed;
681 let inconsistent = {
682 let shared = self.shared.lock();
683 shared.details.as_ref().is_some_and(|details| {
684 details.info.width != info.width
685 || details.info.height != info.height
686 || (shared.adopted.is_some()
687 && shared.info.as_ref().is_some_and(|old| {
688 old.orientation != details.info.orientation
689 || old.source_color != details.info.source_color
690 }))
691 }) || (shared.controlled
692 && shared.adopted.is_some_and(|facts| {
693 facts.width != info.width
694 || facts.height != info.height
695 || facts.descriptor != Some(info.native_format)
696 }))
697 };
698 if inconsistent {
699 self.state = State::Failed;
700 return Err(StepError::Control(ControlError::InconsistentFacts));
701 }
702 if let Err(e) = self.begin_if_ready(sink) {
703 self.state = State::Failed;
704 return Err(e);
705 }
706 if self.begun
707 && let Err(e) = sink.finish()
708 {
709 self.state = State::Failed;
710 return Err(StepError::Sink(e));
711 }
712 self.state = State::Done(info.clone());
713 self.need.clear();
714 let mut shared = self.shared.lock();
715 shared.input.clear();
716 shared.spare = Default::default();
717 self.details = shared.details.take();
718 if self.details.is_some() {
719 self.cached_info = None;
721 shared.info = None;
722 }
723 Ok(Progress::new(consumed, Event::Done(info)))
724 }
725}