1
0
Fork 0
forked from wry/wry

all: split reusable components into workspace crates

This commit is contained in:
kossLAN 2026-05-29 09:14:53 -04:00
parent 2a079ed800
commit 657e7ce2f7
No known key found for this signature in database
225 changed files with 7422 additions and 17602 deletions

26
utils/Cargo.toml Normal file
View file

@ -0,0 +1,26 @@
[package]
name = "jay-utils"
version = "0.1.0"
edition = "2024"
license = "GPL-3.0-only"
[dependencies]
jay-config = { version = "1.10.0", path = "../jay-config" }
ahash = "0.8.7"
arrayvec = "0.7.4"
bstr = { version = "1.9.0", default-features = false, features = ["std"] }
cfg-if = "1.0.0"
isnt = "0.2.0"
linearize = { version = "0.1.3", features = ["derive"] }
log = { version = "0.4.20", features = ["std"] }
parking_lot = "0.12.1"
rand = "0.10.0"
serde = { version = "1.0.196", features = ["derive"] }
smallvec = { version = "1.11.1", features = ["const_generics", "const_new", "union"] }
thiserror = "2.0.11"
uapi = "0.2.13"
[features]
it = []
rc_tracking = []

11
utils/src/array.rs Normal file
View file

@ -0,0 +1,11 @@
pub fn from_fn<F, T, const N: usize>(mut cb: F) -> [T; N]
where
F: FnMut(usize) -> T,
{
let mut idx = 0;
[(); N].map(|_| {
let res = cb(idx);
idx += 1;
res
})
}

View file

@ -0,0 +1,28 @@
pub trait ArrayToTuple {
type Tuple;
fn to_tuple(self) -> Self::Tuple;
}
macro_rules! ignore {
($t:tt) => {
T
};
}
macro_rules! array_to_tuple {
($n:expr, $($field:ident,)*) => {
impl<T> ArrayToTuple for [T; $n] {
type Tuple = ($(ignore!($field),)*);
fn to_tuple(self) -> Self::Tuple {
let [$($field,)*] = self;
#[allow(clippy::allow_attributes)]
#[allow(clippy::unused_unit)]
($($field,)*)
}
}
};
}
array_to_tuple!(2, t1, t2,);

60
utils/src/asyncevent.rs Normal file
View file

@ -0,0 +1,60 @@
use {
crate::numcell::NumCell,
std::{
cell::Cell,
fmt::{Debug, Formatter},
future::Future,
pin::Pin,
task::{Context, Poll, Waker},
},
};
#[derive(Default)]
pub struct AsyncEvent {
triggers: NumCell<u32>,
waker: Cell<Option<Waker>>,
}
impl Debug for AsyncEvent {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AsyncEvent")
.field("triggers", &self.triggers.get())
.finish_non_exhaustive()
}
}
impl AsyncEvent {
pub fn clear(&self) {
self.triggers.set(0);
self.waker.take();
}
pub fn trigger(&self) {
if self.triggers.fetch_add(1) == 0
&& let Some(waker) = self.waker.take()
{
waker.wake();
}
}
pub fn triggered(&self) -> AsyncEventTriggered<'_> {
AsyncEventTriggered { ae: self }
}
}
pub struct AsyncEventTriggered<'a> {
ae: &'a AsyncEvent,
}
impl<'a> Future for AsyncEventTriggered<'a> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.ae.triggers.replace(0) == 0 {
self.ae.waker.set(Some(cx.waker().clone()));
Poll::Pending
} else {
Poll::Ready(())
}
}
}

42
utils/src/atomic_enum.rs Normal file
View file

@ -0,0 +1,42 @@
use {
linearize::Linearize,
std::{
marker::PhantomData,
sync::atomic::{AtomicUsize, Ordering},
},
};
pub struct AtomicEnum<T> {
v: AtomicUsize,
_phantom: PhantomData<T>,
}
impl<T> Default for AtomicEnum<T>
where
T: Default + Linearize + Copy,
{
fn default() -> Self {
Self::new(T::default())
}
}
impl<T> AtomicEnum<T>
where
T: Linearize + Copy,
{
pub fn new(t: T) -> Self {
Self {
v: AtomicUsize::new(t.linearize()),
_phantom: Default::default(),
}
}
#[allow(dead_code)]
pub fn load(&self, ordering: Ordering) -> T {
unsafe { T::from_linear_unchecked(self.v.load(ordering)) }
}
pub fn store(&self, t: T, ordering: Ordering) {
self.v.store(t.linearize(), ordering);
}
}

View file

@ -0,0 +1,219 @@
use {
crate::ptr_ext::{MutPtrExt, PtrExt},
smallvec::SmallVec,
std::{
fmt::{Debug, Formatter},
mem,
},
};
pub struct BinarySearchMap<K, V, const N: usize> {
m: SmallVec<[(K, V); N]>,
}
impl<K: Debug, V: Debug, const N: usize> Debug for BinarySearchMap<K, V, N> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_map()
.entries(self.m.iter().map(|e| (&e.0, &e.1)))
.finish()
}
}
impl<K, V, const N: usize> Default for BinarySearchMap<K, V, N> {
fn default() -> Self {
Self {
m: Default::default(),
}
}
}
impl<K, V, const N: usize> BinarySearchMap<K, V, N> {
pub fn new_with(k: K, v: V) -> Self {
let mut sv = SmallVec::new();
sv.push((k, v));
Self { m: sv }
}
pub fn new() -> Self {
Self {
m: SmallVec::new_const(),
}
}
pub fn len(&self) -> usize {
self.m.len()
}
fn pos(&self, k: &K) -> Result<usize, usize>
where
K: Ord + Eq,
{
self.m.binary_search_by(|(c, _)| c.cmp(k))
}
pub fn contains(&self, k: &K) -> bool
where
K: Ord + Eq,
{
self.pos(k).is_ok()
}
pub fn not_contains(&self, k: &K) -> bool
where
K: Ord + Eq,
{
!self.contains(k)
}
pub fn insert(&mut self, k: K, v: V) -> Option<V>
where
K: Ord + Eq,
{
match self.pos(&k) {
Ok(p) => Some(mem::replace(&mut self.m[p], (k, v)).1),
Err(p) => {
self.m.insert(p, (k, v));
None
}
}
}
pub fn get(&self, k: &K) -> Option<&V>
where
K: Ord + Eq,
{
self.pos(k).ok().map(|p| &self.m[p].1)
}
pub fn get_mut(&mut self, k: &K) -> Option<&mut V>
where
K: Ord + Eq,
{
self.pos(k).ok().map(|p| &mut self.m[p].1)
}
pub fn get_or_default_mut(&mut self, k: K) -> &mut V
where
K: Ord + Eq,
V: Default,
{
self.get_or_insert_with(k, || V::default())
}
pub fn get_or_insert_with<F>(&mut self, k: K, f: F) -> &mut V
where
K: Ord + Eq,
F: FnOnce() -> V,
{
let p = match self.pos(&k) {
Ok(p) => return &mut self.m[p].1,
Err(p) => p,
};
self.m.insert(p, (k, f()));
&mut self.m[p].1
}
pub fn is_empty(&self) -> bool {
self.m.is_empty()
}
pub fn remove(&mut self, k: &K) -> Option<V>
where
K: Ord + Eq,
{
if let Ok(p) = self.pos(k) {
return Some(self.m.remove(p).1);
}
None
}
pub fn clear(&mut self) {
let _v = mem::replace(&mut self.m, SmallVec::new());
}
pub fn take(&mut self) -> SmallVec<[(K, V); N]> {
mem::take(&mut self.m)
}
pub fn iter<'a>(&'a self) -> BinarySearchMapIter<'a, K, V, N> {
BinarySearchMapIter { pos: 0, map: self }
}
pub fn values<'a>(&'a self) -> impl Iterator<Item = &'a V> + 'a {
self.iter().map(|(_, v)| v)
}
pub fn iter_mut<'a>(&'a mut self) -> BinarySearchMapMutIterMut<'a, K, V, N> {
BinarySearchMapMutIterMut { pos: 0, map: self }
}
pub fn values_mut<'a>(&'a mut self) -> impl Iterator<Item = &'a mut V> + 'a {
self.iter_mut().map(|(_, v)| v)
}
pub fn remove_if<F: FnMut(&K, &V) -> bool>(&mut self, mut f: F) {
let mut i = 0;
while i < self.m.len() {
let (k, v) = &self.m[i];
if f(k, v) {
self.m.remove(i);
} else {
i += 1;
}
}
}
}
impl<'a, K: Copy, V, const N: usize> IntoIterator for &'a BinarySearchMap<K, V, N> {
type Item = (&'a K, &'a V);
type IntoIter = BinarySearchMapIter<'a, K, V, N>;
fn into_iter(self) -> Self::IntoIter {
BinarySearchMapIter { pos: 0, map: self }
}
}
impl<'a, K: Copy, V, const N: usize> IntoIterator for &'a mut BinarySearchMap<K, V, N> {
type Item = (&'a K, &'a mut V);
type IntoIter = BinarySearchMapMutIterMut<'a, K, V, N>;
fn into_iter(self) -> Self::IntoIter {
BinarySearchMapMutIterMut { pos: 0, map: self }
}
}
pub struct BinarySearchMapIter<'a, K, V, const N: usize> {
pos: usize,
map: &'a BinarySearchMap<K, V, N>,
}
impl<'a, K, V, const N: usize> Iterator for BinarySearchMapIter<'a, K, V, N> {
type Item = (&'a K, &'a V);
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.map.m.len() {
return None;
}
let (k, v) = &self.map.m[self.pos];
self.pos += 1;
Some((k, v))
}
}
pub struct BinarySearchMapMutIterMut<'a, K, V, const N: usize> {
pos: usize,
map: &'a mut BinarySearchMap<K, V, N>,
}
impl<'a, K, V, const N: usize> Iterator for BinarySearchMapMutIterMut<'a, K, V, N> {
type Item = (&'a K, &'a mut V);
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.map.m.len() {
return None;
}
let (k, v) = &mut self.map.m[self.pos];
self.pos += 1;
unsafe { Some(((k as *const K).deref(), (v as *mut V).deref_mut())) }
}
}

37
utils/src/bitfield.rs Normal file
View file

@ -0,0 +1,37 @@
#![allow(dead_code)]
const SEG_SIZE: usize = usize::BITS as usize;
#[derive(Default)]
pub struct Bitfield {
vals: Vec<usize>,
}
impl Bitfield {
pub fn take(&mut self, val: u32) {
let idx = val as usize / SEG_SIZE;
let pos = val as usize % SEG_SIZE;
while self.vals.len() <= idx {
self.vals.push(!0);
}
self.vals[idx] &= !(1 << pos);
}
pub fn acquire(&mut self) -> u32 {
for (idx, n) in self.vals.iter_mut().enumerate() {
if *n != 0 {
let pos = n.trailing_zeros();
*n &= !(1 << pos);
return (idx * SEG_SIZE) as u32 + pos;
}
}
self.vals.push(!1);
((self.vals.len() - 1) * SEG_SIZE) as u32
}
pub fn release(&mut self, val: u32) {
let idx = val as usize / SEG_SIZE;
let pos = val as usize % SEG_SIZE;
self.vals[idx] |= 1 << pos;
}
}

