1use crate::checksum::checksum;
2use crate::types;
3use crate::IHex;
4use core::fmt;
5use core::iter::FusedIterator;
6use core::str::FromStr;
7
8#[derive(Debug, PartialEq)]
9pub enum ParseError {
10 MissingColon,
11 DecodeError(hex::FromHexError),
12 BadChecksum(u8, u8),
13 BadLength,
14 BadType,
15}
16
17impl fmt::Display for ParseError {
18 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19 match self {
20 ParseError::MissingColon => write!(f, "missing start colon"),
21 ParseError::DecodeError(e) => write!(f, "hex decode error: {}", e),
22 ParseError::BadChecksum(expected, checksum) => write!(
23 f,
24 "incorrect checksum: expected {}, got {}",
25 expected, checksum
26 ),
27 ParseError::BadLength => write!(f, "incorrect data length"),
28 ParseError::BadType => write!(f, "unknown record type"),
29 }
30 }
31}
32
33type ParseResult = Result<IHex, ParseError>;
34
35impl IHex {
36 pub fn parse<T: AsRef<[u8]>>(line: T) -> ParseResult {
37 let line = line.as_ref();
38
39 if line[0] != b':' {
40 return Err(ParseError::MissingColon);
41 }
42
43 let line = &line[1..];
44
45 let mut bytes = [0; 0x200];
46
47 let length = line.len() / 2;
48
49 if let Err(e) = hex::decode_to_slice(line, &mut bytes[..length]) {
50 return Err(ParseError::DecodeError(e));
51 }
52
53 let expected_checksum = bytes[length - 1];
54 let bytes = &bytes[..length - 1];
55
56 let checksum = checksum(bytes);
57
58 if checksum != expected_checksum {
59 return Err(ParseError::BadChecksum(expected_checksum, checksum));
60 }
61
62 let length = bytes[0];
63
64 let mut short = [0; 2];
65
66 short.clone_from_slice(&bytes[1..3]);
67 let address = u16::from_be_bytes(short);
68
69 let record_type = bytes[3];
70 let data = &bytes[4..];
71
72 if data.len() != length as usize {
73 return Err(ParseError::BadLength);
74 }
75
76 match record_type {
77 types::DATA => {
78 let mut bytes = [0; 0xFF];
79
80 bytes[..data.len()].clone_from_slice(data);
81
82 Ok(Self::Data {
83 bytes,
84 length,
85 offset: address,
86 })
87 }
88 types::END_OF_FILE => Ok(Self::EndOfFile),
89 types::EXTENDED_SEGMENT_ADDRESS => {
90 let mut short = [0; 2];
91
92 short.clone_from_slice(&data[0..2]);
93 let address = u16::from_be_bytes(short);
94
95 Ok(Self::ExtendedSegmentAddress(address))
96 }
97 types::START_SEGMENT_ADDRESS => {
98 let mut short = [0; 2];
99
100 short.clone_from_slice(&data[0..2]);
101 let cs = u16::from_be_bytes(short);
102
103 short.clone_from_slice(&data[2..4]);
104 let ip = u16::from_be_bytes(short);
105
106 Ok(Self::StartSegmentAddress { cs, ip })
107 }
108 types::EXTENDED_LINEAR_ADDRESS => {
109 let mut short = [0; 2];
110
111 short.clone_from_slice(&data[0..2]);
112 let ela = u16::from_be_bytes(short);
113
114 Ok(Self::ExtendedLinearAddress(ela))
115 }
116 types::START_LINEAR_ADDRESS => {
117 let mut word = [0; 4];
118
119 word.clone_from_slice(&data[0..4]);
120 let sla = u32::from_be_bytes(word);
121
122 Ok(Self::StartLinearAddress(sla))
123 }
124 _ => Err(ParseError::BadType),
125 }
126 }
127}
128
129impl FromStr for IHex {
130 type Err = ParseError;
131
132 fn from_str(s: &str) -> Result<Self, Self::Err> {
133 Self::parse(s)
134 }
135}
136
137pub struct Parser<'a> {
138 inner: core::str::Lines<'a>,
139 done: bool,
140}
141
142impl<'a> Parser<'a> {
143 pub fn new(s: &'a str) -> Self {
144 Parser {
145 inner: s.lines(),
146 done: false,
147 }
148 }
149
150 fn next_line(&mut self) -> Option<&'a str> {
151 for line in &mut self.inner {
152 if !line.is_empty() {
153 return Some(line);
154 }
155 }
156
157 None
158 }
159}
160
161impl<'a> Iterator for Parser<'a> {
162 type Item = ParseResult;
163
164 fn next(&mut self) -> Option<Self::Item> {
165 if self.done {
166 return None;
167 }
168
169 self.next_line().map(IHex::parse)
170 }
171}
172
173impl<'a> FusedIterator for Parser<'a> {}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 #[test]
180 fn parse_data() {
181 let expected = [
182 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x67, 0x61, 0x70,
183 ];
184
185 let mut bytes = [0; 0xFF];
186 bytes[..expected.len()].clone_from_slice(&expected);
187 let data = IHex::Data {
188 bytes,
189 length: expected.len() as u8,
190 offset: 0x0010,
191 };
192
193 assert_eq!(":0B0010006164647265737320676170A7".parse(), Ok(data));
194 }
195
196 #[test]
197 fn parse_eof() {
198 let eof = IHex::EndOfFile;
199
200 assert_eq!(":00000001FF".parse(), Ok(eof));
201 }
202
203 #[test]
204 fn parse_extended_segment_address() {
205 let esa = IHex::ExtendedSegmentAddress(0x12FE);
206
207 assert_eq!(":0200000212FEEC".parse(), Ok(esa));
208 }
209
210 #[test]
211 fn parse_start_segment_address() {
212 let ssa = IHex::StartSegmentAddress {
213 cs: 0x1234,
214 ip: 0x3800,
215 };
216
217 assert_eq!(":04000003123438007B".parse(), Ok(ssa));
218 }
219
220 #[test]
221 fn parse_extended_linear_address() {
222 let ela = IHex::ExtendedLinearAddress(0xABCD);
223
224 assert_eq!(":02000004ABCD82".parse(), Ok(ela));
225 }
226
227 #[test]
228 fn parse_start_linear_address() {
229 let sla = IHex::StartLinearAddress(0x12345678);
230
231 assert_eq!(":0400000512345678E3".parse(), Ok(sla));
232 }
233
234 #[test]
235 fn multi_line_parser() {
236 let ela = IHex::ExtendedLinearAddress(0xABCD);
237 let sla = IHex::StartLinearAddress(0x12345678);
238
239 let mut parser = Parser::new(":02000004ABCD82\r\n\r\n:0400000512345678E3\r\n");
240
241 assert_eq!(parser.next(), Some(Ok(ela)));
242 assert_eq!(parser.next(), Some(Ok(sla)));
243 assert_eq!(parser.next(), None)
244 }
245}