refactor: split cargo workspace
This commit is contained in:
parent
5db14936e7
commit
1c21bd1259
695 changed files with 32023 additions and 44964 deletions
26
crates/utils/Cargo.toml
Normal file
26
crates/utils/Cargo.toml
Normal 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
crates/utils/src/array.rs
Normal file
11
crates/utils/src/array.rs
Normal 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
|
||||
})
|
||||
}
|
||||
28
crates/utils/src/array_to_tuple.rs
Normal file
28
crates/utils/src/array_to_tuple.rs
Normal 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
crates/utils/src/asyncevent.rs
Normal file
60
crates/utils/src/asyncevent.rs
Normal 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
crates/utils/src/atomic_enum.rs
Normal file
42
crates/utils/src/atomic_enum.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
219
crates/utils/src/binary_search_map.rs
Normal file
219
crates/utils/src/binary_search_map.rs
Normal 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
crates/utils/src/bitfield.rs
Normal file
37
crates/utils/src/bitfield.rs
Normal 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
crates/utils/src/bitflags.rs
Normal file
32
crates/utils/src/bitflags.rs
Normal 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
crates/utils/src/buf.rs
Normal file
328
crates/utils/src/buf.rs
Normal 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
crates/utils/src/cell_ext.rs
Normal file
16
crates/utils/src/cell_ext.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
98
crates/utils/src/clonecell.rs
Normal file
98
crates/utils/src/clonecell.rs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
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 {}
|
||||
|
||||
unsafe impl<T> UnsafeCellCloneSafe for crate::linkedlist::NodeRef<T> {}
|
||||
11
crates/utils/src/compat.rs
Normal file
11
crates/utils/src/compat.rs
Normal 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
crates/utils/src/copyhashmap.rs
Normal file
126
crates/utils/src/copyhashmap.rs
Normal 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
|
||||
}
|
||||
}
|
||||
36
crates/utils/src/double_buffered.rs
Normal file
36
crates/utils/src/double_buffered.rs
Normal 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
crates/utils/src/errorfmt.rs
Normal file
23
crates/utils/src/errorfmt.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
153
crates/utils/src/event_listener.rs
Normal file
153
crates/utils/src/event_listener.rs
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
use {
|
||||
crate::{
|
||||
linkedlist::{LinkedList, LinkedListIter, LinkedNode},
|
||||
queue::AsyncQueue,
|
||||
},
|
||||
std::{
|
||||
cell::Cell,
|
||||
ops::Deref,
|
||||
rc::{Rc, Weak},
|
||||
},
|
||||
};
|
||||
|
||||
pub async fn handle_lazy_event_sources_of(sources: &LazyEventSources) {
|
||||
loop {
|
||||
let source = sources.queue.pop().await;
|
||||
source.queued.set(false);
|
||||
for listener in source.listeners.iter() {
|
||||
listener.triggered();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventSource<T: ?Sized> {
|
||||
listeners: LinkedList<Weak<T>>,
|
||||
on_attach: Cell<Option<Box<dyn FnOnce()>>>,
|
||||
}
|
||||
|
||||
pub struct EventListener<T: ?Sized> {
|
||||
link: LinkedNode<Weak<T>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LazyEventSources {
|
||||
queue: AsyncQueue<Rc<LazyEventSource>>,
|
||||
}
|
||||
|
||||
pub trait LazyEventSourceListener {
|
||||
fn triggered(self: Rc<Self>);
|
||||
}
|
||||
|
||||
pub struct LazyEventSource {
|
||||
sources: Rc<LazyEventSources>,
|
||||
queued: Cell<bool>,
|
||||
listeners: EventSource<dyn LazyEventSourceListener>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Default for EventSource<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
listeners: Default::default(),
|
||||
on_attach: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> EventSource<T> {
|
||||
pub fn clear(&self) {
|
||||
self.on_attach.take();
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> EventSourceIter<T> {
|
||||
EventSourceIter {
|
||||
iter: self.listeners.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_listeners(&self) -> bool {
|
||||
self.listeners.is_not_empty()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.listeners.is_empty()
|
||||
}
|
||||
|
||||
pub fn on_attach(&self, f: Box<dyn FnOnce()>) {
|
||||
self.on_attach.set(Some(f));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventSourceIter<T: ?Sized> {
|
||||
iter: LinkedListIter<Weak<T>>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Iterator for EventSourceIter<T> {
|
||||
type Item = Rc<T>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
for weak in self.iter.by_ref() {
|
||||
if let Some(t) = weak.upgrade() {
|
||||
return Some(t);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> EventListener<T> {
|
||||
pub fn new(t: Weak<T>) -> Self {
|
||||
Self {
|
||||
link: LinkedNode::detached(t),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attach(&self, source: &EventSource<T>) {
|
||||
source.listeners.add_last_existing(&self.link);
|
||||
if let Some(on_attach) = source.on_attach.take() {
|
||||
on_attach();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detach(&self) {
|
||||
self.link.detach();
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Option<Rc<T>> {
|
||||
self.link.upgrade()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for LazyEventSource {
|
||||
type Target = EventSource<dyn LazyEventSourceListener>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.listeners
|
||||
}
|
||||
}
|
||||
|
||||
impl LazyEventSource {
|
||||
pub fn trigger(self: &Rc<Self>) {
|
||||
if self.listeners.is_empty() {
|
||||
return;
|
||||
}
|
||||
if self.queued.replace(true) {
|
||||
return;
|
||||
}
|
||||
self.sources.queue.push(self.clone());
|
||||
}
|
||||
}
|
||||
|
||||
impl LazyEventSources {
|
||||
#[allow(dead_code)]
|
||||
pub fn create_source(self: &Rc<Self>) -> Rc<LazyEventSource> {
|
||||
Rc::new(LazyEventSource {
|
||||
sources: self.clone(),
|
||||
queued: Default::default(),
|
||||
listeners: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.queue.clear();
|
||||
}
|
||||
}
|
||||
52
crates/utils/src/fdcloser.rs
Normal file
52
crates/utils/src/fdcloser.rs
Normal 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
crates/utils/src/free_list.rs
Normal file
93
crates/utils/src/free_list.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
40
crates/utils/src/free_list/tests.rs
Normal file
40
crates/utils/src/free_list/tests.rs
Normal 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);
|
||||
}
|
||||
37
crates/utils/src/geometric_decay.rs
Normal file
37
crates/utils/src/geometric_decay.rs
Normal 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
crates/utils/src/hash_map_ext.rs
Normal file
15
crates/utils/src/hash_map_ext.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
58
crates/utils/src/lib.rs
Normal file
58
crates/utils/src/lib.rs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
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 event_listener;
|
||||
pub mod fdcloser;
|
||||
pub mod free_list;
|
||||
pub mod geometric_decay;
|
||||
pub mod hash_map_ext;
|
||||
pub mod linkedlist;
|
||||
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;
|
||||
414
crates/utils/src/linkedlist.rs
Normal file
414
crates/utils/src/linkedlist.rs
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
use {
|
||||
crate::numcell::NumCell,
|
||||
std::{
|
||||
cell::Cell,
|
||||
fmt::{Debug, Formatter},
|
||||
mem,
|
||||
ops::Deref,
|
||||
ptr::NonNull,
|
||||
},
|
||||
};
|
||||
|
||||
const LINKED_NODE_REF_COUNT: usize = !(!0 >> 1);
|
||||
|
||||
pub struct LinkedList<T> {
|
||||
root: LinkedNode<T>,
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for LinkedList<T> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_list().entries(self.iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for LinkedList<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> LinkedList<T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
root: LinkedNode::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append_all(&self, other: &LinkedList<T>) {
|
||||
if other.is_empty() || self.root.data == other.root.data {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
let o_root = other.root.data;
|
||||
let o_first = o_root.as_ref().next.get();
|
||||
let o_last = o_root.as_ref().prev.get();
|
||||
let s_first = self.root.data;
|
||||
let s_last = s_first.as_ref().prev.get();
|
||||
o_first.as_ref().prev.set(s_last);
|
||||
s_last.as_ref().next.set(o_first);
|
||||
o_last.as_ref().next.set(s_first);
|
||||
s_first.as_ref().prev.set(o_last);
|
||||
o_root.as_ref().next.set(o_root);
|
||||
o_root.as_ref().prev.set(o_root);
|
||||
}
|
||||
}
|
||||
|
||||
fn endpoint(&self, ep: NonNull<NodeData<T>>) -> Option<NodeRef<T>> {
|
||||
unsafe {
|
||||
if ep != self.root.data {
|
||||
ep.as_ref().rc.fetch_add(1);
|
||||
Some(NodeRef { data: ep })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.last().is_none()
|
||||
}
|
||||
|
||||
pub fn is_not_empty(&self) -> bool {
|
||||
!self.is_empty()
|
||||
}
|
||||
|
||||
pub fn last(&self) -> Option<NodeRef<T>> {
|
||||
unsafe { self.endpoint(self.root.data.as_ref().prev.get()) }
|
||||
}
|
||||
|
||||
pub fn first(&self) -> Option<NodeRef<T>> {
|
||||
unsafe { self.endpoint(self.root.data.as_ref().next.get()) }
|
||||
}
|
||||
|
||||
pub fn add_last(&self, t: T) -> LinkedNode<T> {
|
||||
self.root.prepend(t)
|
||||
}
|
||||
|
||||
pub fn add_first(&self, t: T) -> LinkedNode<T> {
|
||||
self.root.append(t)
|
||||
}
|
||||
|
||||
pub fn add_last_existing(&self, t: &NodeRef<T>) {
|
||||
self.root.prepend_existing(t)
|
||||
}
|
||||
|
||||
pub fn add_first_existing(&self, t: &NodeRef<T>) {
|
||||
self.root.append_existing(t)
|
||||
}
|
||||
|
||||
pub fn rotate_last(&self, t: &NodeRef<T>) {
|
||||
unsafe {
|
||||
let root = self.root.data.as_ref();
|
||||
root.prev.get().as_ref().next.set(root.next.get());
|
||||
root.next.get().as_ref().prev.set(root.prev.get());
|
||||
root.prev.set(t.data);
|
||||
root.next.set(t.data.as_ref().next.get());
|
||||
t.data.as_ref().next.get().as_ref().prev.set(self.root.data);
|
||||
t.data.as_ref().next.set(self.root.data);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> LinkedListIter<T> {
|
||||
unsafe {
|
||||
let root = self.root.data.as_ref();
|
||||
root.rc.fetch_add(1);
|
||||
root.next.get().as_ref().rc.fetch_add(1);
|
||||
LinkedListIter {
|
||||
root: self.root.data,
|
||||
next: root.next.get(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rev_iter(&self) -> RevLinkedListIter<T> {
|
||||
unsafe {
|
||||
let root = self.root.data.as_ref();
|
||||
root.rc.fetch_add(1);
|
||||
root.prev.get().as_ref().rc.fetch_add(1);
|
||||
RevLinkedListIter {
|
||||
root: self.root.data,
|
||||
next: root.prev.get(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LinkedListIter<T> {
|
||||
root: NonNull<NodeData<T>>,
|
||||
next: NonNull<NodeData<T>>,
|
||||
}
|
||||
|
||||
impl<T> Iterator for LinkedListIter<T> {
|
||||
type Item = NodeRef<T>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.root == self.next {
|
||||
return None;
|
||||
}
|
||||
unsafe {
|
||||
let old_next = self.next;
|
||||
self.next = old_next.as_ref().next.get();
|
||||
self.next.as_ref().rc.fetch_add(1);
|
||||
Some(NodeRef { data: old_next })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for LinkedListIter<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
dec_ref_count(self.root, 1);
|
||||
dec_ref_count(self.next, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RevLinkedListIter<T> {
|
||||
root: NonNull<NodeData<T>>,
|
||||
next: NonNull<NodeData<T>>,
|
||||
}
|
||||
|
||||
impl<T> Iterator for RevLinkedListIter<T> {
|
||||
type Item = NodeRef<T>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.root == self.next {
|
||||
return None;
|
||||
}
|
||||
unsafe {
|
||||
let old_next = self.next;
|
||||
self.next = old_next.as_ref().prev.get();
|
||||
self.next.as_ref().rc.fetch_add(1);
|
||||
Some(NodeRef { data: old_next })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for RevLinkedListIter<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
dec_ref_count(self.root, 1);
|
||||
dec_ref_count(self.next, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[must_use]
|
||||
pub struct LinkedNode<T> {
|
||||
data: NonNull<NodeData<T>>,
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for LinkedNode<T> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
unsafe { self.data.as_ref().data.as_ref().unwrap_unchecked().fmt(f) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for LinkedNode<T> {
|
||||
type Target = NodeRef<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { mem::transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct NodeRef<T> {
|
||||
data: NonNull<NodeData<T>>,
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for NodeRef<T> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
unsafe { self.data.as_ref().data.as_ref().unwrap_unchecked().fmt(f) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for NodeRef<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { self.data.as_ref().data.as_ref().unwrap_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for NodeRef<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
dec_ref_count(self.data, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for NodeRef<T> {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
self.data.as_ref().rc.fetch_add(1);
|
||||
Self { data: self.data }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> NodeRef<T> {
|
||||
pub fn prepend(&self, t: T) -> LinkedNode<T> {
|
||||
unsafe { prepend(self.data, t) }
|
||||
}
|
||||
|
||||
pub fn append(&self, t: T) -> LinkedNode<T> {
|
||||
unsafe { append(self.data, t) }
|
||||
}
|
||||
|
||||
pub fn prepend_existing(&self, t: &NodeRef<T>) {
|
||||
unsafe { prepend_existing(self.data, t) }
|
||||
}
|
||||
|
||||
pub fn append_existing(&self, t: &NodeRef<T>) {
|
||||
unsafe { append_existing(self.data, t) }
|
||||
}
|
||||
|
||||
fn peer<F>(&self, peer: F) -> Option<NodeRef<T>>
|
||||
where
|
||||
F: FnOnce(&NodeData<T>) -> &Cell<NonNull<NodeData<T>>>,
|
||||
{
|
||||
unsafe {
|
||||
let data = self.data.as_ref();
|
||||
let other = peer(data).get();
|
||||
if other.as_ref().data.is_some() {
|
||||
other.as_ref().rc.fetch_add(1);
|
||||
Some(NodeRef { data: other })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prev(&self) -> Option<NodeRef<T>> {
|
||||
self.peer(|d| &d.prev)
|
||||
}
|
||||
|
||||
pub fn next(&self) -> Option<NodeRef<T>> {
|
||||
self.peer(|d| &d.next)
|
||||
}
|
||||
|
||||
pub fn detach(&self) {
|
||||
unsafe {
|
||||
let data = self.data.as_ref();
|
||||
data.prev.get().as_ref().next.set(data.next.get());
|
||||
data.next.get().as_ref().prev.set(data.prev.get());
|
||||
data.prev.set(self.data);
|
||||
data.next.set(self.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NodeData<T> {
|
||||
rc: NumCell<usize>,
|
||||
prev: Cell<NonNull<NodeData<T>>>,
|
||||
next: Cell<NonNull<NodeData<T>>>,
|
||||
data: Option<T>,
|
||||
}
|
||||
|
||||
unsafe fn dec_ref_count<T>(slf: NonNull<NodeData<T>>, n: usize) {
|
||||
unsafe {
|
||||
if slf.as_ref().rc.fetch_sub(n) == n {
|
||||
drop(Box::from_raw(slf.as_ptr()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for LinkedNode<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.detach();
|
||||
dec_ref_count(self.data, LINKED_NODE_REF_COUNT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> LinkedNode<T> {
|
||||
fn new(t: Option<T>) -> Self {
|
||||
let node = Box::leak(Box::new(NodeData {
|
||||
rc: NumCell::new(LINKED_NODE_REF_COUNT),
|
||||
prev: Cell::new(NonNull::dangling()),
|
||||
next: Cell::new(NonNull::dangling()),
|
||||
data: t,
|
||||
}));
|
||||
let ptr = NonNull::from(&mut *node);
|
||||
node.prev.set(ptr);
|
||||
node.next.set(ptr);
|
||||
LinkedNode { data: node.into() }
|
||||
}
|
||||
|
||||
pub fn detached(t: T) -> Self {
|
||||
Self::new(Some(t))
|
||||
}
|
||||
|
||||
pub fn to_ref(&self) -> NodeRef<T> {
|
||||
unsafe {
|
||||
self.data.as_ref().rc.fetch_add(1);
|
||||
NodeRef { data: self.data }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn prepend_existing<T>(data: NonNull<NodeData<T>>, t: &NodeRef<T>) {
|
||||
unsafe {
|
||||
let dref = data.as_ref();
|
||||
let tref = t.data.as_ref();
|
||||
if tref.rc.get() < LINKED_NODE_REF_COUNT {
|
||||
log::error!("Trying to prepend a node whose linked node has already been dropped");
|
||||
return;
|
||||
}
|
||||
t.detach();
|
||||
tref.prev.set(dref.prev.get());
|
||||
tref.next.set(data);
|
||||
dref.prev.get().as_ref().next.set(t.data);
|
||||
dref.prev.set(t.data);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn prepend<T>(data: NonNull<NodeData<T>>, t: T) -> LinkedNode<T> {
|
||||
unsafe {
|
||||
let dref = data.as_ref();
|
||||
let node = NonNull::new_unchecked(Box::into_raw(Box::new(NodeData {
|
||||
rc: NumCell::new(LINKED_NODE_REF_COUNT),
|
||||
prev: Cell::new(dref.prev.get()),
|
||||
next: Cell::new(data),
|
||||
data: Some(t),
|
||||
})));
|
||||
dref.prev.get().as_ref().next.set(node);
|
||||
dref.prev.set(node);
|
||||
LinkedNode { data: node }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn append_existing<T>(data: NonNull<NodeData<T>>, t: &NodeRef<T>) {
|
||||
unsafe {
|
||||
let dref = data.as_ref();
|
||||
let tref = t.data.as_ref();
|
||||
if tref.rc.get() < LINKED_NODE_REF_COUNT {
|
||||
log::error!("Trying to append a node whose linked node has already been dropped");
|
||||
return;
|
||||
}
|
||||
t.detach();
|
||||
tref.prev.set(data);
|
||||
tref.next.set(dref.next.get());
|
||||
dref.next.get().as_ref().prev.set(t.data);
|
||||
dref.next.set(t.data);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn append<T>(data: NonNull<NodeData<T>>, t: T) -> LinkedNode<T> {
|
||||
unsafe {
|
||||
let dref = data.as_ref();
|
||||
let node = NonNull::new_unchecked(Box::into_raw(Box::new(NodeData {
|
||||
rc: NumCell::new(LINKED_NODE_REF_COUNT),
|
||||
prev: Cell::new(data),
|
||||
next: Cell::new(dref.next.get()),
|
||||
data: Some(t),
|
||||
})));
|
||||
dref.next.get().as_ref().prev.set(node);
|
||||
dref.next.set(node);
|
||||
LinkedNode { data: node }
|
||||
}
|
||||
}
|
||||
7
crates/utils/src/log_on_drop.rs
Normal file
7
crates/utils/src/log_on_drop.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pub struct LogOnDrop(pub &'static str);
|
||||
|
||||
impl Drop for LogOnDrop {
|
||||
fn drop(&mut self) {
|
||||
log::info!("{}", self.0);
|
||||
}
|
||||
}
|
||||
34
crates/utils/src/mmap.rs
Normal file
34
crates/utils/src/mmap.rs
Normal 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
crates/utils/src/nice.rs
Normal file
28
crates/utils/src/nice.rs
Normal 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, ¶m) };
|
||||
if res == 0 {
|
||||
DID_ELEVATE_SCHEDULER.store(true, Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn did_elevate_scheduler() -> bool {
|
||||
DID_ELEVATE_SCHEDULER.load(Relaxed)
|
||||
}
|
||||
16
crates/utils/src/nonblock.rs
Normal file
16
crates/utils/src/nonblock.rs
Normal 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
crates/utils/src/num_cpus.rs
Normal file
20
crates/utils/src/num_cpus.rs
Normal 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
crates/utils/src/numcell.rs
Normal file
129
crates/utils/src/numcell.rs
Normal 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
crates/utils/src/on_change.rs
Normal file
44
crates/utils/src/on_change.rs
Normal 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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
18
crates/utils/src/on_drop_event.rs
Normal file
18
crates/utils/src/on_drop_event.rs
Normal 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
crates/utils/src/once.rs
Normal file
26
crates/utils/src/once.rs
Normal 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
crates/utils/src/opaque.rs
Normal file
109
crates/utils/src/opaque.rs
Normal 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
crates/utils/src/opaque/tests.rs
Normal file
12
crates/utils/src/opaque/tests.rs
Normal 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
crates/utils/src/opaque_cell.rs
Normal file
28
crates/utils/src/opaque_cell.rs
Normal 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
crates/utils/src/opt.rs
Normal file
27
crates/utils/src/opt.rs
Normal 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
crates/utils/src/option_ext.rs
Normal file
9
crates/utils/src/option_ext.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
74
crates/utils/src/ordered_float.rs
Normal file
74
crates/utils/src/ordered_float.rs
Normal 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
crates/utils/src/oserror.rs
Normal file
242
crates/utils/src/oserror.rs
Normal 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
crates/utils/src/page_size.rs
Normal file
8
crates/utils/src/page_size.rs
Normal 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
crates/utils/src/pid_info.rs
Normal file
59
crates/utils/src/pid_info.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
23
crates/utils/src/pidfd_send_signal.rs
Normal file
23
crates/utils/src/pidfd_send_signal.rs
Normal 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
crates/utils/src/pipe.rs
Normal file
32
crates/utils/src/pipe.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
8
crates/utils/src/process_name.rs
Normal file
8
crates/utils/src/process_name.rs
Normal 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
crates/utils/src/ptr_ext.rs
Normal file
28
crates/utils/src/ptr_ext.rs
Normal 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
crates/utils/src/queue.rs
Normal file
107
crates/utils/src/queue.rs
Normal 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
crates/utils/src/rc_eq.rs
Normal file
37
crates/utils/src/rc_eq.rs
Normal 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
crates/utils/src/refcounted.rs
Normal file
90
crates/utils/src/refcounted.rs
Normal 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
crates/utils/src/smallmap.rs
Normal file
319
crates/utils/src/smallmap.rs
Normal 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
crates/utils/src/stack.rs
Normal file
49
crates/utils/src/stack.rs
Normal 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
crates/utils/src/static_text.rs
Normal file
3
crates/utils/src/static_text.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub trait StaticText {
|
||||
fn text(&self) -> &'static str;
|
||||
}
|
||||
11
crates/utils/src/string_ext.rs
Normal file
11
crates/utils/src/string_ext.rs
Normal 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
crates/utils/src/syncqueue.rs
Normal file
68
crates/utils/src/syncqueue.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
24
crates/utils/src/threshold_counter.rs
Normal file
24
crates/utils/src/threshold_counter.rs
Normal 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
crates/utils/src/tri.rs
Normal file
51
crates/utils/src/tri.rs
Normal 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) }
|
||||
}
|
||||
}
|
||||
7
crates/utils/src/unlink_on_drop.rs
Normal file
7
crates/utils/src/unlink_on_drop.rs
Normal 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
crates/utils/src/vec_ext.rs
Normal file
40
crates/utils/src/vec_ext.rs
Normal 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
crates/utils/src/vecdeque_ext.rs
Normal file
35
crates/utils/src/vecdeque_ext.rs
Normal 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
crates/utils/src/vecset.rs
Normal file
58
crates/utils/src/vecset.rs
Normal 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
crates/utils/src/vecstorage.rs
Normal file
105
crates/utils/src/vecstorage.rs
Normal 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
crates/utils/src/windows.rs
Normal file
46
crates/utils/src/windows.rs
Normal 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
crates/utils/src/xrd.rs
Normal file
5
crates/utils/src/xrd.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
pub const XRD: &str = "XDG_RUNTIME_DIR";
|
||||
|
||||
pub fn xrd() -> Option<String> {
|
||||
std::env::var(XRD).ok()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue