cl-arena: Allow the arena to hold its own lifetime.
cl-structures: Stick some arenas inside the interners, rather than taking a reference.
This commit is contained in:
parent
0d937728ed
commit
d7ce33e457
@ -87,22 +87,24 @@ pub mod typed_arena {
|
|||||||
|
|
||||||
/// A [TypedArena] can hold many instances of a single type, and will properly [Drop] them when
|
/// A [TypedArena] can hold many instances of a single type, and will properly [Drop] them when
|
||||||
/// it falls out of scope.
|
/// it falls out of scope.
|
||||||
pub struct TypedArena<T> {
|
pub struct TypedArena<'arena, T> {
|
||||||
|
_lives: PhantomData<&'arena T>,
|
||||||
_drops: PhantomData<T>,
|
_drops: PhantomData<T>,
|
||||||
chunks: RefCell<Vec<ArenaChunk<T>>>,
|
chunks: RefCell<Vec<ArenaChunk<T>>>,
|
||||||
head: Cell<*mut T>,
|
head: Cell<*mut T>,
|
||||||
tail: Cell<*mut T>,
|
tail: Cell<*mut T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for TypedArena<T> {
|
impl<'arena, T> Default for TypedArena<'arena, T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> TypedArena<T> {
|
impl<'arena, T> TypedArena<'arena, T> {
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
_lives: PhantomData,
|
||||||
_drops: PhantomData,
|
_drops: PhantomData,
|
||||||
chunks: RefCell::new(Vec::new()),
|
chunks: RefCell::new(Vec::new()),
|
||||||
head: Cell::new(ptr::null_mut()),
|
head: Cell::new(ptr::null_mut()),
|
||||||
@ -111,7 +113,7 @@ pub mod typed_arena {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::mut_from_ref)]
|
#[allow(clippy::mut_from_ref)]
|
||||||
pub fn alloc(&self, value: T) -> &mut T {
|
pub fn alloc(&'arena self, value: T) -> &'arena mut T {
|
||||||
if self.head == self.tail {
|
if self.head == self.tail {
|
||||||
self.grow(1);
|
self.grow(1);
|
||||||
}
|
}
|
||||||
@ -165,9 +167,9 @@ pub mod typed_arena {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: Send> Send for TypedArena<T> {}
|
unsafe impl<'arena, T: Send> Send for TypedArena<'arena, T> {}
|
||||||
|
|
||||||
unsafe impl<#[may_dangle] T> Drop for TypedArena<T> {
|
unsafe impl<'arena, #[may_dangle] T> Drop for TypedArena<'arena, T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let mut chunks = self.chunks.borrow_mut();
|
let mut chunks = self.chunks.borrow_mut();
|
||||||
|
|
||||||
@ -194,24 +196,27 @@ pub mod dropless_arena {
|
|||||||
use core::{
|
use core::{
|
||||||
alloc::Layout,
|
alloc::Layout,
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
|
marker::PhantomData,
|
||||||
mem, ptr, slice,
|
mem, ptr, slice,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct DroplessArena {
|
pub struct DroplessArena<'arena> {
|
||||||
|
_lives: PhantomData<&'arena u8>,
|
||||||
chunks: RefCell<Vec<ArenaChunk<u8>>>,
|
chunks: RefCell<Vec<ArenaChunk<u8>>>,
|
||||||
head: Cell<*mut u8>,
|
head: Cell<*mut u8>,
|
||||||
tail: Cell<*mut u8>,
|
tail: Cell<*mut u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for DroplessArena {
|
impl Default for DroplessArena<'_> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DroplessArena {
|
impl<'arena> DroplessArena<'arena> {
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
_lives: PhantomData,
|
||||||
chunks: RefCell::new(Vec::new()),
|
chunks: RefCell::new(Vec::new()),
|
||||||
head: Cell::new(ptr::null_mut()),
|
head: Cell::new(ptr::null_mut()),
|
||||||
tail: Cell::new(ptr::null_mut()),
|
tail: Cell::new(ptr::null_mut()),
|
||||||
@ -224,7 +229,7 @@ pub mod dropless_arena {
|
|||||||
/// - Panics if T implements [Drop]
|
/// - Panics if T implements [Drop]
|
||||||
/// - Panics if T is zero-sized
|
/// - Panics if T is zero-sized
|
||||||
#[allow(clippy::mut_from_ref)]
|
#[allow(clippy::mut_from_ref)]
|
||||||
pub fn alloc<T>(&self, value: T) -> &mut T {
|
pub fn alloc<T>(&'arena self, value: T) -> &'arena mut T {
|
||||||
assert!(!mem::needs_drop::<T>());
|
assert!(!mem::needs_drop::<T>());
|
||||||
assert!(mem::size_of::<T>() != 0);
|
assert!(mem::size_of::<T>() != 0);
|
||||||
|
|
||||||
@ -244,7 +249,7 @@ pub mod dropless_arena {
|
|||||||
/// - Panics if T is zero-sized
|
/// - Panics if T is zero-sized
|
||||||
/// - Panics if the slice is empty
|
/// - Panics if the slice is empty
|
||||||
#[allow(clippy::mut_from_ref)]
|
#[allow(clippy::mut_from_ref)]
|
||||||
pub fn alloc_slice<T: Copy>(&self, slice: &[T]) -> &mut [T] {
|
pub fn alloc_slice<T: Copy>(&'arena self, slice: &[T]) -> &'arena mut [T] {
|
||||||
assert!(!mem::needs_drop::<T>());
|
assert!(!mem::needs_drop::<T>());
|
||||||
assert!(mem::size_of::<T>() != 0);
|
assert!(mem::size_of::<T>() != 0);
|
||||||
assert!(!slice.is_empty());
|
assert!(!slice.is_empty());
|
||||||
@ -261,7 +266,7 @@ pub mod dropless_arena {
|
|||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// Panics if the string is empty.
|
/// Panics if the string is empty.
|
||||||
pub fn alloc_str(&self, string: &str) -> &str {
|
pub fn alloc_str(&'arena self, string: &str) -> &'arena str {
|
||||||
let slice = self.alloc_slice(string.as_bytes());
|
let slice = self.alloc_slice(string.as_bytes());
|
||||||
|
|
||||||
// Safety: This is a clone of the input string, which was valid
|
// Safety: This is a clone of the input string, which was valid
|
||||||
@ -272,7 +277,7 @@ pub mod dropless_arena {
|
|||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// Panics if the provided [Layout] has size 0
|
/// Panics if the provided [Layout] has size 0
|
||||||
pub fn alloc_raw(&self, layout: Layout) -> *mut u8 {
|
pub fn alloc_raw(&'arena self, layout: Layout) -> *mut u8 {
|
||||||
/// Rounds the given size (or pointer value) *up* to the given alignment
|
/// Rounds the given size (or pointer value) *up* to the given alignment
|
||||||
fn align_up(size: usize, align: usize) -> usize {
|
fn align_up(size: usize, align: usize) -> usize {
|
||||||
(size + align - 1) & !(align - 1)
|
(size + align - 1) & !(align - 1)
|
||||||
@ -337,7 +342,7 @@ pub mod dropless_arena {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for DroplessArena {}
|
unsafe impl<'arena> Send for DroplessArena<'arena> {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
@ -115,13 +115,12 @@ pub mod string_interner {
|
|||||||
use cl_arena::dropless_arena::DroplessArena;
|
use cl_arena::dropless_arena::DroplessArena;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
ptr::addr_of,
|
|
||||||
sync::{OnceLock, RwLock},
|
sync::{OnceLock, RwLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A string interner hands out [Interned] copies of each unique string given to it.
|
/// A string interner hands out [Interned] copies of each unique string given to it.
|
||||||
pub struct StringInterner<'a> {
|
pub struct StringInterner<'a> {
|
||||||
arena: &'a DroplessArena,
|
arena: DroplessArena<'a>,
|
||||||
keys: RwLock<HashSet<&'a str>>,
|
keys: RwLock<HashSet<&'a str>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,12 +128,11 @@ pub mod string_interner {
|
|||||||
/// Gets a reference to a global string interner whose [Interned] strings are `'static`
|
/// Gets a reference to a global string interner whose [Interned] strings are `'static`
|
||||||
pub fn global() -> &'static Self {
|
pub fn global() -> &'static Self {
|
||||||
static GLOBAL_INTERNER: OnceLock<StringInterner<'static>> = OnceLock::new();
|
static GLOBAL_INTERNER: OnceLock<StringInterner<'static>> = OnceLock::new();
|
||||||
static mut ARENA: DroplessArena = DroplessArena::new();
|
|
||||||
|
|
||||||
// SAFETY: The RwLock within the interner's `keys` protects the arena
|
// SAFETY: The RwLock within the interner's `keys` protects the arena
|
||||||
// from being modified concurrently.
|
// from being modified concurrently.
|
||||||
GLOBAL_INTERNER.get_or_init(|| StringInterner {
|
GLOBAL_INTERNER.get_or_init(|| StringInterner {
|
||||||
arena: unsafe { &*addr_of!(ARENA) },
|
arena: DroplessArena::new(),
|
||||||
keys: Default::default(),
|
keys: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -142,7 +140,7 @@ pub mod string_interner {
|
|||||||
|
|
||||||
impl<'a> StringInterner<'a> {
|
impl<'a> StringInterner<'a> {
|
||||||
/// Creates a new [StringInterner] backed by the provided [DroplessArena]
|
/// Creates a new [StringInterner] backed by the provided [DroplessArena]
|
||||||
pub fn new(arena: &'a DroplessArena) -> Self {
|
pub fn new(arena: DroplessArena<'a>) -> Self {
|
||||||
Self { arena, keys: RwLock::new(HashSet::new()) }
|
Self { arena, keys: RwLock::new(HashSet::new()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +149,7 @@ pub mod string_interner {
|
|||||||
///
|
///
|
||||||
/// # Blocks
|
/// # Blocks
|
||||||
/// This function blocks when the interner is held by another thread.
|
/// This function blocks when the interner is held by another thread.
|
||||||
pub fn get_or_insert(&self, value: &str) -> Interned<'a, str> {
|
pub fn get_or_insert(&'a self, value: &str) -> Interned<'a, str> {
|
||||||
let Self { arena, keys } = self;
|
let Self { arena, keys } = self;
|
||||||
|
|
||||||
// Safety: Holding this write guard for the entire duration of this
|
// Safety: Holding this write guard for the entire duration of this
|
||||||
@ -173,7 +171,7 @@ pub mod string_interner {
|
|||||||
/// Gets a reference to the interned copy of the given value, if it exists
|
/// Gets a reference to the interned copy of the given value, if it exists
|
||||||
/// # Blocks
|
/// # Blocks
|
||||||
/// This function blocks when the interner is held by another thread.
|
/// This function blocks when the interner is held by another thread.
|
||||||
pub fn get(&self, value: &str) -> Option<Interned<'a, str>> {
|
pub fn get(&'a self, value: &str) -> Option<Interned<'a, str>> {
|
||||||
let keys = self.keys.read().expect("should not be poisoned");
|
let keys = self.keys.read().expect("should not be poisoned");
|
||||||
keys.get(value).copied().map(Interned::new)
|
keys.get(value).copied().map(Interned::new)
|
||||||
}
|
}
|
||||||
@ -243,13 +241,13 @@ pub mod typed_interner {
|
|||||||
///
|
///
|
||||||
/// See the [module-level documentation](self) for more information.
|
/// See the [module-level documentation](self) for more information.
|
||||||
pub struct TypedInterner<'a, T: Eq + Hash> {
|
pub struct TypedInterner<'a, T: Eq + Hash> {
|
||||||
arena: &'a TypedArena<T>,
|
arena: TypedArena<'a, T>,
|
||||||
keys: RwLock<HashSet<&'a T>>,
|
keys: RwLock<HashSet<&'a T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Eq + Hash> TypedInterner<'a, T> {
|
impl<'a, T: Eq + Hash> TypedInterner<'a, T> {
|
||||||
/// Creates a new [TypedInterner] backed by the provided [TypedArena]
|
/// Creates a new [TypedInterner] backed by the provided [TypedArena]
|
||||||
pub fn new(arena: &'a TypedArena<T>) -> Self {
|
pub fn new(arena: TypedArena<'a, T>) -> Self {
|
||||||
Self { arena, keys: RwLock::new(HashSet::new()) }
|
Self { arena, keys: RwLock::new(HashSet::new()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +255,7 @@ pub mod typed_interner {
|
|||||||
///
|
///
|
||||||
/// # Blocks
|
/// # Blocks
|
||||||
/// This function blocks when the interner is held by another thread.
|
/// This function blocks when the interner is held by another thread.
|
||||||
pub fn get_or_insert(&self, value: T) -> Interned<'a, T> {
|
pub fn get_or_insert(&'a self, value: T) -> Interned<'a, T> {
|
||||||
let Self { arena, keys } = self;
|
let Self { arena, keys } = self;
|
||||||
|
|
||||||
// Safety: Locking the keyset for the entire duration of this function
|
// Safety: Locking the keyset for the entire duration of this function
|
||||||
|
Loading…
Reference in New Issue
Block a user