diff --git a/src/conversions/channels.rs b/src/conversions/channels.rs index 94bcf442..334af6b1 100644 --- a/src/conversions/channels.rs +++ b/src/conversions/channels.rs @@ -95,13 +95,15 @@ where let consumed = std::cmp::min(self.from.get(), self.next_output_sample_pos) as usize; - let min = ((min + consumed) / self.from.get() as usize * self.to.get() as usize) + // A lower bound can saturate, but an overflowing upper bound is unknown. + let min = (min.saturating_add(consumed) / self.from.get() as usize) + .saturating_mul(self.to.get() as usize) .saturating_sub(self.next_output_sample_pos as usize); - let max = max.map(|max| { - ((max + consumed) / self.from.get() as usize * self.to.get() as usize) - .saturating_sub(self.next_output_sample_pos as usize) - }); + let max = max + .and_then(|max| max.checked_add(consumed)) + .and_then(|max| (max / self.from.get() as usize).checked_mul(self.to.get() as usize)) + .map(|max| max.saturating_sub(self.next_output_sample_pos as usize)); (min, max) } diff --git a/tests/channel_count_size_hint.rs b/tests/channel_count_size_hint.rs new file mode 100644 index 00000000..9e5be529 --- /dev/null +++ b/tests/channel_count_size_hint.rs @@ -0,0 +1,46 @@ +use rodio::conversions::ChannelCountConverter; +use rodio::source::Zero; + +#[test] +fn unbounded_mono_source_can_be_mixed_into_stereo() { + let (mixer, mut source) = + rodio::mixer::mixer(2.try_into().unwrap(), 48_000.try_into().unwrap()); + mixer.add(Zero::new(1.try_into().unwrap(), 48_000.try_into().unwrap())); + + for _ in 0..8 { + assert_eq!(source.next(), Some(0.0)); + let (lower, upper) = source.size_hint(); + assert!(lower > 0); + assert_eq!(upper, None); + } +} + +#[test] +fn unbounded_channel_conversion_hints_do_not_overflow() { + for (from, to) in [(1, 2), (2, 4), (4, 2)] { + let mut source = ChannelCountConverter::new( + std::iter::repeat(0.0), + from.try_into().unwrap(), + to.try_into().unwrap(), + ); + for _ in 0..8 { + let (lower, upper) = source.size_hint(); + assert!(lower > 0); + assert_eq!(upper, None); + assert_eq!(source.next(), Some(0.0)); + } + } +} + +#[test] +fn overflowing_finite_upper_bound_becomes_unknown() { + let mut source = ChannelCountConverter::new( + std::iter::repeat_n(0.0, usize::MAX), + 1.try_into().unwrap(), + 2.try_into().unwrap(), + ); + for _ in 0..8 { + assert_eq!(source.size_hint().1, None); + assert_eq!(source.next(), Some(0.0)); + } +}