32
utils/src/bitflags.rs Normal file
View file

@ -0,0 +1,32 @@
pub trait BitflagsExt {
fn contains(self, other: Self) -> bool;
fn not_contains(self, other: Self) -> bool;
fn intersects(self, other: Self) -> bool;
}
macro_rules! num {
($ty:ident) => {
impl BitflagsExt for $ty {
fn contains(self, other: Self) -> bool {
self & other == other
}
fn not_contains(self, other: Self) -> bool {
self & other != other
}
fn intersects(self, other: Self) -> bool {
self & other != 0
}
}
};
}
num!(u8);
num!(u16);
num!(u32);
num!(u64);
num!(i8);
num!(i16);
num!(i32);
num!(i64);

328
utils/src/buf.rs Normal file
View file

@ -0,0 +1,328 @@
use {
crate::{numcell::NumCell, ptr_ext::PtrExt},
std::{
alloc::Layout,
cmp,
collections::Bound,
fmt::Arguments,
io::{self, Write},
marker::PhantomData,
mem,
ops::{Deref, DerefMut, Range, RangeBounds},
ptr::NonNull,
slice,
},
uapi::Pod,
};
const METADATA_SIZE: u32 = 8;
const METADATA_ALIGN: usize = 4;
const SIZE_OFF: u32 = 0;
const RC_OFF: u32 = 4;
const RC_OFF_INV: u32 = METADATA_SIZE - RC_OFF;
const SIZE_OFF_INV: u32 = METADATA_SIZE - SIZE_OFF;
pub struct Buf {
storage: NonNull<u8>,
range: Range<u32>,
}
impl Buf {
pub fn from_slice(vec: &[u8]) -> Buf {
let len = vec.len();
assert!(len <= (u32::MAX - METADATA_SIZE) as usize);
let len = len as u32;
let size = len + METADATA_SIZE;
let layout = Layout::from_size_align(size as _, METADATA_ALIGN).unwrap();
let ptr = unsafe { std::alloc::alloc(layout) };
if ptr.is_null() {
std::alloc::handle_alloc_error(layout);
}
unsafe {
*ptr.cast::<u32>() = size;
*ptr.add(RC_OFF as _).cast::<u32>() = 1;
let mut buf = Buf {
storage: NonNull::new_unchecked(ptr.add(METADATA_SIZE as _)),
range: Range { start: 0, end: len },
};
buf[..].copy_from_slice(vec);
buf
}
}
pub fn new(len: usize) -> Buf {
assert!(len <= (u32::MAX - METADATA_SIZE) as usize);
let len = len as u32;
let size = len + METADATA_SIZE;
let layout = Layout::from_size_align(size as _, METADATA_ALIGN).unwrap();
let ptr = unsafe { std::alloc::alloc_zeroed(layout) };
if ptr.is_null() {
std::alloc::handle_alloc_error(layout);
}
unsafe {
*ptr.cast::<u32>() = size;
*ptr.add(RC_OFF as _).cast::<u32>() = 1;
Buf {
storage: NonNull::new_unchecked(ptr.add(METADATA_SIZE as _)),
range: Range { start: 0, end: len },
}
}
}
pub fn clone(&mut self) -> Buf {
self.rc().fetch_add(1);
Buf {
storage: self.storage,
range: self.range.clone(),
}
}
pub fn slice(&mut self, range: impl RangeBounds<usize>) -> Buf {
let start = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n.wrapping_add(1),
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&n) => n.wrapping_add(1),
Bound::Excluded(&n) => n,
Bound::Unbounded => self.len(),
};
self.slice_(start as _, end as _)
}
fn slice_(&mut self, start: u32, end: u32) -> Buf {
assert!(start <= end);
assert!(end <= self.len32());
self.rc().fetch_add(1);
Buf {
storage: self.storage,
range: Range {
start: self.range.start + start,
end: self.range.start + end,
},
}
}
fn rc(&self) -> &NumCell<u32> {
unsafe {
self.storage
.as_ptr()
.sub(RC_OFF_INV as _)
.cast::<NumCell<u32>>()
.deref()
}
}
fn assert_unique(&self) {
assert_eq!(self.rc().get(), 1);
}
pub fn len32(&self) -> u32 {
self.range.end - self.range.start
}
pub fn len(&self) -> usize {
self.len32() as _
}
fn size32(&self) -> u32 {
unsafe {
*self
.storage
.as_ptr()
.sub(SIZE_OFF_INV as _)
.cast::<u32>()
.deref()
}
}
pub fn cap32(&self) -> u32 {
self.size32() - METADATA_SIZE
}
pub fn as_ptr(&self) -> *mut u8 {
unsafe { self.storage.as_ptr().add(self.range.start as _) }
}
pub fn write_fmt(&mut self, args: Arguments) -> Result<Self, io::Error> {
let cap = self.len();
let mut buf = self.deref_mut();
buf.write_fmt(args)?;
let len = cap - buf.len();
Ok(self.slice(..len))
}
pub fn into_full(self) -> Self {
let new = Self {
storage: self.storage,
range: 0..self.cap32(),
};
mem::forget(self);
new
}
fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) }
}
fn as_slice_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.as_ptr(), self.len()) }
}
}
impl Default for Buf {
fn default() -> Self {
Self::new(0)
}
}
impl Deref for Buf {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.assert_unique();
self.as_slice()
}
}
impl DerefMut for Buf {
fn deref_mut(&mut self) -> &mut Self::Target {
self.assert_unique();
self.as_slice_mut()
}
}
impl Drop for Buf {
fn drop(&mut self) {
unsafe {
let prev = self.rc().fetch_sub(1);
if prev != 1 {
return;
}
let ptr = self.storage.as_ptr().sub(METADATA_SIZE as _).cast::<u32>();
let size = *ptr as _;
let layout = Layout::from_size_align_unchecked(size, METADATA_ALIGN);
std::alloc::dealloc(ptr as _, layout);
}
}
}
pub struct DynamicBuf {
buf: Buf,
len: usize,
}
impl DynamicBuf {
pub fn new() -> Self {
Self {
buf: Buf::new(0),
len: 0,
}
}
pub fn from_buf(buf: Buf) -> Self {
buf.assert_unique();
Self {
buf: buf.into_full(),
len: 0,
}
}
pub fn unwrap(mut self) -> Buf {
self.buf.slice(..self.len)
}
pub fn len(&self) -> usize {
self.len
}
pub fn reserve(&mut self, n: usize) {
if self.buf.len() - self.len < n {
let cap = self.len.checked_add(n).unwrap();
let cap = cmp::max(self.buf.len() * 2, cap);
let mut new = Buf::new(cap);
new[..self.len].copy_from_slice(&self.buf[..self.len]);
self.buf = new;
}
}
pub fn extend_from_slice(&mut self, buf: &[u8]) {
self.reserve(buf.len());
self.buf.as_slice_mut()[self.len..self.len + buf.len()].copy_from_slice(buf);
self.len += buf.len();
}
pub fn push(&mut self, b: u8) {
self.extend_from_slice(&[b]);
}
pub fn clear(&mut self) {
self.len = 0;
}
pub fn borrow(&mut self) -> BorrowedBuf<'_> {
BorrowedBuf {
buf: self.buf.slice(..self.len),
_phantom: Default::default(),
}
}
}
pub struct BorrowedBuf<'a> {
pub buf: Buf,
_phantom: PhantomData<&'a mut DynamicBuf>,
}
impl<'a> Drop for BorrowedBuf<'a> {
fn drop(&mut self) {
assert_eq!(self.buf.rc().get(), 2);
}
}
impl Write for DynamicBuf {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Deref for DynamicBuf {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.buf.as_slice()
}
}
impl DerefMut for DynamicBuf {
fn deref_mut(&mut self) -> &mut Self::Target {
self.buf.as_slice_mut()
}
}
pub struct TypedBuf<T: Pod> {
buf: Buf,
_phantom: PhantomData<T>,
}
impl<T: Pod> TypedBuf<T> {
pub fn new() -> Self {
Self {
buf: Buf::new(size_of::<T>()),
_phantom: Default::default(),
}
}
pub fn buf(&mut self) -> Buf {
self.buf.clone()
}
pub fn t(&self) -> T {
uapi::pod_read(&self.buf[..]).unwrap()
}
}

16
utils/src/cell_ext.rs Normal file
View file

@ -0,0 +1,16 @@
use {crate::ptr_ext::PtrExt, std::cell::Cell};
pub trait CellExt {
fn is_some(&self) -> bool;
fn is_none(&self) -> bool;
}
impl<T> CellExt for Cell<Option<T>> {
fn is_some(&self) -> bool {
unsafe { self.as_ptr().deref().is_some() }
}
fn is_none(&self) -> bool {
!self.is_some()
}
}

96
utils/src/clonecell.rs Normal file
View file

@ -0,0 +1,96 @@
use {
crate::ptr_ext::{MutPtrExt, PtrExt},
jay_config::{keyboard::mods::Modifiers, window::Window},
std::{
cell::UnsafeCell,
fmt::{Debug, Formatter},
mem,
rc::{Rc, Weak},
sync::Arc,
},
};
pub struct CloneCell<T> {
data: UnsafeCell<T>,
}
impl<T: UnsafeCellCloneSafe> Clone for CloneCell<T> {
fn clone(&self) -> Self {
Self {
data: UnsafeCell::new(self.get()),
}
}
}
impl<T: UnsafeCellCloneSafe + Debug> Debug for CloneCell<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.get().fmt(f)
}
}
impl<T> CloneCell<T> {
pub const fn new(t: T) -> Self {
Self {
data: UnsafeCell::new(t),
}
}
#[inline(always)]
pub fn get(&self) -> T
where
T: UnsafeCellCloneSafe,
{
unsafe { self.data.get().deref().clone() }
}
#[inline(always)]
pub fn set(&self, t: T) -> T {
unsafe { mem::replace(self.data.get().deref_mut(), t) }
}
#[inline(always)]
pub fn take(&self) -> T
where
T: Default,
{
self.set(T::default())
}
}
impl<T> CloneCell<Option<T>> {
#[inline(always)]
pub fn is_some(&self) -> bool {
unsafe { self.data.get().deref().is_some() }
}
#[inline(always)]
pub fn is_none(&self) -> bool {
unsafe { self.data.get().deref().is_none() }
}
}
impl<T: Default> Default for CloneCell<T> {
fn default() -> Self {
Self::new(Default::default())
}
}
pub unsafe trait UnsafeCellCloneSafe: Clone {}
unsafe impl<T: UnsafeCellCloneSafe> UnsafeCellCloneSafe for Option<T> {}
unsafe impl<T: ?Sized> UnsafeCellCloneSafe for Rc<T> {}
unsafe impl<T: ?Sized> UnsafeCellCloneSafe for Weak<T> {}
unsafe impl<T: ?Sized> UnsafeCellCloneSafe for Arc<T> {}
unsafe impl UnsafeCellCloneSafe for () {}
unsafe impl UnsafeCellCloneSafe for u64 {}
unsafe impl UnsafeCellCloneSafe for i32 {}
unsafe impl UnsafeCellCloneSafe for u32 {}
unsafe impl UnsafeCellCloneSafe for usize {}
unsafe impl<A: UnsafeCellCloneSafe, B: UnsafeCellCloneSafe> UnsafeCellCloneSafe for (A, B) {}
unsafe impl UnsafeCellCloneSafe for Modifiers {}
unsafe impl UnsafeCellCloneSafe for Window {}

