1#![warn(missing_docs)]
2
3use ansic::ansi;
16pub use records::Record;
17#[cfg(feature = "serde")]
18use serde::{Deserialize, Serialize};
19use std::cmp::{max, min};
20use std::collections::BTreeMap;
21use std::fmt::Display;
22use std::fs::{metadata, File};
23use std::io::{BufRead, BufReader, Read, Write};
24use std::ops::{Add, AddAssign, Range, RangeBounds};
25use std::path::{Path, PathBuf};
26use std::str::FromStr;
27
28use bytesize::ByteSize;
29pub use error::Error;
30pub use records::ext_tek_hex::ExtTekHexRecord;
31pub use records::ihex::IHexRecord;
32pub use records::srec::SRecord;
33use records::srec::{Address16, Address24, Address32, Count16, Count24, Data};
34use regex::Captures;
35use regex::Regex;
36pub use segments::Segment;
37use segments::Segments;
38
39mod checksums;
40mod error;
41mod records;
42mod segments;
43
44const TI_TXT_BYTES_PER_LINE: usize = 16;
45const EXT_TEK_HEX_BYTES_PER_LINE: u8 = 16;
46const VERILOG_VMEM_BYTES_PER_LINE: usize = 16;
47
48const PRINTABLE: &str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+, -./:;<=>?@[\\]^_`{|}~ ";
49
50const R: &str = ansi!(reset);
51const YELLOW: &str = ansi!(yellow);
52const MAGENTA: &str = ansi!(magenta);
53
54#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, Default, PartialOrd, Ord)]
55pub enum SRecordAddressLength {
58 Length16,
60 Length24,
62 #[default]
64 Length32,
65}
66
67impl Display for SRecordAddressLength {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 match self {
70 SRecordAddressLength::Length16 => write!(f, "16 Byte Length"),
71 SRecordAddressLength::Length24 => write!(f, "24 Byte Length"),
72 SRecordAddressLength::Length32 => write!(f, "32 Byte Length"),
73 }
74 }
75}
76
77#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, Default, PartialOrd, Ord)]
78pub enum IHexFormat {
81 IHex8,
83 IHex16,
85 #[default]
87 IHex32,
88}
89
90impl Display for IHexFormat {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 match self {
93 IHexFormat::IHex8 => write!(f, "Intel Hex 8"),
94 IHexFormat::IHex16 => write!(f, "Intel Hex 16"),
95 IHexFormat::IHex32 => write!(f, "Intel Hex 32"),
96 }
97 }
98}
99
100#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
102#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
103pub struct BinFile {
104 header: Option<Vec<u8>>,
105 execution_start_address: Option<usize>,
106 segments: Segments,
107 #[cfg_attr(feature = "serde", serde(skip_serializing))]
108 cursor: usize,
109}
110
111impl BinFile {
112 pub fn new() -> Self {
119 BinFile {
120 header: None,
121 execution_start_address: None,
122 segments: Segments::new(),
123 cursor: 0,
124 }
125 }
126
127 pub fn from_file<P>(file_name: P) -> Result<Self, Error>
140 where
141 P: AsRef<Path>,
142 {
143 let mut binfile = BinFile {
144 header: None,
145 execution_start_address: None,
146 segments: Segments::new(),
147 cursor: 0,
148 };
149 binfile.add_file(file_name, false)?;
150 Ok(binfile)
151 }
152
153 pub fn from_files<P, T>(file_names: P, overwrite: bool) -> Result<Self, Error>
167 where
168 P: AsRef<[T]>,
169 T: AsRef<Path>,
170 {
171 let mut binfile = BinFile::new();
172 for file_name in file_names.as_ref() {
173 binfile.add_file(file_name, overwrite)?;
174 }
175 Ok(binfile)
176 }
177
178 pub fn header(&self) -> Option<&Vec<u8>> {
180 self.header.as_ref()
181 }
182
183 pub fn set_header_string<S>(&mut self, header: S)
189 where
190 S: Into<String>,
191 {
192 let str: String = header.into();
193 self.header = Some(str.into());
194 }
195
196 pub fn execution_start_address(&self) -> Option<usize> {
198 self.execution_start_address
199 }
200
201 pub fn set_exexution_start_address(&mut self, address: usize) {
207 self.execution_start_address = Some(address);
208 }
209
210 pub fn segments(&self) -> &Segments {
297 &self.segments
298 }
299
300 pub fn segments_list(&self) -> Vec<(usize, Vec<u8>)> {
302 let mut list = Vec::new();
303 for segment in self.segments() {
304 let (address, data) = segment.get_tuple().to_owned();
305 list.push((address, data.to_vec()));
306 }
307 list
308 }
309
310 pub fn minimum_address(&self) -> Option<usize> {
312 self.segments.get_minimum_address()
313 }
314
315 pub fn maximum_address(&self) -> Option<usize> {
317 self.segments.get_maximum_address()
318 }
319
320 pub fn chunks(
328 &self,
329 size: Option<usize>,
330 alignment: Option<usize>,
331 ) -> Result<BTreeMap<usize, Vec<u8>>, Error> {
332 self.segments.chunks(size, alignment)
333 }
334
335 pub fn get_value_by_address(&self, address: usize) -> Option<u8> {
342 self.segments.get_value_by_address(address)
343 }
344
345 pub fn get_values_by_address_range<R>(&self, address: R) -> Option<Vec<u8>>
368 where
369 R: RangeBounds<usize>,
370 {
371 self.segments.get_values_by_address_range(&address)
372 }
373
374 pub fn add_strings<T, S>(&mut self, data: T, overwrite: bool) -> Result<(), Error>
383 where
384 T: AsRef<[S]>,
385 S: AsRef<str>,
386 {
387 if let Some(first_line) = data.as_ref().first() {
388 let data = data.as_ref();
389 if SRecord::is_record_str_correct(first_line) {
390 self.add_srec(data, overwrite)
391 } else if IHexRecord::is_record_str_correct(first_line) {
392 self.add_ihex(data, overwrite)
393 } else if ExtTekHexRecord::is_record_str_correct(first_line) {
394 self.add_ext_tek_hex(data, overwrite)
395 } else if is_ti_txt(data) {
396 self.add_ti_txt(data, overwrite)
397 } else if is_verilog_vmem(data) {
398 self.add_verilog_vmem(data, overwrite)
399 } else {
400 Err(Error::UnsupportedFileFormat)
401 }
402 } else {
403 Ok(())
404 }
405 }
406
407 pub fn add_srec<T, S>(&mut self, records: T, overwrite: bool) -> Result<(), Error>
420 where
421 T: AsRef<[S]>,
422 S: AsRef<str>,
423 {
424 for record_str in records.as_ref() {
425 match SRecord::from_record_string(record_str) {
426 Ok(record) => match record {
427 SRecord::Address16(address) => {
428 self.execution_start_address = Some(address.0 as usize)
429 }
430 SRecord::Header(header) => self.header = Some(header),
431 SRecord::Data16(data) => {
432 let address = data.address.0 as usize;
433 self.segments
434 .add_segment(Segment::new(address, data.data), overwrite)?
435 }
436 SRecord::Data24(data) => {
437 let address = data.address.0 as usize;
438 self.segments
439 .add_segment(Segment::new(address, data.data), overwrite)?
440 }
441 SRecord::Data32(data) => {
442 let address = data.address.0 as usize;
443 self.segments
444 .add_segment(Segment::new(address, data.data), overwrite)?
445 }
446 SRecord::Count16(_) => (),
447 SRecord::Count24(_) => (),
448 SRecord::Address32(address) => {
449 self.execution_start_address = Some(address.0 as usize)
450 }
451 SRecord::Address24(address) => {
452 self.execution_start_address = Some(address.0 as usize)
453 }
454 },
455 Err(err) => return Err(err),
456 }
457 }
458 Ok(())
459 }
460
461 pub fn add_ihex<T, S>(&mut self, data: T, overwrite: bool) -> Result<(), Error>
474 where
475 T: AsRef<[S]>,
476 S: AsRef<str>,
477 {
478 let mut extended_segment_address = 0;
479 let mut extended_linear_address = 0;
480 for record_str in data.as_ref() {
481 match IHexRecord::from_record_string(record_str) {
482 Ok(record) => match record {
483 IHexRecord::Data { offset, value } => {
484 let address =
485 offset as usize + extended_segment_address + extended_linear_address;
486 self.segments
487 .add_segment(Segment::new(address, value), overwrite)?;
488 }
489 IHexRecord::EndOfFile => break,
490 IHexRecord::ExtendedSegmentAddress(address) => {
491 extended_segment_address = address as usize * 16
492 }
493 IHexRecord::StartSegmentAddress(address) => {
494 self.execution_start_address = Some(address as usize)
495 }
496 IHexRecord::ExtendedLinearAddress(address) => {
497 extended_linear_address = (address as usize) << 16
498 }
499 IHexRecord::StartLinearAddress(address) => {
500 self.execution_start_address = Some(address as usize)
501 }
502 },
503 Err(err) => return Err(err),
504 }
505 }
506 Ok(())
507 }
508
509 pub fn add_ext_tek_hex<T, S>(&mut self, data: T, overwrite: bool) -> Result<(), Error>
522 where
523 T: AsRef<[S]>,
524 S: AsRef<str>,
525 {
526 for record_str in data.as_ref() {
527 match ExtTekHexRecord::from_record_string(record_str)? {
528 ExtTekHexRecord::Data { address, value } => {
529 self.segments
530 .add_segment(Segment::new(address, value), overwrite)?;
531 }
532 ExtTekHexRecord::Termination { start_address } => {
533 self.execution_start_address = Some(start_address);
534 break;
535 }
536 ExtTekHexRecord::Symbol(_) => (),
537 }
538 }
539 Ok(())
540 }
541
542 pub fn add_ti_txt<T, S>(&mut self, data: T, overwrite: bool) -> Result<(), Error>
555 where
556 T: AsRef<[S]>,
557 S: AsRef<str>,
558 {
559 let mut address = None;
560 let mut eof_found = false;
561
562 for line in data.as_ref() {
563 if eof_found {
564 return Err(Error::UnsupportedFileFormat);
565 }
566 let line = line.as_ref().trim();
567
568 if line.is_empty() {
569 return Err(Error::RecordTooShort);
570 }
571 if line.starts_with('q') {
572 eof_found = true;
573 } else if line.starts_with('@') {
574 if let Ok(add) = u32::from_str_radix(line.trim_start_matches('@'), 16) {
575 address = Some(add as usize);
576 } else {
577 return Err(Error::ContainsInvalidCharacters);
578 }
579 } else {
580 let mut data_bytes = Vec::new();
581 for byte_str in line
583 .replace(' ', "")
584 .as_bytes()
585 .chunks(2)
586 .map(|chunk| std::str::from_utf8(chunk))
587 {
588 if let Ok(value) = u8::from_str_radix(byte_str?, 16) {
589 data_bytes.push(value);
590 } else {
591 return Err(Error::ContainsInvalidCharacters);
592 }
593 }
594 let size = data_bytes.len();
595
596 if size > TI_TXT_BYTES_PER_LINE {
597 return Err(Error::RecordTooLong);
598 }
599 if let Some(addr) = address {
600 self.segments
601 .add_segment(Segment::new(addr, data_bytes), overwrite)?;
602 if size == TI_TXT_BYTES_PER_LINE {
603 address = Some(addr + size);
604 } else {
605 address = None;
606 }
607 } else {
608 return Err(Error::MissingStartCode);
609 }
610 }
611 }
612 if !eof_found {
613 return Err(Error::UnsupportedFileFormat);
614 }
615 Ok(())
616 }
617
618 pub fn add_verilog_vmem<T, S>(&mut self, data: T, overwrite: bool) -> Result<(), Error>
631 where
632 T: AsRef<[S]>,
633 S: AsRef<str>,
634 {
635 let mut address = None;
636 let regex = Regex::new(r"\s+")?;
637 let mut chunk = Vec::new();
638 let data: Vec<&str> = data.as_ref().iter().map(|val| val.as_ref()).collect();
639 let data = data.join("\n\r");
640 let comment_removed_string = comment_remover(&data);
641 let words = regex.split(comment_removed_string.trim());
642 let mut word_size_bytes = None;
643 let words: Vec<&str> = words.into_iter().collect();
644
645 for word in &words {
646 if !word.starts_with('@') {
647 let mut length = word.len();
648
649 if length % 2 != 0 {
650 return Err(Error::UnsupportedFileFormat);
651 }
652 length /= 2;
653 if let Some(wsl) = word_size_bytes {
654 if wsl != length {
655 return Err(Error::UnsupportedFileFormat);
656 }
657 } else {
658 word_size_bytes = Some(length);
659 }
660 }
661 }
662 for word in words {
663 if word.starts_with('@') {
664 if let Some(addr) = address {
665 self.segments
666 .add_segment(Segment::new(addr, chunk), overwrite)?;
667 }
668 address = usize::from_str_radix(word.trim_start_matches('@'), 16).ok();
669 chunk = Vec::new();
670 } else {
671 let data_bytes: Result<Vec<u8>, Error> = word
672 .replace(' ', "")
673 .as_bytes()
674 .chunks(2)
675 .map(|chunk| {
676 let byte_str = std::str::from_utf8(chunk).map_err(Error::from)?;
677 u8::from_str_radix(byte_str, 16)
678 .map_err(|_| Error::ContainsInvalidCharacters)
679 })
680 .collect();
682 chunk.append(&mut data_bytes?);
683 }
684 }
685 if let Some(addr) = address {
686 if !chunk.is_empty() {
687 self.segments
688 .add_segment(Segment::new(addr, chunk), overwrite)?;
689 }
690 }
691 Ok(())
692 }
693
694 pub fn add_bytes<T>(
708 &mut self,
709 data: T,
710 address: Option<usize>,
711 overwrite: bool,
712 ) -> Result<(), Error>
713 where
714 T: AsRef<[u8]>,
715 {
716 let address = address.unwrap_or(0);
717 self.segments
718 .add_segment(Segment::new(address, data), overwrite)
719 }
720
721 pub fn add_file<P>(&mut self, file_name: P, overwrite: bool) -> Result<(), Error>
735 where
736 P: AsRef<Path>,
737 {
738 self.add_strings(self._open_input(file_name)?.as_slice(), overwrite)?;
739 Ok(())
740 }
741
742 pub fn add_srec_file<P>(&mut self, file_name: P, overwrite: bool) -> Result<(), Error>
756 where
757 P: AsRef<Path>,
758 {
759 self.add_srec(self._open_input(file_name)?.as_slice(), overwrite)?;
760 Ok(())
761 }
762
763 pub fn add_ihex_file<P>(&mut self, file_name: P, overwrite: bool) -> Result<(), Error>
776 where
777 P: AsRef<Path>,
778 {
779 self.add_ihex(self._open_input(file_name)?.as_slice(), overwrite)?;
780 Ok(())
781 }
782
783 pub fn add_ext_tek_hex_file<P>(&mut self, file_name: P, overwrite: bool) -> Result<(), Error>
796 where
797 P: AsRef<Path>,
798 {
799 self.add_ext_tek_hex(self._open_input(file_name)?.as_slice(), overwrite)?;
800 Ok(())
801 }
802
803 pub fn add_ti_txt_file<P>(&mut self, file_name: P, overwrite: bool) -> Result<(), Error>
816 where
817 P: AsRef<Path>,
818 {
819 self.add_ti_txt(self._open_input(file_name)?.as_slice(), overwrite)?;
820 Ok(())
821 }
822
823 pub fn add_verilog_vmem_file<P>(&mut self, file_name: P, overwrite: bool) -> Result<(), Error>
836 where
837 P: AsRef<Path>,
838 {
839 self.add_verilog_vmem(self._open_input(file_name)?.as_slice(), overwrite)?;
840 Ok(())
841 }
842
843 fn _open_input<P>(&self, file_name: P) -> Result<Vec<String>, Error>
853 where
854 P: AsRef<Path>,
855 {
856 if let Ok(file) = File::open(file_name) {
857 let reader = BufReader::new(file);
858 let data: Vec<String> = reader.lines().map_while(Result::ok).collect();
859 Ok(data)
860 } else {
861 Err(Error::IoError)
862 }
863 }
864
865 pub fn add_binary_file<P>(
879 &mut self,
880 file_name: P,
881 address: Option<usize>,
882 overwrite: bool,
883 ) -> Result<(), Error>
884 where
885 P: AsRef<Path>,
886 {
887 if let Ok(mut file) = File::open(&file_name) {
888 let metadata_len = metadata(&file_name).map(|md| md.len()).unwrap_or(0) as usize;
889 let mut data: Vec<u8> = Vec::with_capacity(metadata_len);
890 if let Ok(n) = file.read_to_end(&mut data) {
891 self.add_bytes(&data[..n], address, overwrite)?;
892 } else {
893 return Err(Error::IoError);
894 }
895 return Ok(());
896 }
897 Err(Error::IoError)
898 }
899
900 fn to_srec_int(
901 &self,
902 number_of_data_bytes: Option<usize>,
903 address_length_bits: SRecordAddressLength,
904 pretty: bool,
905 ) -> Result<Vec<String>, Error> {
906 if let Some(maximum_address) = self.maximum_address() {
907 match address_length_bits {
908 SRecordAddressLength::Length16 => {
909 if maximum_address > 0xFFFF {
910 return Err(Error::AddressTooBig);
911 }
912 }
913 SRecordAddressLength::Length24 => {
914 if maximum_address > 0xFFFFFF {
915 return Err(Error::AddressTooBig);
916 }
917 }
918 SRecordAddressLength::Length32 => {
919 if maximum_address > 0xFFFFFFFF {
920 return Err(Error::AddressTooBig);
921 }
922 }
923 }
924 }
925 let number_of_data_bytes = number_of_data_bytes.unwrap_or(32);
926 let mut lines = Vec::new();
927 if let Some(header) = &self.header {
928 let record = SRecord::Header(header.to_owned());
929 if pretty {
930 lines.push(record.to_pretty_record_string()?)
931 } else {
932 lines.push(record.to_record_string()?)
933 }
934 }
935 let mut number_of_records = 0;
936 let data = self.segments.chunks(Some(number_of_data_bytes), None)?;
937 for (address, datas) in data {
938 let record = match address_length_bits {
939 SRecordAddressLength::Length16 => SRecord::Data16(Data {
940 address: Address16(address as u16),
941 data: datas,
942 }),
943 SRecordAddressLength::Length24 => SRecord::Data24(Data {
944 address: Address24(address as u32),
945 data: datas,
946 }),
947 SRecordAddressLength::Length32 => SRecord::Data32(Data {
948 address: Address32(address as u32),
949 data: datas,
950 }),
951 };
952 if pretty {
953 lines.push(record.to_pretty_record_string()?)
954 } else {
955 lines.push(record.to_record_string()?)
956 }
957 number_of_records += 1;
958 }
959 let record = if number_of_records <= 0xFFFF {
960 SRecord::Count16(Count16(number_of_records as u16))
961 } else {
962 SRecord::Count24(Count24(number_of_records))
963 };
964 if pretty {
965 lines.push(record.to_pretty_record_string()?)
966 } else {
967 lines.push(record.to_record_string()?)
968 }
969
970 if let Some(execution_start_address) = self.execution_start_address {
972 let record = match address_length_bits {
973 SRecordAddressLength::Length16 => {
974 SRecord::Address16(Address16(execution_start_address as u16))
975 }
976 SRecordAddressLength::Length24 => {
977 SRecord::Address24(Address24(execution_start_address as u32))
978 }
979 SRecordAddressLength::Length32 => {
980 SRecord::Address32(Address32(execution_start_address as u32))
981 }
982 };
983 if pretty {
984 lines.push(record.to_pretty_record_string()?)
985 } else {
986 lines.push(record.to_record_string()?)
987 }
988 }
989 Ok(lines)
990 }
991
992 pub fn to_srec(
1034 &self,
1035 number_of_data_bytes: Option<usize>,
1036 address_length_bits: SRecordAddressLength,
1037 ) -> Result<Vec<String>, Error> {
1038 self.to_srec_int(number_of_data_bytes, address_length_bits, false)
1039 }
1040
1041 pub fn to_srec_pretty(
1079 &self,
1080 number_of_data_bytes: Option<usize>,
1081 address_length_bits: SRecordAddressLength,
1082 ) -> Result<Vec<String>, Error> {
1083 self.to_srec_int(number_of_data_bytes, address_length_bits, true)
1084 }
1085
1086 fn to_ihex_int(
1087 &self,
1088 number_of_bytes: Option<usize>,
1089 ihex_format: IHexFormat,
1090 pretty: bool,
1091 ) -> Result<Vec<String>, Error> {
1092 let i32hex = |address: usize,
1093 extended_linear_address: u32,
1094 lines: &mut Vec<String>|
1095 -> Result<(usize, u32), Error> {
1096 if address > 0xffffffff {
1097 return Err(Error::AddressTooBig);
1098 }
1099 let mut extended_linear_address = extended_linear_address;
1100 let address_upper_16_bits = (address >> 16) & 0xffff;
1101 if address_upper_16_bits > extended_linear_address as usize {
1102 extended_linear_address = address_upper_16_bits as u32;
1103 let record = IHexRecord::ExtendedLinearAddress(address_upper_16_bits as u16);
1104 lines.push(record.to_record_string().unwrap());
1105 }
1106 Ok((address, extended_linear_address))
1107 };
1108 let i16hex = |address: usize,
1109 extended_segment_address: u32,
1110 lines: &mut Vec<String>|
1111 -> Result<(usize, u32), Error> {
1112 if address > 16 * 0xffff + 0xffff {
1113 return Err(Error::AddressTooBig);
1114 }
1115 let mut extended_segment_address = extended_segment_address;
1116 let mut address_lower = address - 16 * extended_segment_address as usize;
1117
1118 if address_lower > 0xffff {
1119 extended_segment_address = 4096 * (address >> 16) as u32;
1120 if extended_segment_address > 0xffff {
1121 extended_segment_address = 0xffff;
1122 }
1123 address_lower = address - 16 * extended_segment_address as usize;
1124 let record = IHexRecord::ExtendedSegmentAddress(extended_segment_address as u16);
1125 lines.push(record.to_record_string().unwrap());
1126 }
1127 Ok((address_lower, extended_segment_address))
1128 };
1129 let i8hex = |address: usize| -> Result<(), Error> {
1130 if address > 0xffff {
1131 return Err(Error::AddressTooBig);
1132 }
1133 Ok(())
1134 };
1135
1136 let mut lines = Vec::new();
1137 let mut extended_segment_address = 0;
1138 let mut extended_linear_address = 0;
1139 let number_of_data_words = number_of_bytes.unwrap_or(32);
1140
1141 for (address, data) in self.segments.chunks(Some(number_of_data_words), None)? {
1142 let mut address = address;
1143 match ihex_format {
1144 IHexFormat::IHex8 => i8hex(address)?,
1145 IHexFormat::IHex16 => {
1146 (address, extended_segment_address) =
1147 i16hex(address, extended_segment_address, &mut lines)?
1148 }
1149 IHexFormat::IHex32 => {
1150 (address, extended_linear_address) =
1151 i32hex(address, extended_linear_address, &mut lines)?
1152 }
1153 }
1154 let record = IHexRecord::Data {
1155 offset: address as u16,
1156 value: data,
1157 };
1158 if pretty {
1159 lines.push(record.to_pretty_record_string()?)
1160 } else {
1161 lines.push(record.to_record_string()?)
1162 }
1163 }
1164 if let Some(start_address) = self.execution_start_address {
1165 match ihex_format {
1166 IHexFormat::IHex8 => (),
1167 IHexFormat::IHex16 => {
1168 let record = IHexRecord::StartSegmentAddress(start_address as u32);
1169 if pretty {
1170 lines.push(record.to_pretty_record_string()?)
1171 } else {
1172 lines.push(record.to_record_string()?)
1173 }
1174 }
1175 IHexFormat::IHex32 => {
1176 let record = IHexRecord::StartLinearAddress(start_address as u32);
1177 if pretty {
1178 lines.push(record.to_pretty_record_string()?)
1179 } else {
1180 lines.push(record.to_record_string()?)
1181 }
1182 }
1183 }
1184 }
1185 if pretty {
1186 lines.push(IHexRecord::EndOfFile.to_pretty_record_string()?)
1187 } else {
1188 lines.push(IHexRecord::EndOfFile.to_record_string()?)
1189 }
1190 Ok(lines)
1191 }
1192
1193 pub fn to_ihex(
1229 &self,
1230 number_of_bytes: Option<usize>,
1231 ihex_format: IHexFormat,
1232 ) -> Result<Vec<String>, Error> {
1233 self.to_ihex_int(number_of_bytes, ihex_format, false)
1234 }
1235
1236 pub fn to_ihex_pretty(
1268 &self,
1269 number_of_bytes: Option<usize>,
1270 ihex_format: IHexFormat,
1271 ) -> Result<Vec<String>, Error> {
1272 self.to_ihex_int(number_of_bytes, ihex_format, true)
1273 }
1274
1275 fn to_ext_tek_hex_int(
1276 &self,
1277 number_of_bytes_per_line: Option<u8>,
1278 pretty: bool,
1279 ) -> Result<Vec<String>, Error> {
1280 let mut lines = Vec::new();
1281 let number_of_data_words =
1282 number_of_bytes_per_line.unwrap_or(EXT_TEK_HEX_BYTES_PER_LINE) as usize;
1283 if number_of_data_words > 255 - 6 {
1284 Err(Error::RecordTooLong)
1285 } else {
1286 for segment in self.segments.segments() {
1287 for (address, data) in segment.chunks(Some(number_of_data_words), None)? {
1288 let record = ExtTekHexRecord::Data {
1289 address,
1290 value: data,
1291 };
1292 if pretty {
1293 lines.push(record.to_pretty_record_string()?);
1294 } else {
1295 lines.push(record.to_record_string()?);
1296 }
1297 }
1298 }
1299 Ok(lines)
1300 }
1301 }
1302
1303 pub fn to_ext_tek_hex(
1331 &self,
1332 number_of_bytes_per_line: Option<u8>,
1333 ) -> Result<Vec<String>, Error> {
1334 self.to_ext_tek_hex_int(number_of_bytes_per_line, false)
1335 }
1336
1337 pub fn to_ext_tek_hex_pretty(
1362 &self,
1363 number_of_bytes_per_line: Option<u8>,
1364 ) -> Result<Vec<String>, Error> {
1365 self.to_ext_tek_hex_int(number_of_bytes_per_line, true)
1366 }
1367
1368 fn to_ti_txt_int(&self, pretty: bool) -> Result<Vec<String>, Error> {
1369 let mut lines = Vec::new();
1370 let number_of_data_words = TI_TXT_BYTES_PER_LINE;
1371
1372 for segment in self.segments.segments() {
1373 lines.push(if pretty {
1374 format!(
1375 "{YELLOW}@{:04X}{R} (segment address)",
1376 segment.minimum_address()
1377 )
1378 } else {
1379 format!("@{:04X}", segment.minimum_address())
1380 });
1381 for (_, data) in segment.chunks(Some(number_of_data_words), None)? {
1382 let mut string = String::new();
1383 for value in data {
1384 string += format!("{:02X} ", value).as_str();
1385 }
1386 lines.push(if pretty {
1387 format!("{} (data)", string.trim_end())
1388 } else {
1389 string.trim_end().to_string()
1390 });
1391 }
1392 }
1393 if pretty {
1394 lines.push(format!("{MAGENTA}q{R} (end of file)"))
1395 } else {
1396 lines.push("q".into());
1397 }
1398 Ok(lines)
1399 }
1400
1401 pub fn to_ti_txt(&self) -> Result<Vec<String>, Error> {
1435 self.to_ti_txt_int(false)
1436 }
1437
1438 pub fn to_ti_txt_pretty(&self) -> Result<Vec<String>, Error> {
1475 self.to_ti_txt_int(true)
1476 }
1477
1478 pub fn to_verilog_vmem(&self) -> Result<Vec<String>, Error> {
1504 let mut lines = Vec::new();
1505 if let Some(header) = self.header.clone() {
1506 lines.push(format!("/* {} */", String::from_utf8(header).unwrap()));
1507 }
1508 for segment in self.segments.segments() {
1509 for (address, data) in segment.chunks(Some(VERILOG_VMEM_BYTES_PER_LINE), None)? {
1510 let mut string = format!("@{:08X}", address);
1511 for value in data {
1512 string += format!(" {:02x}", value).as_str();
1513 }
1514 }
1515 }
1516 Ok(lines)
1517 }
1518
1519 pub fn to_bytes<R>(&self, range: R, padding: Option<u8>) -> Result<Vec<u8>, Error>
1546 where
1547 R: RangeBounds<usize>,
1548 {
1549 if self.segments.segments().is_empty() {
1550 return Ok(Vec::new());
1551 }
1552 let mut current_maximum_address = match range.start_bound() {
1553 std::ops::Bound::Included(val) => *val,
1554 std::ops::Bound::Excluded(val) => *val + 1,
1555 std::ops::Bound::Unbounded => self.minimum_address().unwrap(),
1556 };
1557 let maximum_address = match range.end_bound() {
1558 std::ops::Bound::Included(val) => *val + 1,
1559 std::ops::Bound::Excluded(val) => *val,
1560 std::ops::Bound::Unbounded => self.maximum_address().unwrap(),
1561 };
1562
1563 if current_maximum_address >= maximum_address {
1564 return Ok(Vec::new());
1566 }
1567 let padding = padding.unwrap_or(0xFF);
1568 let mut binary = Vec::new();
1569
1570 for segment in self.segments.segments() {
1571 let (mut address, data) = segment.get_tuple();
1572 let mut data = data.to_vec();
1573 let mut length = data.len();
1574
1575 if address < current_maximum_address {
1577 if address + length <= current_maximum_address {
1578 continue;
1579 }
1580 let offset = current_maximum_address - address;
1581 data = data[offset..].to_vec();
1582 length = data.len();
1583 address = current_maximum_address;
1584 }
1585
1586 if address + length > maximum_address {
1588 if address < maximum_address {
1589 let size = maximum_address - address;
1590 data = data[..size].to_vec();
1591 } else if maximum_address > current_maximum_address {
1592 let mut pad_vec = vec![padding; maximum_address - current_maximum_address];
1593 binary.append(&mut pad_vec);
1594 }
1595 }
1596 if current_maximum_address < address {
1597 let mut pad_vec = vec![padding; address - current_maximum_address];
1598 binary.append(&mut pad_vec);
1599 }
1600 binary.append(&mut data);
1601 current_maximum_address = address + length;
1602 }
1603 Ok(binary)
1604 }
1605
1606 pub fn to_array_str<R>(
1640 &self,
1641 range: R,
1642 padding: Option<u8>,
1643 separator: Option<&str>,
1644 ) -> Result<String, Error>
1645 where
1646 R: RangeBounds<usize>,
1647 {
1648 let binary_data = self.to_bytes(range, padding)?;
1649 let separator = separator.unwrap_or(", ");
1650 let mut string = String::new();
1651 for value in binary_data {
1652 string += format!("0x{:02x}{}", value, separator).as_str();
1653 }
1654 Ok(string.trim_end_matches(separator).to_string())
1655 }
1656
1657 pub fn to_hexdump(&self) -> Result<Vec<String>, Error> {
1685 if self.is_empty() {
1686 Ok(Vec::new())
1687 } else {
1688 let not_dot_characters = PRINTABLE;
1689 let align_to_line = |address: usize| address - (address % 16);
1690 let padding = |length: usize| -> Vec<Option<u8>> { vec![None; length] };
1691 let format_line = |address: usize, data: &Vec<Option<u8>>| -> String {
1692 let mut data = data.clone();
1693 let mut padding = padding(16 - data.len());
1694 data.append(&mut padding);
1695
1696 let mut hexdata = Vec::new();
1697 for byte in &data {
1698 let elem = if let Some(byte) = byte {
1699 format!("{:02x}", byte)
1700 } else {
1701 " ".to_string()
1702 };
1703 hexdata.push(elem);
1704 }
1705 let first_half = hexdata[..8].join(" ");
1706 let second_half = hexdata[8..].join(" ");
1707 let mut text = String::new();
1708
1709 for byte in data {
1710 if let Some(byte) = byte {
1711 if not_dot_characters.contains(byte as char) {
1712 text += format!("{}", byte as char).as_str();
1713 } else {
1714 text += "."
1715 }
1716 } else {
1717 text += " "
1718 }
1719 }
1720 format!(
1721 "{:08x} {:23} {:23} |{:16}|",
1722 address, first_half, second_half, text
1723 )
1724 };
1725 let mut lines = Vec::new();
1726 let mut line_address = align_to_line(self.minimum_address().unwrap());
1727 let mut line_data = Vec::new();
1728
1729 for (address, data) in self.segments.chunks(Some(16), Some(16))? {
1730 let aligned_chunk_address = align_to_line(address);
1731 if aligned_chunk_address > line_address {
1732 lines.push(format_line(line_address, &line_data));
1733 if aligned_chunk_address > line_address + 16 {
1734 lines.push("...".to_string());
1735 }
1736 line_address = aligned_chunk_address;
1737 line_data.clear();
1738 }
1739 let mut padding = padding(address - line_address - line_data.len());
1740 line_data.append(&mut padding);
1741 for value in data {
1742 line_data.push(Some(value));
1743 }
1744 }
1745 lines.push(format_line(line_address, &line_data));
1746 Ok(lines)
1747 }
1748 }
1749
1750 pub fn fill(&mut self, value: Option<u8>, max_words: Option<usize>) -> Result<(), Error> {
1760 let value = value.unwrap_or(0xff);
1761 let mut previous_segment_maximum_address = None;
1762 let mut fill_segments = Vec::new();
1763
1764 for segment in self.segments.segments() {
1765 let address = segment.minimum_address();
1766 let maximum_address = address + segment.len();
1767
1768 if let Some(previous_segment_maximum_address) = previous_segment_maximum_address {
1769 let fill_size = address - previous_segment_maximum_address;
1770
1771 if max_words.is_none() || fill_size <= max_words.unwrap() {
1772 fill_segments.push(Segment::new(
1773 previous_segment_maximum_address,
1774 vec![value; fill_size],
1775 ));
1776 }
1777 }
1778 previous_segment_maximum_address = Some(maximum_address);
1779 }
1780 for segment in fill_segments {
1781 self.segments.add_segment(segment, false)?;
1782 }
1783 Ok(())
1784 }
1785
1786 pub fn exclude(&mut self, range: Range<usize>) -> Result<(), Error> {
1792 if range.end < range.start {
1793 Err(Error::InvalidAddressRange)
1794 } else {
1795 self.segments.remove(range);
1796 Ok(())
1797 }
1798 }
1799
1800 pub fn crop(&mut self, range: Range<usize>) -> Result<(), Error> {
1806 let maximum_address = self.segments.get_maximum_address().unwrap();
1807 self.segments.remove(0..range.start);
1808 self.segments.remove(range.end..maximum_address);
1809 Ok(())
1810 }
1811
1812 pub fn len(&self) -> usize {
1814 let mut len = 0;
1815 for segment in self.segments.segments() {
1816 len += segment.len()
1817 }
1818 len
1819 }
1820
1821 pub fn is_empty(&self) -> bool {
1823 self.segments.segments().is_empty()
1824 }
1825
1826 pub fn info(&self) -> String {
1848 let mut info = String::new();
1849 if let Some(header) = self.header.as_ref() {
1850 info += format!(
1851 "Header: \"{}\"\n",
1852 String::from_utf8(header.to_vec()).unwrap()
1853 )
1854 .as_str()
1855 }
1856 if let Some(execution_start_address) = self.execution_start_address {
1857 info += format!(
1858 "Execution start address: 0x{:08x}\n",
1859 execution_start_address
1860 )
1861 .as_str()
1862 }
1863 info += "Data ranges:\n\n";
1864 for segment in self.segments() {
1865 let minimum_address = segment.minimum_address();
1866 let size = segment.len();
1867 let maximum_address = minimum_address + size;
1868 info += format!(
1869 " 0x{:08x} - 0x{:08x} ({})\n",
1870 minimum_address,
1871 maximum_address,
1872 ByteSize(size as u64)
1873 )
1874 .as_str();
1875 }
1876 info
1877 }
1878
1879 pub fn layout(&self) -> Result<String, Error> {
1905 if let (Some(minimum_address), Some(maximum_address)) =
1906 (self.minimum_address(), self.maximum_address())
1907 {
1908 let size = maximum_address - minimum_address;
1909 let width = min(80, size);
1910 let mut chunk_address = minimum_address;
1911 let chunk_size = size / width;
1912 let minimum_address = format!("0x{:x}", minimum_address);
1913 let maximum_address = format!("0x{:x}", maximum_address);
1914 let padding = " ".repeat(width - minimum_address.len() - maximum_address.len());
1915 let mut output = format!("{}{}{}\n", minimum_address, padding, maximum_address);
1916 for i in 0..width {
1917 let mut chunk = BinFile::try_from(self)?;
1918
1919 let maximum_address = if i < width - 1 {
1920 chunk_address + chunk_size
1921 } else {
1922 chunk.maximum_address().unwrap()
1923 };
1924 chunk.crop(chunk_address..maximum_address)?;
1925
1926 if chunk.is_empty() {
1927 output += " ";
1928 } else if chunk.len() != maximum_address - chunk_address {
1929 output += "-";
1930 } else {
1931 output += "=";
1932 }
1933 chunk_address += chunk_size;
1934 }
1935 Ok(output)
1936 } else {
1937 Ok("Empty File".into())
1938 }
1939 }
1940}
1941
1942impl Default for BinFile {
1943 fn default() -> Self {
1944 Self::new()
1945 }
1946}
1947
1948impl TryFrom<PathBuf> for BinFile {
1949 type Error = Error;
1950
1951 fn try_from(file_name: PathBuf) -> Result<Self, Self::Error> {
1952 Self::from_file(file_name)
1953 }
1954}
1955
1956impl TryFrom<&PathBuf> for BinFile {
1957 type Error = Error;
1958
1959 fn try_from(file_name: &PathBuf) -> Result<Self, Self::Error> {
1960 Self::from_file(file_name)
1961 }
1962}
1963
1964impl TryFrom<&Path> for BinFile {
1965 type Error = Error;
1966
1967 fn try_from(file_name: &Path) -> Result<Self, Self::Error> {
1968 Self::from_file(file_name)
1969 }
1970}
1971
1972impl TryFrom<&BinFile> for BinFile {
1973 type Error = Error;
1974 fn try_from(value: &BinFile) -> Result<Self, Self::Error> {
1975 let mut binfile = BinFile::new();
1976 binfile.add_srec(value.to_srec(None, SRecordAddressLength::default())?, false)?;
1977 Ok(binfile)
1978 }
1979}
1980
1981impl Add for BinFile {
1982 type Output = Self;
1983
1984 fn add(self, rhs: Self) -> Self::Output {
1985 let mut binfile = self;
1986 let _ = binfile.add_srec(
1987 rhs.to_srec(None, SRecordAddressLength::default()).unwrap(),
1988 false,
1989 );
1990 binfile
1991 }
1992}
1993
1994impl AddAssign for BinFile {
1995 fn add_assign(&mut self, rhs: Self) {
1996 let _ = self.add_srec(
1997 rhs.to_srec(None, SRecordAddressLength::default()).unwrap(),
1998 false,
1999 );
2000 }
2001}
2002
2003impl Add<&[String]> for BinFile {
2004 type Output = Self;
2005
2006 fn add(self, rhs: &[String]) -> Self::Output {
2007 let mut binfile = self;
2008 let _ = binfile.add_strings(rhs, false);
2009 binfile
2010 }
2011}
2012
2013impl AddAssign<&[String]> for BinFile {
2014 fn add_assign(&mut self, rhs: &[String]) {
2015 let _ = self.add_strings(rhs, false);
2016 }
2017}
2018
2019impl Display for BinFile {
2020 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2021 write!(f, "{}", self.segments)
2022 }
2023}
2024
2025impl Read for BinFile {
2026 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2027 if self.is_empty() {
2028 Ok(0)
2029 } else {
2030 let minimum_address = max(self.minimum_address().unwrap(), self.cursor);
2031 let max_buffer_address = minimum_address + buf.len();
2032 if max_buffer_address < self.maximum_address().unwrap() {
2033 self.cursor = max_buffer_address;
2034 } else {
2035 self.cursor = 0;
2036 }
2037 let maximum_address = min(max_buffer_address, self.maximum_address().unwrap());
2038 match self.to_bytes(minimum_address..maximum_address, None) {
2039 Ok(values) => {
2040 for (pos, val) in values.iter().enumerate() {
2041 buf[pos] = *val;
2042 }
2043 Ok(maximum_address - minimum_address)
2044 }
2045 Err(err) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, err)),
2046 }
2047 }
2048 }
2049}
2050
2051impl Write for BinFile {
2052 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
2053 match self.add_bytes(buf, None, false) {
2054 Ok(_) => Ok(buf.len()),
2055 Err(err) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, err)),
2056 }
2057 }
2058
2059 fn flush(&mut self) -> std::io::Result<()> {
2060 Ok(())
2061 }
2062}
2063
2064impl FromStr for BinFile {
2065 type Err = Error;
2066
2067 fn from_str(s: &str) -> Result<Self, Self::Err> {
2068 let mut binfile = BinFile {
2069 header: None,
2070 execution_start_address: None,
2071 segments: Segments::new(),
2072 cursor: 0,
2073 };
2074 let lines: Vec<&str> = s.lines().collect();
2075 binfile.add_strings(lines, false)?;
2076 Ok(binfile)
2077 }
2078}
2079
2080fn comment_remover(data: &str) -> String {
2081 let regex =
2082 regex::RegexBuilder::new(r#"//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*""#)
2083 .dot_matches_new_line(true)
2084 .multi_line(true)
2085 .build()
2086 .unwrap();
2087 let replacer = |caps: &Captures| -> String {
2088 let s = caps.get(0).unwrap();
2089 if s.as_str().starts_with('/') {
2090 " ".to_string()
2091 } else {
2092 s.as_str().to_string()
2093 }
2094 };
2095 regex.replace_all(data, &replacer).to_string()
2096}
2097
2098fn is_verilog_vmem<T, S>(data: T) -> bool
2099where
2100 T: AsRef<[S]>,
2101 S: AsRef<str>,
2102{
2103 BinFile {
2104 header: None,
2105 execution_start_address: None,
2106 segments: Segments::new(),
2107 cursor: 0,
2108 }
2109 .add_verilog_vmem(data, false)
2110 .is_ok()
2111}
2112
2113fn is_ti_txt<T, S>(data: T) -> bool
2114where
2115 T: AsRef<[S]>,
2116 S: AsRef<str>,
2117{
2118 BinFile {
2119 header: None,
2120 execution_start_address: None,
2121 segments: Segments::new(),
2122 cursor: 0,
2123 }
2124 .add_ti_txt(data, false)
2125 .is_ok()
2126}
2127
2128#[cfg(test)]
2129mod tests {
2130
2131 use super::*;
2132
2133 fn open_text_file(file_name: &str) -> Vec<String> {
2134 let file = File::open(file_name).unwrap();
2135 let reader = BufReader::new(file);
2136 let data: Vec<String> = reader.lines().map_while(Result::ok).collect();
2137 data
2138 }
2139
2140 fn open_binary_file(file_name: &str) -> Vec<u8> {
2141 let mut file = File::open(file_name).unwrap();
2142 let metadata = metadata(file_name).unwrap();
2143 let mut data: Vec<u8> = vec![0; metadata.len() as usize];
2144 let n = file.read(&mut data);
2145 data[..n.unwrap()].to_vec()
2146 }
2147
2148 #[test]
2149 fn it_works() {
2150 let binfile = BinFile::new();
2151 print!("{}", binfile.info())
2152 }
2153
2154 #[test]
2155 fn it_fails_with_report() {
2156 let mut binfile = BinFile::new();
2157 assert!(binfile
2158 .add_binary_file("/dev/this surely doesnt exist", None, false)
2159 .is_err())
2160 }
2161
2162 #[test]
2163 fn test_srec() {
2164 let data = open_text_file("tests/in.s19");
2165 let mut binfile = BinFile::new();
2166 assert!(binfile.add_srec(&data, false).is_ok());
2167 assert_eq!(
2168 binfile
2169 .to_srec(Some(28), SRecordAddressLength::Length16)
2170 .unwrap(),
2171 data
2172 );
2173
2174 let data = open_binary_file("tests/empty_main.bin");
2175 let mut binfile = BinFile::new();
2176 assert!(binfile.add_srec_file("tests/empty_main.s19", false).is_ok());
2177 let mut binfile2 = BinFile::new();
2178 assert!(binfile2
2179 .add_srec_file("tests/empty_main.s19", false)
2180 .is_ok());
2181 assert!(binfile2
2182 .add_srec_file("tests/empty_main_rearranged.s19", true)
2183 .is_ok());
2184 assert_eq!(binfile.to_bytes(.., Some(0x00)).unwrap().len(), data.len());
2185 assert_eq!(binfile.to_bytes(.., Some(0x00)).unwrap(), data);
2186 assert_eq!(binfile2.to_bytes(.., Some(0x00)).unwrap().len(), data.len());
2187 assert_eq!(binfile2.to_bytes(.., Some(0x00)).unwrap(), data);
2188
2189 let mut binfile = BinFile::new();
2190 let res = binfile.add_srec_file("tests/bad_crc.s19", false);
2191 assert!(res.is_err());
2192 assert_eq!(res, Err(Error::ChecksumMismatch(0x22, 0x25)));
2193 }
2194
2195 #[test]
2196 fn test_ti_txt() {
2197 let data = open_text_file("tests/in.s19.txt");
2198 let mut binfile = BinFile::new();
2199 assert!(binfile.add_ti_txt(&data, false).is_ok());
2200 assert_eq!(binfile.to_ti_txt().unwrap(), data);
2201
2202 let data = open_binary_file("tests/empty_main.bin");
2203 let mut binfile = BinFile::new();
2204 assert!(binfile
2205 .add_ti_txt_file("tests/empty_main.s19.txt", false)
2206 .is_ok());
2207 let mut binfile2 = BinFile::new();
2208 assert!(binfile2
2209 .add_ti_txt_file("tests/empty_main.s19.txt", false)
2210 .is_ok());
2211 assert!(binfile2
2212 .add_ti_txt_file("tests/empty_main_rearranged.s19.txt", true)
2213 .is_ok());
2214 assert_eq!(binfile.to_bytes(.., Some(0x00)).unwrap().len(), data.len());
2215 assert_eq!(binfile.to_bytes(.., Some(0x00)).unwrap(), data);
2216 assert_eq!(binfile2.to_bytes(.., Some(0x00)).unwrap().len(), data.len());
2217 assert_eq!(binfile2.to_bytes(.., Some(0x00)).unwrap(), data);
2218
2219 let empty = BinFile::new();
2220 let mut binfile = BinFile::new();
2221 assert!(binfile.add_ti_txt_file("tests/empty.txt", false).is_ok());
2222 assert_eq!(empty.to_ti_txt(), binfile.to_ti_txt());
2223 }
2224
2225 #[test]
2226 fn test_bad_ti_txt() {
2227 let datas = [
2228 (
2229 "bad_ti_txt_address_value.txt",
2230 Error::ContainsInvalidCharacters,
2231 ),
2232 ("bad_ti_txt_bad_q.txt", Error::UnsupportedFileFormat),
2233 (
2234 "bad_ti_txt_data_value.txt",
2235 Error::ContainsInvalidCharacters,
2236 ),
2237 ("bad_ti_txt_record_short.txt", Error::MissingStartCode),
2238 ("bad_ti_txt_record_long.txt", Error::RecordTooLong),
2239 ("bad_ti_txt_no_offset.txt", Error::MissingStartCode),
2240 ("bad_ti_txt_no_q.txt", Error::UnsupportedFileFormat),
2241 ("bad_ti_txt_blank_line.txt", Error::RecordTooShort),
2242 ];
2243 for (filename, error) in datas {
2244 let mut binfile = BinFile::new();
2245 let res = binfile.add_ti_txt_file(format!("tests/{}", filename), false);
2246 assert_eq!(res, Err(error), "Testing: {}", filename);
2247 }
2248 }
2249
2250 #[test]
2251 fn test_compare_ti_txt() {
2252 let filenames = [
2253 "in.s19",
2254 "empty_main.s19",
2255 "convert.s19",
2256 "out.s19",
2257 "non_sorted_segments_merged_and_sorted.s19",
2259 "in.hex",
2260 "empty_main.hex",
2261 "convert.hex",
2262 "out.hex",
2263 ];
2264
2265 for file_1 in filenames {
2266 let file_2 = format!("{}.txt", file_1);
2267 let mut binfile1 = BinFile::new();
2268 let mut binfile2 = BinFile::new();
2269 let res1 = binfile1.add_file(format!("tests/{}", file_1), false);
2270
2271 assert_eq!(
2272 res1.is_ok(),
2273 binfile2
2274 .add_file(format!("tests/{}", file_2), false)
2275 .is_ok(),
2276 "Testing: {}",
2277 file_1
2278 );
2279 assert_eq!(
2280 binfile1.to_ti_txt(),
2281 binfile2.to_ti_txt(),
2282 "Testing: {}",
2283 file_1
2284 )
2285 }
2286 }
2287
2288 #[test]
2289 fn test_hex() {
2290 let data = open_text_file("tests/in.hex");
2291 let mut binfile = BinFile::new();
2292 assert!(binfile.add_ihex(&data, false).is_ok());
2293 assert_eq!(binfile.to_ihex(None, IHexFormat::default()).unwrap(), data);
2294
2295 let mut binfile = BinFile::new();
2296 assert!(binfile.add_ihex_file("tests/in.hex", false).is_ok());
2297 assert!(binfile.add_ihex_file("tests/in.hex", true).is_ok());
2298 assert_eq!(binfile.to_ihex(None, IHexFormat::default()).unwrap(), data);
2299 }
2300
2301 #[test]
2302 fn test_i8hex() {
2303 let mut binfile = BinFile::new();
2304
2305 let data = [
2306 ":0100000001FE".to_string(),
2307 ":0101000002FC".to_string(),
2308 ":01FFFF0003FE".to_string(),
2309 ":0400000300000000F9".to_string(),
2311 ":00000001FF".to_string(),
2312 ];
2313 assert!(binfile.add_ihex(&data, false).is_ok());
2314 assert_eq!(binfile.segments().len(), 3);
2315 assert_eq!(binfile.segments()[0].get_tuple(), (0, &vec![0x01]));
2316 assert_eq!(binfile.segments()[1].get_tuple(), (0x100, &vec![0x02]));
2317 assert_eq!(binfile.segments()[2].get_tuple(), (0xffff, &vec![0x03]));
2318 let data_exp = [
2319 ":0100000001FE".to_string(),
2320 ":0101000002FC".to_string(),
2321 ":01FFFF0003FE".to_string(),
2322 ":00000001FF".to_string(),
2323 ];
2324 assert_eq!(
2325 binfile.to_ihex(None, IHexFormat::IHex8),
2326 Ok(data_exp.to_vec())
2327 );
2328 }
2329
2330 #[test]
2331 fn test_i8hex_address_above_64k() {
2332 let mut binfile = BinFile::new();
2333
2334 assert!(binfile.add_bytes([0x00], Some(65536), false).is_ok());
2335 let res = binfile.to_ihex(None, IHexFormat::IHex8);
2336 assert_eq!(res, Err(Error::AddressTooBig));
2337 }
2338
2339 #[test]
2340 fn test_i16hex() {
2341 let mut binfile = BinFile::new();
2342
2343 let data = [
2344 ":0100000001FE".to_string(),
2345 ":01F00000020D".to_string(),
2346 ":01FFFF0003FE".to_string(),
2347 ":02000002C0003C".to_string(),
2348 ":0110000005EA".to_string(),
2349 ":02000002FFFFFE".to_string(),
2350 ":0100000006F9".to_string(),
2351 ":01FFFF0007FA".to_string(),
2352 ":020000021000EC".to_string(),
2353 ":0100000004FB".to_string(),
2354 ":0400000500000000F7".to_string(),
2356 ":00000001FF".to_string(),
2357 ];
2358 assert!(binfile.add_ihex(&data, false).is_ok());
2359 assert_eq!(binfile.segments().len(), 6);
2360 assert_eq!(binfile.segments()[0].get_tuple(), (0, &vec![0x01]));
2361 assert_eq!(binfile.segments()[1].get_tuple(), (0xf000, &vec![0x02]));
2362 assert_eq!(
2363 binfile.segments()[2].get_tuple(),
2364 (0xffff, &vec![0x03, 0x04])
2365 );
2366 assert_eq!(
2367 binfile.segments()[3].get_tuple(),
2368 (16 * 0xc000 + 0x1000, &vec![0x05])
2369 );
2370 assert_eq!(
2371 binfile.segments()[4].get_tuple(),
2372 (16 * 0xffff, &vec![0x06])
2373 );
2374 assert_eq!(
2375 binfile.segments()[5].get_tuple(),
2376 (17 * 0xffff, &vec![0x07])
2377 );
2378 let data_exp = [
2379 ":0100000001FE".to_string(),
2380 ":01F00000020D".to_string(),
2381 ":02FFFF000304F9".to_string(),
2382 ":02000002C0003C".to_string(),
2383 ":0110000005EA".to_string(),
2384 ":02000002F0000C".to_string(),
2385 ":01FFF000060A".to_string(),
2386 ":02000002FFFFFE".to_string(),
2387 ":01FFFF0007FA".to_string(),
2388 ":0400000300000000F9".to_string(),
2389 ":00000001FF".to_string(),
2390 ];
2391 assert_eq!(
2392 binfile.to_ihex(None, IHexFormat::IHex16),
2393 Ok(data_exp.to_vec())
2394 );
2395 }
2396
2397 #[test]
2398 fn test_i16hex_address_above_1meg() {
2399 let mut binfile = BinFile::new();
2400
2401 assert!(binfile
2402 .add_bytes([0x00], Some(17 * 65535 + 1), false)
2403 .is_ok());
2404 let res = binfile.to_ihex(None, IHexFormat::IHex16);
2405 assert_eq!(res, Err(Error::AddressTooBig));
2406 }
2407
2408 #[test]
2409 fn test_i32hex() {
2410 let mut binfile = BinFile::new();
2411
2412 let data = [
2413 ":0100000001FE".to_string(),
2414 ":01FFFF0002FF".to_string(),
2415 ":02000004FFFFFC".to_string(),
2416 ":0100000004FB".to_string(),
2417 ":01FFFF0005FC".to_string(),
2418 ":020000040001F9".to_string(),
2419 ":0100000003FC".to_string(),
2420 ":0400000500000000F7".to_string(),
2421 ":00000001FF".to_string(),
2422 ];
2423 assert!(binfile.add_ihex(&data, false).is_ok());
2424 let data_exp = [
2425 ":0100000001FE".to_string(),
2426 ":02FFFF000203FB".to_string(),
2427 ":02000004FFFFFC".to_string(),
2428 ":0100000004FB".to_string(),
2429 ":01FFFF0005FC".to_string(),
2430 ":0400000500000000F7".to_string(),
2431 ":00000001FF".to_string(),
2432 ];
2433 assert_eq!(
2434 binfile.to_ihex(None, IHexFormat::IHex32),
2435 Ok(data_exp.to_vec())
2436 );
2437 assert_eq!(binfile.minimum_address(), Some(0));
2438 assert_eq!(binfile.maximum_address(), Some(0x100000000));
2439 assert_eq!(binfile.execution_start_address(), Some(0));
2440 assert_eq!(binfile.get_value_by_address(0), Some(1));
2441 assert_eq!(binfile.get_value_by_address(0xffff), Some(2));
2442 assert_eq!(binfile.get_value_by_address(0x10000), Some(3));
2443 assert_eq!(binfile.get_value_by_address(0xffff0000), Some(4));
2444 assert_eq!(binfile.get_value_by_address(0xffff0002), None);
2445 assert_eq!(binfile.get_value_by_address(0xffff0004), None);
2446 assert_eq!(binfile.get_value_by_address(0xffffffff), Some(0x05));
2447 }
2448
2449 #[test]
2450 fn test_i32hex_address_above_4gig() {
2451 let mut binfile = BinFile::new();
2452
2453 assert!(binfile.add_bytes([0x00], Some(0x100000000), false).is_ok());
2454 let res = binfile.to_ihex(None, IHexFormat::IHex32);
2455 assert_eq!(res, Err(Error::AddressTooBig));
2456 }
2457
2458 #[test]
2459 fn test_binary() {
2460 let data = open_binary_file("tests/binary1.bin");
2461 let mut binfile = BinFile::new();
2462 assert!(binfile.add_bytes(&data, None, false).is_ok());
2463 assert_eq!(binfile.to_bytes(.., None), Ok(data));
2464
2465 let mut binfile = BinFile::new();
2467 assert!(binfile
2468 .add_binary_file("tests/binary2.bin", Some(15), false)
2469 .is_ok());
2470 assert!(binfile
2471 .add_binary_file("tests/binary2.bin", Some(15), true)
2472 .is_ok());
2473 let data = open_binary_file("tests/binary2.bin");
2474 assert_eq!(
2475 binfile.add_bytes(&data, Some(20), false),
2476 Err(Error::AddDataError),
2477 );
2478
2479 assert!(binfile.exclude(20..1024).is_ok());
2481 assert!(binfile.add_bytes(&data, Some(20), false).is_ok());
2482
2483 let mut data = open_binary_file("tests/binary3.bin");
2484 assert_eq!(binfile.to_bytes(0.., Some(0x00)), Ok(data.clone()));
2485
2486 assert!(binfile.exclude(0..1).is_ok());
2488 assert!(binfile.add_bytes([1], None, false).is_ok());
2489 let first = data.first_mut().unwrap();
2490 *first = 1;
2491 assert_eq!(binfile.to_bytes(0.., Some(0x00)), Ok(data.clone()));
2492
2493 assert_eq!(binfile.minimum_address(), Some(0));
2495 assert_eq!(binfile.maximum_address(), Some(184));
2496 assert_eq!(binfile.len(), 170);
2497
2498 assert_eq!(binfile.to_bytes(512.., None), Ok(vec![]));
2500
2501 assert_eq!(binfile.to_bytes(184.., None), Ok(vec![]));
2503
2504 assert_eq!(binfile.to_bytes(183.., None), Ok(vec![10]));
2506
2507 assert_eq!(binfile.to_bytes(1.., Some(0)), Ok(data[1..].to_vec()));
2509
2510 assert_eq!(binfile.to_bytes(16..18, None), Ok(vec![0x32, 0x30]));
2512
2513 assert_eq!(binfile.to_bytes(16..16, None), Ok(vec![]));
2515
2516 assert_eq!(binfile.to_bytes(..1024, Some(0)), Ok(data));
2518
2519 assert_eq!(binfile.to_bytes(2..0, None), Ok(vec![]));
2521 }
2522
2523 #[test]
2524 fn test_add() {
2525 let mut binfile = BinFile::new();
2526 let data = open_text_file("tests/in.s19");
2527 let binfile_2 = binfile.clone() + data.as_slice();
2528 binfile += data.as_slice();
2529 assert_eq!(
2530 binfile.to_srec(Some(28), SRecordAddressLength::Length16),
2531 Ok(data.clone())
2532 );
2533 assert_eq!(
2534 binfile_2.to_srec(Some(28), SRecordAddressLength::Length16),
2535 Ok(data)
2536 );
2537
2538 let mut binfile = BinFile::new();
2539 let data = open_text_file("tests/in.hex");
2540 binfile += data.as_slice();
2541 assert_eq!(binfile.to_ihex(None, IHexFormat::default()), Ok(data));
2542 }
2543
2544 #[test]
2545 fn test_add_strings() {
2546 let mut binfile = BinFile::new();
2547 let data = open_text_file("tests/in.s19");
2548 assert!(binfile.add_strings(data.as_slice(), false).is_ok());
2549 assert_eq!(
2550 binfile.to_srec(Some(28), SRecordAddressLength::Length16),
2551 Ok(data)
2552 );
2553
2554 let mut binfile = BinFile::new();
2555 let data = open_text_file("tests/in.hex");
2556 assert!(binfile.add_strings(data.as_slice(), false).is_ok());
2557 assert_eq!(binfile.to_ihex(None, IHexFormat::default()), Ok(data));
2558
2559 let mut binfile = BinFile::new();
2560 let res = binfile.add_strings(["invalid data"], false);
2561 assert_eq!(res, Err(Error::UnsupportedFileFormat));
2562
2563 let mut binfile = BinFile::new();
2564 let res = binfile.add_strings(
2565 [
2566 "S214400420ED044000E8B7FFFFFFF4660F1F440000EE",
2567 "invalid data",
2568 ],
2569 false,
2570 );
2571 assert_eq!(res, Err(Error::MissingStartCode));
2572
2573 let mut binfile = BinFile::new();
2574 let res = binfile.add_strings([":020000040040BA", "invalid data"], false);
2575 assert_eq!(res, Err(Error::MissingStartCode));
2576 }
2577
2578 #[test]
2579 fn test_add_file() {
2580 let mut binfile = BinFile::new();
2581 assert!(binfile
2582 .add_file("tests/empty_main_rearranged.s19", false)
2583 .is_ok());
2584 let data = open_binary_file("tests/empty_main.bin");
2585 assert_eq!(binfile.to_bytes(.., Some(0x00)), Ok(data));
2586
2587 let mut binfile = BinFile::new();
2588 assert!(binfile.add_file("tests/in.hex", false).is_ok());
2589 let data = open_text_file("tests/in.hex");
2590 assert_eq!(binfile.to_ihex(None, IHexFormat::default()), Ok(data));
2591
2592 let mut binfile = BinFile::new();
2593 assert_eq!(
2594 binfile.add_file("tests/hexdump.txt", false),
2595 Err(Error::UnsupportedFileFormat)
2596 );
2597 }
2598
2599 #[test]
2600 fn test_from_file() {
2601 let binfile = BinFile::from_file("tests/empty_main_rearranged.s19").unwrap();
2602 let data = open_binary_file("tests/empty_main.bin");
2603 assert_eq!(binfile.to_bytes(.., Some(0x00)), Ok(data));
2604
2605 assert_eq!(
2606 BinFile::from_file("tests/hexdump.txt").err(),
2607 Some(Error::UnsupportedFileFormat)
2608 );
2609
2610 let binfile =
2611 BinFile::try_from(PathBuf::from("tests/empty_main_rearranged.s19").as_path()).unwrap();
2612 let data = open_binary_file("tests/empty_main.bin");
2613 assert_eq!(binfile.to_bytes(.., Some(0x00)), Ok(data));
2614
2615 assert_eq!(
2616 BinFile::from_file("tests/hexdump.txt").err(),
2617 Some(Error::UnsupportedFileFormat)
2618 );
2619 }
2620
2621 #[test]
2622 fn test_from_files() {
2623 let binfile = BinFile::from_files(["tests/in.hex", "tests/in.hex"], true).unwrap();
2624 let data = open_text_file("tests/in.hex");
2625 assert_eq!(binfile.to_ihex(None, IHexFormat::default()), Ok(data));
2626 }
2627
2628 #[test]
2629 fn test_array() {
2630 let mut binfile = BinFile::new();
2631 assert!(binfile.add_ihex_file("tests/in.hex", false).is_ok());
2632 let data = open_text_file("tests/in.i");
2633 assert_eq!(binfile.to_array_str(.., None, None), Ok(data[0].to_owned()));
2634 }
2635
2636 #[test]
2637 fn test_hexdump_1() {
2638 let mut binfile = BinFile::new();
2639 assert!(binfile.add_bytes("12".as_bytes(), Some(17), false).is_ok());
2640 assert!(binfile.add_bytes("34".as_bytes(), Some(26), false).is_ok());
2641 assert!(binfile
2642 .add_bytes("5678".as_bytes(), Some(30), false)
2643 .is_ok());
2644 assert!(binfile.add_bytes("9".as_bytes(), Some(47), false).is_ok());
2645 let data = open_text_file("tests/hexdump.txt");
2646 assert_eq!(binfile.to_hexdump(), Ok(data));
2647 }
2648
2649 #[test]
2650 fn test_hexdump_2() {
2651 let mut binfile = BinFile::new();
2652 assert!(binfile
2653 .add_bytes("34".as_bytes(), Some(0x150), false)
2654 .is_ok());
2655 assert!(binfile
2656 .add_bytes("3".as_bytes(), Some(0x163), false)
2657 .is_ok());
2658 assert!(binfile.add_bytes([0x01], Some(0x260), false).is_ok());
2659 assert!(binfile
2660 .add_bytes("3".as_bytes(), Some(0x263), false)
2661 .is_ok());
2662 let data = open_text_file("tests/hexdump2.txt");
2663 assert_eq!(binfile.to_hexdump(), Ok(data));
2664 }
2665
2666 #[test]
2667 fn test_hexdump_gaps() {
2668 let mut binfile = BinFile::new();
2669 assert!(binfile.add_bytes("1".as_bytes(), Some(0), false).is_ok());
2670 assert!(binfile.add_bytes("3".as_bytes(), Some(32), false).is_ok());
2671 assert!(binfile.add_bytes("6".as_bytes(), Some(80), false).is_ok());
2672 let data = open_text_file("tests/hexdump3.txt");
2673 assert_eq!(binfile.to_hexdump(), Ok(data));
2674 }
2675
2676 #[test]
2677 fn test_hexdump_empty() {
2678 let binfile = BinFile::new();
2679 assert_eq!(binfile.to_hexdump(), Ok(vec![]));
2680 }
2681
2682 #[test]
2683 fn test_srec_ihex_binary() {
2684 let mut binfile = BinFile::new();
2685 let data = open_text_file("tests/in.hex");
2686 assert!(binfile.add_ihex(data, false).is_ok());
2687 let data = open_text_file("tests/in.s19");
2688 assert!(binfile.add_srec(data, false).is_ok());
2689 let data = open_binary_file("tests/binary1.bin");
2690 assert!(binfile.add_bytes(data, Some(1024), false).is_ok());
2691 let data = open_text_file("tests/out.hex");
2692 assert_eq!(binfile.to_ihex(None, IHexFormat::default()), Ok(data));
2693 let data = open_text_file("tests/out.s19");
2694 assert_eq!(
2695 binfile.to_srec(None, SRecordAddressLength::Length16),
2696 Ok(data)
2697 );
2698 assert!(binfile.fill(Some(0x00), None).is_ok());
2699 let data = open_binary_file("tests/out.bin");
2700 assert_eq!(binfile.to_bytes(.., None), Ok(data));
2701 }
2702
2703 #[test]
2704 fn test_exclude() {
2705 let mut binfile = BinFile::new();
2706 assert!(binfile.add_file("tests/in.s19", false).is_ok());
2707 assert!(binfile.exclude(2..4).is_ok());
2708 let data = open_text_file("tests/in_exclude_2_4.s19");
2709 assert_eq!(
2710 binfile.to_srec(Some(32), SRecordAddressLength::Length16),
2711 Ok(data)
2712 );
2713
2714 let mut binfile = BinFile::new();
2715 assert!(binfile.add_file("tests/in.s19", false).is_ok());
2716 assert!(binfile.exclude(3..1024).is_ok());
2717 let data = open_text_file("tests/in_exclude_3_1024.s19");
2718 assert_eq!(
2719 binfile.to_srec(Some(32), SRecordAddressLength::Length16),
2720 Ok(data)
2721 );
2722
2723 let mut binfile = BinFile::new();
2724 assert!(binfile.add_file("tests/in.s19", false).is_ok());
2725 assert!(binfile.exclude(0..9).is_ok());
2726 let data = open_text_file("tests/in_exclude_0_9.s19");
2727 assert_eq!(
2728 binfile.to_srec(Some(32), SRecordAddressLength::Length16),
2729 Ok(data)
2730 );
2731
2732 let mut binfile = BinFile::new();
2733 assert!(binfile.add_file("tests/empty_main.s19", false).is_ok());
2734 assert!(binfile.exclude(0x400240..0x400600).is_ok());
2735 let data = open_binary_file("tests/empty_main_mod.bin");
2736 assert_eq!(binfile.to_bytes(.., Some(0x00)), Ok(data));
2737
2738 let mut binfile = BinFile::new();
2739 assert!(binfile
2740 .add_bytes("111111".as_bytes(), Some(8), false)
2741 .is_ok());
2742 assert!(binfile
2743 .add_bytes("222222".as_bytes(), Some(16), false)
2744 .is_ok());
2745 assert!(binfile
2746 .add_bytes("333333".as_bytes(), Some(24), false)
2747 .is_ok());
2748 assert!(binfile.exclude(7..8).is_ok());
2749 assert!(binfile.exclude(15..16).is_ok());
2750 assert!(binfile.exclude(23..24).is_ok());
2751 let data = vec![
2752 "111111".as_bytes().to_owned(),
2753 vec![0xff; 2],
2754 "222222".as_bytes().to_owned(),
2755 vec![0xff; 2],
2756 "333333".as_bytes().to_owned(),
2757 ]
2758 .into_iter()
2759 .flatten()
2760 .collect();
2761 assert_eq!(binfile.to_bytes(.., None), Ok(data));
2762 assert_eq!(binfile.segments().len(), 3);
2763
2764 assert!(binfile.exclude(20..24).is_ok());
2765 let data = vec![
2766 "111111".as_bytes().to_owned(),
2767 vec![0xff; 2],
2768 "2222".as_bytes().to_owned(),
2769 vec![0xff; 4],
2770 "333333".as_bytes().to_owned(),
2771 ]
2772 .into_iter()
2773 .flatten()
2774 .collect();
2775 assert_eq!(binfile.to_bytes(.., None), Ok(data));
2776 assert_eq!(binfile.segments().len(), 3);
2777
2778 assert!(binfile.exclude(12..24).is_ok());
2779 let data = vec![
2780 "1111".as_bytes().to_owned(),
2781 vec![0xff; 12],
2782 "333333".as_bytes().to_owned(),
2783 ]
2784 .into_iter()
2785 .flatten()
2786 .collect();
2787 assert_eq!(binfile.to_bytes(.., None), Ok(data));
2788 assert_eq!(binfile.segments().len(), 2);
2789
2790 assert!(binfile.exclude(11..26).is_ok());
2791 let data = vec![
2792 "111".as_bytes().to_owned(),
2793 vec![0xff; 15],
2794 "3333".as_bytes().to_owned(),
2795 ]
2796 .into_iter()
2797 .flatten()
2798 .collect();
2799 assert_eq!(binfile.to_bytes(.., None), Ok(data));
2800 assert_eq!(binfile.segments().len(), 2);
2801
2802 assert!(binfile.exclude(27..29).is_ok());
2803 let data = vec![
2804 "111".as_bytes().to_owned(),
2805 vec![0xff; 15],
2806 "3".as_bytes().to_owned(),
2807 vec![0xff; 2],
2808 "3".as_bytes().to_owned(),
2809 ]
2810 .into_iter()
2811 .flatten()
2812 .collect();
2813 assert_eq!(binfile.to_bytes(.., None), Ok(data));
2814 assert_eq!(binfile.segments().len(), 3);
2815
2816 let mut binfile = BinFile::new();
2817 assert!(binfile
2818 .add_bytes("111111".as_bytes(), Some(8), false)
2819 .is_ok());
2820 assert_eq!(binfile.exclude(4..2), Err(Error::InvalidAddressRange));
2821
2822 assert!(binfile.exclude(2..2).is_ok());
2823 assert_eq!(
2824 binfile.to_bytes(.., None),
2825 Ok("111111".as_bytes().to_owned())
2826 );
2827 }
2828
2829 #[test]
2830 fn test_crop() {
2831 let mut binfile = BinFile::new();
2832 assert!(binfile.add_file("tests/in.s19", false).is_ok());
2833 assert!(binfile.crop(2..4).is_ok());
2834 let data = open_text_file("tests/in_crop_2_4.s19");
2835 assert_eq!(
2836 binfile.to_srec(Some(32), SRecordAddressLength::Length16),
2837 Ok(data)
2838 );
2839 assert!(binfile.exclude(2..4).is_ok());
2840 assert_eq!(binfile.to_bytes(.., Some(0x00)), Ok(vec![]));
2841 }
2842
2843 #[test]
2844 fn text_minimum_maximum_length() {
2845 let mut binfile = BinFile::new();
2846
2847 assert_eq!(binfile.minimum_address(), None);
2849 assert_eq!(binfile.maximum_address(), None);
2850 assert_eq!(binfile.len(), 0);
2851 assert!(binfile.is_empty());
2852
2853 assert!(binfile.add_file("tests/in.s19", false).is_ok());
2855
2856 assert_eq!(binfile.minimum_address(), Some(0));
2857 assert_eq!(binfile.maximum_address(), Some(70));
2858 assert_eq!(binfile.len(), 70);
2859 assert!(!binfile.is_empty());
2860
2861 assert!(binfile.add_bytes([0x01; 9], Some(80), false).is_ok());
2863
2864 assert_eq!(binfile.minimum_address(), Some(0));
2865 assert_eq!(binfile.maximum_address(), Some(89));
2866 assert_eq!(binfile.len(), 79);
2867 assert!(!binfile.is_empty());
2868 }
2869
2870 #[test]
2871 fn test_iterate_segments() {
2872 let binfile = BinFile::from_file("tests/in.s19").unwrap();
2873
2874 let mut i = 0;
2875 for _segment in binfile.segments() {
2876 i += 1;
2877 }
2878 assert_eq!(i, 1);
2879 assert_eq!(binfile.segments().len(), 1);
2880 }
2881
2882 #[test]
2883 fn test_segments_list() {
2884 let mut binfile = BinFile::new();
2885
2886 assert!(binfile.add_bytes([0x00], Some(0), false).is_ok());
2887 assert!(binfile.add_bytes([0x01, 0x02], Some(10), false).is_ok());
2888 assert!(binfile.add_bytes([0x03], Some(12), false).is_ok());
2889 assert!(binfile.add_bytes([0x04], Some(1000), false).is_ok());
2890
2891 assert_eq!(
2892 binfile.segments_list(),
2893 vec![
2894 (0, vec![0x00]),
2895 (10, vec![0x01, 0x02, 0x03]),
2896 (1000, vec![0x04]),
2897 ]
2898 )
2899 }
2900
2901 #[test]
2902 fn test_chunks_list() {
2903 let mut binfile = BinFile::new();
2904
2905 assert!(binfile
2906 .add_bytes([0x00, 0x00, 0x01, 0x01, 0x02], Some(0), false)
2907 .is_ok());
2908 assert!(binfile
2909 .add_bytes([0x04, 0x05, 0x05, 0x06, 0x06, 0x07], Some(9), false)
2910 .is_ok());
2911 assert!(binfile.add_bytes([0x09], Some(19), false).is_ok());
2912 assert!(binfile.add_bytes([0x0a], Some(21), false).is_ok());
2913
2914 assert_eq!(
2915 binfile.to_bytes(.., None),
2916 Ok(vec![
2917 0x00, 0x00, 0x01, 0x01, 0x02, 0xff, 0xff, 0xff, 0xff, 0x04, 0x05, 0x05, 0x06, 0x06,
2918 0x07, 0xff, 0xff, 0xff, 0xff, 0x09, 0xff, 0x0a
2919 ])
2920 );
2921
2922 let mut data = BTreeMap::new();
2924 data.insert(0, vec![0x00, 0x00, 0x01, 0x01, 0x02]);
2925 data.insert(9, vec![0x04, 0x05, 0x05, 0x06, 0x06, 0x07]);
2926 data.insert(19, vec![0x09]);
2927 data.insert(21, vec![0x0a]);
2928 assert_eq!(binfile.segments().chunks(Some(8), None), Ok(data));
2929
2930 let mut data = BTreeMap::new();
2932 data.insert(0, vec![0x00, 0x00, 0x01, 0x01, 0x02]);
2933 data.insert(9, vec![0x04]);
2934 data.insert(10, vec![0x05, 0x05, 0x06, 0x06, 0x07]);
2935 data.insert(19, vec![0x09]);
2936 data.insert(21, vec![0x0a]);
2937 assert_eq!(binfile.segments().chunks(Some(8), Some(2)), Ok(data));
2938
2939 let mut data = BTreeMap::new();
2941 data.insert(0, vec![0x00, 0x00, 0x01, 0x01, 0x02]);
2942 data.insert(9, vec![0x04, 0x05, 0x05]);
2943 data.insert(12, vec![0x06, 0x06, 0x07]);
2944 data.insert(19, vec![0x09]);
2945 data.insert(21, vec![0x0a]);
2946 assert_eq!(binfile.segments().chunks(Some(8), Some(4)), Ok(data));
2947
2948 let mut data = BTreeMap::new();
2950 data.insert(0, vec![0x00, 0x00, 0x01, 0x01, 0x02]);
2951 data.insert(9, vec![0x04, 0x05, 0x05, 0x06, 0x06, 0x07]);
2952 data.insert(19, vec![0x09]);
2953 data.insert(21, vec![0x0a]);
2954 assert_eq!(binfile.segments().chunks(Some(8), Some(8)), Ok(data));
2955
2956 let mut data = BTreeMap::new();
2958 data.insert(0, vec![0x00, 0x00, 0x01, 0x01]);
2959 data.insert(4, vec![0x02]);
2960 data.insert(9, vec![0x04, 0x05, 0x05, 0x06]);
2961 data.insert(13, vec![0x06, 0x07]);
2962 data.insert(19, vec![0x09]);
2963 data.insert(21, vec![0x0a]);
2964 assert_eq!(binfile.segments().chunks(Some(4), None), Ok(data));
2965
2966 let mut data = BTreeMap::new();
2968 data.insert(0, vec![0x00, 0x00, 0x01, 0x01]);
2969 data.insert(4, vec![0x02]);
2970 data.insert(9, vec![0x04]);
2971 data.insert(10, vec![0x05, 0x05, 0x06, 0x06]);
2972 data.insert(14, vec![0x07]);
2973 data.insert(19, vec![0x09]);
2974 data.insert(21, vec![0x0a]);
2975 assert_eq!(binfile.segments().chunks(Some(4), Some(2)), Ok(data));
2976
2977 let mut data = BTreeMap::new();
2979 data.insert(0, vec![0x00, 0x00, 0x01, 0x01]);
2980 data.insert(4, vec![0x02]);
2981 data.insert(9, vec![0x04, 0x05, 0x05]);
2982 data.insert(12, vec![0x06, 0x06, 0x07]);
2983 data.insert(19, vec![0x09]);
2984 data.insert(21, vec![0x0a]);
2985 assert_eq!(binfile.segments().chunks(Some(4), Some(4)), Ok(data));
2986 }
2987
2988 #[test]
2989 fn test_segment() {
2990 let mut binfile = BinFile::new();
2991 assert!(binfile
2992 .add_bytes([0x00, 0x01, 0x02, 0x03, 0x04], Some(2), false)
2993 .is_ok());
2994
2995 let mut data = BTreeMap::new();
2997 data.insert(2, vec![0x00, 0x01]);
2998 data.insert(4, vec![0x02, 0x03, 0x04]);
2999 assert_eq!(binfile.segments()[0].chunks(Some(4), Some(4)), Ok(data));
3000
3001 assert_eq!(
3003 binfile.segments()[0].chunks(Some(4), Some(8)),
3004 Err(Error::AlignmentToSizeError {
3005 size: 4,
3006 alignment: 8
3007 })
3008 );
3009
3010 assert!(
3012 std::panic::catch_unwind(|| binfile.segments()[1].chunks(Some(4), Some(4))).is_err()
3013 );
3014 }
3015
3016 #[test]
3017 fn test_add_files() {
3018 let mut binfile = BinFile::new();
3019 assert!(binfile.add_bytes([0], None, false).is_ok());
3020
3021 let mut binfile_1_2 = BinFile::new();
3022 assert!(binfile_1_2.add_bytes([1], Some(1), false).is_ok());
3023
3024 binfile += binfile_1_2;
3025 assert_eq!(binfile.to_bytes(.., None), Ok(vec![0, 1]));
3026 }
3027
3028 #[test]
3029 fn test_info() {
3030 let binfile = BinFile::try_from(PathBuf::from("tests/empty_main.s19")).unwrap();
3031 let data = r#"Header: "bincopy/empty_main.s19"
3032Execution start address: 0x00400400
3033Data ranges:
3034
3035 0x00400238 - 0x004002b4 (124 B)
3036 0x004002b8 - 0x0040033e (134 B)
3037 0x00400340 - 0x004003c2 (130 B)
3038 0x004003d0 - 0x00400572 (418 B)
3039 0x00400574 - 0x0040057d (9 B)
3040 0x00400580 - 0x004006ac (300 B)
3041 0x00600e10 - 0x00601038 (552 B)
3042"#;
3043 assert_eq!(binfile.info(), data);
3044 }
3045
3046 #[test]
3047 fn test_layout_empty_main() {
3048 let binfile = BinFile::try_from(PathBuf::from("tests/empty_main.s19")).unwrap();
3049 let data = r###"0x400238 0x601038
3050- -"###;
3051 assert_eq!(binfile.layout(), Ok(data.to_string()));
3052 }
3053
3054 #[test]
3055 fn test_layout_out() {
3056 let binfile = BinFile::try_from(PathBuf::from("tests/out.hex")).unwrap();
3057 let data = r###"0x0 0x403
3058=====- -====- -"###;
3059 assert_eq!(binfile.layout(), Ok(data.to_string()));
3060 }
3061
3062 #[test]
3063 fn test_layout_in_exclude_2_4() {
3064 let binfile = BinFile::try_from(PathBuf::from("tests/in_exclude_2_4.s19")).unwrap();
3065 let data = r###"0x0 0x46
3066== =================================================================="###;
3067 assert_eq!(binfile.layout(), Ok(data.to_string()));
3068 }
3069
3070 #[test]
3071 fn test_execution_start_address() {
3072 let mut binfile = BinFile::try_from(PathBuf::from("tests/empty_main.s19")).unwrap();
3073 assert_eq!(binfile.execution_start_address(), Some(0x00400400));
3074 binfile.set_exexution_start_address(0x00400401);
3075 assert_eq!(binfile.execution_start_address(), Some(0x00400401));
3076 }
3077
3078 #[test]
3079 fn test_add_ihex_record_type_3() {
3080 let mut binfile = BinFile::new();
3081 assert!(binfile.add_ihex([":0400000302030405EB"], false).is_ok());
3082 assert_eq!(binfile.execution_start_address(), Some(0x02030405));
3083 }
3084
3085 #[test]
3086 fn test_add_ihex_record_type_5() {
3087 let mut binfile = BinFile::new();
3088 assert!(binfile.add_ihex([":0400000501020304ED"], false).is_ok());
3089 assert_eq!(binfile.execution_start_address(), Some(0x01020304));
3090 }
3091
3092 #[test]
3093 fn test_add_ihex_record_type_6() {
3094 let mut binfile = BinFile::new();
3095 assert_eq!(
3096 binfile.add_ihex([":00000006FA"], false),
3097 Err(Error::UnsupportedRecordType {
3098 record: ":00000006FA".into(),
3099 record_type: 6
3100 })
3101 );
3102 }
3103
3104 #[test]
3105 fn test_add_to_srec_record_5() {
3106 let mut binfile = BinFile::new();
3107 assert!(binfile.add_bytes([0x00; 65535], None, false).is_ok());
3108 let records = binfile.to_srec(Some(1), SRecordAddressLength::default());
3109 assert!(records.is_ok());
3110 assert_eq!(records.clone().unwrap().len(), 65536);
3111 assert_eq!(records.unwrap().last(), Some(&"S503FFFFFE".to_string()));
3112 }
3113
3114 #[test]
3115 fn test_add_to_srec_record_6() {
3116 let mut binfile = BinFile::new();
3117 assert!(binfile.add_bytes([0x00; 65536], None, false).is_ok());
3118 let records = binfile.to_srec(Some(1), SRecordAddressLength::default());
3119 assert!(records.is_ok());
3120 assert_eq!(records.clone().unwrap().len(), 65537);
3121 assert_eq!(records.unwrap().last(), Some(&"S604010000FA".to_string()));
3122 }
3123
3124 #[test]
3125 fn test_add_to_srec_record_8() {
3126 let mut binfile = BinFile::new();
3127 assert!(binfile.add_bytes([0x00], None, false).is_ok());
3128 binfile.set_exexution_start_address(0x123456);
3129 assert_eq!(
3130 binfile.to_srec(Some(1), SRecordAddressLength::Length24),
3131 Ok(vec![
3132 "S20500000000FA".to_string(),
3133 "S5030001FB".to_string(),
3134 "S8041234565F".to_string()
3135 ])
3136 );
3137 }
3138
3139 #[test]
3140 fn test_issue_4_1() {
3141 let mut binfile = BinFile::new();
3142 assert!(binfile.add_ihex_file("tests/issue_4_in.hex", false).is_ok());
3143 let data = open_text_file("tests/issue_4_out.hex");
3144 assert_eq!(binfile.to_ihex(None, IHexFormat::default()), Ok(data));
3145 }
3146
3147 #[test]
3148 fn test_issue_4_2() {
3149 let mut binfile = BinFile::new();
3150 assert!(binfile.add_srec_file("tests/empty_main.s19", false).is_ok());
3151 let data = open_text_file("tests/empty_main.hex");
3152 assert_eq!(binfile.to_ihex(None, IHexFormat::default()), Ok(data));
3153 }
3154
3155 #[test]
3156 fn test_overwrite() {
3157 let mut binfile = BinFile::new();
3158
3159 assert!(binfile
3161 .add_bytes("1234".as_bytes(), Some(512), true)
3162 .is_ok());
3163 assert_eq!(
3164 binfile.to_bytes(512.., None),
3165 Ok("1234".as_bytes().to_vec())
3166 );
3167
3168 assert!(binfile
3170 .add_bytes("123456".as_bytes(), Some(1024), false)
3171 .is_ok());
3172 assert!(binfile.add_bytes("99".as_bytes(), Some(1026), true).is_ok());
3173 let data = vec![
3174 "1234".as_bytes().to_owned(),
3175 vec![0xff; 508],
3176 "129956".as_bytes().to_owned(),
3177 ]
3178 .into_iter()
3179 .flatten()
3180 .collect();
3181 assert_eq!(binfile.to_bytes(512.., None), Ok(data));
3182
3183 assert!(binfile
3185 .add_bytes("abc".as_bytes(), Some(1022), true)
3186 .is_ok());
3187 assert!(binfile
3188 .add_bytes("def".as_bytes(), Some(1029), true)
3189 .is_ok());
3190 let data = vec![
3191 "1234".as_bytes().to_owned(),
3192 vec![0xff; 506],
3193 "abc2995def".as_bytes().to_owned(),
3194 ]
3195 .into_iter()
3196 .flatten()
3197 .collect();
3198 assert_eq!(binfile.to_bytes(512.., None), Ok(data));
3199
3200 assert!(binfile
3202 .add_bytes("111111111111".as_bytes(), Some(1021), true)
3203 .is_ok());
3204 let data = vec![
3205 "1234".as_bytes().to_owned(),
3206 vec![0xff; 505],
3207 "111111111111".as_bytes().to_owned(),
3208 ]
3209 .into_iter()
3210 .flatten()
3211 .collect();
3212 assert_eq!(binfile.to_bytes(512.., None), Ok(data));
3213
3214 assert!(binfile
3216 .add_bytes(["1".as_bytes()[0]; 1204], Some(256), true)
3217 .is_ok());
3218 assert_eq!(
3219 binfile.to_bytes(256.., None),
3220 Ok(vec!["1".as_bytes()[0]; 1204])
3221 );
3222 }
3223
3224 #[test]
3225 fn test_non_sorted_segments() {
3226 let mut binfile = BinFile::new();
3227 assert!(binfile
3228 .add_srec_file("tests/non_sorted_segments.s19", false)
3229 .is_ok());
3230 let data = open_text_file("tests/non_sorted_segments_merged_and_sorted.s19");
3231 assert_eq!(
3232 binfile.to_srec(None, SRecordAddressLength::default()),
3233 Ok(data)
3234 );
3235 }
3236
3237 #[test]
3238 fn test_fill() {
3239 let mut binfile = BinFile::new();
3240
3241 assert!(binfile.fill(None, None).is_ok());
3243 assert!(binfile.is_empty());
3244
3245 assert!(binfile
3247 .add_bytes([0x01, 0x02, 0x03, 0x04], Some(0), false)
3248 .is_ok());
3249 assert!(binfile
3250 .add_bytes([0x01, 0x02, 0x03, 0x04], Some(8), false)
3251 .is_ok());
3252 assert!(binfile.fill(None, None).is_ok());
3253 assert_eq!(
3254 binfile.to_bytes(.., None),
3255 Ok(vec![
3256 0x01, 0x02, 0x03, 0x04, 0xff, 0xff, 0xff, 0xff, 0x01, 0x02, 0x03, 0x04
3257 ])
3258 );
3259 }
3260
3261 #[test]
3262 fn test_fill_max_words() {
3263 let mut binfile = BinFile::new();
3264
3265 assert!(binfile.add_bytes([0x01], Some(0), false).is_ok());
3267 assert!(binfile.add_bytes([0x02], Some(2), false).is_ok());
3268 assert!(binfile.add_bytes([0x03], Some(5), false).is_ok());
3269 assert!(binfile.add_bytes([0x04], Some(9), false).is_ok());
3270 assert!(binfile.fill(Some(0xaa), Some(2)).is_ok());
3271 assert_eq!(binfile.segments().len(), 2);
3272 assert_eq!(binfile.segments()[0].minimum_address(), 0);
3273 assert_eq!(
3274 binfile.segments()[0].data(),
3275 &[0x01, 0xaa, 0x02, 0xaa, 0xaa, 0x03]
3276 );
3277 assert_eq!(binfile.segments()[1].minimum_address(), 9);
3278 assert_eq!(binfile.segments()[1].data(), &[0x04]);
3279 }
3280
3281 #[test]
3282 fn test_get_value_by_address() {
3283 let mut binfile = BinFile::new();
3284 assert!(binfile
3285 .add_bytes([0x01, 0x02, 0x03, 0x04], Some(1), false)
3286 .is_ok());
3287 assert!(binfile.get_value_by_address(0).is_none());
3288 assert_eq!(binfile.get_value_by_address(1), Some(1));
3289 assert_eq!(binfile.get_value_by_address(2), Some(2));
3290 assert_eq!(binfile.get_value_by_address(3), Some(3));
3291 assert_eq!(binfile.get_value_by_address(4), Some(4));
3292 assert!(binfile.get_value_by_address(5).is_none());
3293 }
3294
3295 #[test]
3296 fn test_get_values_by_address_ranges() {
3297 let mut binfile = BinFile::new();
3298 assert!(binfile
3299 .add_bytes([0x01, 0x02, 0x03, 0x04], Some(1), false)
3300 .is_ok());
3301 assert!(binfile
3302 .add_bytes([0x01, 0x02, 0x03, 0x04], Some(11), false)
3303 .is_ok());
3304 assert_eq!(
3305 binfile.get_values_by_address_range(3..5),
3306 Some(vec![0x03, 0x04])
3307 );
3308 assert!(binfile.get_values_by_address_range(3..6).is_none());
3309 assert!(binfile.get_values_by_address_range(0..3).is_none());
3310 assert_eq!(
3311 binfile.get_values_by_address_range(3..),
3312 Some(vec![0x03, 0x04])
3313 );
3314 assert_eq!(
3315 binfile.get_values_by_address_range(..3),
3316 Some(vec![0x01, 0x02])
3317 );
3318 assert_eq!(
3319 binfile.get_values_by_address_range(11..15),
3320 Some(vec![0x01, 0x02, 0x03, 0x04])
3321 );
3322
3323 assert_eq!(
3324 binfile.get_values_by_address_range(13..15),
3325 Some(vec![0x03, 0x04])
3326 );
3327 assert!(binfile.get_values_by_address_range(13..16).is_none());
3328 assert!(binfile.get_values_by_address_range(10..13).is_none());
3329 assert_eq!(
3330 binfile.get_values_by_address_range(13..),
3331 Some(vec![0x03, 0x04])
3332 );
3333 assert_eq!(
3334 binfile.get_values_by_address_range(..13),
3335 Some(vec![0x01, 0x02])
3336 );
3337 assert_eq!(
3338 binfile.get_values_by_address_range(11..15),
3339 Some(vec![0x01, 0x02, 0x03, 0x04])
3340 );
3341 }
3342
3343 #[test]
3344 fn test_performance() {
3345 let mut binfile = BinFile::new();
3346
3347 let chunk = vec!["1".as_bytes()[0]; 1024];
3349 for i in 0..1024 {
3350 assert!(binfile
3351 .add_bytes(chunk.clone(), Some(1024 * i), false)
3352 .is_ok());
3353 }
3354 assert_eq!(binfile.minimum_address(), Some(0));
3355 assert_eq!(binfile.maximum_address(), Some(1024 * 1024));
3356
3357 let ihex = binfile.to_ihex(None, IHexFormat::default()).unwrap();
3358 let srec = binfile
3359 .to_srec(None, SRecordAddressLength::default())
3360 .unwrap();
3361
3362 binfile = BinFile::new();
3363 assert!(binfile.add_ihex(ihex, false).is_ok());
3364
3365 binfile = BinFile::new();
3366 assert!(binfile.add_srec(srec, false).is_ok());
3367 }
3368
3369 #[test]
3370 fn test_print() {
3371 let mut binfile = BinFile::new();
3372
3373 assert!(binfile.add_bytes([0, 1, 2], Some(0), false).is_ok());
3374 assert!(binfile.add_bytes([3, 4, 5], Some(10), false).is_ok());
3375
3376 for segment in binfile.segments() {
3377 println!("{}", segment);
3378 for (address, data) in segment.chunks(Some(2), None).unwrap() {
3379 println!("Adress: {} Data: {:?}", address, data);
3380 }
3381 }
3382 }
3383
3384 #[test]
3385 fn test_verilog_vmem() {
3386 let mut binfile = BinFile::new();
3387
3388 assert!(binfile.add_verilog_vmem(
3389 [
3390 "@00000100 21 46 01 36 01 21 47 01 36 00 7E FE 09 D2 19 01 21 46 01 7E 17 C2 00 01 FF 5F 16 00 21 48 01 19",
3391 "@00000120 19 4E 79 23 46 23 96 57 78 23 9E DA 3F 01 B2 CA 3F 01 56 70 2B 5E 71 2B 72 2B 73 21 46 01 34 21",
3392 ], false).is_ok());
3393 }
3394}