1use super::*;
2
3use std::io;
4use std::path::Path;
5
6#[macro_export]
10macro_rules! assert_err {
11 ($ex:expr) => {
12 if let Ok(v) = $ex {
13 panic!("assertion failed, expected Err(..), got {:?}", v);
14 }
15 };
16}
17
18#[macro_export]
20macro_rules! io_error_other {
21 ($msg:expr) => {
22 io::Error::new(io::ErrorKind::Other, $msg.to_string())
23 };
24}
25
26pub fn to_io_error_other<E: std::error::Error + Send + Sync + 'static>(x: E) -> io::Error {
28 io::Error::other(x)
29}
30
31#[macro_export]
33macro_rules! bail_io_error_other {
34 ($msg:expr) => {
35 return io::Result::Err(io::Error::new(io::ErrorKind::Other, $msg.to_string()))
36 };
37}
38
39cfg_if! {
42 if #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] {
43 #[must_use]
45 pub fn get_concurrency() -> u32 {
46 std::thread::available_parallelism()
47 .map(|x| x.get())
48 .unwrap_or_else(|e| {
49 warn!("unable to get concurrency defaulting to single core: {}", e);
50 1
51 }) as u32
52 }
53 }
54}
55
56#[must_use]
60pub fn timestamp_duration_to_secs(dur: u64) -> f64 {
61 let mut mul: f64 = 1.0f64 / 1_000_000.0f64;
63 let mut usec = dur;
64 while usec > (u32::MAX as u64) {
65 usec >>= 1;
66 mul *= 2.0f64;
67 }
68 f64::from(usec as u32) * mul
69}
70
71#[must_use]
73pub fn secs_to_timestamp_duration(secs: f64) -> u64 {
74 (secs * 1000000.0f64) as u64
75}
76
77#[must_use]
79pub fn ms_to_us(ms: u32) -> u64 {
80 (ms as u64) * 1000u64
81}
82
83pub fn us_to_ms(us: u64) -> Result<u32, String> {
87 u32::try_from(us / 1000u64).map_err(|e| format!("could not convert microseconds: {}", e))
88}
89
90#[must_use]
95pub fn retry_falloff_log(
96 last_us: u64,
97 cur_us: u64,
98 interval_start_us: u64,
99 interval_max_us: u64,
100 interval_multiplier_us: f64,
101) -> bool {
102 if cur_us < interval_start_us {
104 false
106 } else if cur_us >= last_us + interval_max_us {
107 true
109 } else {
110 last_us
112 <= secs_to_timestamp_duration(
113 timestamp_duration_to_secs(cur_us) / interval_multiplier_us,
114 )
115 }
116}
117
118pub fn try_at_most_n_things<T, I, C, R>(max: usize, things: I, closure: C) -> Option<R>
122where
123 I: IntoIterator<Item = T>,
124 C: Fn(T) -> Option<R>,
125{
126 let mut fails = 0usize;
127 for thing in things.into_iter() {
128 if let Some(r) = closure(thing) {
129 return Some(r);
130 }
131 fails += 1;
132 if fails >= max {
133 break;
134 }
135 }
136 None
137}
138
139pub async fn async_try_at_most_n_things<T, I, C, R, F>(
143 max: usize,
144 things: I,
145 closure: C,
146) -> Option<R>
147where
148 I: IntoIterator<Item = T>,
149 C: Fn(T) -> F,
150 F: Future<Output = Option<R>>,
151{
152 let mut fails = 0usize;
153 for thing in things.into_iter() {
154 if let Some(r) = closure(thing).await {
155 return Some(r);
156 }
157 fails += 1;
158 if fails >= max {
159 break;
160 }
161 }
162 None
163}
164
165pub trait CmpAssign {
167 fn min_assign(&mut self, other: Self);
169 fn max_assign(&mut self, other: Self);
171}
172
173impl<T> CmpAssign for T
174where
175 T: core::cmp::Ord,
176{
177 fn min_assign(&mut self, other: Self) {
178 if &other < self {
179 *self = other;
180 }
181 }
182 fn max_assign(&mut self, other: Self) {
183 if &other > self {
184 *self = other;
185 }
186 }
187}
188
189#[must_use]
191pub fn compatible_unspecified_socket_addr(socket_addr: &SocketAddr) -> SocketAddr {
192 match socket_addr {
193 SocketAddr::V4(_) => SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0),
194 SocketAddr::V6(_) => SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), 0),
195 }
196}
197
198cfg_if! {
199 if #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] {
200 use std::net::UdpSocket;
201
202 static IPV6_IS_SUPPORTED: Mutex<Option<bool>> = Mutex::new(None);
203
204 pub fn is_ipv6_supported() -> bool {
206 let mut opt_supp = IPV6_IS_SUPPORTED.lock();
207 if let Some(supp) = *opt_supp {
208 return supp;
209 }
210 let supp = UdpSocket::bind(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 0, 0, 0)).is_ok();
212 *opt_supp = Some(supp);
213 supp
214 }
215
216 static IPV4_IS_SUPPORTED: Mutex<Option<bool>> = Mutex::new(None);
217
218 pub fn is_ipv4_supported() -> bool {
220 let mut opt_supp = IPV4_IS_SUPPORTED.lock();
221 if let Some(supp) = *opt_supp {
222 return supp;
223 }
224 let supp = UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0)).is_ok();
226 *opt_supp = Some(supp);
227 supp
228 }
229
230 }
231}
232
233#[must_use]
235pub fn available_unspecified_addresses() -> Vec<IpAddr> {
236 if is_ipv6_supported() {
237 vec![
238 IpAddr::V4(Ipv4Addr::UNSPECIFIED),
239 IpAddr::V6(Ipv6Addr::UNSPECIFIED),
240 ]
241 } else {
242 vec![IpAddr::V4(Ipv4Addr::UNSPECIFIED)]
243 }
244}
245
246pub fn listen_address_to_socket_addrs(listen_address: &str) -> Result<Vec<SocketAddr>, String> {
254 let ip_addrs = available_unspecified_addresses();
258
259 Ok(if let Some(portstr) = listen_address.strip_prefix(':') {
260 let port = portstr
261 .parse::<u16>()
262 .map_err(|e| format!("Invalid port format in udp listen address: {}", e))?;
263 ip_addrs.iter().map(|a| SocketAddr::new(*a, port)).collect()
264 } else if let Ok(port) = listen_address.parse::<u16>() {
265 ip_addrs.iter().map(|a| SocketAddr::new(*a, port)).collect()
266 } else {
267 let listen_address_with_port = if listen_address.contains(':') {
268 listen_address.to_string()
269 } else {
270 format!("{}:0", listen_address)
271 };
272 cfg_if! {
273 if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
274 use core::str::FromStr;
275 vec![SocketAddr::from_str(&listen_address_with_port).map_err(|e| format!("Unable to parse address: {}",e))?]
276 } else {
277 listen_address_with_port
278 .to_socket_addrs()
279 .map_err(|e| format!("Unable to resolve address: {}", e))?
280 .collect()
281 }
282 }
283 })
284}
285
286pub trait RemoveDuplicates<T: PartialEq> {
288 fn remove_duplicates(&mut self);
290}
291
292impl<T: PartialEq + Ord> RemoveDuplicates<T> for Vec<T> {
293 fn remove_duplicates(&mut self) {
294 let mut firsts = Vec::<bool>::with_capacity(self.len());
295 {
296 let mut seen = BTreeSet::<&T>::new();
297 for item in self.iter() {
298 firsts.push(seen.insert(item));
299 }
300 }
301 let mut index = 0;
302 self.retain(|_| {
303 let first = firsts[index];
304 index += 1;
305 first
306 });
307 }
308}
309
310pub trait HasDuplicates<T: PartialEq> {
312 fn has_duplicates(&self) -> bool;
314}
315
316impl<T: PartialEq + Ord> HasDuplicates<T> for Vec<T> {
317 fn has_duplicates(&self) -> bool {
318 let mut seen = BTreeSet::<&T>::new();
319 for item in self.iter() {
320 if !seen.insert(item) {
321 return true;
322 }
323 }
324 false
325 }
326}
327
328cfg_if::cfg_if! {
329 if #[cfg(unix)] {
330 use std::os::unix::fs::MetadataExt;
331 use std::os::unix::prelude::PermissionsExt;
332
333 pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> Result<(), String>
338 {
339 let path = path.as_ref();
340 if !path.is_file() {
341 return Ok(());
342 }
343
344 let uid = unsafe { libc::geteuid() };
345 let gid = unsafe { libc::getegid() };
346 let meta = std::fs::metadata(path).map_err(|e| format!("unable to get metadata for path: {}", e))?;
347
348 if meta.mode() != 0o600 {
349 std::fs::set_permissions(path,std::fs::Permissions::from_mode(0o600)).map_err(|e| format!("unable to set correct permissions on path: {}", e))?;
350 }
351 if meta.uid() != uid || meta.gid() != gid {
352 return Err("path has incorrect owner/group".to_owned());
353 }
354 Ok(())
355 }
356
357 pub fn ensure_directory_private_owner<P:AsRef<Path>>(path: P, group_read: bool) -> Result<(), String>
362 {
363 let path = path.as_ref();
364 if !path.is_dir() {
365 return Ok(());
366 }
367
368 let uid = unsafe { libc::geteuid() };
369 let gid = unsafe { libc::getegid() };
370 let meta = std::fs::metadata(path).map_err(|e| format!("unable to get metadata for path: {}", e))?;
371
372 let perm = if group_read {
373 0o750
374 } else {
375 0o700
376 };
377
378 if meta.mode() != perm {
379 std::fs::set_permissions(path,std::fs::Permissions::from_mode(perm)).map_err(|e| format!("unable to set correct permissions on path: {}", e))?;
380 }
381 if meta.uid() != uid || meta.gid() != gid {
382 return Err("path has incorrect owner/group".to_owned());
383 }
384 Ok(())
385 }
386 } else if #[cfg(windows)] {
387 pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> Result<(), String>
392 {
393 let path = path.as_ref();
394 if !path.is_file() {
395 return Ok(());
396 }
397
398 Ok(())
399 }
400
401 pub fn ensure_directory_private_owner<P:AsRef<Path>>(path: P, _group_read: bool) -> Result<(), String>
403 {
404 let path = path.as_ref();
405 if !path.is_dir() {
406 return Ok(());
407 }
408
409 Ok(())
410 }
411
412 } else {
413 pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> Result<(), String>
415 {
416 let path = path.as_ref();
417 if !path.is_file() {
418 return Ok(());
419 }
420
421 Ok(())
422 }
423
424 pub fn ensure_directory_private_owner<P:AsRef<Path>>(path: P, _group_read: bool) -> Result<(), String>
426 {
427 let path = path.as_ref();
428 if !path.is_dir() {
429 return Ok(());
430 }
431
432 Ok(())
433 }
434 }
435}
436
437#[repr(C, align(8))]
438struct AlignToEight([u8; 8]);
439
440#[must_use]
443pub unsafe fn aligned_8_u8_vec_uninit(n_bytes: usize) -> Vec<u8> {
444 let n_units = n_bytes.div_ceil(mem::size_of::<AlignToEight>());
445 let mut aligned: Vec<AlignToEight> = Vec::with_capacity(n_units);
446 let ptr = aligned.as_mut_ptr();
447 let cap_units = aligned.capacity();
448 mem::forget(aligned);
449
450 Vec::from_raw_parts(
451 ptr as *mut u8,
452 n_bytes,
453 cap_units * mem::size_of::<AlignToEight>(),
454 )
455}
456
457#[must_use]
460pub unsafe fn unaligned_u8_vec_uninit(n_bytes: usize) -> Vec<u8> {
461 let mut unaligned: Vec<u8> = Vec::with_capacity(n_bytes);
462 let ptr = unaligned.as_mut_ptr();
463 mem::forget(unaligned);
464
465 Vec::from_raw_parts(ptr, n_bytes, n_bytes)
466}
467
468pub fn type_name_of_val<T: ?Sized>(_val: &T) -> &'static str {
470 std::any::type_name::<T>()
471}
472
473pub struct DebugGuard {
477 name: &'static str,
478 counter: &'static AtomicUsize,
479}
480
481impl DebugGuard {
482 pub fn new(name: &'static str, counter: &'static AtomicUsize) -> Self {
484 let c = counter.fetch_add(1, Ordering::SeqCst);
485 eprintln!("{} entered: {}", name, c + 1);
486 Self { name, counter }
487 }
488}
489
490impl Drop for DebugGuard {
491 fn drop(&mut self) {
492 let c = self.counter.fetch_sub(1, Ordering::SeqCst);
493 eprintln!("{} exited: {}", self.name, c - 1);
494 }
495}