11
utils/src/compat.rs Normal file
View file

@ -0,0 +1,11 @@
use {cfg_if::cfg_if, uapi::c};
cfg_if! {
if #[cfg(target_env = "musl")] {
pub type IoctlNumber = c::c_int;
pub type IovLength = c::c_int;
} else {
pub type IoctlNumber = c::c_ulong;
pub type IovLength = usize;
}
}

126
utils/src/copyhashmap.rs Normal file
View file

@ -0,0 +1,126 @@
use {
crate::{
clonecell::UnsafeCellCloneSafe,
ptr_ext::{MutPtrExt, PtrExt},
},
ahash::AHashMap,
std::{
borrow::Borrow,
cell::UnsafeCell,
fmt::{Debug, Formatter},
hash::Hash,
mem,
ops::{Deref, DerefMut},
},
};
pub struct CopyHashMap<K, V> {
map: UnsafeCell<AHashMap<K, V>>,
}
impl<K: Debug, V: Debug> Debug for CopyHashMap<K, V> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.map.fmt(f)
}
}
impl<K, V> Default for CopyHashMap<K, V> {
fn default() -> Self {
Self {
map: Default::default(),
}
}
}
impl<K: Eq + Hash, V> CopyHashMap<K, V> {
pub fn new() -> Self {
Self::default()
}
pub fn set(&self, k: K, v: V) -> Option<V> {
unsafe { self.map.get().deref_mut().insert(k, v) }
}
pub fn get<Q>(&self, k: &Q) -> Option<V>
where
V: UnsafeCellCloneSafe,
Q: Hash + Eq + ?Sized,
K: Borrow<Q>,
{
unsafe { self.map.get().deref().get(k).cloned() }
}
pub fn remove<Q>(&self, k: &Q) -> Option<V>
where
Q: Hash + Eq + ?Sized,
K: Borrow<Q>,
{
unsafe { self.map.get().deref_mut().remove(k) }
}
pub fn contains<Q>(&self, k: &Q) -> bool
where
Q: Hash + Eq + ?Sized,
K: Borrow<Q>,
{
unsafe { self.map.get().deref().contains_key(k) }
}
pub fn not_contains<Q>(&self, k: &Q) -> bool
where
Q: Hash + Eq + ?Sized,
K: Borrow<Q>,
{
!self.contains(k)
}
pub fn lock(&self) -> Locked<'_, K, V> {
Locked {
source: self,
map: self.clear(),
}
}
pub fn clear(&self) -> AHashMap<K, V> {
unsafe { mem::take(self.map.get().deref_mut()) }
}
pub fn is_empty(&self) -> bool {
unsafe { self.map.get().deref().is_empty() }
}
pub fn is_not_empty(&self) -> bool {
!self.is_empty()
}
pub fn len(&self) -> usize {
unsafe { self.map.get().deref().len() }
}
}
pub struct Locked<'a, K, V> {
source: &'a CopyHashMap<K, V>,
map: AHashMap<K, V>,
}
impl<'a, K, V> Drop for Locked<'a, K, V> {
fn drop(&mut self) {
unsafe {
mem::swap(&mut self.map, self.source.map.get().deref_mut());
}
}
}
impl<'a, K, V> Deref for Locked<'a, K, V> {
type Target = AHashMap<K, V>;
fn deref(&self) -> &Self::Target {
&self.map
}
}
impl<'a, K, V> DerefMut for Locked<'a, K, V> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.map
}
}

View file

@ -0,0 +1,36 @@
use std::{cell::Cell, ops::Deref};
#[derive(Default)]
pub struct DoubleBuffered<T> {
bufs: [T; 2],
front: Cell<usize>,
}
impl<T> DoubleBuffered<T> {
pub fn new(bufs: [T; 2]) -> Self {
Self {
bufs,
front: Cell::new(0),
}
}
pub fn front(&self) -> &T {
unsafe { self.bufs.get_unchecked(self.front.get()) }
}
pub fn back(&self) -> &T {
unsafe { self.bufs.get_unchecked(1 - self.front.get()) }
}
pub fn flip(&self) {
self.front.set(1 - self.front.get());
}
}
impl<T> Deref for DoubleBuffered<T> {
type Target = [T; 2];
fn deref(&self) -> &Self::Target {
&self.bufs
}
}

23
utils/src/errorfmt.rs Normal file
View file

@ -0,0 +1,23 @@
use std::{
error::Error,
fmt::{Display, Formatter},
};
pub struct ErrorFmt<E>(pub E);
impl<E: Error> Display for ErrorFmt<E> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut e_opt = Some(&self.0 as &dyn Error);
let mut first = true;
while let Some(e) = e_opt {
if first {
write!(f, "{}", e)?;
first = false;
} else {
write!(f, ": {}", e)?;
}
e_opt = e.source();
}
Ok(())
}
}

52
utils/src/fdcloser.rs Normal file
View file

@ -0,0 +1,52 @@
use {
parking_lot::{Condvar, Mutex},
std::{mem, rc::Rc, sync::Arc},
uapi::OwnedFd,
};
pub struct FdCloser {
fds: Mutex<Vec<OwnedFd>>,
cv: Condvar,
}
impl FdCloser {
pub fn new() -> Arc<Self> {
let slf = Arc::new(Self {
fds: Mutex::new(Vec::new()),
cv: Condvar::new(),
});
let slf2 = slf.clone();
std::thread::Builder::new()
.name("fd closer".to_string())
.spawn(move || {
let mut fds = vec![];
let mut lock = slf2.fds.lock();
loop {
mem::swap(&mut *lock, &mut fds);
if fds.len() > 0 {
drop(lock);
fds.clear();
lock = slf2.fds.lock();
} else {
slf2.cv.wait(&mut lock);
}
}
})
.unwrap();
slf
}
pub fn close(&self, fd: Rc<OwnedFd>) {
match Rc::try_unwrap(fd) {
Ok(fd) => {
self.fds.lock().push(fd);
self.cv.notify_all();
}
Err(_e) => {
log::warn!(
"Could not close file descriptor in separate thread. There are still references."
);
}
}
}
}

93
utils/src/free_list.rs Normal file
View file

@ -0,0 +1,93 @@
#[cfg(test)]
mod tests;
use {
crate::ptr_ext::MutPtrExt,
std::{
array,
cell::UnsafeCell,
fmt::{Debug, Formatter},
marker::PhantomData,
},
};
type Seg = usize;
const SEG_SIZE: usize = size_of::<Seg>() * 8;
pub struct FreeList<T, const N: usize> {
levels: UnsafeCell<[Vec<Seg>; N]>,
_phantom: PhantomData<T>,
}
impl<T, const N: usize> Default for FreeList<T, N> {
fn default() -> Self {
Self {
levels: UnsafeCell::new(array::from_fn(|_| Vec::new())),
_phantom: Default::default(),
}
}
}
impl<T, const N: usize> Debug for FreeList<T, N> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FreeList")
.field("levels", self.get())
.finish()
}
}
impl<T, const N: usize> FreeList<T, N> {
fn get(&self) -> &mut [Vec<Seg>; N] {
unsafe { self.levels.get().deref_mut() }
}
pub fn release(&self, n: T)
where
T: Into<u32>,
{
let mut ext = n.into() as usize;
let mut int;
let levels = self.get();
assert!(ext / SEG_SIZE < levels[0].len());
for level in self.get() {
int = ext % SEG_SIZE;
ext /= SEG_SIZE;
unsafe {
*level.get_unchecked_mut(ext) |= 1 << int;
}
}
}
pub fn acquire(&self) -> T
where
u32: Into<T>,
{
let mut ext = 'last: {
let level = &mut self.get()[N - 1];
for (idx, &seg) in level.iter().enumerate() {
if seg != 0 {
break 'last idx;
}
}
level.len()
};
for level in self.get().iter_mut().rev() {
if ext == level.len() {
level.push(!0);
}
let seg = unsafe { level.get_unchecked(ext) };
ext = SEG_SIZE * ext + seg.trailing_zeros() as usize;
}
let id = ext as u32;
for level in self.get().iter_mut() {
let int = ext % SEG_SIZE;
ext /= SEG_SIZE;
let seg = unsafe { level.get_unchecked_mut(ext) };
*seg &= !(1 << int);
if *seg != 0 {
break;
}
}
id.into()
}
}

View file

@ -0,0 +1,40 @@
use crate::free_list::FreeList;
#[test]
fn test() {
let list = FreeList::<u32, 3>::default();
for i in 0..4097 {
assert_eq!(list.acquire(), i);
}
list.release(100);
assert_eq!(list.acquire(), 100);
assert_eq!(list.acquire(), 4097);
for i in 1..22 {
list.release(i);
}
for i in 1..22 {
assert_eq!(list.acquire(), i);
}
assert_eq!(list.acquire(), 4098);
for i in 64..128 {
list.release(i);
}
for i in 64..128 {
assert_eq!(list.acquire(), i);
}
assert_eq!(list.acquire(), 4099);
for i in 0..4100 {
list.release(i);
}
for i in 0..4101 {
assert_eq!(list.acquire(), i);
}
}
#[test]
#[should_panic]
fn release_out_of_bounds() {
let list = FreeList::<u32, 3>::default();
list.acquire();
list.release(500);
}

View file

@ -0,0 +1,37 @@
use std::cell::Cell;
pub struct GeometricDecay {
p1: f64,
p2: f64,
v: Cell<f64>,
}
impl GeometricDecay {
pub fn new(mut p1: f64, reset: u64) -> Self {
if p1.is_nan() || p1 < 0.01 {
p1 = 0.01;
}
if p1 > 0.99 {
p1 = 0.99;
}
let p2 = 1.0 - p1;
Self {
p1,
p2,
v: Cell::new(reset as f64 / p1),
}
}
pub fn reset(&self, v: u64) {
self.v.set(v as f64 / self.p1);
}
pub fn get(&self) -> u64 {
(self.p1 * self.v.get()) as u64
}
pub fn add(&self, n: u64) {
let v = n as f64 + self.p2 * self.v.get();
self.v.set(v);
}
}

15
utils/src/hash_map_ext.rs Normal file
View file

@ -0,0 +1,15 @@
use std::collections::HashMap;
pub trait HashMapExt {
type V;
fn drain_values(&mut self) -> impl Iterator<Item = Self::V>;
}
impl<K, V, S> HashMapExt for HashMap<K, V, S> {
type V = V;
fn drain_values(&mut self) -> impl Iterator<Item = Self::V> {
self.drain().map(|(_, v)| v)
}
}

56
utils/src/lib.rs Normal file
View file

