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}