1use crate::Error;
4use crate::Result;
5
6pub mod hex {
8 use std::io;
9
10 use crate::Result;
11
12 pub fn encode<B: AsRef<[u8]>>(buffer: B) -> String {
14 super::to_hex(buffer.as_ref(), false)
15 }
16
17 pub fn encode_pretty<B: AsRef<[u8]>>(buffer: B) -> String {
19 super::to_hex(buffer.as_ref(), true)
20 }
21
22 pub fn decode<H: AsRef<str>>(hex: H) -> Result<Vec<u8>> {
24 super::from_hex(hex.as_ref(), false)
25 }
26
27 pub fn decode_pretty<H: AsRef<str>>(hex: H) -> Result<Vec<u8>> {
29 super::from_hex(hex.as_ref(), true)
30 }
31
32 pub fn dump<W: io::Write, B: AsRef<[u8]>>(sink: W, data: B)
34 -> io::Result<()> {
35 Dumper::new(sink, "").write_ascii(data)
36 }
37
38 pub struct Dumper<W: io::Write> {
61 inner: W,
62 indent: String,
63 offset: usize,
64 }
65
66 assert_send_and_sync!(Dumper<W> where W: io::Write);
67
68 impl<W: io::Write> Dumper<W> {
69 pub fn new<I: AsRef<str>>(inner: W, indent: I) -> Self {
74 Self::with_offset(inner, indent, 0)
75 }
76
77 pub fn with_offset<I>(inner: W, indent: I, offset: usize) -> Self
82 where
83 I: AsRef<str>,
84 {
85 Dumper {
86 inner,
87 indent: indent.as_ref().into(),
88 offset,
89 }
90 }
91
92 pub fn into_inner(self) -> W {
94 self.inner
95 }
96
97 pub fn write<B, M>(&mut self, buf: B, msg: M) -> io::Result<()>
101 where B: AsRef<[u8]>,
102 M: AsRef<str>,
103 {
104 let mut first = true;
105 self.write_labeled(buf.as_ref(), move |_, _| {
106 if first {
107 first = false;
108 Some(msg.as_ref().into())
109 } else {
110 None
111 }
112 })
113 }
114
115 pub fn write_ascii<B>(&mut self, buf: B) -> io::Result<()>
119 where B: AsRef<[u8]>,
120 {
121 self.write_labeled(buf, |offset, data| {
122 let mut l = String::new();
123 for _ in 0..offset {
124 l.push(' ');
125 }
126 for &c in data {
127 l.push(if c < 32 {
128 '.'
129 } else if c < 128 {
130 c.into()
131 } else {
132 '.'
133 })
134 }
135 Some(l)
136 })
137 }
138
139 pub fn write_labeled<B, L>(&mut self, buf: B, mut labeler: L)
146 -> io::Result<()>
147 where B: AsRef<[u8]>,
148 L: FnMut(usize, &[u8]) -> Option<String>,
149 {
150 let buf = buf.as_ref();
151 let mut first_label_offset = self.offset % 16;
152
153 write!(self.inner, "{}{:08x} ", self.indent, self.offset)?;
154 for i in 0 .. self.offset % 16 {
155 if i != 7 {
156 write!(self.inner, " ")?;
157 } else {
158 write!(self.inner, " ")?;
159 }
160 }
161
162 let mut offset_printed = true;
163 let mut data_start = 0;
164 for (i, c) in buf.iter().enumerate() {
165 if ! offset_printed {
166 write!(self.inner,
167 "\n{}{:08x} ", self.indent, self.offset)?;
168 offset_printed = true;
169 }
170
171 write!(self.inner, " {:02x}", c)?;
172 self.offset += 1;
173 match self.offset % 16 {
174 0 => {
175 if let Some(msg) = Some(&buf[data_start..i + 1])
176 .filter(|b| ! b.is_empty())
177 .and_then(|b| labeler(first_label_offset, b))
178 {
179 write!(self.inner, " {}", msg)?;
180 first_label_offset = 0;
182 }
183 data_start = i + 1;
184 offset_printed = false;
185 },
186 8 => write!(self.inner, " ")?,
187 _ => (),
188 }
189 }
190
191 if let Some(msg) = Some(&buf[data_start..])
192 .filter(|b| ! b.is_empty())
193 .and_then(|b| labeler(first_label_offset, b))
194 {
195 for i in self.offset % 16 .. 16 {
196 if i != 7 {
197 write!(self.inner, " ")?;
198 } else {
199 write!(self.inner, " ")?;
200 }
201 }
202
203 write!(self.inner, " {}", msg)?;
204 }
205 writeln!(self.inner)?;
206 Ok(())
207 }
208 }
209}
210
211#[allow(dead_code)]
213pub(crate) fn to_hex(s: &[u8], pretty: bool) -> String {
214 use std::fmt::Write;
215
216 let mut result = String::new();
217 for (i, b) in s.iter().enumerate() {
218 if pretty && i > 0 && i % 2 == 0 {
221 write!(&mut result, " ").unwrap();
222 }
223 write!(&mut result, "{:02X}", b).unwrap();
224 }
225 result
226}
227
228pub(crate) fn from_hex(hex: &str, pretty: bool) -> Result<Vec<u8>> {
231 const BAD: u8 = 255u8;
232 const X: u8 = b'x';
233
234 let mut nibbles = hex.chars().filter_map(|x| {
235 match x {
236 '0' => Some(0u8),
237 '1' => Some(1u8),
238 '2' => Some(2u8),
239 '3' => Some(3u8),
240 '4' => Some(4u8),
241 '5' => Some(5u8),
242 '6' => Some(6u8),
243 '7' => Some(7u8),
244 '8' => Some(8u8),
245 '9' => Some(9u8),
246 'a' | 'A' => Some(10u8),
247 'b' | 'B' => Some(11u8),
248 'c' | 'C' => Some(12u8),
249 'd' | 'D' => Some(13u8),
250 'e' | 'E' => Some(14u8),
251 'f' | 'F' => Some(15u8),
252 'x' | 'X' if pretty => Some(X),
253 _ if pretty && x.is_whitespace() => None,
254 _ => Some(BAD),
255 }
256 }).collect::<Vec<u8>>();
257
258 if pretty && nibbles.len() >= 2 && nibbles[0] == 0 && nibbles[1] == X {
259 nibbles.remove(0);
261 nibbles.remove(0);
262 }
263
264 if nibbles.iter().any(|&b| b == BAD || b == X) {
265 return
267 Err(Error::InvalidArgument("Invalid characters".into()).into());
268 }
269
270 if nibbles.len() % 2 != 0 {
272 nibbles.insert(0, 0);
273 }
274
275 let bytes = nibbles.chunks(2).map(|nibbles| {
276 (nibbles[0] << 4) | nibbles[1]
277 }).collect::<Vec<u8>>();
278
279 Ok(bytes)
280}
281
282pub(crate) fn time(t: &std::time::SystemTime) -> String {
288 #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
291 chrono::DateTime::<chrono::Utc>::from(t.clone())
292 .format("%Y-%m-%dT%H:%M:%SZ")
293 .to_string()
294 }
295 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] {
296 extern "C" {
297 fn strftime(
298 s: *mut libc::c_char,
299 max: libc::size_t,
300 format: *const libc::c_char,
301 tm: *const libc::tm,
302 ) -> usize;
303 }
304
305 let t = match t.duration_since(std::time::UNIX_EPOCH) {
306 Ok(t) => t.as_secs() as libc::time_t,
307 Err(_) => return format!("{:?}", t),
308 };
309 let fmt = b"%Y-%m-%dT%H:%M:%SZ\x00";
310 assert_eq!(b"2020-03-26T10:08:10Z\x00".len(), 21);
311 let mut s = [0u8; 21];
312
313 unsafe {
314 let mut tm: libc::tm = std::mem::zeroed();
315
316 #[cfg(unix)]
317 libc::gmtime_r(&t, &mut tm);
318 #[cfg(windows)]
319 libc::gmtime_s(&mut tm, &t);
320
321 strftime(s.as_mut_ptr() as *mut libc::c_char,
322 s.len(),
323 fmt.as_ptr() as *const libc::c_char,
324 &tm);
325 }
326
327 std::ffi::CStr::from_bytes_with_nul(&s)
328 .expect("strftime nul terminates string")
329 .to_string_lossy().into()
330 }
331}
332
333#[cfg(test)]
334mod test {
335 #[test]
336 fn from_hex() {
337 use super::from_hex as fh;
338 assert_eq!(fh("", false).ok(), Some(vec![]));
339 assert_eq!(fh("0", false).ok(), Some(vec![0x00]));
340 assert_eq!(fh("00", false).ok(), Some(vec![0x00]));
341 assert_eq!(fh("09", false).ok(), Some(vec![0x09]));
342 assert_eq!(fh("0f", false).ok(), Some(vec![0x0f]));
343 assert_eq!(fh("99", false).ok(), Some(vec![0x99]));
344 assert_eq!(fh("ff", false).ok(), Some(vec![0xff]));
345 assert_eq!(fh("000", false).ok(), Some(vec![0x00, 0x00]));
346 assert_eq!(fh("0000", false).ok(), Some(vec![0x00, 0x00]));
347 assert_eq!(fh("0009", false).ok(), Some(vec![0x00, 0x09]));
348 assert_eq!(fh("000f", false).ok(), Some(vec![0x00, 0x0f]));
349 assert_eq!(fh("0099", false).ok(), Some(vec![0x00, 0x99]));
350 assert_eq!(fh("00ff", false).ok(), Some(vec![0x00, 0xff]));
351 assert_eq!(fh("\t\n\x0c\r ", false).ok(), None);
352 assert_eq!(fh("a", false).ok(), Some(vec![0x0a]));
353 assert_eq!(fh("0x", false).ok(), None);
354 assert_eq!(fh("0x0", false).ok(), None);
355 assert_eq!(fh("0x00", false).ok(), None);
356 }
357
358 #[test]
359 fn from_pretty_hex() {
360 use super::from_hex as fh;
361 assert_eq!(fh(" ", true).ok(), Some(vec![]));
362 assert_eq!(fh(" 0", true).ok(), Some(vec![0x00]));
363 assert_eq!(fh(" 00", true).ok(), Some(vec![0x00]));
364 assert_eq!(fh(" 09", true).ok(), Some(vec![0x09]));
365 assert_eq!(fh(" 0f", true).ok(), Some(vec![0x0f]));
366 assert_eq!(fh(" 99", true).ok(), Some(vec![0x99]));
367 assert_eq!(fh(" ff", true).ok(), Some(vec![0xff]));
368 assert_eq!(fh(" 00 0", true).ok(), Some(vec![0x00, 0x00]));
369 assert_eq!(fh(" 00 00", true).ok(), Some(vec![0x00, 0x00]));
370 assert_eq!(fh(" 00 09", true).ok(), Some(vec![0x00, 0x09]));
371 assert_eq!(fh(" 00 0f", true).ok(), Some(vec![0x00, 0x0f]));
372 assert_eq!(fh(" 00 99", true).ok(), Some(vec![0x00, 0x99]));
373 assert_eq!(fh(" 00 ff", true).ok(), Some(vec![0x00, 0xff]));
374 assert_eq!(fh("\t\n\x0c\r ", true).ok(), Some(vec![]));
375 assert_eq!(fh(" 23", true).ok(), Some(vec![0x23]));
377 assert_eq!(fh("a", true).ok(), Some(vec![0x0a]));
378 assert_eq!(fh(" 0x", true).ok(), Some(vec![]));
379 assert_eq!(fh(" 0x0", true).ok(), Some(vec![0x00]));
380 assert_eq!(fh(" 0x00", true).ok(), Some(vec![0x00]));
381 }
382
383 quickcheck! {
384 fn hex_roundtrip(data: Vec<u8>) -> bool {
385 let hex = super::to_hex(&data, false);
386 data == super::from_hex(&hex, false).unwrap()
387 }
388 }
389
390 quickcheck! {
391 fn pretty_hex_roundtrip(data: Vec<u8>) -> bool {
392 let hex = super::to_hex(&data, true);
393 data == super::from_hex(&hex, true).unwrap()
394 }
395 }
396
397 #[test]
398 fn hex_dumper() {
399 use super::hex::Dumper;
400
401 let mut dumper = Dumper::new(Vec::new(), "III");
402 dumper.write(&[0x89, 0x01, 0x33], "frame").unwrap();
403 let buf = dumper.into_inner();
404 assert_eq!(
405 ::std::str::from_utf8(&buf[..]).unwrap(),
406 "III00000000 \
407 89 01 33 \
408 frame\n");
409
410 let mut dumper = Dumper::new(Vec::new(), "III");
411 dumper.write(&[0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01], "frame")
412 .unwrap();
413 let buf = dumper.into_inner();
414 assert_eq!(
415 ::std::str::from_utf8(&buf[..]).unwrap(),
416 "III00000000 \
417 89 01 33 89 01 33 89 01 \
418 frame\n");
419
420 let mut dumper = Dumper::new(Vec::new(), "III");
421 dumper.write(&[0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01,
422 0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01], "frame")
423 .unwrap();
424 let buf = dumper.into_inner();
425 assert_eq!(
426 ::std::str::from_utf8(&buf[..]).unwrap(),
427 "III00000000 \
428 89 01 33 89 01 33 89 01 89 01 33 89 01 33 89 01 \
429 frame\n");
430
431 let mut dumper = Dumper::new(Vec::new(), "III");
432 dumper.write(&[0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01,
433 0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01,
434 0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01,
435 0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01], "frame")
436 .unwrap();
437 let buf = dumper.into_inner();
438 assert_eq!(
439 ::std::str::from_utf8(&buf[..]).unwrap(),
440 "III00000000 \
441 89 01 33 89 01 33 89 01 89 01 33 89 01 33 89 01 \
442 frame\n\
443 III00000010 \
444 89 01 33 89 01 33 89 01 89 01 33 89 01 33 89 01\n");
445
446 let mut dumper = Dumper::new(Vec::new(), "");
447 dumper.write(&[0x89, 0x01, 0x33], "frame").unwrap();
448 dumper.write(&[0x04], "version").unwrap();
449 dumper.write(&[0x00], "type").unwrap();
450 let buf = dumper.into_inner();
451 assert_eq!(
452 ::std::str::from_utf8(&buf[..]).unwrap(),
453 "00000000 89 01 33 \
454 frame\n\
455 00000003 04 \
456 version\n\
457 00000004 00 \
458 type\n\
459 ");
460 }
461
462 #[test]
463 fn time() {
464 use super::time;
465 use crate::types::Timestamp;
466 let t = |epoch| -> std::time::SystemTime {
467 Timestamp::from(epoch).into()
468 };
469 assert_eq!(&time(&t(1585217290)), "2020-03-26T10:08:10Z");
470 }
471}