@ -0,0 +1,56 @@
pub mod array;
pub mod array_to_tuple;
pub mod asyncevent;
pub mod atomic_enum;
pub mod binary_search_map;
pub mod bitfield;
pub mod bitflags;
pub mod buf;
pub mod cell_ext;
pub mod clonecell;
pub mod compat;
pub mod copyhashmap;
pub mod double_buffered;
pub mod errorfmt;
pub mod fdcloser;
pub mod free_list;
pub mod geometric_decay;
pub mod hash_map_ext;
pub mod log_on_drop;
pub mod mmap;
pub mod nice;
pub mod nonblock;
pub mod num_cpus;
pub mod numcell;
pub mod on_change;
pub mod on_drop_event;
pub mod once;
pub mod opaque;
pub mod opaque_cell;
pub mod opt;
pub mod option_ext;
pub mod ordered_float;
pub mod oserror;
pub mod page_size;
pub mod pid_info;
pub mod pidfd_send_signal;
pub mod pipe;
pub mod process_name;
pub mod ptr_ext;
pub mod queue;
pub mod rc_eq;
pub mod refcounted;
pub mod smallmap;
pub mod stack;
pub mod static_text;
pub mod string_ext;
pub mod syncqueue;
pub mod threshold_counter;
pub mod tri;
pub mod unlink_on_drop;
pub mod vec_ext;
pub mod vecdeque_ext;
pub mod vecset;
pub mod vecstorage;
pub mod windows;
pub mod xrd;

7
utils/src/log_on_drop.rs Normal file
View file

@ -0,0 +1,7 @@
pub struct LogOnDrop(pub &'static str);
impl Drop for LogOnDrop {
fn drop(&mut self) {
log::info!("{}", self.0);
}
}

34
utils/src/mmap.rs Normal file
View file

@ -0,0 +1,34 @@
use {
crate::{oserror::OsError, ptr_ext::PtrExt},
std::ptr,
uapi::c,
};
pub struct Mmapped {
pub ptr: *const [u8],
}
pub fn mmap(
len: usize,
prot: c::c_int,
flags: c::c_int,
fd: c::c_int,
offset: c::off_t,
) -> Result<Mmapped, OsError> {
let res = unsafe { c::mmap(ptr::null_mut(), len, prot, flags, fd, offset) };
if res == c::MAP_FAILED {
Err(OsError::default())
} else {
Ok(Mmapped {
ptr: ptr::slice_from_raw_parts(res.cast(), len),
})
}
}
impl Drop for Mmapped {
fn drop(&mut self) {
unsafe {
c::munmap(self.ptr.deref().as_ptr() as _, self.ptr.deref().len());
}
}
}

28
utils/src/nice.rs Normal file
View file

@ -0,0 +1,28 @@
use {
c::sched_setscheduler,
std::{
env, mem,
sync::atomic::{AtomicBool, Ordering::Relaxed},
},
uapi::c::{self, SCHED_RESET_ON_FORK, SCHED_RR, sched_param},
};
static DID_ELEVATE_SCHEDULER: AtomicBool = AtomicBool::new(false);
pub const JAY_NO_REALTIME: &str = "JAY_NO_REALTIME";
pub fn elevate_scheduler() {
if env::var(JAY_NO_REALTIME).as_deref().unwrap_or_default() == "1" {
return;
}
let mut param = unsafe { mem::zeroed::<sched_param>() };
param.sched_priority = 1;
let res = unsafe { sched_setscheduler(0, SCHED_RR | SCHED_RESET_ON_FORK, &param) };
if res == 0 {
DID_ELEVATE_SCHEDULER.store(true, Relaxed);
}
}
pub fn did_elevate_scheduler() -> bool {
DID_ELEVATE_SCHEDULER.load(Relaxed)
}

16
utils/src/nonblock.rs Normal file
View file

@ -0,0 +1,16 @@
use {
crate::oserror::{OsError, OsErrorExt},
uapi::c,
};
pub fn set_nonblock(fd: c::c_int) -> Result<(), OsError> {
let fl = uapi::fcntl_getfl(fd).to_os_error()?;
uapi::fcntl_setfl(fd, fl | c::O_NONBLOCK).to_os_error()?;
Ok(())
}
pub fn set_block(fd: c::c_int) -> Result<(), OsError> {
let fl = uapi::fcntl_getfl(fd).to_os_error()?;
uapi::fcntl_setfl(fd, fl & !c::O_NONBLOCK).to_os_error()?;
Ok(())
}

20
utils/src/num_cpus.rs Normal file
View file

@ -0,0 +1,20 @@
use {
crate::oserror::{OsError, OsErrorExt},
smallvec::{SmallVec, smallvec_inline},
uapi::c,
};
pub fn num_cpus() -> Result<u32, OsError> {
let mut buf: SmallVec<[usize; 32]> = smallvec_inline![0; 32];
loop {
match uapi::sched_getaffinity(0, &mut buf).to_os_error() {
Ok(_) => return Ok(count(&buf)),
Err(OsError(c::EINVAL)) => buf.extend_from_slice(&[0; 32][..]),
Err(e) => return Err(e),
}
}
}
fn count(buf: &[usize]) -> u32 {
buf.iter().copied().map(|n| n.count_ones()).sum()
}

129
utils/src/numcell.rs Normal file
View file

@ -0,0 +1,129 @@
use std::{
cell::Cell,
fmt::{Debug, Formatter},
ops::{Add, BitAnd, BitOr, Sub},
};
#[derive(Default)]
pub struct NumCell<T> {
t: Cell<T>,
}
impl<T: Copy + Debug> Debug for NumCell<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.t.get().fmt(f)
}
}
impl<T> NumCell<T> {
#[inline(always)]
pub fn new(t: T) -> Self {
Self { t: Cell::new(t) }
}
#[inline(always)]
pub fn set(&self, n: T) {
let _ = self.t.replace(n);
}
#[inline(always)]
pub fn replace(&self, n: T) -> T {
self.t.replace(n)
}
#[inline(always)]
pub fn get(&self) -> T
where
T: Copy,
{
self.t.get()
}
#[inline(always)]
pub fn fetch_add(&self, n: T) -> T
where
T: Copy + Add<T, Output = T>,
{
let res = self.t.get();
self.t.set(res + n);
res
}
#[inline(always)]
pub fn add_fetch(&self, n: T) -> T
where
T: Copy + Add<T, Output = T>,
{
let res = self.t.get() + n;
self.t.set(res);
res
}
#[inline(always)]
pub fn fetch_sub(&self, n: T) -> T
where
T: Copy + Sub<T, Output = T>,
{
let res = self.t.get();
self.t.set(res - n);
res
}
#[inline(always)]
pub fn or_assign(&self, n: T)
where
T: Copy + BitOr<Output = T>,
{
self.t.set(self.t.get() | n);
}
#[inline(always)]
pub fn and_assign(&self, n: T)
where
T: Copy + BitAnd<Output = T>,
{
self.t.set(self.t.get() & n);
}
#[inline(always)]
pub fn is_zero(&self) -> bool
where
T: Eq + Copy + Default,
{
self.t.get() == T::default()
}
#[inline(always)]
pub fn is_not_zero(&self) -> bool
where
T: Eq + Copy + Default,
{
!self.is_zero()
}
#[inline(always)]
pub fn take(&self) -> T
where
T: Default,
{
self.t.replace(T::default())
}
}
impl<T: BitOr<Output = T> + Copy> BitOr<T> for &'_ NumCell<T> {
type Output = T;
#[inline(always)]
fn bitor(self, rhs: T) -> Self::Output {
self.t.get() | rhs
}
}
impl<T: BitAnd<Output = T> + Copy> BitAnd<T> for &'_ NumCell<T> {
type Output = T;
#[inline(always)]
fn bitand(self, rhs: T) -> Self::Output {
self.t.get() & rhs
}
}

44
utils/src/on_change.rs Normal file
View file

@ -0,0 +1,44 @@
use {
crate::{clonecell::CloneCell, syncqueue::SyncQueue},
std::{
fmt::{Debug, Formatter},
rc::Rc,
},
};
pub struct OnChange<T> {
pub on_change: CloneCell<Option<Rc<dyn Fn()>>>,
pub events: SyncQueue<T>,
}
impl<T> OnChange<T> {
pub fn clear(&self) {
self.on_change.take();
self.events.take();
}
pub fn send_event(&self, event: T) {
self.events.push(event);
if let Some(cb) = self.on_change.get() {
cb();
}
}
}
impl<T> Default for OnChange<T> {
fn default() -> Self {
Self {
on_change: Default::default(),
events: Default::default(),
}
}
}
impl<T> Debug for OnChange<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self.on_change.get() {
None => f.write_str("None"),
Some(_) => f.write_str("Some"),
}
}
}

View file

@ -0,0 +1,18 @@
use {crate::asyncevent::AsyncEvent, std::rc::Rc};
#[derive(Default)]
pub struct OnDropEvent {
ae: Rc<AsyncEvent>,
}
impl OnDropEvent {
pub fn event(&self) -> Rc<AsyncEvent> {
self.ae.clone()
}
}
impl Drop for OnDropEvent {
fn drop(&mut self) {
self.ae.trigger()
}
}

26
utils/src/once.rs Normal file
View file

@ -0,0 +1,26 @@
#![allow(dead_code)]
use std::{cell::Cell, future::Future};
#[derive(Default)]
pub struct Once {
done: Cell<bool>,
}
impl Once {
pub fn set(&self) -> bool {
!self.done.replace(true)
}
pub fn exec<F: FnOnce()>(&self, f: F) {
if !self.done.replace(true) {
f();
}
}
pub async fn exec_async<G: Future<Output = ()>, F: FnOnce() -> G>(&self, f: F) {
if !self.done.replace(true) {
f().await;
}
}
}

109
utils/src/opaque.rs Normal file
View file

@ -0,0 +1,109 @@
use {
crate::array,
arrayvec::ArrayString,
rand::{RngExt, rng},
serde::{Deserialize, Deserializer, Serialize, Serializer, de},
std::{
fmt::{Debug, Display, Formatter},
num::ParseIntError,
str::FromStr,
},
thiserror::Error,
};
#[cfg(test)]
mod tests;
#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[repr(transparent)]
pub struct Opaque {
v: [u64; 3],
}
pub fn opaque() -> Opaque {
let mut rng = rng();
Opaque {
v: array::from_fn(|_| rng.random()),
}
}
impl Opaque {
pub fn to_string(self) -> ArrayString<OPAQUE_LEN> {
use std::fmt::Write;
let mut s = ArrayString::new();
write!(s, "{}", self).unwrap();
s
}
}
impl Display for Opaque {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:016x}", self.v[2])?;
write!(f, "{:016x}", self.v[1])?;
write!(f, "{:016x}", self.v[0])?;
Ok(())
}
}
impl Debug for Opaque {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
impl Serialize for Opaque {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = self.to_string();
serializer.serialize_str(&s)
}
}
impl<'de> Deserialize<'de> for Opaque {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = <&str>::deserialize(deserializer)?;
Opaque::from_str(s).map_err(de::Error::custom)
}
}
impl FromStr for Opaque {
type Err = OpaqueError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != OPAQUE_LEN {
return Err(OpaqueError::InvalidStringLength);
}
if !s.is_char_boundary(OPAQUE_SEGMENT) {
return Err(OpaqueError::NotAscii);
}
let (a, s) = s.split_at(OPAQUE_SEGMENT);
if !s.is_char_boundary(OPAQUE_SEGMENT) {
return Err(OpaqueError::NotAscii);
}
let (b, c) = s.split_at(OPAQUE_SEGMENT);
let v = [
u64::from_str_radix(c, 16).map_err(OpaqueError::Parse)?,
u64::from_str_radix(b, 16).map_err(OpaqueError::Parse)?,
u64::from_str_radix(a, 16).map_err(OpaqueError::Parse)?,
];
Ok(Self { v })
}
}
pub const OPAQUE_LEN: usize = 48;
const OPAQUE_SEGMENT: usize = OPAQUE_LEN / 3;
#[derive(Debug, Error)]
pub enum OpaqueError {
#[error("The string is not exactly {OPAQUE_LEN} bytes long")]
InvalidStringLength,
#[error("The string is not ascii")]
NotAscii,
#[error("Could not parse the string as a hex number")]
Parse(ParseIntError),
}

