1use std::io::*;
5
6pub mod config;
7pub use config::*;
8
9pub mod format;
10pub use format::*;
11
12mod streamer;
13use streamer::*;
14
15const OUTPUT_LOST_MESSAGE: &str = "Somewhere we lost the output";
16
17pub struct Printer<
19 O: Write,
20 A: AddressFormatting + Clone,
21 B: ByteFormatting + Clone,
22 C: CharFormatting + Clone,
23> {
24 out: Option<O>,
26
27 streamer: Streamer<A, B, C>,
28
29 is_finished: bool,
30}
31
32impl<
33 O: Write,
34 A: AddressFormatting + Clone,
35 B: ByteFormatting + Clone,
36 C: CharFormatting + Clone,
37 > Printer<O, A, B, C>
38{
39 pub fn new(out: O, start_address: usize, config: Config<A, B, C>) -> Printer<O, A, B, C> {
52 Printer {
53 out: Some(out),
54 streamer: Streamer::new(
55 config.addr,
56 config.byte,
57 config.text,
58 start_address,
59 config.dedup_enabled,
60 ),
61 is_finished: false,
62 }
63 }
64
65 pub fn finish(mut self) -> O {
67 _ = self.print_last_line();
68 self.is_finished = true;
69 self.out.take().unwrap()
70 }
71}
72
73impl<
74 O: Write,
75 A: AddressFormatting + Clone,
76 B: ByteFormatting + Clone,
77 C: CharFormatting + Clone,
78 > Printer<O, A, B, C>
79{
80 pub fn push(&mut self, bytes: &[u8]) -> Result<usize> {
83 let mut out = self.out.take().expect(OUTPUT_LOST_MESSAGE);
84
85 self.streamer.push(bytes, &mut out)?;
86
87 self.out = Some(out);
88
89 Ok(bytes.len())
90 }
91
92 fn print_last_line(&mut self) -> Result<()> {
93 if self.is_finished {
94 return Ok(());
95 }
96
97 let mut out = self.out.take().expect(OUTPUT_LOST_MESSAGE);
98
99 let result = self.streamer.write_tail(&mut out);
100
101 self.out = Some(out);
102
103 result
104 }
105}
106
107impl<
108 O: Write,
109 A: AddressFormatting + Clone + Default,
110 B: ByteFormatting + Clone + Default,
111 C: CharFormatting + Clone + Default,
112 > Printer<O, A, B, C>
113{
114 pub fn default_with(out: O, start_address: usize) -> Printer<O, A, B, C> {
115 Self::new(out, start_address, Config::<A, B, C>::default())
116 }
117}
118
119impl<O: Write> Printer<O, AddressFormatter, ByteFormatter, CharFormatter> {
120 pub fn default_fmt_with(
121 out: O,
122 start_address: usize,
123 ) -> Printer<O, AddressFormatter, ByteFormatter, CharFormatter> {
124 Self::new(
125 out,
126 start_address,
127 Config::<AddressFormatter, ByteFormatter, CharFormatter>::default(),
128 )
129 }
130}
131
132impl<
133 O: Write,
134 A: AddressFormatting + Clone,
135 B: ByteFormatting + Clone,
136 C: CharFormatting + Clone,
137 > Write for Printer<O, A, B, C>
138{
139 fn write(&mut self, buf: &[u8]) -> Result<usize> {
140 self.push(buf)
141 }
142
143 fn flush(&mut self) -> Result<()> {
145 Ok(())
146 }
147}
148
149impl<
150 O: Write,
151 A: AddressFormatting + Clone,
152 B: ByteFormatting + Clone,
153 C: CharFormatting + Clone,
154 > Drop for Printer<O, A, B, C>
155{
156 fn drop(&mut self) {
157 _ = self.print_last_line();
158 }
159}
160
161#[cfg(test)]
162mod test {
163 use super::*;
164
165 #[test]
166 fn simple() {
167 let result = vec![];
168 let mut printer = Printer::default_fmt_with(result, 0);
169
170 let bytes1 = &[222u8, 173, 190, 239];
171 let bytes2 = &[0xfeu8, 0xed, 0xfa];
172 let title = b"Simple printing";
173
174 for _ in 0..10 {
175 _ = printer.push(bytes1);
176 }
177
178 _ = printer.push(title);
179
180 for _ in 0..11 {
181 _ = printer.push(bytes2);
182 }
183
184 let result = printer.finish();
185 let result_str = String::from_utf8(result).expect("Invalid characters in result");
186
187 let expected = "00000000 deadbeef deadbeef deadbeef deadbeef |................|
188*
18900000020 deadbeef deadbeef 53696d70 6c652070 |........Simple p|
19000000030 72696e74 696e67fe edfafeed fafeedfa |rinting.........|
19100000040 feedfafe edfafeed fafeedfa feedfafe |................|
19200000050 edfafeed fafeedfa ........ ........ |........ |
19300000058 \n";
194
195 assert_eq!(result_str, expected);
196 }
197
198 #[test]
199 fn stable_reading() {
200 println!("Testing reading stability");
201 stable_reading_with("testable/lorem_ipsum");
202 stable_reading_with("testable/duplications");
203 }
204
205 fn stable_reading_with(path: &str) {
206 println!("Path: {path}");
207
208 let patterns = vec![
209 vec![1],
210 vec![1, 2, 1],
211 vec![5],
212 vec![4],
213 vec![4, 7],
214 vec![4, 1],
215 vec![18, 1, 16, 7, 4, 5, 3],
216 vec![2000],
217 vec![444],
218 ];
219 let test_data =
220 std::fs::read(path).expect("Could not opent testable data");
221
222 let mut last_result: Option<String> = None;
223
224 for pat in patterns {
225 let result = string_with(&test_data, pat);
226 if let Some(last_result) = last_result {
227 assert_eq!(result, last_result);
228 }
229
230 last_result = Some(result);
231 }
232 }
233
234 fn string_with(bytes: &[u8], read_len_pattern: Vec<usize>) -> String {
235 use std::cmp::min;
236
237 let result = vec![];
238 let mut printer = Printer::default_fmt_with(result, 0);
239
240 let mut tmp = bytes;
241
242 let mut pat_idx = 0;
243 while tmp.len() != 0 {
244 let to_read = min(read_len_pattern[pat_idx], tmp.len());
245
246 printer
247 .write(&tmp[..to_read])
248 .expect("Writing to printer error");
249
250 tmp = &tmp[to_read..];
251
252 pat_idx += 1;
253 pat_idx %= read_len_pattern.len();
254 }
255
256 let result = printer.finish();
257 String::from_utf8(result).expect("Invalid characters in result")
258 }
259
260 #[test]
261 fn duplications() {
262 let result = string_with_file("testable/duplications");
263 let expected = "00000000 61626364 65666768 696a6b6c 6d6e6f70 |abcdefghijklmnop|
26400000010 61626364 65666768 69316b6c 6d6e6f70 |abcdefghi1klmnop|
26500000020 61626364 65666768 696a6b6c 6d6e6f70 |abcdefghijklmnop|
266*
26700000040 61626364 65666768 696a6b6c 6d6e6f67 |abcdefghijklmnog|
26800000050 61626364 65666768 6935366c 6d6e6f70 |abcdefghi56lmnop|
26900000060 61626364 65666768 696a6b6c 6d6e6f70 |abcdefghijklmnop|
270*
271000000e0 \n";
272
273 assert_eq!(result, expected);
274 }
275
276 fn string_with_file(path: &str) -> String {
277 let test_data =
278 std::fs::read(path).expect("Could not opent testable data");
279 string_with(&test_data, vec![100])
280 }
281}