autd3_driver/datagram/modulation/
boxed.rs

1use std::mem::MaybeUninit;
2
3use autd3_core::derive::*;
4use autd3_derive::Modulation;
5
6/// A dyn-compatible version of [`Modulation`].
7pub trait DModulation {
8    fn dyn_calc(&mut self, limits: &FirmwareLimits) -> Result<Vec<u8>, ModulationError>;
9    fn dyn_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
10}
11
12impl<T: Modulation> DModulation for MaybeUninit<T> {
13    fn dyn_calc(&mut self, limits: &FirmwareLimits) -> Result<Vec<u8>, ModulationError> {
14        let mut tmp: MaybeUninit<T> = MaybeUninit::uninit();
15        std::mem::swap(&mut tmp, self);
16        // SAFETY: This function is called only once from `Modulation::calc`.
17        let g = unsafe { tmp.assume_init() };
18        g.calc(limits)
19    }
20
21    fn dyn_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        // SAFETY: This function is never called after `dyn_init`.
23        unsafe { self.assume_init_ref() }.fmt(f)
24    }
25}
26
27/// Boxed [`Modulation`].
28///
29/// This provides the ability to wrap any [`Modulation`] in a common type.
30#[derive(Modulation)]
31pub struct BoxedModulation {
32    m: Box<dyn DModulation>,
33    sampling_config: SamplingConfig,
34}
35
36impl BoxedModulation {
37    /// Creates a new [`BoxedModulation`].
38    pub fn new<M: Modulation + 'static>(m: M) -> BoxedModulation {
39        let sampling_config = m.sampling_config();
40        BoxedModulation {
41            m: Box::new(MaybeUninit::new(m)),
42            sampling_config,
43        }
44    }
45}
46
47impl std::fmt::Debug for BoxedModulation {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        self.m.as_ref().dyn_fmt(f)
50    }
51}
52
53impl Modulation for BoxedModulation {
54    fn calc(self, limits: &FirmwareLimits) -> Result<Vec<u8>, ModulationError> {
55        let Self { mut m, .. } = self;
56        m.dyn_calc(limits)
57    }
58
59    fn sampling_config(&self) -> SamplingConfig {
60        self.sampling_config
61    }
62}
63
64#[cfg(test)]
65pub mod tests {
66    use super::*;
67    use crate::datagram::modulation::tests::TestModulation;
68
69    #[test]
70    fn boxed_modulation_unsafe() {
71        let m = TestModulation {
72            sampling_config: SamplingConfig::FREQ_4K,
73        };
74
75        let mb = BoxedModulation::new(m.clone());
76
77        assert_eq!(format!("{m:?}"), format!("{:?}", mb));
78        assert_eq!(SamplingConfig::FREQ_4K, mb.sampling_config());
79        assert_eq!(Ok(vec![0; 2]), mb.calc(&FirmwareLimits::unused()));
80    }
81}