12
utils/src/opaque/tests.rs Normal file
View file

@ -0,0 +1,12 @@
use {
crate::opaque::{Opaque, opaque},
std::str::FromStr,
};
#[test]
fn roundtrip() {
let v1 = opaque();
let s = v1.to_string();
let v2 = Opaque::from_str(&s).unwrap();
assert_eq!(v1, v2);
}

28
utils/src/opaque_cell.rs Normal file
View file

@ -0,0 +1,28 @@
use std::{
cell::Cell,
fmt::{Debug, Formatter},
ops::{Deref, DerefMut},
};
#[derive(Default)]
pub struct OpaqueCell<T>(Cell<T>);
impl<T> Deref for OpaqueCell<T> {
type Target = Cell<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for OpaqueCell<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> Debug for OpaqueCell<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Cell<{}> {{ ... }}", std::any::type_name::<T>())
}
}

27
utils/src/opt.rs Normal file
View file

@ -0,0 +1,27 @@
use {crate::clonecell::CloneCell, std::rc::Rc};
pub struct Opt<T> {
t: CloneCell<Option<Rc<T>>>,
}
impl<T> Default for Opt<T> {
fn default() -> Self {
Self {
t: Default::default(),
}
}
}
impl<T> Opt<T> {
pub fn set(&self, t: Option<Rc<T>>) {
self.t.set(t);
}
pub fn get(&self) -> Option<Rc<T>> {
self.t.get()
}
pub fn is_none(&self) -> bool {
self.t.is_none()
}
}

9
utils/src/option_ext.rs Normal file
View file

@ -0,0 +1,9 @@
pub trait OptionExt<T> {
fn get_or_insert_default_ext(&mut self) -> &mut T;
}
impl<T: Default> OptionExt<T> for Option<T> {
fn get_or_insert_default_ext(&mut self) -> &mut T {
self.get_or_insert_with(|| Default::default())
}
}

View file

@ -0,0 +1,74 @@
use std::{
fmt::{Debug, Display, Formatter},
hash::{Hash, Hasher},
ops::{Add, Div, Mul, Sub},
};
macro_rules! define {
($big:ident, $little:ty) => {
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct $big(pub $little);
impl Eq for $big {}
impl PartialEq for $big {
fn eq(&self, other: &Self) -> bool {
self.0.to_bits() == other.0.to_bits()
}
}
impl Hash for $big {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.to_bits().hash(state);
}
}
impl Add<Self> for $big {
type Output = Self;
fn add(self, rhs: $big) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl Sub<Self> for $big {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}
impl Mul<Self> for $big {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self(self.0 * rhs.0)
}
}
impl Div<Self> for $big {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
Self(self.0 / rhs.0)
}
}
impl Display for $big {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.0, f)
}
}
impl Debug for $big {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self.0, f)
}
}
};
}
define!(F64, f64);
define!(F32, f32);

242
utils/src/oserror.rs Normal file
View file

@ -0,0 +1,242 @@
use {
std::{
error::Error,
fmt::{Display, Formatter},
sync::LazyLock,
},
uapi::{
Errno,
c::{self, c_int},
},
};
static ERRORS: LazyLock<&'static [Option<&'static str>]> = LazyLock::new(|| {
static MSGS: &[(c::c_int, &str)] = &[
(c::EWOULDBLOCK, "Operation would block"),
(c::ENOTSUP, "Not supported"),
(c::EHWPOISON, "Memory page has hardware error"),
(c::ERFKILL, "Operation not possible due to RF-kill"),
(c::EKEYREJECTED, "Key was rejected by service"),
(c::EKEYREVOKED, "Key has been revoked"),
(c::EKEYEXPIRED, "Key has expired"),
(c::ENOKEY, "Required key not available"),
(c::EMEDIUMTYPE, "Wrong medium type"),
(c::ENOMEDIUM, "No medium found"),
(c::EREMOTEIO, "Remote I/O error"),
(c::EISNAM, "Is a named type file"),
(c::ENAVAIL, "No XENIX semaphores available"),
(c::ENOTNAM, "Not a XENIX named type file"),
(c::EUCLEAN, "Structure needs cleaning"),
(c::ESTRPIPE, "Streams pipe error"),
(c::ELIBEXEC, "Cannot exec a shared library directly"),
(
c::ELIBMAX,
"Attempting to link in too many shared libraries",
),
(c::ELIBSCN, ".lib section in a.out corrupted"),
(c::ELIBBAD, "Accessing a corrupted shared library"),
(c::ELIBACC, "Can not access a needed shared library"),
(c::EREMCHG, "Remote address changed"),
(c::EBADFD, "File descriptor in bad state"),
(c::ENOTUNIQ, "Name not unique on network"),
(c::EDOTDOT, "RFS specific error"),
(c::ECOMM, "Communication error on send"),
(c::ESRMNT, "Srmount error"),
(c::EADV, "Advertise error"),
(c::ENOPKG, "Package not installed"),
(c::ENONET, "Machine is not on the network"),
(c::EBFONT, "Bad font file format"),
(c::EBADSLT, "Invalid slot"),
(c::EBADRQC, "Invalid request code"),
(c::ENOANO, "No anode"),
(c::EXFULL, "Exchange full"),
(c::EBADR, "Invalid request descriptor"),
(c::EBADE, "Invalid exchange"),
(c::EL2HLT, "Level 2 halted"),
(c::ENOCSI, "No CSI structure available"),
(c::EUNATCH, "Protocol driver not attached"),
(c::ELNRNG, "Link number out of range"),
(c::EL3RST, "Level 3 reset"),
(c::EL3HLT, "Level 3 halted"),
(c::EL2NSYNC, "Level 2 not synchronized"),
(c::ECHRNG, "Channel number out of range"),
(c::ERESTART, "Interrupted system call should be restarted"),
(c::ENOTRECOVERABLE, "State not recoverable"),
(c::EOWNERDEAD, "Owner died"),
(c::ECANCELED, "Operation canceled"),
(c::ETIME, "Timer expired"),
(c::EPROTO, "Protocol error"),
(c::EOVERFLOW, "Value too large for defined data type"),
(c::ENOSTR, "Device not a stream"),
(c::ENOSR, "Out of streams resources"),
(c::ENOMSG, "No message of desired type"),
(c::ENOLINK, "Link has been severed"),
(c::ENODATA, "No data available"),
(c::EMULTIHOP, "Multihop attempted"),
(c::EIDRM, "Identifier removed"),
(c::EBADMSG, "Bad message"),
(
c::EILSEQ,
"Invalid or incomplete multibyte or wide character",
),
(c::ENOSYS, "Function not implemented"),
(c::ENOLCK, "No locks available"),
(c::EREMOTE, "Object is remote"),
(c::ESTALE, "Stale file handle"),
(c::EDQUOT, "Disk quota exceeded"),
(c::EUSERS, "Too many users"),
(c::ENOTEMPTY, "Directory not empty"),
(c::EHOSTUNREACH, "No route to host"),
(c::EHOSTDOWN, "Host is down"),
(c::ENAMETOOLONG, "File name too long"),
(c::ELOOP, "Too many levels of symbolic links"),
(c::ECONNREFUSED, "Connection refused"),
(c::ETIMEDOUT, "Connection timed out"),
(c::ETOOMANYREFS, "Too many references: cannot splice"),
(
c::ESHUTDOWN,
"Cannot send after transport endpoint shutdown",
),
(c::EDESTADDRREQ, "Destination address required"),
(c::ENOTCONN, "Transport endpoint is not connected"),
(c::EISCONN, "Transport endpoint is already connected"),
(c::ENOBUFS, "No buffer space available"),
(c::ECONNRESET, "Connection reset by peer"),
(c::ECONNABORTED, "Software caused connection abort"),
(c::ENETRESET, "Network dropped connection on reset"),
(c::ENETUNREACH, "Network is unreachable"),
(c::ENETDOWN, "Network is down"),
(c::EADDRNOTAVAIL, "Cannot assign requested address"),
(c::EADDRINUSE, "Address already in use"),
(c::EAFNOSUPPORT, "Address family not supported by protocol"),
(c::EPFNOSUPPORT, "Protocol family not supported"),
(c::EOPNOTSUPP, "Operation not supported"),
(c::ESOCKTNOSUPPORT, "Socket type not supported"),
(c::EPROTONOSUPPORT, "Protocol not supported"),
(c::ENOPROTOOPT, "Protocol not available"),
(c::EPROTOTYPE, "Protocol wrong type for socket"),
(c::EMSGSIZE, "Message too long"),
(c::ENOTSOCK, "Socket operation on non-socket"),
(c::EALREADY, "Operation already in progress"),
(c::EINPROGRESS, "Operation now in progress"),
(c::EAGAIN, "Resource temporarily unavailable"),
(c::ERANGE, "Numerical result out of range"),
(c::EDOM, "Numerical argument out of domain"),
(c::EPIPE, "Broken pipe"),
(c::EMLINK, "Too many links"),
(c::EROFS, "Read-only file system"),
(c::ESPIPE, "Illegal seek"),
(c::ENOSPC, "No space left on device"),
(c::EFBIG, "File too large"),
(c::ETXTBSY, "Text file busy"),
(c::ENOTTY, "Inappropriate ioctl for device"),
(c::ENFILE, "Too many open files in system"),
(c::EMFILE, "Too many open files"),
(c::EINVAL, "Invalid argument"),
(c::EISDIR, "Is a directory"),
(c::ENOTDIR, "Not a directory"),
(c::ENODEV, "No such device"),
(c::EXDEV, "Invalid cross-device link"),
(c::EEXIST, "File exists"),
(c::EBUSY, "Device or resource busy"),
(c::ENOTBLK, "Block device required"),
(c::EFAULT, "Bad address"),
(c::EACCES, "Permission denied"),
(c::ENOMEM, "Cannot allocate memory"),
(c::EDEADLK, "Resource deadlock avoided"),
(c::ECHILD, "No child processes"),
(c::EBADF, "Bad file descriptor"),
(c::ENOEXEC, "Exec format error"),
(c::E2BIG, "Argument list too long"),
(c::ENXIO, "No such device or address"),
(c::EIO, "Input/output error"),
(c::EINTR, "Interrupted system call"),
(c::ESRCH, "No such process"),
(c::ENOENT, "No such file or directory"),
(c::EPERM, "Operation not permitted"),
];
let mut res = vec![];
for &(idx, msg) in MSGS {
let idx = idx as usize;
while res.len() <= idx {
res.push(None);
}
res[idx] = Some(msg);
}
res.leak()
});
#[derive(Debug, Eq, PartialEq)]
pub struct OsError(pub c::c_int);
impl From<c::c_int> for OsError {
fn from(v: c_int) -> Self {
Self(v)
}
}
impl From<std::io::Error> for OsError {
fn from(v: std::io::Error) -> Self {
match v.raw_os_error() {
Some(v) => Self(v),
None => Self(c::EINVAL),
}
}
}
impl Default for OsError {
fn default() -> Self {
OsError(Errno::default().0)
}
}
impl Error for OsError {}
impl Display for OsError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let msg = ERRORS
.get(self.0 as usize)
.and_then(|v| *v)
.unwrap_or("unknown error");
write!(f, "{} (os error {})", msg, self.0)
}
}
pub trait OsErrorExt {
type Container;
fn to_os_error(self) -> Self::Container;
}
impl<T> OsErrorExt for Result<T, Errno> {
type Container = Result<T, OsError>;
fn to_os_error(self) -> Self::Container {
match self {
Ok(v) => Ok(v),
Err(e) => Err(OsError(e.0)),
}
}
}
pub trait OsErrorExt2 {
type T;
fn map_os_err<F, O>(self, op: O) -> Result<Self::T, F>
where
O: FnOnce(OsError) -> F;
}
impl<T> OsErrorExt2 for Result<T, Errno> {
type T = T;
fn map_os_err<F, O>(self, op: O) -> Result<T, F>
where
O: FnOnce(OsError) -> F,
{
match self {
Ok(t) => Ok(t),
Err(e) => Err(op(OsError(e.0))),
}
}
}

