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 use crate::fmt::hex;
336
337 #[test]
338 fn from_hex() {
339 use super::from_hex as fh;
340 assert_eq!(fh("", false).ok(), Some(vec![]));
341 assert_eq!(fh("0", false).ok(), Some(vec![0x00]));
342 assert_eq!(fh("00", false).ok(), Some(vec![0x00]));
343 assert_eq!(fh("09", false).ok(), Some(vec![0x09]));
344 assert_eq!(fh("0f", false).ok(), Some(vec![0x0f]));
345 assert_eq!(fh("99", false).ok(), Some(vec![0x99]));
346 assert_eq!(fh("ff", false).ok(), Some(vec![0xff]));
347 assert_eq!(fh("000", false).ok(), Some(vec![0x00, 0x00]));
348 assert_eq!(fh("0000", false).ok(), Some(vec![0x00, 0x00]));
349 assert_eq!(fh("0009", false).ok(), Some(vec![0x00, 0x09]));
350 assert_eq!(fh("000f", false).ok(), Some(vec![0x00, 0x0f]));
351 assert_eq!(fh("0099", false).ok(), Some(vec![0x00, 0x99]));
352 assert_eq!(fh("00ff", false).ok(), Some(vec![0x00, 0xff]));
353 assert_eq!(fh("\t\n\x0c\r ", false).ok(), None);
354 assert_eq!(fh("a", false).ok(), Some(vec![0x0a]));
355 assert_eq!(fh("0x", false).ok(), None);
356 assert_eq!(fh("0x0", false).ok(), None);
357 assert_eq!(fh("0x00", false).ok(), None);
358 }
359
360 #[test]
361 fn from_pretty_hex() {
362 use super::from_hex as fh;
363 assert_eq!(fh(" ", true).ok(), Some(vec![]));
364 assert_eq!(fh(" 0", true).ok(), Some(vec![0x00]));
365 assert_eq!(fh(" 00", true).ok(), Some(vec![0x00]));
366 assert_eq!(fh(" 09", true).ok(), Some(vec![0x09]));
367 assert_eq!(fh(" 0f", true).ok(), Some(vec![0x0f]));
368 assert_eq!(fh(" 99", true).ok(), Some(vec![0x99]));
369 assert_eq!(fh(" ff", true).ok(), Some(vec![0xff]));
370 assert_eq!(fh(" 00 0", true).ok(), Some(vec![0x00, 0x00]));
371 assert_eq!(fh(" 00 00", true).ok(), Some(vec![0x00, 0x00]));
372 assert_eq!(fh(" 00 09", true).ok(), Some(vec![0x00, 0x09]));
373 assert_eq!(fh(" 00 0f", true).ok(), Some(vec![0x00, 0x0f]));
374 assert_eq!(fh(" 00 99", true).ok(), Some(vec![0x00, 0x99]));
375 assert_eq!(fh(" 00 ff", true).ok(), Some(vec![0x00, 0xff]));
376 assert_eq!(fh("\t\n\x0c\r ", true).ok(), Some(vec![]));
377 assert_eq!(fh(" 23", true).ok(), Some(vec![0x23]));
379 assert_eq!(fh("a", true).ok(), Some(vec![0x0a]));
380 assert_eq!(fh(" 0x", true).ok(), Some(vec![]));
381 assert_eq!(fh(" 0x0", true).ok(), Some(vec![0x00]));
382 assert_eq!(fh(" 0x00", true).ok(), Some(vec![0x00]));
383 }
384
385 quickcheck! {
386 fn hex_roundtrip(data: Vec<u8>) -> bool {
387 let hex = super::to_hex(&data, false);
388 data == super::from_hex(&hex, false).unwrap()
389 }
390 }
391
392 quickcheck! {
393 fn pretty_hex_roundtrip(data: Vec<u8>) -> bool {
394 let hex = super::to_hex(&data, true);
395 data == super::from_hex(&hex, true).unwrap()
396 }
397 }
398
399 #[test]
400 fn hex_decode_accepts_mixed_case() {
401 assert_eq!(hex::decode("aBcD").unwrap(), vec![0xAB, 0xCD]);
402 }
403
404 #[test]
405 fn hex_decode_odd_nibbles_are_left_padded() {
406 assert_eq!(hex::decode("ABC").unwrap(), vec![0x0A, 0xBC]);
407 }
408
409 #[test]
410 fn hex_decode_rejects_invalid_characters() {
411 assert!(hex::decode("GG").is_err());
412 assert!(hex::decode_pretty("0xGG").is_err());
413 }
414
415 #[test]
416 fn hex_dumper() {
417 use super::hex::Dumper;
418
419 let mut dumper = Dumper::new(Vec::new(), "III");
420 dumper.write(&[0x89, 0x01, 0x33], "frame").unwrap();
421 let buf = dumper.into_inner();
422 assert_eq!(
423 ::std::str::from_utf8(&buf[..]).unwrap(),
424 "III00000000 \
425 89 01 33 \
426 frame\n");
427
428 let mut dumper = Dumper::new(Vec::new(), "III");
429 dumper.write(&[0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01], "frame")
430 .unwrap();
431 let buf = dumper.into_inner();
432 assert_eq!(
433 ::std::str::from_utf8(&buf[..]).unwrap(),
434 "III00000000 \
435 89 01 33 89 01 33 89 01 \
436 frame\n");
437
438 let mut dumper = Dumper::new(Vec::new(), "III");
439 dumper.write(&[0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01,
440 0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01], "frame")
441 .unwrap();
442 let buf = dumper.into_inner();
443 assert_eq!(
444 ::std::str::from_utf8(&buf[..]).unwrap(),
445 "III00000000 \
446 89 01 33 89 01 33 89 01 89 01 33 89 01 33 89 01 \
447 frame\n");
448
449 let mut dumper = Dumper::new(Vec::new(), "III");
450 dumper.write(&[0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01,
451 0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01,
452 0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01,
453 0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01], "frame")
454 .unwrap();
455 let buf = dumper.into_inner();
456 assert_eq!(
457 ::std::str::from_utf8(&buf[..]).unwrap(),
458 "III00000000 \
459 89 01 33 89 01 33 89 01 89 01 33 89 01 33 89 01 \
460 frame\n\
461 III00000010 \
462 89 01 33 89 01 33 89 01 89 01 33 89 01 33 89 01\n");
463
464 let mut dumper = Dumper::new(Vec::new(), "");
465 dumper.write(&[0x89, 0x01, 0x33], "frame").unwrap();
466 dumper.write(&[0x04], "version").unwrap();
467 dumper.write(&[0x00], "type").unwrap();
468 let buf = dumper.into_inner();
469 assert_eq!(
470 ::std::str::from_utf8(&buf[..]).unwrap(),
471 "00000000 89 01 33 \
472 frame\n\
473 00000003 04 \
474 version\n\
475 00000004 00 \
476 type\n\
477 ");
478 }
479
480 quickcheck! {
481 fn hex_encode_decode_roundtrip_qc(data: Vec<u8>) -> bool {
482 hex::decode(hex::encode(&data)).unwrap() == data
483 }
484 }
485
486 #[test]
487 fn hex_encode_formats_uppercase() {
488 assert_eq!(hex::encode(&[0xAB, 0xCD, 0xEF]), "ABCDEF");
489 }
490
491 quickcheck! {
492 fn hex_pretty_accepts_0x_prefix_qc(data: Vec<u8>) -> bool {
493 let s = format!("0x{}", hex::encode(&data));
494 hex::decode_pretty(&s).unwrap() == data
495 }
496 }
497
498 #[test]
499 fn hex_pretty_accepts_bare_0x_as_empty() {
500 assert_eq!(hex::decode_pretty("0x").unwrap(), Vec::<u8>::new());
501 }
502
503 quickcheck! {
504 fn hex_pretty_encode_decode_roundtrip_qc(data: Vec<u8>) -> bool {
505 hex::decode_pretty(hex::encode_pretty(&data)).unwrap() == data
506 }
507 }
508
509 #[test]
510 fn hex_pretty_accepts_prefix_and_spaces() {
511 assert_eq!(hex::decode_pretty(" 0xAB CD \n").unwrap(),
512 vec![0xAB, 0xCD]);
513 }
514
515 #[test]
516 fn hex_pretty_rejects_input_with_several_0x() {
517 assert!(hex::decode_pretty("0x0x0x").is_err());
518 }
519 #[test]
520 fn hex_pretty_rejects_stray_x() {
521 assert!(hex::decode_pretty("ABxC").is_err());
522 }
523
524 #[test]
525 fn hex_pretty_rejects_x_when_not_part_of_leading_prefix() {
526 assert!(hex::decode_pretty("1x").is_err());
527 }
528
529 #[test]
530 fn hex_pretty_whitespace_invariant() {
531 let data = vec![0x01, 0x23, 0x45, 0x67, 0x89];
532 let base = hex::encode_pretty(&data);
533 let noisy = format!(" \n{}\t\n", base.replace(" ", " \t "));
534 assert_eq!(hex::decode_pretty(&noisy).unwrap(), data);
535 }
536
537 #[test]
538 fn time() {
539 use super::time;
540 use crate::types::Timestamp;
541 let t = |epoch| -> std::time::SystemTime {
542 Timestamp::from(epoch).into()
543 };
544 assert_eq!(&time(&t(1585217290)), "2020-03-26T10:08:10Z");
545 }
546}