use super::*;
use std::io;
use std::path::Path;
#[macro_export]
macro_rules! assert_err {
($ex:expr) => {
if let Ok(v) = $ex {
panic!("assertion failed, expected Err(..), got {:?}", v);
}
};
}
#[macro_export]
macro_rules! io_error_other {
($msg:expr) => {
io::Error::new(io::ErrorKind::Other, $msg.to_string())
};
}
pub fn to_io_error_other<E: std::error::Error + Send + Sync + 'static>(x: E) -> io::Error {
io::Error::other(x)
}
#[macro_export]
macro_rules! bail_io_error_other {
($msg:expr) => {
return io::Result::Err(io::Error::new(io::ErrorKind::Other, $msg.to_string()))
};
}
cfg_if! {
if #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] {
#[must_use]
pub fn get_concurrency() -> u32 {
std::thread::available_parallelism()
.map(|x| x.get())
.unwrap_or_else(|e| {
warn!("unable to get concurrency defaulting to single core: {}", e);
1
}) as u32
}
}
}
#[must_use]
pub fn timestamp_duration_to_secs(dur: u64) -> f64 {
let mut mul: f64 = 1.0f64 / 1_000_000.0f64;
let mut usec = dur;
while usec > (u32::MAX as u64) {
usec >>= 1;
mul *= 2.0f64;
}
f64::from(usec as u32) * mul
}
#[must_use]
pub fn secs_to_timestamp_duration(secs: f64) -> u64 {
(secs * 1000000.0f64) as u64
}
#[must_use]
pub fn ms_to_us(ms: u32) -> u64 {
(ms as u64) * 1000u64
}
pub fn us_to_ms(us: u64) -> Result<u32, String> {
u32::try_from(us / 1000u64).map_err(|e| format!("could not convert microseconds: {}", e))
}
#[must_use]
pub fn retry_falloff_log(
last_us: u64,
cur_us: u64,
interval_start_us: u64,
interval_max_us: u64,
interval_multiplier_us: f64,
) -> bool {
if cur_us < interval_start_us {
false
} else if cur_us >= last_us + interval_max_us {
true
} else {
last_us
<= secs_to_timestamp_duration(
timestamp_duration_to_secs(cur_us) / interval_multiplier_us,
)
}
}
pub fn try_at_most_n_things<T, I, C, R>(max: usize, things: I, closure: C) -> Option<R>
where
I: IntoIterator<Item = T>,
C: Fn(T) -> Option<R>,
{
let mut fails = 0usize;
for thing in things.into_iter() {
if let Some(r) = closure(thing) {
return Some(r);
}
fails += 1;
if fails >= max {
break;
}
}
None
}
pub async fn async_try_at_most_n_things<T, I, C, R, F>(
max: usize,
things: I,
closure: C,
) -> Option<R>
where
I: IntoIterator<Item = T>,
C: Fn(T) -> F,
F: Future<Output = Option<R>>,
{
let mut fails = 0usize;
for thing in things.into_iter() {
if let Some(r) = closure(thing).await {
return Some(r);
}
fails += 1;
if fails >= max {
break;
}
}
None
}
pub trait CmpAssign {
fn min_assign(&mut self, other: Self);
fn max_assign(&mut self, other: Self);
}
impl<T> CmpAssign for T
where
T: core::cmp::Ord,
{
fn min_assign(&mut self, other: Self) {
if &other < self {
*self = other;
}
}
fn max_assign(&mut self, other: Self) {
if &other > self {
*self = other;
}
}
}
#[must_use]
pub fn compatible_unspecified_socket_addr(socket_addr: &SocketAddr) -> SocketAddr {
match socket_addr {
SocketAddr::V4(_) => SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0),
SocketAddr::V6(_) => SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), 0),
}
}
cfg_if! {
if #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] {
use std::net::UdpSocket;
static IPV6_IS_SUPPORTED: Mutex<Option<bool>> = Mutex::new(None);
pub fn is_ipv6_supported() -> bool {
let mut opt_supp = IPV6_IS_SUPPORTED.lock();
if let Some(supp) = *opt_supp {
return supp;
}
let supp = UdpSocket::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 0, 0, 0)).is_ok();
*opt_supp = Some(supp);
supp
}
static IPV4_IS_SUPPORTED: Mutex<Option<bool>> = Mutex::new(None);
pub fn is_ipv4_supported() -> bool {
let mut opt_supp = IPV4_IS_SUPPORTED.lock();
if let Some(supp) = *opt_supp {
return supp;
}
let supp = UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0)).is_ok();
*opt_supp = Some(supp);
supp
}
}
}
#[must_use]
pub fn available_unspecified_addresses() -> Vec<IpAddr> {
if is_ipv6_supported() {
vec![
IpAddr::V4(Ipv4Addr::UNSPECIFIED),
IpAddr::V6(Ipv6Addr::UNSPECIFIED),
]
} else {
vec![IpAddr::V4(Ipv4Addr::UNSPECIFIED)]
}
}
pub fn listen_address_to_socket_addrs(listen_address: &str) -> Result<Vec<SocketAddr>, String> {
let ip_addrs = available_unspecified_addresses();
Ok(if let Some(portstr) = listen_address.strip_prefix(':') {
let port = portstr
.parse::<u16>()
.map_err(|e| format!("Invalid port format in udp listen address: {}", e))?;
ip_addrs.iter().map(|a| SocketAddr::new(*a, port)).collect()
} else if let Ok(port) = listen_address.parse::<u16>() {
ip_addrs.iter().map(|a| SocketAddr::new(*a, port)).collect()
} else {
let listen_address_with_port = if listen_address.contains(':') {
listen_address.to_string()
} else {
format!("{}:0", listen_address)
};
cfg_if! {
if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
use core::str::FromStr;
vec![SocketAddr::from_str(&listen_address_with_port).map_err(|e| format!("Unable to parse address: {}",e))?]
} else {
listen_address_with_port
.to_socket_addrs()
.map_err(|e| format!("Unable to resolve address: {}", e))?
.collect()
}
}
})
}
pub trait RemoveDuplicates<T: PartialEq> {
fn remove_duplicates(&mut self);
}
impl<T: PartialEq + Ord> RemoveDuplicates<T> for Vec<T> {
fn remove_duplicates(&mut self) {
let mut firsts = Vec::<bool>::with_capacity(self.len());
{
let mut seen = BTreeSet::<&T>::new();
for item in self.iter() {
firsts.push(seen.insert(item));
}
}
let mut index = 0;
self.retain(|_| {
let first = firsts[index];
index += 1;
first
});
}
}
pub trait HasDuplicates<T: PartialEq> {
fn has_duplicates(&self) -> bool;
}
impl<T: PartialEq + Ord> HasDuplicates<T> for Vec<T> {
fn has_duplicates(&self) -> bool {
let mut seen = BTreeSet::<&T>::new();
for item in self.iter() {
if !seen.insert(item) {
return true;
}
}
false
}
}
cfg_if::cfg_if! {
if #[cfg(unix)] {
use std::os::unix::fs::MetadataExt;
use std::os::unix::prelude::PermissionsExt;
pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> Result<(), String>
{
let path = path.as_ref();
if !path.is_file() {
return Ok(());
}
let uid = unsafe { libc::geteuid() };
let gid = unsafe { libc::getegid() };
let meta = std::fs::metadata(path).map_err(|e| format!("unable to get metadata for path: {}", e))?;
if meta.mode() != 0o600 {
std::fs::set_permissions(path,std::fs::Permissions::from_mode(0o600)).map_err(|e| format!("unable to set correct permissions on path: {}", e))?;
}
if meta.uid() != uid || meta.gid() != gid {
return Err("path has incorrect owner/group".to_owned());
}
Ok(())
}
pub fn ensure_directory_private_owner<P:AsRef<Path>>(path: P, group_read: bool) -> Result<(), String>
{
let path = path.as_ref();
if !path.is_dir() {
return Ok(());
}
let uid = unsafe { libc::geteuid() };
let gid = unsafe { libc::getegid() };
let meta = std::fs::metadata(path).map_err(|e| format!("unable to get metadata for path: {}", e))?;
let perm = if group_read {
0o750
} else {
0o700
};
if meta.mode() != perm {
std::fs::set_permissions(path,std::fs::Permissions::from_mode(perm)).map_err(|e| format!("unable to set correct permissions on path: {}", e))?;
}
if meta.uid() != uid || meta.gid() != gid {
return Err("path has incorrect owner/group".to_owned());
}
Ok(())
}
} else if #[cfg(windows)] {
pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> Result<(), String>
{
let path = path.as_ref();
if !path.is_file() {
return Ok(());
}
Ok(())
}
pub fn ensure_directory_private_owner<P:AsRef<Path>>(path: P, _group_read: bool) -> Result<(), String>
{
let path = path.as_ref();
if !path.is_dir() {
return Ok(());
}
Ok(())
}
} else {
pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> Result<(), String>
{
let path = path.as_ref();
if !path.is_file() {
return Ok(());
}
Ok(())
}
pub fn ensure_directory_private_owner<P:AsRef<Path>>(path: P, _group_read: bool) -> Result<(), String>
{
let path = path.as_ref();
if !path.is_dir() {
return Ok(());
}
Ok(())
}
}
}
#[repr(C, align(8))]
struct AlignToEight([u8; 8]);
#[must_use]
pub unsafe fn aligned_8_u8_vec_uninit(n_bytes: usize) -> Vec<u8> {
let n_units = n_bytes.div_ceil(mem::size_of::<AlignToEight>());
let mut aligned: Vec<AlignToEight> = Vec::with_capacity(n_units);
let ptr = aligned.as_mut_ptr();
let cap_units = aligned.capacity();
mem::forget(aligned);
Vec::from_raw_parts(
ptr as *mut u8,
n_bytes,
cap_units * mem::size_of::<AlignToEight>(),
)
}
#[must_use]
pub unsafe fn unaligned_u8_vec_uninit(n_bytes: usize) -> Vec<u8> {
let mut unaligned: Vec<u8> = Vec::with_capacity(n_bytes);
let ptr = unaligned.as_mut_ptr();
mem::forget(unaligned);
Vec::from_raw_parts(ptr, n_bytes, n_bytes)
}
pub fn type_name_of_val<T: ?Sized>(_val: &T) -> &'static str {
std::any::type_name::<T>()
}
pub struct DebugGuard {
name: &'static str,
counter: &'static AtomicUsize,
}
impl DebugGuard {
pub fn new(name: &'static str, counter: &'static AtomicUsize) -> Self {
let c = counter.fetch_add(1, Ordering::SeqCst);
eprintln!("{} entered: {}", name, c + 1);
Self { name, counter }
}
}
impl Drop for DebugGuard {
fn drop(&mut self) {
let c = self.counter.fetch_sub(1, Ordering::SeqCst);
eprintln!("{} exited: {}", self.name, c - 1);
}
}