srex/srecord/
data_chunk.rs

1use std::cmp::{min, Ordering};
2use std::ops::Range;
3
4use crate::srecord::slice_index::SliceIndex;
5use crate::srecord::DataRecord;
6
7/// A contiguous chunk of data at a specific address.
8///
9/// [`DataChunk`]s are intended to be the largest contiguous ranges of data, allowing flexible
10/// slicing of contiguous data.
11#[derive(Debug, PartialEq, Eq)]
12pub struct DataChunk {
13    /// Start address of the [`DataChunk`]. The first byte of the data is located at this address.
14    pub address: u64,
15    /// Raw contiguous data of data chunk, starting at `address`.
16    pub data: Vec<u8>,
17}
18
19impl DataChunk {
20    /// Returns inclusive start address of [`DataChunk`]. Same as `address`.
21    pub fn start_address(&self) -> u64 {
22        self.address
23    }
24
25    /// Exclusive end address of [`DataChunk`]. This is the first address in ascending order after
26    /// [`DataChunk`] that does not contain any data inside the chunk.
27    pub fn end_address(&self) -> u64 {
28        self.address + self.data.len() as u64
29    }
30
31    /// Returns a reference to a byte or byte data subslice depending on the type of index.
32    ///
33    /// - If given an address, returns a reference to the byte at that address or `None` if out of
34    ///   bounds.
35    /// - If given an address range, returns the data subslice corresponding to that range, or
36    ///   `None` if out of bounds.
37    ///
38    /// # Examples
39    ///
40    /// ```
41    /// use srex::srecord::DataChunk;
42    ///
43    /// let data_chunk = DataChunk{
44    ///     address: 0x10000,
45    ///     data: vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05],
46    /// };
47    /// assert!(data_chunk.get(0x10000).is_some());
48    /// assert!(data_chunk.get(0x10006).is_none());
49    /// assert_eq!(data_chunk.get(0x10001..0x10003).unwrap(), &[0x01u8, 0x02u8]);
50    /// assert!(data_chunk.get(0x10000..0x10006).is_some());
51    /// assert!(data_chunk.get(0x10000..0x10007).is_none());
52    /// ```
53    pub fn get<I>(&self, index: I) -> Option<&I::Output>
54    where
55        I: SliceIndex<Self>,
56    {
57        index.get(self)
58    }
59
60    /// Returns a mutable reference to a byte or byte data subslice depending on the type of index.
61    ///
62    /// - If given an address, returns a mutable reference to the byte at that address or `None` if
63    ///   out of bounds.
64    /// - If given an address range, returns the data subslice corresponding to that range, or
65    ///   `None` if out of bounds.
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// use srex::srecord::DataChunk;
71    ///
72    /// let mut data_chunk = DataChunk{
73    ///     address: 0x10000,
74    ///     data: vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05],
75    /// };
76    /// assert!(data_chunk.get_mut(0x10000).is_some());
77    /// assert_eq!(*data_chunk.get_mut(0x10000).unwrap(), 0x00u8);
78    /// *data_chunk.get_mut(0x10000).unwrap() = 0x10;
79    /// assert_eq!(*data_chunk.get_mut(0x10000).unwrap(), 0x10u8);
80    /// ```
81    pub fn get_mut<I>(&mut self, index: I) -> Option<&mut I::Output>
82    where
83        I: SliceIndex<Self>,
84    {
85        index.get_mut(self)
86    }
87
88    /// Iterate over [`DataChunk`] with [`DataRecord`]s.
89    ///
90    /// Each record contains `record_size` bytes of data. The data is aligned to the start of the
91    /// chunk.
92    ///
93    /// # Examples
94    ///
95    /// ```
96    /// use srex::srecord::{DataChunk, DataRecord};
97    ///
98    /// let data_chunk = DataChunk{
99    ///     address: 0x1000,
100    ///     data: vec![0x00, 0x01, 0x02, 0x03],
101    /// };
102    /// let mut iterator = data_chunk.iter_records(2);
103    /// assert_eq!(iterator.next().unwrap(), DataRecord{ address: 0x1000, data: &[0x00, 0x01] });
104    /// assert_eq!(iterator.next().unwrap(), DataRecord{ address: 0x1002, data: &[0x02, 0x03] });
105    /// assert!(iterator.next().is_none());
106    /// ```
107    // TODO: Alignment
108    pub fn iter_records(&self, record_size: usize) -> DataChunkIterator {
109        DataChunkIterator {
110            data_chunk: self,
111            record_size,
112            address: self.address,
113        }
114    }
115}
116
117impl SliceIndex<DataChunk> for u64 {
118    type Output = u8;
119
120    /// Returns a reference to a single byte in a [`DataChunk`], at the address that `self` points
121    /// to, or `None` if out of bounds.
122    ///
123    /// # Examples
124    ///
125    /// ```
126    /// use srex::srecord::DataChunk;
127    /// use srex::srecord::slice_index::SliceIndex;
128    ///
129    /// let data_chunk = DataChunk{
130    ///     address: 0x1000,
131    ///     data: vec![0x00, 0x01, 0x02, 0x03],
132    /// };
133    /// assert_eq!(*(0x1001 as u64).get(&data_chunk).unwrap(), 0x01);
134    /// assert!((0x1004 as u64).get(&data_chunk).is_none());
135    /// ```
136    fn get(self, data_chunk: &DataChunk) -> Option<&u8> {
137        match self.checked_sub(data_chunk.address) {
138            Some(index) => data_chunk.data.get(index as usize),
139            None => None,
140        }
141    }
142
143    /// Returns a mutable reference to a single byte in a [`DataChunk`], at the address that `self`
144    /// points to, or `None` if out of bounds.
145    ///
146    /// # Examples
147    ///
148    /// ```
149    /// use srex::srecord::DataChunk;
150    /// use srex::srecord::slice_index::SliceIndex;
151    ///
152    /// let mut data_chunk = DataChunk{
153    ///     address: 0x1000,
154    ///     data: vec![0x00, 0x01, 0x02, 0x03],
155    /// };
156    /// assert_eq!(*(0x1001 as u64).get_mut(&mut data_chunk).unwrap(), 0x01);
157    /// *(0x1001 as u64).get_mut(&mut data_chunk).unwrap() = 0xFF;
158    /// assert_eq!(*(0x1001 as u64).get_mut(&mut data_chunk).unwrap(), 0xFF);
159    /// assert!((0x1004 as u64).get(&data_chunk).is_none());
160    /// ```
161    fn get_mut(self, data_chunk: &mut DataChunk) -> Option<&mut u8> {
162        match self.checked_sub(data_chunk.address) {
163            Some(index) => data_chunk.data.get_mut(index as usize),
164            None => None,
165        }
166    }
167}
168
169impl SliceIndex<DataChunk> for Range<u64> {
170    type Output = [u8];
171
172    /// Returns a reference to a data slice in a [`DataChunk`], at the address range that `self`
173    /// points to, or `None` if out of bounds.
174    ///
175    /// # Examples
176    ///
177    /// ```
178    /// use srex::srecord::DataChunk;
179    /// use srex::srecord::slice_index::SliceIndex;
180    ///
181    /// let data_chunk = DataChunk{
182    ///     address: 0x1000,
183    ///     data: vec![0x00, 0x01, 0x02, 0x03],
184    /// };
185    /// assert_eq!(*(0x1001 as u64..0x1003 as u64).get(&data_chunk).unwrap(), [0x01, 0x02]);
186    /// assert!((0x1000 as u64..0x1005 as u64).get(&data_chunk).is_none());
187    /// ```
188    fn get(self, data_chunk: &DataChunk) -> Option<&[u8]> {
189        match self.start.checked_sub(data_chunk.address) {
190            Some(start_index) => match self.end.checked_sub(data_chunk.address) {
191                Some(end_index) => data_chunk
192                    .data
193                    .get(start_index as usize..end_index as usize),
194                None => None,
195            },
196            None => None,
197        }
198    }
199
200    /// Returns a mutable reference to a data slice in a [`DataChunk`], at the address range that
201    /// `self` points to, or `None` if out of bounds.
202    ///
203    /// # Examples
204    ///
205    /// ```
206    /// use srex::srecord::DataChunk;
207    /// use srex::srecord::slice_index::SliceIndex;
208    ///
209    /// let mut data_chunk = DataChunk{
210    ///     address: 0x1000,
211    ///     data: vec![0x00, 0x01, 0x02, 0x03],
212    /// };
213    /// assert_eq!(*(0x1001 as u64..0x1003 as u64).get_mut(&mut data_chunk).unwrap(), [0x01, 0x02]);
214    /// (0x1001 as u64..0x1003).get_mut(&mut data_chunk).unwrap().fill(0xAA);
215    /// assert_eq!(*(0x1001 as u64..0x1003 as u64).get_mut(&mut data_chunk).unwrap(), [0xAA, 0xAA]);
216    /// assert!((0x1000 as u64..0x1005 as u64).get_mut(&mut data_chunk).is_none());
217    /// ```
218    fn get_mut(self, data_chunk: &mut DataChunk) -> Option<&mut [u8]> {
219        match self.start.checked_sub(data_chunk.address) {
220            Some(start_index) => match self.end.checked_sub(data_chunk.address) {
221                Some(end_index) => data_chunk
222                    .data
223                    .get_mut(start_index as usize..end_index as usize),
224                None => None,
225            },
226            None => None,
227        }
228    }
229}
230
231/// Iterator that returns [`DataRecords`](`DataRecord`) in sequence for the data chunk, with a
232/// specified length.
233pub struct DataChunkIterator<'a> {
234    /// Reference to [`DataChunk`] to iterate through.
235    data_chunk: &'a DataChunk,
236    /// Data size (bytes of actual data) in each record.
237    record_size: usize,
238    /// Address that the current [`DataRecord`] should point to.
239    address: u64,
240}
241
242impl<'a> Iterator for DataChunkIterator<'a> {
243    type Item = DataRecord<'a>;
244
245    fn next(&mut self) -> Option<Self::Item> {
246        let start_address = self.address;
247        let data_chunk_end_address = self.data_chunk.end_address();
248        match start_address.cmp(&data_chunk_end_address) {
249            Ordering::Less => {
250                let end_address = min(
251                    start_address + self.record_size as u64,
252                    self.data_chunk.end_address(),
253                );
254                match self.data_chunk.get(start_address..end_address) {
255                    Some(data) => {
256                        self.address = end_address;
257                        Some(DataRecord {
258                            address: start_address,
259                            data,
260                        })
261                    }
262                    None => None,
263                }
264            }
265            _ => None,
266        }
267    }
268}