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}