hex_utils/lib.rs
1/**
2 * File: src/lib.rs
3 * Author: Anicka Burova <anicka.burova@gmail.com>
4 * Date: 03.10.2017
5 * Last Modified Date: 03.10.2017
6 * Last Modified By: Anicka Burova <anicka.burova@gmail.com>
7 */
8use std::io::{Read,Bytes};
9use std::fmt::Write;
10#[cfg(test)]
11use std::fs::File;
12use std::iter::Iterator;
13
14/// Format configuration of xxd output
15#[derive(Clone)]
16pub struct Format {
17 /// How many bytes per line, the default is 16.
18 pub size: usize,
19 /// How to pack xx bytes next to each other. For each multiple of the value, space will be
20 /// inserted.
21 /// The default is [2,4,8]. (Every two bytes one space, every 4 bytes a space and every 8 bytes
22 /// a space).
23 pub pack: Vec<usize>,
24 /// A character to print in case of unprintable characters for ASCII output, the default is '.'.
25 pub ascii_none: char,
26 /// True to output ASCII. The default is true.
27 pub ascii: bool,
28 /// Gaps in formatting: offset{gaps.0}hex{gaps.1}ascii. The default is 2 spaces for both.
29 pub gaps: (usize, usize),
30}
31
32impl Format {
33 /// Create default format
34 ///
35 /// # Examples
36 ///
37 /// ```
38 /// extern crate hex_utils;
39 ///
40 /// let format = hex_utils::Format::default().unwrap();
41 ///
42 /// assert_eq!(16, format.size);
43 ///
44 /// ```
45 pub fn default() -> Option<Format> {
46 Some(
47 Format {
48 size: 16,
49 pack: vec![2,4,8],
50 ascii_none: '.',
51 ascii: true,
52 gaps: (2,2),
53 }
54 )
55 }
56
57 /// Get the format out of Option or get the default one.
58 ///
59 /// # Examples
60 ///
61 /// ```
62 /// let format = Some(hex_utils::Format {
63 /// size: 9,
64 /// pack: vec![3,5],
65 /// ascii_none: '#',
66 /// ascii: true,
67 /// gaps: (2,3),
68 /// });
69 ///
70 /// let opt = hex_utils::Format::or_default(format);
71 /// assert_eq!(9, opt.size);
72 /// assert_eq!(vec![3,5], opt.pack);
73 /// assert_eq!('#', opt.ascii_none);
74 ///
75 /// let opt = hex_utils::Format::or_default(None);
76 /// assert_eq!(16, opt.size);
77 /// assert_eq!(vec![2,4,8], opt.pack);
78 /// assert_eq!('.', opt.ascii_none);
79 ///
80 /// ```
81 pub fn or_default(format: Option<Format>) -> Format {
82 match format {
83 Some(o) => o,
84 None => Format::default().unwrap(),
85 }
86 }
87
88 /// Create formatter function from this formatting configuration.
89 pub fn formatter(&self) -> Box<Fn( (usize, String, Option<String>)) -> String> {
90 let hex_size =
91 self.size * 2
92 + self.pack.iter().fold(0, | sum, val| sum + if *val == 0 { 0 } else { (self.size - 1) / val } );
93 let gap0 = self.gaps.0;
94 let gap1 = self.gaps.1;
95 Box::new(move |line| {
96 let (i,h,t) = line;
97 let mut result = String::new();
98 match t {
99 Some(t) => {
100 let _ = write!(&mut result, "{:0index$x}:{:gap0$}{:hex$}{:gap1$}{}", i,"", h.trim(),"", t, index=6, hex = hex_size, gap0 = gap0, gap1 = gap1);
101 }
102 None => {
103 let _ = write!(&mut result, "{:0index$x}:{:gap0$}{:hex$}", i, "", h.trim(), index=6, hex = hex_size, gap0 = gap0);
104 }
105 }
106 result
107 })
108 }
109}
110
111/// Iterator over lines returned by xxd.
112///
113/// Iterators returns (usize, String, Option<String>)
114/// where ther first is the offset of the data,
115/// the second is data formatted to hex output
116/// and the third is Option to return data formatted as normal text.
117///
118pub struct XxdLines<T: Read> {
119 iter: Bytes<T>,
120 format: Format,
121 offset: usize,
122}
123
124impl<T: Read> Iterator for XxdLines<T> {
125 type Item = (usize, String, Option<String>);
126 fn next(&mut self) -> Option<Self::Item> {
127 match self.iter {
128 ref mut iter => {
129 match iter.take(self.format.size) {
130 ref mut iter => {
131 let mut any = false;
132 let mut hexed = String::new();
133 let mut ascii = if self.format.ascii { Some(String::new()) } else { None };
134 let mut pack = vec![0;self.format.pack.len()];
135 for c in iter {
136 any = true;
137 let c = c.unwrap();
138 let _ = write!(&mut hexed, "{:02x}", c);
139 for i in 0..pack.len() {
140 pack[i] += 1;
141 if pack[i] == self.format.pack[i] {
142 let _ = write!(&mut hexed, " ");
143 pack[i] = 0;
144 }
145 }
146 match ascii {
147 Some(ref mut ascii) => {
148 if 32<= c && c < 127 {
149 let _ = write!(ascii, "{}", c as char);
150 } else {
151 let _ = write!(ascii, "{}", self.format.ascii_none);
152 }
153 }
154 None => (),
155 }
156 }
157 let offset = self.offset;
158 self.offset += self.format.size;
159 if any {
160 Some((offset,hexed,ascii))
161 } else {
162 None
163 }
164 }
165 }
166 }
167 }
168 }
169}
170
171
172/// Returns xxd iterator over the data configured by format.
173/// Passing None to format will use defaults.
174///
175/// # Examples
176/// ```
177/// extern crate hex_utils;
178///
179///
180/// let text = "The quick brown fox jumps over the lazy dog";
181///
182/// for (offset, hex, txt) in hex_utils::xxd(text.as_bytes(), None) {
183/// println!("offset = {:03x} hex = {:60} txt = {}", offset, hex, txt.unwrap());
184/// }
185///
186/// ```
187/// ```
188/// extern crate hex_utils;
189///
190///
191/// let text = "The quick brown fox jumps over the lazy dog";
192/// let format = hex_utils::Format {
193/// size: 18,
194/// pack: vec![3,6],
195/// ascii_none: '-',
196/// ascii: true,
197/// gaps:(4,2),
198/// };
199///
200/// let fmt = format.formatter();
201///
202/// for line in hex_utils::xxd(text.as_bytes(), Some(format)) {
203/// println!("{}", fmt(line));
204/// }
205/// ```
206pub fn xxd<T: Read>( data: T, format: Option<Format>) -> XxdLines<T> {
207 XxdLines {
208 iter: data.bytes(),
209 format: Format::or_default(format),
210 offset: 0
211 }
212}
213
214/// Returns the whole xxd output as one string.
215///
216/// # Examples
217///
218/// ```
219/// extern crate hex_utils;
220///
221///
222/// let text = "The quick brown fox jumps over the lazy dog";
223///
224/// println!("{}", hex_utils::xxd_str(text.as_bytes(), None));
225///
226/// ```
227pub fn xxd_str<T: Read>( data: T, format: Option<Format>) -> String {
228 let format = Format::or_default(format);
229 let fmt = format.formatter();
230 xxd(data, Some(format)).map(|line| fmt(line)).fold(String::new(), |mut res, line| {let _ = writeln!(&mut res, "{}", line);res})
231}
232
233
234#[test]
235fn xxd_test() {
236 let text = "[package]
237name = \"hex-utils\"
238version = \"0.1.5\"
239authors = [\"Anicka Burova <anicka.burova@gmail.com>\"]
240
241[dependencies]
242itertools = \"*\"";
243 let output = "000000: 5b70 6163 6b61 6765 5d0a 6e61 6d65 203d [package].name =\n\
244000010: 2022 6865 782d 7574 696c 7322 0a76 6572 \"hex-utils\".ver\n\
245000020: 7369 6f6e 203d 2022 302e 312e 3522 0a61 sion = \"0.1.5\".a\n\
246000030: 7574 686f 7273 203d 205b 2241 6e69 636b uthors = [\"Anick\n\
247000040: 6120 4275 726f 7661 203c 616e 6963 6b61 a Burova <anicka\n\
248000050: 2e62 7572 6f76 6140 676d 6169 6c2e 636f .burova@gmail.co\n\
249000060: 6d3e 225d 0a0a 5b64 6570 656e 6465 6e63 m>\"]..[dependenc\n\
250000070: 6965 735d 0a69 7465 7274 6f6f 6c73 203d ies].itertools =\n\
251000080: 2022 2a22 \"*\"\n";
252 let format = Format {
253 size: 16,
254 pack: vec![2,4],
255 ascii_none: '.',
256 ascii: true,
257 gaps:(1,2),
258 };
259 let xxd_output = xxd_str(text.as_bytes(), Some(format));
260 assert_eq!(output.to_string(), xxd_output);
261}
262#[test]
263fn print_test() {
264 let mut file = File::open("Cargo.toml").unwrap();
265 let mut content = String::new();
266 let _ = file.read_to_string(&mut content);
267 let format = Format {
268 size: 16,
269 pack: vec![2,4],
270 ascii_none: '.',
271 ascii: true,
272 gaps: (1,4)
273 };
274 let _ = format.clone();
275
276 //let fmt = format.formatter();
277
278 //let res = xxd(content.as_bytes(), Some(format)).map(|line| fmt(line)).fold(String::new(), |mut res, line| { let _ = writeln!(&mut res, "{}", line);res});
279 //let res = xxd_str(content.as_bytes(), Some(format));
280 let res = xxd_str(content.as_bytes(), Some(format));
281 println!("{}", res);
282
283 //for line in xxd(content.as_bytes(), Some(format)).map(|line| fmt(line)) {
284 //println!("{}", line);
285 //}
286}