1use 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
90#[non_exhaustive]
91pub struct ByteRange {
92 pub offset: u64,
94 pub length: u64,
96}
97
98impl ByteRange {
99 #[inline]
101 pub const fn new(offset: u64, length: u64) -> Self {
102 Self { offset, length }
103 }
104
105 #[inline]
107 pub const fn end(&self) -> u64 {
108 self.offset.saturating_add(self.length)
109 }
110
111 #[inline]
113 pub const fn is_empty(&self) -> bool {
114 self.length == 0
115 }
116
117 #[inline]
119 pub const fn contains(&self, target_offset: u64) -> bool {
120 target_offset >= self.offset && target_offset < self.end()
121 }
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
126#[non_exhaustive]
127pub enum InputRequest {
128 AtLeast {
134 offset: u64,
136 min_len: usize,
138 },
139 Range(ByteRange),
141 Len,
143}
144
145#[deprecated(note = "use InputRequest")]
147pub type Request = InputRequest;
148
149impl InputRequest {
150 #[inline]
152 pub const fn at_least(offset: u64, min_len: usize) -> Self {
153 Self::AtLeast { offset, min_len }
154 }
155
156 #[inline]
158 pub const fn range(offset: u64, length: u64) -> Self {
159 Self::Range(ByteRange::new(offset, length))
160 }
161
162 #[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 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
191#[non_exhaustive]
192pub struct Input<'a> {
193 pub offset: u64,
195 pub bytes: &'a [u8],
197 pub total_len: Option<u64>,
199}
200
201impl<'a> Input<'a> {
202 #[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 #[inline]
214 pub const fn empty() -> Self {
215 Self {
216 offset: 0,
217 bytes: &[],
218 total_len: None,
219 }
220 }
221
222 #[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 #[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 #[inline]
238 pub const fn end(&self) -> u64 {
239 self.offset.saturating_add(self.bytes.len() as u64)
240 }
241
242 #[inline]
244 pub fn reaches_end(&self) -> bool {
245 self.total_len == Some(self.end())
246 }
247}
248
249#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
254#[non_exhaustive]
255pub enum MetadataUpdate {
256 HeaderReady,
259 ColorReady,
261 Updated,
264}
265
266impl MetadataUpdate {
267 #[inline]
269 pub const fn is_header_ready(&self) -> bool {
270 matches!(self, Self::HeaderReady | Self::ColorReady | Self::Updated)
271 }
272}
273
274#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
276#[non_exhaustive]
277pub struct BatchMeta {
278 pub y: u32,
280 pub pass: u16,
282 pub is_final: bool,
284}
285
286impl BatchMeta {
287 #[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 #[inline]
299 pub const fn final_rows(y: u32) -> Self {
300 Self::new(y, true)
301 }
302
303 #[inline]
305 pub const fn with_pass(mut self, pass: u16) -> Self {
306 self.pass = pass;
307 self
308 }
309}
310
311#[derive(Debug, Clone, PartialEq, Eq)]
313#[non_exhaustive]
314pub enum Event {
315 Need,
318 Rows {
320 meta: BatchMeta,
322 rows: u32,
324 },
325 Metadata(MetadataUpdate),
327 Done(OutputInfo),
329}
330
331impl Event {
332 #[inline]
334 pub const fn is_done(&self) -> bool {
335 matches!(self, Self::Done(_))
336 }
337}
338
339#[derive(Debug, Clone, PartialEq, Eq)]
341#[non_exhaustive]
342pub struct Progress {
343 pub consumed: usize,
345 pub event: Event,
347}
348
349impl Progress {
350 #[inline]
352 pub const fn new(consumed: usize, event: Event) -> Self {
353 Self { consumed, event }
354 }
355
356 #[inline]
358 pub const fn need(consumed: usize) -> Self {
359 Self::new(consumed, Event::Need)
360 }
361}
362
363#[derive(Debug)]
365#[non_exhaustive]
366pub enum StepError<E> {
367 Codec(E),
370 Sink(SinkError),
372 Cancelled(StopReason),
374 Finished,
376 Control(ControlError),
378}
379
380impl<E> StepError<E> {
381 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
422pub trait IncrementalDecode {
428 type Error: core::error::Error + Send + Sync + 'static;
430
431 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 fn requested(&self) -> &[InputRequest];
446
447 fn info(&self) -> Option<&ImageInfo>;
449
450 fn take_details(&mut self) -> Option<crate::decode::DecodeDetails> {
461 None
462 }
463
464 fn output_descriptor(&self) -> Option<zenpixels::PixelDescriptor> {
466 None
467 }
468
469 fn access_pattern(&self) -> crate::source::AccessPattern {
472 crate::source::AccessPattern::Sequential
473 }
474}
475
476pub struct ErasedError(crate::traits::BoxedError);
484
485impl ErasedError {
486 pub fn new(err: crate::traits::BoxedError) -> Self {
488 Self(err)
489 }
490
491 pub fn into_inner(self) -> crate::traits::BoxedError {
493 self.0
494 }
495
496 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 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
531pub 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#[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 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 pub fn get_ref(&self) -> &D {
645 &self.inner
646 }
647
648 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 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 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
821pub 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
859pub 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 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 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 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;