use crate::Error;
use crate::Result;
pub mod hex {
use std::io;
use crate::Result;
pub fn encode<B: AsRef<[u8]>>(buffer: B) -> String {
super::to_hex(buffer.as_ref(), false)
}
pub fn encode_pretty<B: AsRef<[u8]>>(buffer: B) -> String {
super::to_hex(buffer.as_ref(), true)
}
pub fn decode<H: AsRef<str>>(hex: H) -> Result<Vec<u8>> {
super::from_hex(hex.as_ref(), false)
}
pub fn decode_pretty<H: AsRef<str>>(hex: H) -> Result<Vec<u8>> {
super::from_hex(hex.as_ref(), true)
}
pub fn dump<W: io::Write, B: AsRef<[u8]>>(sink: W, data: B)
-> io::Result<()> {
Dumper::new(sink, "").write_ascii(data)
}
pub struct Dumper<W: io::Write> {
inner: W,
indent: String,
offset: usize,
}
assert_send_and_sync!(Dumper<W> where W: io::Write);
impl<W: io::Write> Dumper<W> {
pub fn new<I: AsRef<str>>(inner: W, indent: I) -> Self {
Self::with_offset(inner, indent, 0)
}
pub fn with_offset<I>(inner: W, indent: I, offset: usize) -> Self
where
I: AsRef<str>,
{
Dumper {
inner,
indent: indent.as_ref().into(),
offset,
}
}
pub fn into_inner(self) -> W {
self.inner
}
pub fn write<B, M>(&mut self, buf: B, msg: M) -> io::Result<()>
where B: AsRef<[u8]>,
M: AsRef<str>,
{
let mut first = true;
self.write_labeled(buf.as_ref(), move |_, _| {
if first {
first = false;
Some(msg.as_ref().into())
} else {
None
}
})
}
pub fn write_ascii<B>(&mut self, buf: B) -> io::Result<()>
where B: AsRef<[u8]>,
{
self.write_labeled(buf, |offset, data| {
let mut l = String::new();
for _ in 0..offset {
l.push(' ');
}
for &c in data {
l.push(if c < 32 {
'.'
} else if c < 128 {
c.into()
} else {
'.'
})
}
Some(l)
})
}
pub fn write_labeled<B, L>(&mut self, buf: B, mut labeler: L)
-> io::Result<()>
where B: AsRef<[u8]>,
L: FnMut(usize, &[u8]) -> Option<String>,
{
let buf = buf.as_ref();
let mut first_label_offset = self.offset % 16;
write!(self.inner, "{}{:08x} ", self.indent, self.offset)?;
for i in 0 .. self.offset % 16 {
if i != 7 {
write!(self.inner, " ")?;
} else {
write!(self.inner, " ")?;
}
}
let mut offset_printed = true;
let mut data_start = 0;
for (i, c) in buf.iter().enumerate() {
if ! offset_printed {
write!(self.inner,
"\n{}{:08x} ", self.indent, self.offset)?;
offset_printed = true;
}
write!(self.inner, " {:02x}", c)?;
self.offset += 1;
match self.offset % 16 {
0 => {
if let Some(msg) = Some(&buf[data_start..i + 1])
.filter(|b| ! b.is_empty())
.and_then(|b| labeler(first_label_offset, b))
{
write!(self.inner, " {}", msg)?;
first_label_offset = 0;
}
data_start = i + 1;
offset_printed = false;
},
8 => write!(self.inner, " ")?,
_ => (),
}
}
if let Some(msg) = Some(&buf[data_start..])
.filter(|b| ! b.is_empty())
.and_then(|b| labeler(first_label_offset, b))
{
for i in self.offset % 16 .. 16 {
if i != 7 {
write!(self.inner, " ")?;
} else {
write!(self.inner, " ")?;
}
}
write!(self.inner, " {}", msg)?;
}
writeln!(self.inner)?;
Ok(())
}
}
}
#[allow(dead_code)]
pub(crate) fn to_hex(s: &[u8], pretty: bool) -> String {
use std::fmt::Write;
let mut result = String::new();
for (i, b) in s.iter().enumerate() {
if pretty && i > 0 && i % 2 == 0 {
write!(&mut result, " ").unwrap();
}
write!(&mut result, "{:02X}", b).unwrap();
}
result
}
pub(crate) fn from_hex(hex: &str, pretty: bool) -> Result<Vec<u8>> {
const BAD: u8 = 255u8;
const X: u8 = b'x';
let mut nibbles = hex.chars().filter_map(|x| {
match x {
'0' => Some(0u8),
'1' => Some(1u8),
'2' => Some(2u8),
'3' => Some(3u8),
'4' => Some(4u8),
'5' => Some(5u8),
'6' => Some(6u8),
'7' => Some(7u8),
'8' => Some(8u8),
'9' => Some(9u8),
'a' | 'A' => Some(10u8),
'b' | 'B' => Some(11u8),
'c' | 'C' => Some(12u8),
'd' | 'D' => Some(13u8),
'e' | 'E' => Some(14u8),
'f' | 'F' => Some(15u8),
'x' | 'X' if pretty => Some(X),
_ if pretty && x.is_whitespace() => None,
_ => Some(BAD),
}
}).collect::<Vec<u8>>();
if pretty && nibbles.len() >= 2 && nibbles[0] == 0 && nibbles[1] == X {
nibbles.remove(0);
nibbles.remove(0);
}
if nibbles.iter().any(|&b| b == BAD || b == X) {
return
Err(Error::InvalidArgument("Invalid characters".into()).into());
}
if nibbles.len() % 2 != 0 {
nibbles.insert(0, 0);
}
let bytes = nibbles.chunks(2).map(|nibbles| {
(nibbles[0] << 4) | nibbles[1]
}).collect::<Vec<u8>>();
Ok(bytes)
}
pub(crate) fn time(t: &std::time::SystemTime) -> String {
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
chrono::DateTime::<chrono::Utc>::from(t.clone())
.format("%Y-%m-%dT%H:%M:%SZ")
.to_string()
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] {
extern "C" {
fn strftime(
s: *mut libc::c_char,
max: libc::size_t,
format: *const libc::c_char,
tm: *const libc::tm,
) -> usize;
}
let t = match t.duration_since(std::time::UNIX_EPOCH) {
Ok(t) => t.as_secs() as libc::time_t,
Err(_) => return format!("{:?}", t),
};
let fmt = b"%Y-%m-%dT%H:%M:%SZ\x00";
assert_eq!(b"2020-03-26T10:08:10Z\x00".len(), 21);
let mut s = [0u8; 21];
unsafe {
let mut tm: libc::tm = std::mem::zeroed();
#[cfg(unix)]
libc::gmtime_r(&t, &mut tm);
#[cfg(windows)]
libc::gmtime_s(&mut tm, &t);
strftime(s.as_mut_ptr() as *mut libc::c_char,
s.len(),
fmt.as_ptr() as *const libc::c_char,
&tm);
}
std::ffi::CStr::from_bytes_with_nul(&s)
.expect("strftime nul terminates string")
.to_string_lossy().into()
}
}
#[cfg(test)]
mod test {
#[test]
fn from_hex() {
use super::from_hex as fh;
assert_eq!(fh("", false).ok(), Some(vec![]));
assert_eq!(fh("0", false).ok(), Some(vec![0x00]));
assert_eq!(fh("00", false).ok(), Some(vec![0x00]));
assert_eq!(fh("09", false).ok(), Some(vec![0x09]));
assert_eq!(fh("0f", false).ok(), Some(vec![0x0f]));
assert_eq!(fh("99", false).ok(), Some(vec![0x99]));
assert_eq!(fh("ff", false).ok(), Some(vec![0xff]));
assert_eq!(fh("000", false).ok(), Some(vec![0x00, 0x00]));
assert_eq!(fh("0000", false).ok(), Some(vec![0x00, 0x00]));
assert_eq!(fh("0009", false).ok(), Some(vec![0x00, 0x09]));
assert_eq!(fh("000f", false).ok(), Some(vec![0x00, 0x0f]));
assert_eq!(fh("0099", false).ok(), Some(vec![0x00, 0x99]));
assert_eq!(fh("00ff", false).ok(), Some(vec![0x00, 0xff]));
assert_eq!(fh("\t\n\x0c\r ", false).ok(), None);
assert_eq!(fh("a", false).ok(), Some(vec![0x0a]));
assert_eq!(fh("0x", false).ok(), None);
assert_eq!(fh("0x0", false).ok(), None);
assert_eq!(fh("0x00", false).ok(), None);
}
#[test]
fn from_pretty_hex() {
use super::from_hex as fh;
assert_eq!(fh(" ", true).ok(), Some(vec![]));
assert_eq!(fh(" 0", true).ok(), Some(vec![0x00]));
assert_eq!(fh(" 00", true).ok(), Some(vec![0x00]));
assert_eq!(fh(" 09", true).ok(), Some(vec![0x09]));
assert_eq!(fh(" 0f", true).ok(), Some(vec![0x0f]));
assert_eq!(fh(" 99", true).ok(), Some(vec![0x99]));
assert_eq!(fh(" ff", true).ok(), Some(vec![0xff]));
assert_eq!(fh(" 00 0", true).ok(), Some(vec![0x00, 0x00]));
assert_eq!(fh(" 00 00", true).ok(), Some(vec![0x00, 0x00]));
assert_eq!(fh(" 00 09", true).ok(), Some(vec![0x00, 0x09]));
assert_eq!(fh(" 00 0f", true).ok(), Some(vec![0x00, 0x0f]));
assert_eq!(fh(" 00 99", true).ok(), Some(vec![0x00, 0x99]));
assert_eq!(fh(" 00 ff", true).ok(), Some(vec![0x00, 0xff]));
assert_eq!(fh("\t\n\x0c\r ", true).ok(), Some(vec![]));
assert_eq!(fh(" 23", true).ok(), Some(vec![0x23]));
assert_eq!(fh("a", true).ok(), Some(vec![0x0a]));
assert_eq!(fh(" 0x", true).ok(), Some(vec![]));
assert_eq!(fh(" 0x0", true).ok(), Some(vec![0x00]));
assert_eq!(fh(" 0x00", true).ok(), Some(vec![0x00]));
}
quickcheck! {
fn hex_roundtrip(data: Vec<u8>) -> bool {
let hex = super::to_hex(&data, false);
data == super::from_hex(&hex, false).unwrap()
}
}
quickcheck! {
fn pretty_hex_roundtrip(data: Vec<u8>) -> bool {
let hex = super::to_hex(&data, true);
data == super::from_hex(&hex, true).unwrap()
}
}
#[test]
fn hex_dumper() {
use super::hex::Dumper;
let mut dumper = Dumper::new(Vec::new(), "III");
dumper.write(&[0x89, 0x01, 0x33], "frame").unwrap();
let buf = dumper.into_inner();
assert_eq!(
::std::str::from_utf8(&buf[..]).unwrap(),
"III00000000 \
89 01 33 \
frame\n");
let mut dumper = Dumper::new(Vec::new(), "III");
dumper.write(&[0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01], "frame")
.unwrap();
let buf = dumper.into_inner();
assert_eq!(
::std::str::from_utf8(&buf[..]).unwrap(),
"III00000000 \
89 01 33 89 01 33 89 01 \
frame\n");
let mut dumper = Dumper::new(Vec::new(), "III");
dumper.write(&[0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01,
0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01], "frame")
.unwrap();
let buf = dumper.into_inner();
assert_eq!(
::std::str::from_utf8(&buf[..]).unwrap(),
"III00000000 \
89 01 33 89 01 33 89 01 89 01 33 89 01 33 89 01 \
frame\n");
let mut dumper = Dumper::new(Vec::new(), "III");
dumper.write(&[0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01,
0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01,
0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01,
0x89, 0x01, 0x33, 0x89, 0x01, 0x33, 0x89, 0x01], "frame")
.unwrap();
let buf = dumper.into_inner();
assert_eq!(
::std::str::from_utf8(&buf[..]).unwrap(),
"III00000000 \
89 01 33 89 01 33 89 01 89 01 33 89 01 33 89 01 \
frame\n\
III00000010 \
89 01 33 89 01 33 89 01 89 01 33 89 01 33 89 01\n");
let mut dumper = Dumper::new(Vec::new(), "");
dumper.write(&[0x89, 0x01, 0x33], "frame").unwrap();
dumper.write(&[0x04], "version").unwrap();
dumper.write(&[0x00], "type").unwrap();
let buf = dumper.into_inner();
assert_eq!(
::std::str::from_utf8(&buf[..]).unwrap(),
"00000000 89 01 33 \
frame\n\
00000003 04 \
version\n\
00000004 00 \
type\n\
");
}
#[test]
fn time() {
use super::time;
use crate::types::Timestamp;
let t = |epoch| -> std::time::SystemTime {
Timestamp::from(epoch).into()
};
assert_eq!(&time(&t(1585217290)), "2020-03-26T10:08:10Z");
}
}