8
utils/src/page_size.rs Normal file
View file

@ -0,0 +1,8 @@
use {std::sync::LazyLock, uapi::c};
static PAGE_SIZE: LazyLock<usize> =
LazyLock::new(|| uapi::sysconf(c::_SC_PAGESIZE).unwrap_or(4096) as _);
pub fn page_size() -> usize {
*PAGE_SIZE
}

59
utils/src/pid_info.rs Normal file
View file

@ -0,0 +1,59 @@
use {
crate::{errorfmt::ErrorFmt, oserror::OsErrorExt},
bstr::ByteSlice,
std::os::unix::ffi::OsStrExt,
uapi::{OwnedFd, c},
};
pub struct PidInfo {
pub uid: c::uid_t,
pub pid: c::pid_t,
pub comm: String,
pub exe: String,
}
pub fn get_pid_info(uid: c::uid_t, pid: c::pid_t) -> PidInfo {
let comm = match std::fs::read(format!("/proc/{}/comm", pid)) {
Ok(name) => name.trim_ascii_end().as_bstr().to_string(),
Err(e) => {
log::warn!("Could not read `comm` of pid {}: {}", pid, ErrorFmt(e));
"Unknown".to_string()
}
};
let exe = match std::fs::read_link(format!("/proc/{}/exe", pid)) {
Ok(name) => name
.as_os_str()
.as_bytes()
.trim_ascii_end()
.as_bstr()
.to_string(),
Err(e) => {
log::warn!("Could not read `exe` of pid {}: {}", pid, ErrorFmt(e));
"Unknown".to_string()
}
};
PidInfo {
uid,
pid,
comm,
exe,
}
}
pub fn get_socket_creds(socket: &OwnedFd) -> Option<(c::uid_t, c::pid_t)> {
let mut cred = c::ucred {
pid: 0,
uid: 0,
gid: 0,
};
match uapi::getsockopt(socket.raw(), c::SOL_SOCKET, c::SO_PEERCRED, &mut cred).to_os_error() {
Ok(_) => Some((cred.uid, cred.pid)),
Err(e) => {
log::error!(
"Cannot determine peer credentials of new connection: {}",
ErrorFmt(e),
);
None
}
}
}

View file

@ -0,0 +1,23 @@
use {
crate::oserror::{OsError, OsErrorExt},
c::{c_int, syscall},
std::{ptr, rc::Rc},
uapi::{
OwnedFd,
c::{self, SYS_pidfd_send_signal, siginfo_t},
map_err,
},
};
pub fn pidfd_send_signal(pidfd: &Rc<OwnedFd>, signal: c_int) -> Result<(), OsError> {
let res = unsafe {
syscall(
SYS_pidfd_send_signal,
pidfd.raw(),
signal,
ptr::null_mut::<siginfo_t>(),
0,
)
};
map_err!(res).map(drop).to_os_error()
}

32
utils/src/pipe.rs Normal file
View file

@ -0,0 +1,32 @@
use {
crate::oserror::{OsError, OsErrorExt},
uapi::{OwnedFd, c, pipe2},
};
pub struct Pipe<L, R> {
pub read: L,
pub write: R,
}
pub fn pipe() -> Result<Pipe<OwnedFd, OwnedFd>, OsError> {
let (read, write) = pipe2(c::O_CLOEXEC).to_os_error()?;
Ok(Pipe { read, write })
}
impl<L, R> Pipe<L, R> {
#[allow(dead_code)]
pub fn map_read<Lprime>(self, map: impl FnOnce(L) -> Lprime) -> Pipe<Lprime, R> {
Pipe {
read: map(self.read),
write: self.write,
}
}
#[allow(dead_code)]
pub fn map_write<Rprime>(self, map: impl FnOnce(R) -> Rprime) -> Pipe<L, Rprime> {
Pipe {
read: self.read,
write: map(self.write),
}
}
}

View file

@ -0,0 +1,8 @@
use uapi::{IntoUstr, c};
pub fn set_process_name(name: &str) {
unsafe {
let name = name.into_ustr();
c::prctl(c::PR_SET_NAME, name.as_ptr());
}
}

28
utils/src/ptr_ext.rs Normal file
View file

@ -0,0 +1,28 @@
pub trait PtrExt<T: ?Sized> {
unsafe fn deref<'a>(self) -> &'a T;
}
pub trait MutPtrExt<T: ?Sized> {
unsafe fn deref_mut<'a>(self) -> &'a mut T;
}
impl<T: ?Sized> PtrExt<T> for *const T {
#[inline(always)]
unsafe fn deref<'a>(self) -> &'a T {
unsafe { &*self }
}
}
impl<T: ?Sized> PtrExt<T> for *mut T {
#[inline(always)]
unsafe fn deref<'a>(self) -> &'a T {
unsafe { &*self }
}
}
impl<T: ?Sized> MutPtrExt<T> for *mut T {
#[inline(always)]
unsafe fn deref_mut<'a>(self) -> &'a mut T {
unsafe { &mut *self }
}
}

107
utils/src/queue.rs Normal file
View file

@ -0,0 +1,107 @@
use {
crate::ptr_ext::{MutPtrExt, PtrExt},
std::{
cell::{Cell, UnsafeCell},
collections::VecDeque,
future::Future,
mem,
pin::Pin,
task::{Context, Poll, Waker},
},
};
pub struct AsyncQueue<T> {
data: UnsafeCell<VecDeque<T>>,
waiter: Cell<Option<Waker>>,
}
impl<T> Default for AsyncQueue<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> AsyncQueue<T> {
pub fn new() -> Self {
Self {
data: Default::default(),
waiter: Default::default(),
}
}
pub fn push(&self, t: T) {
unsafe {
self.data.get().deref_mut().push_back(t);
}
if let Some(waiter) = self.waiter.take() {
waiter.wake();
}
}
pub fn try_pop(&self) -> Option<T> {
unsafe { self.data.get().deref_mut().pop_front() }
}
pub fn pop<'a>(&'a self) -> AsyncQueuePop<'a, T> {
AsyncQueuePop { queue: self }
}
pub fn non_empty<'a>(&'a self) -> AsyncQueueNonEmpty<'a, T> {
AsyncQueueNonEmpty { queue: self }
}
pub fn is_empty(&self) -> bool {
unsafe { self.data.get().deref().is_empty() }
}
pub fn is_not_empty(&self) -> bool {
!self.is_empty()
}
pub fn clear(&self) {
unsafe {
mem::take(self.data.get().deref_mut());
}
self.waiter.take();
}
pub fn move_to(&self, other: &mut VecDeque<T>) {
unsafe {
other.append(self.data.get().deref_mut());
}
}
}
pub struct AsyncQueuePop<'a, T> {
queue: &'a AsyncQueue<T>,
}
impl<'a, T> Future for AsyncQueuePop<'a, T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if let Some(t) = self.queue.try_pop() {
Poll::Ready(t)
} else {
self.queue.waiter.set(Some(cx.waker().clone()));
Poll::Pending
}
}
}
pub struct AsyncQueueNonEmpty<'a, T> {
queue: &'a AsyncQueue<T>,
}
impl<'a, T> Future for AsyncQueueNonEmpty<'a, T> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if unsafe { self.queue.data.get().deref().len() } > 0 {
Poll::Ready(())
} else {
self.queue.waiter.set(Some(cx.waker().clone()));
Poll::Pending
}
}
}

37
utils/src/rc_eq.rs Normal file
View file

@ -0,0 +1,37 @@
use std::{
ops::Deref,
rc::{Rc, Weak},
};
pub fn rc_eq<T: ?Sized>(a: &Rc<T>, b: &Rc<T>) -> bool {
Rc::as_ptr(a) as *const u8 == Rc::as_ptr(b) as *const u8
}
pub fn rc_weak_eq<T: ?Sized>(a: &Rc<T>, b: &Weak<T>) -> bool {
Rc::as_ptr(a) as *const u8 == b.as_ptr() as *const u8
}
#[derive(Default)]
pub struct RcEq<T>(pub Rc<T>);
impl<T> Clone for RcEq<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T> PartialEq for RcEq<T> {
fn eq(&self, other: &Self) -> bool {
rc_eq(&self.0, &other.0)
}
}
impl<T> Eq for RcEq<T> {}
impl<T> Deref for RcEq<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}

90
utils/src/refcounted.rs Normal file
View file

@ -0,0 +1,90 @@
use {
crate::ptr_ext::{MutPtrExt, PtrExt},
std::{cell::UnsafeCell, mem, ops::Deref},
};
pub struct RefCounted<T> {
map: UnsafeCell<Vec<(T, usize)>>,
}
impl<T> Default for RefCounted<T> {
fn default() -> Self {
Self {
map: UnsafeCell::new(vec![]),
}
}
}
impl<T: Eq> RefCounted<T> {
pub fn add(&self, t: T) -> bool {
unsafe {
let map = self.map.get().deref_mut();
for (k, v) in &mut *map {
if k == &t {
*v += 1;
return false;
}
}
map.push((t, 1));
true
}
}
pub fn remove(&self, t: &T) -> bool {
unsafe {
let map = self.map.get().deref_mut();
let idx = 'idx: {
for (idx, (k, v)) in map.iter_mut().enumerate() {
if k == t {
*v -= 1;
if *v > 0 {
return false;
} else {
break 'idx idx;
}
}
}
return false;
};
let _v = map.swap_remove(idx);
true
}
}
pub fn to_vec(&self) -> Vec<T>
where
T: Copy,
{
unsafe { self.map.get().deref().iter().map(|k| k.0).collect() }
}
pub fn lock(&self) -> Locked<'_, T> {
unsafe {
Locked {
vec: mem::take(self.map.get().deref_mut()),
rc: self,
}
}
}
}
pub struct Locked<'a, T> {
rc: &'a RefCounted<T>,
vec: Vec<(T, usize)>,
}
impl<'a, T> Deref for Locked<'a, T> {
type Target = [(T, usize)];
fn deref(&self) -> &Self::Target {
&self.vec
}
}
impl<'a, T> Drop for Locked<'a, T> {
fn drop(&mut self) {
unsafe {
*self.rc.map.get() = mem::take(&mut self.vec);
}
}
}

