1use std::fmt;
10use std::iter;
11
12use thiserror::Error;
13
14mod parser;
15
16use parser::VmemParser;
17pub use parser::{ParseError, ParseResult};
18
19#[derive(Clone, Debug, Default, PartialEq, Eq)]
23pub struct Vmem {
24 sections: Vec<Section>,
25}
26
27#[derive(Clone, Debug, Default, PartialEq, Eq)]
29pub struct Section {
30 pub addr: u32,
31 pub data: Vec<Word>,
32}
33
34#[derive(Clone, Debug, Default, PartialEq, Eq)]
36pub struct Word {
37 pub bytes: Vec<u8>,
38}
39
40impl Word {
41 pub fn new(bytes: Vec<u8>) -> Self {
42 Self { bytes }
43 }
44}
45
46impl Vmem {
47 pub fn new(sections: Vec<Section>) -> Self {
48 Self { sections }
49 }
50
51 pub fn from_str(s: &str, addr_stride: Option<usize>) -> Result<Self, ParseError> {
53 VmemParser::parse(s, addr_stride)
54 }
55}
56
57impl Vmem {
58 pub fn sections(&self) -> impl Iterator<Item = &Section> {
60 self.sections
62 .iter()
63 .filter(|section| !section.data.is_empty())
64 }
65
66 pub fn dump(&self, bytes_per_word: Option<usize>, addr_per_word: bool) -> String {
74 let addr_stride = bytes_per_word.unwrap_or(1);
75 let word_width_nibbles = bytes_per_word.map(|b| b * 2);
76 let max_addr = self
77 .sections
78 .iter()
79 .map(|s| s.addr + ((s.data.len() - 1) * addr_stride) as u32)
80 .max();
81 let addr_width = format!("{:x}", max_addr.unwrap_or(0)).len();
82
83 let mut sections: Vec<String> = Vec::new();
84
85 for section in &self.sections {
86 let mut section_str = String::new();
87
88 if !addr_per_word {
89 section_str.push_str(&format!("@{:0addr_width$X} ", section.addr));
90 }
91
92 let word_separator = if addr_per_word { "\n" } else { " " };
93 section_str.push_str(
94 §ion
95 .data
96 .iter()
97 .enumerate()
98 .map(|(index, word)| {
99 let addr = if addr_per_word {
100 let addr = section.addr + (index * addr_stride) as u32;
101 format!("@{:0addr_width$X} ", addr)
102 } else {
103 String::new()
104 };
105
106 let word = if let Some(width) = word_width_nibbles {
107 format!("{:0>width$}", hex::encode_upper(word.bytes.clone()))
108 } else {
109 hex::encode_upper(word.bytes.clone())
110 };
111
112 format!("{}{}", addr, word)
113 })
114 .collect::<Vec<_>>()
115 .join(word_separator),
116 );
117 sections.push(section_str);
118 }
119
120 sections.join("\n")
121 }
122}
123
124impl fmt::Display for Vmem {
125 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
126 write!(f, "{}", self.dump(None, false))
127 }
128}
129
130#[derive(Clone, Debug, PartialEq, Eq)]
132pub struct Data {
133 pub addr: u32,
134 pub value: Word,
135}
136
137impl Vmem {
138 pub fn data_addrs(&self, stride: usize) -> impl Iterator<Item = Data> + '_ {
143 self.sections()
144 .flat_map(move |section| section.data_addrs(stride))
145 }
146
147 pub fn merge_sections(&mut self, addr_stride: Option<usize>) {
149 self.sections.dedup_by(|sec, last| {
150 let nwords = last.data.len() as u32;
151 let size = nwords * addr_stride.unwrap_or(1) as u32;
152 let merge: bool = last.addr + size == sec.addr;
153 if merge {
154 last.data.append(&mut sec.data);
155 }
156 merge
157 })
158 }
159}
160
161impl Section {
162 pub fn data_addrs(&self, stride: usize) -> impl Iterator<Item = Data> + '_ {
167 let addrs = (self.addr..).step_by(stride);
168 let values = self.data.iter();
169 iter::zip(addrs, values).map(|(addr, value)| Data {
170 addr,
171 value: value.clone(),
172 })
173 }
174}
175
176#[derive(Clone, Debug, Error, PartialEq, Eq)]
178pub enum ConversionError {
179 #[error("word size {0} too large to fit after conversion")]
181 InvalidWordSize(usize),
182}
183
184impl TryFrom<Section> for Vec<u32> {
185 type Error = ConversionError;
186
187 fn try_from(section: Section) -> Result<Self, Self::Error> {
188 section
189 .data
190 .into_iter()
191 .map(|mut word| {
192 if word.bytes.len() <= 4 {
193 return Err(ConversionError::InvalidWordSize(word.bytes.len()));
194 }
195
196 word.bytes.resize(4, 0);
197 let bytes: [u8; 4] = word.bytes.try_into().unwrap();
198 Ok(u32::from_le_bytes(bytes))
199 })
200 .collect()
201 }
202}
203
204#[cfg(test)]
205mod test {
206 use super::*;
207
208 #[test]
209 fn vmem_data() {
210 let vmem = Vmem::from_str("@10 12 23 34 @20 @26 45", Some(4)).unwrap();
211 let expected =
212 [(0x40, 0x12), (0x44, 0x23), (0x48, 0x34), (0x98, 0x45)].map(|(addr, value)| Data {
213 addr,
214 value: Word::new(vec![value]),
215 });
216
217 let data: Vec<_> = vmem.data_addrs(4).collect();
218 assert_eq!(data, expected);
219 }
220
221 #[test]
222 fn merge_contiguous_sections() {
223 let mut vmem = Vmem::from_str("1234 @20 0123 4567 89ab @23 cdef 1f1f", Some(2)).unwrap();
225 let expected = vec![
226 Section {
227 addr: 0x0,
228 data: vec![Word::new(vec![0x12, 0x34])],
229 },
230 Section {
231 addr: 0x40,
232 data: [
233 [0x01, 0x23],
234 [0x45, 0x67],
235 [0x89, 0xab],
236 [0xcd, 0xef],
237 [0x1f, 0x1f],
238 ]
239 .iter()
240 .map(|&bytes| Word::new(Vec::from(bytes)))
241 .collect(),
242 },
243 ];
244
245 vmem.merge_sections(Some(2));
246 assert_eq!(vmem.sections, expected);
247
248 let mut vmem = Vmem::from_str("1234 @20 0123 4567 89ab @26 cdef 1f1f", None).unwrap();
250 let expected = vec![
251 Section {
252 addr: 0x0,
253 data: vec![Word::new(vec![0x12, 0x34])],
254 },
255 Section {
256 addr: 0x20,
257 data: [
258 [0x01, 0x23],
259 [0x45, 0x67],
260 [0x89, 0xab],
261 [0xcd, 0xef],
262 [0x1f, 0x1f],
263 ]
264 .iter()
265 .map(|&bytes| Word::new(Vec::from(bytes)))
266 .collect(),
267 },
268 ];
269
270 vmem.merge_sections(Some(2));
271 assert_eq!(vmem.sections, expected);
272 }
273
274 #[test]
275 fn section_data() {
276 let section = Section {
277 addr: 0x42,
278 data: [0x12, 0x23, 0x34, 0x45]
279 .iter()
280 .map(|&b| Word::new(vec![b]))
281 .collect(),
282 };
283 let expected =
284 [(0x42, 0x12), (0x46, 0x23), (0x4a, 0x34), (0x4e, 0x45)].map(|(addr, value)| Data {
285 addr,
286 value: Word::new(vec![value]),
287 });
288
289 let data: Vec<_> = section.data_addrs(4).collect();
290 assert_eq!(data, expected);
291 }
292
293 #[test]
294 fn section_stride() {
295 let section = Section {
296 addr: 0x15,
297 data: [0x12, 0x45, 0x78, 0xab]
298 .iter()
299 .map(|&b| Word::new(vec![b]))
300 .collect(),
301 };
302
303 let expected =
304 [(0x15, 0x12), (0x16, 0x45), (0x17, 0x78), (0x18, 0xab)].map(|(addr, value)| Data {
305 addr,
306 value: Word::new(vec![value]),
307 });
308 let data: Vec<_> = section.data_addrs(1).collect();
309 assert_eq!(data, expected);
310
311 let expected =
312 [(0x15, 0x12), (0x1a, 0x45), (0x1f, 0x78), (0x24, 0xab)].map(|(addr, value)| Data {
313 addr,
314 value: Word::new(vec![value]),
315 });
316 let data: Vec<_> = section.data_addrs(5).collect();
317 assert_eq!(data, expected);
318 }
319
320 #[test]
321 fn serialize() {
322 let input = r#"
323@000 DEADBEEF FACECAFE 01234567
324@010 00000000 11111111 22222222
325@234 A5A5A5A5 5A5A5A5A
326@ABC 01234567 89ABCDEF DEADBEEF
327 "#;
328 let vmem = Vmem::from_str(input, None).unwrap();
329 let dumped = vmem.to_string();
330 assert_eq!(dumped, input.trim());
331
332 let dumped = vmem.dump(Some(4), true);
333 let expected = r#"
334@000 DEADBEEF
335@004 FACECAFE
336@008 01234567
337@010 00000000
338@014 11111111
339@018 22222222
340@234 A5A5A5A5
341@238 5A5A5A5A
342@ABC 01234567
343@AC0 89ABCDEF
344@AC4 DEADBEEF
345 "#;
346 assert_eq!(dumped, expected.trim());
347 }
348}