1use std::num::Wrapping;
2
3use crate::srecord::error::{ErrorType, SRecordParseError};
4use crate::srecord::record_type::RecordType;
5
6#[inline]
8pub(crate) fn parse_record_type(record_str: &str) -> Result<RecordType, SRecordParseError> {
9 let mut chars = record_str.chars();
10 match chars.next() {
11 Some('S') => match chars.next() {
12 Some('0') => Ok(RecordType::S0),
13 Some('1') => Ok(RecordType::S1),
14 Some('2') => Ok(RecordType::S2),
15 Some('3') => Ok(RecordType::S3),
16 Some('4') => Err(SRecordParseError {
17 error_type: ErrorType::S4Reserved,
18 }),
19 Some('5') => Ok(RecordType::S5),
20 Some('6') => Ok(RecordType::S6),
21 Some('7') => Ok(RecordType::S7),
22 Some('8') => Ok(RecordType::S8),
23 Some('9') => Ok(RecordType::S9),
24 Some(_) => Err(SRecordParseError {
25 error_type: ErrorType::InvalidRecordType,
26 }),
27 None => Err(SRecordParseError {
28 error_type: ErrorType::EolWhileParsingRecordType,
29 }),
30 },
31 Some(_) => Err(SRecordParseError {
32 error_type: ErrorType::InvalidFirstCharacter,
33 }),
34 None => Err(SRecordParseError {
35 error_type: ErrorType::EolWhileParsingRecordType,
36 }),
37 }
38}
39
40#[inline]
42pub(crate) fn parse_byte_count(record_str: &str) -> Result<u8, SRecordParseError> {
43 match record_str.get(2..4) {
44 Some(byte_count_str) => match u8::from_str_radix(byte_count_str, 16) {
45 Ok(i) => Ok(i),
46 Err(_) => Err(SRecordParseError {
47 error_type: ErrorType::InvalidByteCount,
48 }),
49 },
50 None => Err(SRecordParseError {
51 error_type: ErrorType::EolWhileParsingByteCount,
52 }),
53 }
54}
55
56#[inline]
58pub(crate) fn parse_address(
59 record_str: &str,
60 record_type: &RecordType,
61) -> Result<u64, SRecordParseError> {
62 let num_address_bytes = record_type.num_address_bytes();
63 let num_address_chars = num_address_bytes * 2;
64 let address_start_index = 4;
65 let address_end_index = address_start_index + num_address_chars;
66
67 match record_str.get(address_start_index..address_end_index) {
68 Some(address_str) => match u64::from_str_radix(address_str, 16) {
69 Ok(i) => Ok(i),
70 Err(_) => Err(SRecordParseError {
71 error_type: ErrorType::InvalidAddress,
72 }),
73 },
74 None => Err(SRecordParseError {
75 error_type: ErrorType::EolWhileParsingAddress,
76 }),
77 }
78}
79
80#[inline]
84pub(crate) fn parse_data_and_checksum(
85 record_str: &str,
86 record_type: &RecordType,
87 byte_count: &u8,
88 address: &u64,
89 data: &mut [u8],
90) -> Result<(), SRecordParseError> {
91 let num_address_bytes = record_type.num_address_bytes();
94 let num_data_bytes = match (*byte_count as usize).checked_sub(num_address_bytes + 1) {
95 Some(i) => i,
96 None => {
97 return Err(SRecordParseError {
98 error_type: ErrorType::ByteCountTooLowForRecordType,
99 })
100 }
101 };
102 let data = &mut data[..num_data_bytes];
103
104 let data_start_index = 2 + 2 + 2 * num_address_bytes; let data_end_index = data_start_index + num_data_bytes * 2;
107 match record_str.get(data_start_index..data_end_index) {
108 Some(data_str) => match hex::decode_to_slice(data_str, data) {
109 Ok(_) => {}
110 Err(_) => {
111 return Err(SRecordParseError {
112 error_type: ErrorType::InvalidData,
113 })
114 }
115 },
116 None => {
117 return Err(SRecordParseError {
118 error_type: ErrorType::EolWhileParsingData,
119 })
120 }
121 };
122
123 let checksum_start_index = data_end_index;
125 let checksum_end_index = checksum_start_index + 2;
126 let checksum: u8 = match record_str.get(checksum_start_index..checksum_end_index) {
127 Some(checksum_str) => match u8::from_str_radix(checksum_str, 16) {
128 Ok(i) => i,
129 Err(_) => {
130 return Err(SRecordParseError {
131 error_type: ErrorType::InvalidChecksum,
132 });
133 }
134 },
135 None => {
136 return Err(SRecordParseError {
137 error_type: ErrorType::EolWhileParsingChecksum,
138 });
139 }
140 };
141 let expected_checksum = calculate_checksum(byte_count, address, data);
142 if checksum != expected_checksum {
143 return Err(SRecordParseError {
144 error_type: ErrorType::CalculatedChecksumNotMatchingParsedChecksum,
145 });
146 }
147
148 if record_str.len() != checksum_end_index {
150 return Err(SRecordParseError {
151 error_type: ErrorType::LineNotTerminatedAfterChecksum,
152 });
153 }
154
155 Ok(())
156}
157
158pub fn calculate_checksum(byte_count: &u8, address: &u64, data: &[u8]) -> u8 {
176 let mut checksum = Wrapping(*byte_count);
177 for byte in address.to_be_bytes().iter() {
178 checksum += byte;
179 }
180 for byte in data.iter() {
181 checksum += byte;
182 }
183 0xFF - checksum.0
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn test_calculate_checksum() {
192 assert_eq!(
193 calculate_checksum(
194 &0x13,
195 &0x7AF0,
196 &[0x0A, 0x0A, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
197 ),
198 0x61
199 );
200 assert_eq!(
201 calculate_checksum(
202 &0x0F,
203 &0x0000,
204 &[0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x20, 0x20, 0x20, 0x20, 0, 0]
205 ),
206 0x3C
207 );
208 assert_eq!(
209 calculate_checksum(
210 &0x1F,
211 &0x0000,
212 &[
213 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x04, 0x94, 0x21, 0xFF, 0xF0, 0x7C,
214 0x6C, 0x1B, 0x78, 0x7C, 0x8C, 0x23, 0x78, 0x3C, 0x60, 0x00, 0x00, 0x38, 0x63,
215 0x00, 0x00
216 ]
217 ),
218 0x26
219 );
220 assert_eq!(
221 calculate_checksum(
222 &0x1F,
223 &0x001C,
224 &[
225 0x4B, 0xFF, 0xFF, 0xE5, 0x39, 0x80, 0x00, 0x00, 0x7D, 0x83, 0x63, 0x78, 0x80,
226 0x01, 0x00, 0x14, 0x38, 0x21, 0x00, 0x10, 0x7C, 0x08, 0x03, 0xA6, 0x4E, 0x80,
227 0x00, 0x20
228 ]
229 ),
230 0xE9
231 );
232 assert_eq!(
233 calculate_checksum(
234 &0x11,
235 &0x0038,
236 &[
237 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x2E, 0x0A,
238 0x00
239 ]
240 ),
241 0x42
242 );
243 assert_eq!(calculate_checksum(&0x03, &0x0003, &[]), 0xF9);
244 assert_eq!(calculate_checksum(&0x03, &0x0000, &[]), 0xFC);
245 }
246}