319
utils/src/smallmap.rs Normal file
View file

@ -0,0 +1,319 @@
use {
crate::{
clonecell::UnsafeCellCloneSafe,
ptr_ext::{MutPtrExt, PtrExt},
},
smallvec::SmallVec,
std::{
cell::UnsafeCell,
fmt::{Debug, Formatter},
mem,
},
};
pub struct SmallMap<K, V, const N: usize> {
m: UnsafeCell<SmallMapMut<K, V, N>>,
}
impl<K: Debug, V: Debug, const N: usize> Debug for SmallMap<K, V, N> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
unsafe { self.m.get().deref().fmt(f) }
}
}
impl<K, V, const N: usize> Default for SmallMap<K, V, N> {
fn default() -> Self {
Self {
m: Default::default(),
}
}
}
impl<K: Eq, V, const N: usize> SmallMap<K, V, N> {
pub fn new_with(k: K, v: V) -> Self {
Self {
m: UnsafeCell::new(SmallMapMut::new_with(k, v)),
}
}
pub fn new() -> Self {
Self {
m: UnsafeCell::new(SmallMapMut::new()),
}
}
pub fn contains(&self, k: &K) -> bool {
unsafe { self.m.get().deref().contains(k) }
}
pub fn len(&self) -> usize {
unsafe { self.m.get().deref().len() }
}
pub fn insert(&self, k: K, v: V) -> Option<V> {
unsafe { self.m.get().deref_mut().insert(k, v) }
}
pub fn is_empty(&self) -> bool {
unsafe { self.m.get().deref().is_empty() }
}
pub fn is_not_empty(&self) -> bool {
!self.is_empty()
}
pub fn remove(&self, k: &K) -> Option<V> {
unsafe { self.m.get().deref_mut().remove(k) }
}
pub fn clear(&self) {
unsafe {
self.m.get().deref_mut().clear();
}
}
pub fn take(&self) -> SmallVec<[(K, V); N]> {
unsafe { self.m.get().deref_mut().take() }
}
pub fn replace(&self, other: SmallVec<[(K, V); N]>) -> SmallVec<[(K, V); N]> {
unsafe { mem::replace(&mut self.m.get().deref_mut().m, other) }
}
pub fn pop(&self) -> Option<(K, V)> {
unsafe { self.m.get().deref_mut().pop() }
}
pub fn iter<'a>(&'a self) -> SmallMapIter<'a, K, V, N> {
SmallMapIter { pos: 0, map: self }
}
}
impl<K: Eq, V: UnsafeCellCloneSafe, const N: usize> SmallMap<K, V, N> {
pub fn get(&self, k: &K) -> Option<V> {
unsafe {
let m = &self.m.get().deref().m;
for (ek, ev) in m {
if ek == k {
return Some(ev.clone());
}
}
None
}
}
}
impl<'a, K: Copy, V: UnsafeCellCloneSafe, const N: usize> IntoIterator for &'a SmallMap<K, V, N> {
type Item = (K, V);
type IntoIter = SmallMapIter<'a, K, V, N>;
fn into_iter(self) -> Self::IntoIter {
SmallMapIter { pos: 0, map: self }
}
}
pub struct SmallMapIter<'a, K, V, const N: usize> {
pos: usize,
map: &'a SmallMap<K, V, N>,
}
impl<'a, K: Copy, V: UnsafeCellCloneSafe, const N: usize> Iterator for SmallMapIter<'a, K, V, N> {
type Item = (K, V);
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let v = &self.map.m.get().deref().m;
if self.pos >= v.len() {
return None;
}
let (k, v) = &v[self.pos];
self.pos += 1;
Some((*k, v.clone()))
}
}
}
pub struct SmallMapMut<K, V, const N: usize> {
m: SmallVec<[(K, V); N]>,
}
impl<K: Debug, V: Debug, const N: usize> Debug for SmallMapMut<K, V, N> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_map()
.entries(self.m.iter().map(|e| (&e.0, &e.1)))
.finish()
}
}
impl<K, V, const N: usize> Default for SmallMapMut<K, V, N> {
fn default() -> Self {
Self {
m: Default::default(),
}
}
}
impl<K: Eq, V, const N: usize> SmallMapMut<K, V, N> {
pub fn new_with(k: K, v: V) -> Self {
let mut sv = SmallVec::new();
sv.push((k, v));
Self { m: sv }
}
pub fn new() -> Self {
Self {
m: SmallVec::new_const(),
}
}
pub fn len(&self) -> usize {
self.m.len()
}
pub fn contains(&self, k: &K) -> bool {
for (ek, _) in &self.m {
if ek == k {
return true;
}
}
false
}
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
for (ek, ev) in &mut self.m {
if ek == &k {
return Some(mem::replace(ev, v));
}
}
self.m.push((k, v));
None
}
pub fn get(&self, k: &K) -> Option<&V> {
for (ek, ev) in &self.m {
if ek == k {
return Some(ev);
}
}
None
}
pub fn get_mut(&mut self, k: &K) -> Option<&mut V> {
for (ek, ev) in &mut self.m {
if ek == k {
return Some(ev);
}
}
None
}
pub fn get_or_default_mut(&mut self, k: K) -> &mut V
where
V: Default,
{
self.get_or_insert_with(k, || V::default())
}
pub fn get_or_insert_with<F>(&mut self, k: K, f: F) -> &mut V
where
F: FnOnce() -> V,
{
for (ek, ev) in &mut self.m {
if ek == &k {
return unsafe { (ev as *mut V).deref_mut() };
}
}
self.m.push((k, f()));
&mut self.m.last_mut().unwrap().1
}
pub fn is_empty(&self) -> bool {
self.m.is_empty()
}
pub fn remove(&mut self, k: &K) -> Option<V> {
for (idx, (ek, _)) in self.m.iter_mut().enumerate() {
if ek == k {
return Some(self.m.swap_remove(idx).1);
}
}
None
}
pub fn clear(&mut self) {
let _v = mem::replace(&mut self.m, SmallVec::new());
}
pub fn take(&mut self) -> SmallVec<[(K, V); N]> {
mem::take(&mut self.m)
}
pub fn pop(&mut self) -> Option<(K, V)> {
self.m.pop()
}
pub fn iter<'a>(&'a self) -> SmallMapMutIter<'a, K, V, N> {
SmallMapMutIter { pos: 0, map: self }
}
pub fn iter_mut<'a>(&'a mut self) -> SmallMapMutIterMut<'a, K, V, N> {
SmallMapMutIterMut { pos: 0, map: self }
}
pub fn remove_if<F: FnMut(&K, &V) -> bool>(&mut self, mut f: F) {
let mut i = 0;
while i < self.m.len() {
let (k, v) = &self.m[i];
if f(k, v) {
self.m.swap_remove(i);
} else {
i += 1;
}
}
}
}
impl<'a, K: Copy, V, const N: usize> IntoIterator for &'a SmallMapMut<K, V, N> {
type Item = (&'a K, &'a V);
type IntoIter = SmallMapMutIter<'a, K, V, N>;
fn into_iter(self) -> Self::IntoIter {
SmallMapMutIter { pos: 0, map: self }
}
}
pub struct SmallMapMutIter<'a, K, V, const N: usize> {
pos: usize,
map: &'a SmallMapMut<K, V, N>,
}
impl<'a, K, V, const N: usize> Iterator for SmallMapMutIter<'a, K, V, N> {
type Item = (&'a K, &'a V);
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.map.m.len() {
return None;
}
let (k, v) = &self.map.m[self.pos];
self.pos += 1;
Some((k, v))
}
}
pub struct SmallMapMutIterMut<'a, K, V, const N: usize> {
pos: usize,
map: &'a mut SmallMapMut<K, V, N>,
}
impl<'a, K, V, const N: usize> Iterator for SmallMapMutIterMut<'a, K, V, N> {
type Item = (&'a mut K, &'a mut V);
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.map.m.len() {
return None;
}
let (k, v) = &mut self.map.m[self.pos];
self.pos += 1;
unsafe { Some(((k as *mut K).deref_mut(), (v as *mut V).deref_mut())) }
}
}

49
utils/src/stack.rs Normal file
View file

@ -0,0 +1,49 @@
use {
crate::{
clonecell::UnsafeCellCloneSafe,
ptr_ext::{MutPtrExt, PtrExt},
},
std::{cell::UnsafeCell, mem},
};
pub struct Stack<T> {
vec: UnsafeCell<Vec<T>>,
}
impl<T> Default for Stack<T> {
fn default() -> Self {
Self {
vec: Default::default(),
}
}
}
impl<T> Stack<T> {
pub fn push(&self, v: T) {
unsafe {
self.vec.get().deref_mut().push(v);
}
}
pub fn pop(&self) -> Option<T> {
unsafe { self.vec.get().deref_mut().pop() }
}
pub fn to_vec(&self) -> Vec<T>
where
T: UnsafeCellCloneSafe,
{
unsafe {
let v = self.vec.get().deref();
(*v).clone()
}
}
pub fn take(&self) -> Vec<T> {
unsafe { mem::take(self.vec.get().deref_mut()) }
}
pub fn len(&self) -> usize {
unsafe { self.vec.get().deref().len() }
}
}

3
utils/src/static_text.rs Normal file
View file

@ -0,0 +1,3 @@
pub trait StaticText {
fn text(&self) -> &'static str;
}

11
utils/src/string_ext.rs Normal file
View file

@ -0,0 +1,11 @@
use isnt::std_1::primitive::IsntStrExt;
pub trait StringExt {
fn to_string_if_not_empty(&self) -> Option<String>;
}
impl StringExt for str {
fn to_string_if_not_empty(&self) -> Option<String> {
self.is_not_empty().then(|| self.to_string())
}
}

68
utils/src/syncqueue.rs Normal file
View file

