From 885825f0cc3edeae5e7d358c458cb3da25ce2f66 Mon Sep 17 00:00:00 2001 From: Ulrik Date: Tue, 15 Nov 2022 17:38:07 +0100 Subject: [PATCH] dasp_frame:Generic impl for all `[impl Sample;const N: usize]` Replace the hard-coded macro implementing `Frame` for `[impl Sample;0-32]`, with a generic impl based on const-generics. I discovered the need for this, when trying to simplify some macro- -implementations in the `ebur128` crate with const-generics. We cannot remove the need for `NumChannels`, since (see reference 1) "Associated Const Equality" is not yet stable. We can however adapt the `NumChannels` strategy from `N1..N32` to a `NChannels`-generic type. Some opportunities like removing `unsafe` from `zip_map`, remain out of reach due to pending stabilization of the standard library. 1: https://github.com/rust-lang/rust/issues/92827 --- dasp_frame/src/lib.rs | 298 ++++++++++++++++++------------------------ 1 file changed, 127 insertions(+), 171 deletions(-) diff --git a/dasp_frame/src/lib.rs b/dasp_frame/src/lib.rs index c562dd2c..971bc4cd 100644 --- a/dasp_frame/src/lib.rs +++ b/dasp_frame/src/lib.rs @@ -5,7 +5,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use core::iter::DoubleEndedIterator; +use core::{iter::DoubleEndedIterator, mem::MaybeUninit}; use dasp_sample::Sample; @@ -295,190 +295,125 @@ pub struct ChannelsRef<'a, F: Frame>(core::slice::Iter<'a, F::Sample>); /// Like [`ChannelsRef`], but yields mutable references instead. pub struct ChannelsMut<'a, F: Frame>(core::slice::IterMut<'a, F::Sample>); -macro_rules! impl_frame_for_fixed_size_array { - ($($NChan:ident $N:expr, [$($idx:expr)*],)*) => { - $( - /// A typified version of a number of channels. - pub struct $NChan; - impl NumChannels for $NChan {} - - impl Frame for [S; $N] - where - S: Sample, - { - type Sample = S; - type NumChannels = $NChan; - type Channels = Channels; - type Float = [S::Float; $N]; - type Signed = [S::Signed; $N]; - - const EQUILIBRIUM: Self = [S::EQUILIBRIUM; $N]; - const CHANNELS: usize = $N; +pub struct NChannels {} +impl NumChannels for NChannels {} - #[inline] - fn channels(self) -> Self::Channels { - Channels { - next_idx: 0, - frame: self, - } - } - - #[inline] - fn channels_ref(&self) -> ChannelsRef<'_, Self> { - ChannelsRef(self.iter()) - } - - #[inline] - fn channels_mut(&mut self) -> ChannelsMut<'_, Self> { - ChannelsMut(self.iter_mut()) - } +impl Frame for [S; N] +where + S: Sample, +{ + type Sample = S; + type NumChannels = NChannels; + type Channels = Channels; + type Float = [S::Float; N]; + type Signed = [S::Signed; N]; - #[inline] - fn channel(&self, idx: usize) -> Option<&Self::Sample> { - self.get(idx) - } + const EQUILIBRIUM: Self = [S::EQUILIBRIUM; N]; + const CHANNELS: usize = N; - #[inline] - fn channel_mut(&mut self, idx: usize) -> Option<&mut Self::Sample> { - self.get_mut(idx) - } + #[inline] + fn channels(self) -> Self::Channels { + Channels { + next_idx: 0, + frame: self, + } + } - #[inline] - fn from_fn(mut from: F) -> Self - where - F: FnMut(usize) -> S, - { - [$(from($idx), )*] - } + #[inline] + fn channels_ref(&self) -> ChannelsRef<'_, Self> { + ChannelsRef(self.iter()) + } - #[inline] - fn from_samples(samples: &mut I) -> Option - where - I: Iterator - { - Some([$( { - $idx; - match samples.next() { - Some(sample) => sample, - None => return None, - } - }, )*]) - } + #[inline] + fn channels_mut(&mut self) -> ChannelsMut<'_, Self> { + ChannelsMut(self.iter_mut()) + } - #[inline(always)] - unsafe fn channel_unchecked(&self, idx: usize) -> &Self::Sample { - self.get_unchecked(idx) - } + #[inline] + fn channel(&self, idx: usize) -> Option<&Self::Sample> { + self.get(idx) + } - #[inline(always)] - unsafe fn channel_unchecked_mut(&mut self, idx: usize) -> &mut Self::Sample { - self.get_unchecked_mut(idx) - } + #[inline] + fn channel_mut(&mut self, idx: usize) -> Option<&mut Self::Sample> { + self.get_mut(idx) + } - #[inline] - fn to_signed_frame(self) -> Self::Signed { - self.map(|s| s.to_sample()) - } + #[inline] + fn from_fn(from: F) -> Self + where + F: FnMut(usize) -> S, + { + core::array::from_fn(from) + } - #[inline] - fn to_float_frame(self) -> Self::Float { - self.map(|s| s.to_sample()) - } + #[inline] + fn from_samples(samples: &mut I) -> Option + where + I: Iterator, + { + array_from_iter(samples) + } - #[inline] - fn map(self, mut map: M) -> F - where - F: Frame, - M: FnMut(Self::Sample) -> F::Sample, - { - F::from_fn(|channel_idx| { + #[inline(always)] + unsafe fn channel_unchecked(&self, idx: usize) -> &Self::Sample { + self.get_unchecked(idx) + } - // Here we do not require run-time bounds checking as we have asserted that - // the two arrays have the same number of channels at compile time with our - // where clause, i.e. - // - // `F: Frame` - unsafe { map(*self.channel_unchecked(channel_idx)) } - }) - } + #[inline(always)] + unsafe fn channel_unchecked_mut(&mut self, idx: usize) -> &mut Self::Sample { + self.get_unchecked_mut(idx) + } - #[inline] - fn zip_map(self, other: O, mut zip_map: M) -> F - where - O: Frame, - F: Frame, - M: FnMut(Self::Sample, O::Sample) -> F::Sample - { - F::from_fn(|channel_idx| { + #[inline] + fn to_signed_frame(self) -> Self::Signed { + self.map(|s| s.to_sample()) + } - // Here we do not require run-time bounds checking as we have asserted that the two - // arrays have the same number of channels at compile time with our where clause, i.e. - // - // ``` - // O: Frame - // F: Frame - // ``` - unsafe { - zip_map(*self.channel_unchecked(channel_idx), - *other.channel_unchecked(channel_idx)) - } - }) - } + #[inline] + fn to_float_frame(self) -> Self::Float { + self.map(|s| s.to_sample()) + } - #[inline] - fn scale_amp(self, amp: S::Float) -> Self { - [$(self[$idx].mul_amp(amp), )*] - } + #[inline] + fn map(self, mut map: M) -> F + where + F: Frame, + M: FnMut(Self::Sample) -> F::Sample, + { + F::from_fn(|channel_idx| { + // Here we do not require run-time bounds checking as we have asserted that + // the two arrays have the same number of channels at compile time with our + // where clause, i.e. + // + // `F: Frame` + unsafe { map(*self.channel_unchecked(channel_idx)) } + }) + } - #[inline] - fn add_amp(self, other: F) -> Self - where - F: Frame, - { - // Here we do not require run-time bounds checking as we have asserted that the two - // arrays have the same number of channels at compile time with our where clause, i.e. - unsafe { - [$(self[$idx].add_amp(*other.channel_unchecked($idx)), )*] - } - } + #[inline] + fn zip_map(self, other: O, mut zip_map: M) -> F + where + O: Frame, + F: Frame, + M: FnMut(Self::Sample, O::Sample) -> F::Sample, + { + F::from_fn(|channel_idx| { + // Here we do not require run-time bounds checking as we have asserted that the two + // arrays have the same number of channels at compile time with our where clause, i.e. + // + // ``` + // O: Frame + // F: Frame + // ``` + unsafe { + zip_map( + *self.channel_unchecked(channel_idx), + *other.channel_unchecked(channel_idx), + ) } - )* - }; -} - -impl_frame_for_fixed_size_array! { - N1 1, [0], - N2 2, [0 1], - N3 3, [0 1 2], - N4 4, [0 1 2 3], - N5 5, [0 1 2 3 4], - N6 6, [0 1 2 3 4 5], - N7 7, [0 1 2 3 4 5 6], - N8 8, [0 1 2 3 4 5 6 7], - N9 9, [0 1 2 3 4 5 6 7 8], - N10 10, [0 1 2 3 4 5 6 7 8 9], - N11 11, [0 1 2 3 4 5 6 7 8 9 10], - N12 12, [0 1 2 3 4 5 6 7 8 9 10 11], - N13 13, [0 1 2 3 4 5 6 7 8 9 10 11 12], - N14 14, [0 1 2 3 4 5 6 7 8 9 10 11 12 13], - N15 15, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14], - N16 16, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15], - N17 17, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], - N18 18, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17], - N19 19, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18], - N20 20, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19], - N21 21, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20], - N22 22, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21], - N23 23, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22], - N24 24, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23], - N25 25, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24], - N26 26, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25], - N27 27, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26], - N28 28, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27], - N29 29, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28], - N30 30, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29], - N31 31, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30], - N32 32, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31], + }) + } } macro_rules! impl_frame_for_sample { @@ -486,7 +421,7 @@ macro_rules! impl_frame_for_sample { $( impl Frame for $T { type Sample = $T; - type NumChannels = N1; + type NumChannels = NChannels<1>; type Channels = Channels; type Float = <$T as Sample>::Float; type Signed = <$T as Sample>::Signed; @@ -612,7 +547,7 @@ macro_rules! impl_frame_for_sample { #[inline] fn add_amp(self, other: F) -> Self where - F: Frame::Signed, NumChannels=N1>, + F: Frame::Signed, NumChannels=NChannels<1>>, { // Here we do not require run-time bounds checking as we have asserted that the two // arrays have the same number of channels at compile time with our where clause, i.e. @@ -714,3 +649,24 @@ impl<'a, F: Frame> DoubleEndedIterator for ChannelsMut<'a, F> { self.0.next_back() } } + +fn array_from_iter(mut iter: impl Iterator) -> Option<[T; N]> { + // Hopefully something equivalent will be in stdlib some day + + unsafe { + let mut result: [MaybeUninit; N] = MaybeUninit::uninit().assume_init(); + + for i in 0..N { + if let Some(sample) = iter.next() { + result[i].write(sample); + } else { + for i in 0..i { + result[i].assume_init_drop() + } + return None; + } + } + + Some(result.map(|v| v.assume_init())) + } +}