micro_ihex/
parser.rs

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}