autd3-rs 0.9.0

Core async client library for the AUTD3 phased-array kit.
Documentation
use std::sync::Arc;

use crate::legacy::wire::TxFrame;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LegacyFrame {
    frames: Arc<Vec<TxFrame>>,
    start: usize,
    num_devices: usize,
}

impl LegacyFrame {
    pub(crate) fn frames(&self) -> &[TxFrame] {
        &self.frames[self.start..self.start + self.num_devices]
    }

    #[must_use]
    pub const fn num_devices(&self) -> usize {
        self.num_devices
    }
}

#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct LegacyFrames {
    frames: Arc<Vec<TxFrame>>,
    num_devices: usize,
}

impl LegacyFrames {
    #[must_use]
    pub fn len(&self) -> usize {
        self.frames.len().checked_div(self.num_devices).unwrap_or(0)
    }

    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.frames.is_empty()
    }

    #[must_use]
    pub fn frame(&self, index: usize) -> Option<LegacyFrame> {
        if self.num_devices == 0 || index >= self.len() {
            return None;
        }
        Some(LegacyFrame {
            frames: Arc::clone(&self.frames),
            start: index * self.num_devices,
            num_devices: self.num_devices,
        })
    }

    #[must_use]
    pub fn iter(&self) -> LegacyFrameIter<'_> {
        LegacyFrameIter {
            frames: self,
            index: 0,
        }
    }

    pub(crate) fn clear(&mut self) {
        match Arc::get_mut(&mut self.frames) {
            Some(frames) => frames.clear(),
            None => self.frames = Arc::default(),
        }
        self.num_devices = 0;
    }

    pub(crate) fn reserve_round(&mut self, num_devices: usize) -> &mut [TxFrame] {
        debug_assert!(self.num_devices == 0 || self.num_devices == num_devices);
        self.num_devices = num_devices;
        let frames = Arc::make_mut(&mut self.frames);
        let start = frames.len();
        frames.resize(start + num_devices, TxFrame::new());
        &mut frames[start..]
    }
}

pub struct LegacyFrameIter<'a> {
    frames: &'a LegacyFrames,
    index: usize,
}

impl Iterator for LegacyFrameIter<'_> {
    type Item = LegacyFrame;

    fn next(&mut self) -> Option<LegacyFrame> {
        let frame = self.frames.frame(self.index)?;
        self.index += 1;
        Some(frame)
    }
}

impl<'a> IntoIterator for &'a LegacyFrames {
    type Item = LegacyFrame;
    type IntoIter = LegacyFrameIter<'a>;

    fn into_iter(self) -> LegacyFrameIter<'a> {
        self.iter()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn frames(rounds: usize, num_devices: usize) -> LegacyFrames {
        let mut out = LegacyFrames::default();
        for round in 0..rounds {
            for (device, frame) in out.reserve_round(num_devices).iter_mut().enumerate() {
                frame.payload[0] = u8::try_from(round * 16 + device).unwrap();
            }
        }
        out
    }

    #[test]
    fn empty_frames_have_no_rounds() {
        let out = LegacyFrames::default();
        assert_eq!(out.len(), 0);
        assert!(out.is_empty());
        assert!(out.frame(0).is_none());
        assert_eq!(out.iter().count(), 0);
    }

    #[test]
    fn rounds_are_sliced_per_device() {
        let out = frames(3, 2);
        assert_eq!(out.len(), 3);
        assert!(!out.is_empty());
        for round in 0..3 {
            let frame = out.frame(round).expect("round exists");
            assert_eq!(frame.num_devices(), 2);
            for (device, tx) in frame.frames().iter().enumerate() {
                assert_eq!(tx.payload[0], u8::try_from(round * 16 + device).unwrap());
            }
        }
        assert!(out.frame(3).is_none());
        assert_eq!(out.iter().count(), 3);
        assert_eq!((&out).into_iter().count(), 3);
    }

    #[test]
    fn clear_resets_the_device_count() {
        let mut out = frames(2, 4);
        out.clear();
        assert_eq!(out.len(), 0);
        assert!(out.is_empty());
    }

    #[test]
    fn an_outstanding_frame_keeps_its_own_view_after_a_rebuild() {
        let mut out = frames(2, 2);
        let held = out.frame(1).unwrap();
        out.clear();
        for (device, frame) in out.reserve_round(2).iter_mut().enumerate() {
            frame.payload[0] = u8::try_from(0xF0 + device).unwrap();
        }

        assert_eq!(held.num_devices(), 2);
        assert_eq!(held.frames()[0].payload[0], 16);
        assert_eq!(held.frames()[1].payload[0], 17);
        assert_eq!(out.frame(0).unwrap().frames()[0].payload[0], 0xF0);
    }
}