@ -0,0 +1,68 @@
use {
crate::ptr_ext::MutPtrExt,
std::{cell::UnsafeCell, collections::VecDeque, mem},
};
#[derive(Debug)]
pub struct SyncQueue<T> {
el: UnsafeCell<VecDeque<T>>,
}
impl<T> Default for SyncQueue<T> {
fn default() -> Self {
Self {
el: Default::default(),
}
}
}
impl<T> SyncQueue<T> {
pub fn push(&self, t: T) {
unsafe {
self.el.get().deref_mut().push_back(t);
}
}
pub fn push_front(&self, t: T) {
unsafe {
self.el.get().deref_mut().push_front(t);
}
}
pub fn append(&self, src: &mut Vec<T>) {
unsafe {
self.el.get().deref_mut().extend(src.drain(..));
}
}
#[inline]
pub fn pop(&self) -> Option<T> {
unsafe { self.el.get().deref_mut().pop_front() }
}
pub fn is_empty(&self) -> bool {
unsafe { self.el.get().deref_mut().is_empty() }
}
pub fn is_not_empty(&self) -> bool {
!self.is_empty()
}
pub fn swap(&self, queue: &mut VecDeque<T>) {
unsafe {
mem::swap(self.el.get().deref_mut(), queue);
}
}
pub fn take(&self) -> VecDeque<T> {
let mut res = VecDeque::new();
self.swap(&mut res);
res
}
pub fn clear(&self) {
unsafe {
self.el.get().deref_mut().clear();
}
}
}

View file

@ -0,0 +1,24 @@
use crate::numcell::NumCell;
#[derive(Default)]
pub struct ThresholdCounter {
counter: NumCell<usize>,
}
impl ThresholdCounter {
pub fn inc(&self) -> bool {
self.counter.fetch_add(1) == 0
}
pub fn dec(&self) -> bool {
self.counter.fetch_sub(1) == 1
}
pub fn adj(&self, inc: bool) -> bool {
if inc { self.inc() } else { self.dec() }
}
pub fn active(&self) -> bool {
self.counter.get() > 0
}
}

51
utils/src/tri.rs Normal file
View file

@ -0,0 +1,51 @@
use std::{
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
pub trait Try: Sized {
fn tri<F>(f: F) -> Result<(), Self>
where
F: FnOnce() -> Result<(), Self>;
fn tria<F>(f: F) -> Tria<Self, F>
where
F: Future<Output = Result<(), Self>>;
}
impl<E> Try for E {
fn tri<F>(f: F) -> Result<(), Self>
where
F: FnOnce() -> Result<(), Self>,
{
f()
}
fn tria<F>(f: F) -> Tria<E, F>
where
F: Future<Output = Result<(), Self>>,
{
Tria {
f,
_phantom: Default::default(),
}
}
}
pub struct Tria<E, F> {
f: F,
_phantom: PhantomData<E>,
}
impl<E, F> Future for Tria<E, F>
where
F: Future<Output = Result<(), E>>,
{
type Output = Result<(), E>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unsafe { Pin::new_unchecked(&mut Pin::get_unchecked_mut(self).f).poll(cx) }
}
}

View file

@ -0,0 +1,7 @@
pub struct UnlinkOnDrop<'a>(pub &'a str);
impl<'a> Drop for UnlinkOnDrop<'a> {
fn drop(&mut self) {
let _ = uapi::unlink(self.0);
}
}

40
utils/src/vec_ext.rs Normal file
View file

@ -0,0 +1,40 @@
use std::{mem::MaybeUninit, ops::Range, slice};
pub trait VecExt<T> {
fn split_at_spare_mut_ext(&mut self) -> (&mut [T], &mut [MaybeUninit<T>]);
fn split_at_spare_mut_bytes_ext(&mut self) -> (&mut [T], &mut [MaybeUninit<u8>]);
}
impl<T> VecExt<T> for Vec<T> {
fn split_at_spare_mut_ext(&mut self) -> (&mut [T], &mut [MaybeUninit<T>]) {
let Range {
start: ptr,
end: spare_ptr,
} = self.as_mut_ptr_range();
let spare_ptr = spare_ptr.cast::<MaybeUninit<T>>();
let spare_len = self.capacity() - self.len();
unsafe {
let initialized = slice::from_raw_parts_mut(ptr, self.len());
let spare = slice::from_raw_parts_mut(spare_ptr, spare_len);
(initialized, spare)
}
}
fn split_at_spare_mut_bytes_ext(&mut self) -> (&mut [T], &mut [MaybeUninit<u8>]) {
let (l, r) = self.split_at_spare_mut_ext();
unsafe { (l, uapi::as_maybe_uninit_bytes_mut2(r)) }
}
}
pub trait UninitVecExt<T> {
fn set_len_safe(&mut self, n: usize);
}
impl<T> UninitVecExt<T> for Vec<MaybeUninit<T>> {
fn set_len_safe(&mut self, n: usize) {
assert!(n <= self.capacity());
unsafe {
self.set_len(n);
}
}
}

35
utils/src/vecdeque_ext.rs Normal file
View file

@ -0,0 +1,35 @@
use std::{
collections::{Bound, VecDeque},
ops::RangeBounds,
};
pub trait VecDequeExt<T> {
fn get_slices(&self, range: impl RangeBounds<usize>) -> (&[T], &[T]);
}
impl<T> VecDequeExt<T> for VecDeque<T> {
fn get_slices(&self, range: impl RangeBounds<usize>) -> (&[T], &[T]) {
let (l, r) = self.as_slices();
let start = match range.start_bound().cloned() {
Bound::Included(n) => n,
Bound::Excluded(n) => n + 1,
Bound::Unbounded => 0,
};
let end = match range.end_bound().cloned() {
Bound::Included(n) => n + 1,
Bound::Excluded(n) => n,
Bound::Unbounded => self.len(),
};
let left = {
let lo = start.min(l.len());
let hi = end.min(l.len());
&l[lo..hi]
};
let right = {
let lo = start.saturating_sub(l.len());
let hi = end.saturating_sub(l.len());
&r[lo..hi]
};
(left, right)
}
}

58
utils/src/vecset.rs Normal file
View file

@ -0,0 +1,58 @@
use std::ops::Deref;
pub struct VecSet<T> {
vec: Vec<T>,
}
impl<T> Default for VecSet<T> {
fn default() -> Self {
Self { vec: vec![] }
}
}
impl<T> Deref for VecSet<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
&self.vec
}
}
impl<T> VecSet<T> {
pub fn clear(&mut self) {
self.vec.clear();
}
}
impl<T: PartialEq> VecSet<T> {
pub fn insert(&mut self, val: T) -> bool {
if self.vec.contains(&val) {
return false;
}
self.vec.push(val);
true
}
pub fn remove(&mut self, val: &T) -> bool {
for i in 0..self.vec.len() {
if self.vec[i] == *val {
self.vec.swap_remove(i);
return true;
}
}
false
}
pub fn pop(&mut self) -> Option<T> {
self.vec.pop()
}
pub fn extend(&mut self, vals: &[T])
where
T: Copy,
{
for v in vals.iter().copied() {
self.insert(v);
}
}
}

105
utils/src/vecstorage.rs Normal file
View file

@ -0,0 +1,105 @@
use std::{
mem::ManuallyDrop,
ops::{Deref, DerefMut},
};
macro_rules! assert_size_eq {
($t:ty, $u:ty) => {{
struct AssertEqSize<T, U>(std::marker::PhantomData<T>, std::marker::PhantomData<U>);
impl<T, U> AssertEqSize<T, U> {
const VAL: usize = {
if std::mem::size_of::<T>() != std::mem::size_of::<U>() {
panic!("Types have different size");
}
1
};
}
let _ = AssertEqSize::<$t, $u>::VAL;
}};
}
macro_rules! assert_align_eq {
($t:ty, $u:ty) => {{
struct AssertEqAlign<T, U>(std::marker::PhantomData<T>, std::marker::PhantomData<U>);
impl<T, U> AssertEqAlign<T, U> {
const VAL: usize = {
if std::mem::align_of::<T>() != std::mem::align_of::<U>() {
panic!("Types have different alignment");
}
1
};
}
let _ = AssertEqAlign::<$t, $u>::VAL;
}};
}
pub struct VecStorage<T> {
ptr: *mut T,
cap: usize,
}
impl<T> Default for VecStorage<T> {
fn default() -> Self {
let mut v = ManuallyDrop::new(vec![]);
Self {
ptr: v.as_mut_ptr(),
cap: v.capacity(),
}
}
}
impl<T> VecStorage<T> {
pub fn take<'a>(&'a mut self) -> RealizedVec<'a, T, T> {
self.take_as()
}
pub fn take_as<'a, U>(&'a mut self) -> RealizedVec<'a, T, U> {
assert_size_eq!(T, U);
assert_align_eq!(T, U);
unsafe {
RealizedVec {
vec: ManuallyDrop::new(self.to_vector()),
storage: self,
}
}
}
unsafe fn to_vector<U>(&mut self) -> Vec<U> {
unsafe { Vec::from_raw_parts(self.ptr as _, 0, self.cap) }
}
}
impl<T> Drop for VecStorage<T> {
fn drop(&mut self) {
unsafe {
drop(self.to_vector::<T>());
}
}
}
pub struct RealizedVec<'a, T, U> {
vec: ManuallyDrop<Vec<U>>,
storage: &'a mut VecStorage<T>,
}
impl<'a, T, U> Drop for RealizedVec<'a, T, U> {
fn drop(&mut self) {
self.vec.clear();
self.storage.ptr = self.vec.as_mut_ptr() as _;
self.storage.cap = self.vec.capacity();
}
}
impl<'a, T, U> Deref for RealizedVec<'a, T, U> {
type Target = Vec<U>;
fn deref(&self) -> &Self::Target {
self.vec.deref()
}
}
impl<'a, T, U> DerefMut for RealizedVec<'a, T, U> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.vec.deref_mut()
}
}

46
utils/src/windows.rs Normal file
View file

@ -0,0 +1,46 @@
use crate::ptr_ext::PtrExt;
pub trait WindowsExt<T> {
type Windows<'a, const N: usize>: Iterator<Item = &'a [T; N]>
where
Self: 'a,
T: 'a;
fn array_windows_ext<'a, const N: usize>(&'a self) -> Self::Windows<'a, N>;
fn array_chunks_ext<'a, const N: usize>(&'a self) -> &'a [[T; N]];
}
impl<T> WindowsExt<T> for [T] {
type Windows<'a, const N: usize>
= WindowsIter<'a, T, N>
where
T: 'a;
fn array_windows_ext<'a, const N: usize>(&'a self) -> Self::Windows<'a, N> {
WindowsIter { slice: self }
}
fn array_chunks_ext<'a, const N: usize>(&'a self) -> &'a [[T; N]] {
let len = self.len() / N;
unsafe { std::slice::from_raw_parts(self.as_ptr() as _, len) }
}
}
pub struct WindowsIter<'a, T, const N: usize> {
slice: &'a [T],
}
impl<'a, T, const N: usize> Iterator for WindowsIter<'a, T, N> {
type Item = &'a [T; N];
fn next(&mut self) -> Option<Self::Item> {
if self.slice.len() < N {
return None;
}
let res = unsafe { self.slice.as_ptr().cast::<[T; N]>().deref() };
if N > 0 {
self.slice = &self.slice[1..];
}
Some(res)
}
}

5
utils/src/xrd.rs Normal file
View file

@ -0,0 +1,5 @@
pub const XRD: &str = "XDG_RUNTIME_DIR";
pub fn xrd() -> Option<String> {
std::env::var(XRD).ok()
}