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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use crate::cpu::{Header, EC_OUTPUT_FRAME_SIZE};

const PAYLOAD_SIZE: usize = EC_OUTPUT_FRAME_SIZE - std::mem::size_of::<Header>();
type Payload = [u8; PAYLOAD_SIZE];

#[repr(C)]
#[derive(Clone)]
pub struct TxMessage {
    pub header: Header,
    pub payload: Payload,
}

#[derive(Clone)]
pub struct TxDatagram {
    data: Vec<TxMessage>,
    num_devices: usize,
}

impl TxDatagram {
    pub fn new(num_devices: usize) -> Self {
        Self {
            num_devices,
            data: vec![
                TxMessage {
                    header: Header {
                        msg_id: 0,
                        _pad: 0,
                        slot_2_offset: 0,
                    },
                    payload: [0; PAYLOAD_SIZE],
                };
                num_devices
            ],
        }
    }

    pub const fn num_devices(&self) -> usize {
        self.num_devices
        // TODO@23.0.0: remove const and replace with this
        // self.data.len()
    }

    pub fn all_data(&self) -> &[u8] {
        unsafe {
            std::slice::from_raw_parts(
                self.data.as_ptr() as *const u8,
                self.data.len() * EC_OUTPUT_FRAME_SIZE,
            )
        }
    }

    pub fn all_data_mut(&mut self) -> &mut [u8] {
        unsafe {
            std::slice::from_raw_parts_mut(
                self.data.as_mut_ptr() as *mut u8,
                self.data.len() * EC_OUTPUT_FRAME_SIZE,
            )
        }
    }

    pub fn data(&self, i: usize) -> &[u8] {
        unsafe {
            std::slice::from_raw_parts(&self.data[i] as *const _ as *const u8, EC_OUTPUT_FRAME_SIZE)
        }
    }

    #[deprecated(note = "use indexer and accsess header directly", since = "22.0.2")]
    pub fn headers(&self) -> impl Iterator<Item = &Header> {
        (0..self.num_devices).map(|i| &self[i].header)
    }

    #[deprecated(note = "use indexer and accsess header directly", since = "22.0.2")]
    pub fn header_mut(&mut self, i: usize) -> &mut Header {
        &mut self[i].header
    }

    #[deprecated(note = "use indexer and accsess payload directly", since = "22.0.2")]
    pub fn payload_mut(&mut self, i: usize) -> &mut [u8] {
        &mut self.data[i].payload
    }

    #[deprecated(note = "use indexer and accsess payload directly", since = "22.0.2")]
    pub fn payloads(&self) -> impl Iterator<Item = &[u8]> {
        (0..self.num_devices).map(|i| &self[i].payload[..])
    }
}

impl std::ops::Deref for TxDatagram {
    type Target = [TxMessage];

    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

impl std::ops::DerefMut for TxDatagram {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.data
    }
}

#[cfg(test)]
mod tests {
    use itertools::Itertools;

    use super::*;

    #[rstest::fixture]
    fn tx() -> TxDatagram {
        let mut tx = TxDatagram::new(2);
        tx.all_data_mut().iter_mut().enumerate().for_each(|(i, d)| {
            *d = i as u8;
        });
        tx
    }

    #[rstest::rstest]
    #[test]
    fn test_num_devices(tx: TxDatagram) {
        assert_eq!(2, tx.num_devices());
    }

    #[rstest::rstest]
    #[test]
    fn test_all_data(tx: TxDatagram) {
        assert_eq!(2 * EC_OUTPUT_FRAME_SIZE, tx.all_data().len());
    }

    #[rstest::rstest]
    #[test]
    #[case::device_0((0..EC_OUTPUT_FRAME_SIZE).map(|i| i as u8).collect_vec(), 0)]
    #[case::device_1((EC_OUTPUT_FRAME_SIZE..2*EC_OUTPUT_FRAME_SIZE).map(|i| i as u8).collect_vec(), 1)]
    fn test_data(#[case] expect: Vec<u8>, #[case] dev: usize, tx: TxDatagram) {
        assert_eq!(expect, tx.data(dev));
    }
}