//! This is a general utility crate used by all our rust projects. use failure::*; use lazy_static::lazy_static; pub mod borrow; pub mod common_regex; pub mod fd; pub mod fs; pub mod io; pub mod serde; pub mod vec; /// Evaluates to the offset (in bytes) of a given member within a struct #[macro_export] macro_rules! offsetof { ($ty:ty, $field:ident) => { unsafe { &(*(0 as *const $ty)).$field as *const _ as usize } }; } /// Macro to write error-handling blocks (like perl eval {}) /// /// #### Example: /// ``` /// # use proxmox_tools::try_block; /// # use failure::*; /// # let some_condition = false; /// let result = try_block!({ /// if (some_condition) { /// bail!("some error"); /// } /// Ok(()) /// }) /// .map_err(|e| format_err!("my try block returned an error - {}", e)); /// ``` #[macro_export] macro_rules! try_block { { $($token:tt)* } => {{ (|| -> Result<_,_> { $($token)* })() }} } const HEX_CHARS: &[u8; 16] = b"0123456789abcdef"; pub fn digest_to_hex(digest: &[u8]) -> String { bin_to_hex(digest) } /// Convert a byte slice to a string of hexadecimal digits. /// /// ``` /// # use proxmox_tools::bin_to_hex; /// /// let text = bin_to_hex(&[1, 2, 0xff]); /// assert_eq!(text, "0102ff"); /// ``` pub fn bin_to_hex(digest: &[u8]) -> String { let mut buf = Vec::::with_capacity(digest.len() * 2); for i in 0..digest.len() { buf.push(HEX_CHARS[(digest[i] >> 4) as usize]); buf.push(HEX_CHARS[(digest[i] & 0xf) as usize]); } unsafe { String::from_utf8_unchecked(buf) } } /// Convert a string of hexadecimal digits to a byte vector. Any non-digits are treated as an /// error, so when there is possible whitespace in the string it must be stripped by the caller /// first. Also, only full bytes are allowed, so the input must consist of an even number of /// digits. /// /// ``` /// # use proxmox_tools::hex_to_bin; /// /// let data = hex_to_bin("aabb0123").unwrap(); /// assert_eq!(&data, &[0xaa, 0xbb, 0x01, 0x23]); /// ``` pub fn hex_to_bin(hex: &str) -> Result, Error> { let mut result = vec![]; let bytes = hex.as_bytes(); if (bytes.len() % 2) != 0 { bail!("hex_to_bin: got wrong input length."); } let val = |c| { if c >= b'0' && c <= b'9' { return Ok(c - b'0'); } if c >= b'a' && c <= b'f' { return Ok(c - b'a' + 10); } if c >= b'A' && c <= b'F' { return Ok(c - b'A' + 10); } bail!("found illegal hex character."); }; for pair in bytes.chunks(2) { let h = val(pair[0])?; let l = val(pair[1])?; result.push((h << 4) | l); } Ok(result) } // FIXME: This should be renamed to contain the digest algorithm, so that the array's size makes // sense. pub fn hex_to_digest(hex: &str) -> Result<[u8; 32], Error> { let mut digest = [0u8; 32]; let bytes = hex.as_bytes(); if bytes.len() != 64 { bail!("got wrong digest length."); } let val = |c| { if c >= b'0' && c <= b'9' { return Ok(c - b'0'); } if c >= b'a' && c <= b'f' { return Ok(c - b'a' + 10); } if c >= b'A' && c <= b'F' { return Ok(c - b'A' + 10); } bail!("found illegal hex character."); }; let mut pos = 0; for pair in bytes.chunks(2) { if pos >= digest.len() { bail!("hex digest too long."); } let h = val(pair[0])?; let l = val(pair[1])?; digest[pos] = (h << 4) | l; pos += 1; } if pos != digest.len() { bail!("hex digest too short."); } Ok(digest) } /// Returns the hosts node name (UTS node name) pub fn nodename() -> &'static str { lazy_static! { static ref NODENAME: String = { nix::sys::utsname::uname() .nodename() .split('.') .next() .unwrap() .to_owned() }; } &NODENAME }