use std::collections::BTreeMap;
use std::error::Error;
use std::fmt;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TraceContractVersion {
V1,
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
pub struct ManagedId(u64);
impl ManagedId {
pub const fn allocation_ordinal(self) -> u64 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
pub struct RootId(u64);
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct ManagedHandle {
id: ManagedId,
}
impl ManagedHandle {
pub const fn id(self) -> ManagedId {
self.id
}
pub const fn downgrade(self) -> WeakHandle {
WeakHandle { id: self.id }
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct RootedHandle {
root: RootId,
handle: ManagedHandle,
}
impl RootedHandle {
pub const fn root_id(self) -> RootId {
self.root
}
pub const fn handle(self) -> ManagedHandle {
self.handle
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct WeakHandle {
id: ManagedId,
}
impl WeakHandle {
pub const fn id(self) -> ManagedId {
self.id
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
pub struct EdgeId(pub u32);
pub trait EdgeVisitor {
fn strong(&mut self, edge: EdgeId, target: ManagedId);
fn weak(&mut self, edge: EdgeId, target: ManagedId);
fn ephemeron(&mut self, edge: EdgeId, key: ManagedId, value: ManagedId);
}
pub trait ManagedObject {
fn trace_edges(&self, visitor: &mut dyn EdgeVisitor);
fn clear_weak_edge(&mut self, edge: EdgeId, expected: ManagedId) -> bool;
fn clear_ephemeron_edge(
&mut self,
_edge: EdgeId,
_expected_key: ManagedId,
_expected_value: ManagedId,
) -> bool {
false
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct HardCappedRetainPolicy {
max_objects: usize,
}
impl HardCappedRetainPolicy {
pub fn new(max_objects: usize) -> Result<Self, ArenaError> {
if max_objects == 0 {
return Err(ArenaError::InvalidCap);
}
Ok(Self { max_objects })
}
pub const fn max_objects(self) -> usize {
self.max_objects
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ArenaError {
InvalidCap,
CapacityExceeded {
cap: usize,
},
IdentityExhausted,
StaleHandle(ManagedId),
StaleRoot(RootId),
ObjectRooted(ManagedId),
MutationEpochChanged {
expected: u64,
actual: u64,
},
}
impl fmt::Display for ArenaError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidCap => f.write_str("managed arena cap must be non-zero"),
Self::CapacityExceeded { cap } => write!(f, "managed arena hard cap {cap} reached"),
Self::IdentityExhausted => f.write_str("managed arena identity space exhausted"),
Self::StaleHandle(id) => write!(f, "stale managed handle {}", id.0),
Self::StaleRoot(id) => write!(f, "stale managed root {}", id.0),
Self::ObjectRooted(id) => write!(f, "managed object {} is rooted", id.0),
Self::MutationEpochChanged { expected, actual } => write!(
f,
"managed arena mutation epoch changed from {expected} to {actual}"
),
}
}
}
impl Error for ArenaError {}
pub struct TraceSnapshot<'a, T> {
roots: Vec<ManagedId>,
kept_alive: Vec<ManagedId>,
objects: &'a BTreeMap<ManagedId, T>,
mutation_epoch: u64,
}
impl<T: ManagedObject> TraceSnapshot<'_, T> {
pub const fn mutation_epoch(&self) -> u64 {
self.mutation_epoch
}
pub fn roots(&self) -> impl ExactSizeIterator<Item = ManagedId> + '_ {
self.roots.iter().copied()
}
pub fn kept_alive(&self) -> impl ExactSizeIterator<Item = ManagedId> + '_ {
self.kept_alive.iter().copied()
}
pub fn objects(&self) -> impl ExactSizeIterator<Item = ManagedId> + '_ {
self.objects.keys().copied()
}
pub fn visit_edges(
&self,
owner: ManagedId,
visitor: &mut dyn EdgeVisitor,
) -> Result<(), ArenaError> {
self.objects
.get(&owner)
.ok_or(ArenaError::StaleHandle(owner))?
.trace_edges(visitor);
Ok(())
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SafepointReceipt {
pub sequence: u64,
pub roots: Vec<ManagedId>,
pub objects: Vec<ManagedId>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TeardownReceipt {
pub objects: Vec<ManagedId>,
pub roots: Vec<RootId>,
}
pub struct CollectionMutationReceipt {
pub cleared_weak: Vec<(ManagedId, EdgeId)>,
pub cleared_ephemerons: Vec<(ManagedId, EdgeId)>,
pub swept: Vec<ManagedId>,
}
pub struct ManagedArena<T> {
policy: HardCappedRetainPolicy,
next_id: u64,
next_root: u64,
next_safepoint: u64,
mutation_epoch: u64,
objects: BTreeMap<ManagedId, T>,
roots: BTreeMap<RootId, ManagedId>,
kept_alive: BTreeMap<ManagedId, u64>,
}
impl<T> ManagedArena<T> {
pub fn new(policy: HardCappedRetainPolicy) -> Self {
Self {
policy,
next_id: 0,
next_root: 0,
next_safepoint: 0,
mutation_epoch: 0,
objects: BTreeMap::new(),
roots: BTreeMap::new(),
kept_alive: BTreeMap::new(),
}
}
pub const fn trace_contract_version(&self) -> TraceContractVersion {
TraceContractVersion::V1
}
pub fn len(&self) -> usize {
self.objects.len()
}
pub fn is_empty(&self) -> bool {
self.objects.is_empty()
}
pub const fn mutation_epoch(&self) -> u64 {
self.mutation_epoch
}
fn advance_mutation_epoch(&mut self) -> Result<(), ArenaError> {
self.mutation_epoch = self
.mutation_epoch
.checked_add(1)
.ok_or(ArenaError::IdentityExhausted)?;
Ok(())
}
pub fn allocate(&mut self, object: T) -> Result<ManagedHandle, ArenaError> {
if self.objects.len() >= self.policy.max_objects {
return Err(ArenaError::CapacityExceeded {
cap: self.policy.max_objects,
});
}
let next = self
.next_id
.checked_add(1)
.ok_or(ArenaError::IdentityExhausted)?;
let id = ManagedId(self.next_id);
self.advance_mutation_epoch()?;
self.objects.insert(id, object);
self.next_id = next;
Ok(ManagedHandle { id })
}
pub fn get(&self, handle: ManagedHandle) -> Result<&T, ArenaError> {
self.objects
.get(&handle.id)
.ok_or(ArenaError::StaleHandle(handle.id))
}
pub fn get_mut(&mut self, handle: ManagedHandle) -> Result<&mut T, ArenaError> {
if !self.objects.contains_key(&handle.id) {
return Err(ArenaError::StaleHandle(handle.id));
}
self.advance_mutation_epoch()?;
Ok(self
.objects
.get_mut(&handle.id)
.expect("validated managed id"))
}
pub fn upgrade(&mut self, weak: WeakHandle) -> Result<ManagedHandle, ArenaError> {
if !self.objects.contains_key(&weak.id) {
return Err(ArenaError::StaleHandle(weak.id));
}
self.kept_alive.insert(weak.id, self.mutation_epoch);
Ok(ManagedHandle { id: weak.id })
}
pub fn handle(&self, id: ManagedId) -> Result<ManagedHandle, ArenaError> {
self.objects
.contains_key(&id)
.then_some(ManagedHandle { id })
.ok_or(ArenaError::StaleHandle(id))
}
pub fn root(&mut self, handle: ManagedHandle) -> Result<RootedHandle, ArenaError> {
self.get(handle)?;
let next = self
.next_root
.checked_add(1)
.ok_or(ArenaError::IdentityExhausted)?;
let root = RootId(self.next_root);
self.advance_mutation_epoch()?;
self.roots.insert(root, handle.id);
self.next_root = next;
Ok(RootedHandle { root, handle })
}
pub fn release_root(&mut self, rooted: RootedHandle) -> Result<ManagedHandle, ArenaError> {
match self.roots.get(&rooted.root) {
Some(id) if *id == rooted.handle.id => {
self.advance_mutation_epoch()?;
self.roots.remove(&rooted.root);
Ok(rooted.handle)
}
_ => Err(ArenaError::StaleRoot(rooted.root)),
}
}
pub fn remove(&mut self, handle: ManagedHandle) -> Result<T, ArenaError> {
if self.roots.values().any(|id| *id == handle.id) {
return Err(ArenaError::ObjectRooted(handle.id));
}
if !self.objects.contains_key(&handle.id) {
return Err(ArenaError::StaleHandle(handle.id));
}
self.advance_mutation_epoch()?;
let removed = self
.objects
.remove(&handle.id)
.expect("validated managed id");
Ok(removed)
}
pub fn clear_weak_edge(
&mut self,
owner: ManagedHandle,
edge: EdgeId,
expected: WeakHandle,
) -> Result<bool, ArenaError>
where
T: ManagedObject,
{
if !self.objects.contains_key(&owner.id) {
return Err(ArenaError::StaleHandle(owner.id));
}
self.advance_mutation_epoch()?;
let cleared = self
.objects
.get_mut(&owner.id)
.expect("validated managed id")
.clear_weak_edge(edge, expected.id);
Ok(cleared)
}
pub fn sweep_at_epoch(
&mut self,
expected_epoch: u64,
objects: &[ManagedId],
) -> Result<Vec<ManagedId>, ArenaError> {
if self.mutation_epoch != expected_epoch {
return Err(ArenaError::MutationEpochChanged {
expected: expected_epoch,
actual: self.mutation_epoch,
});
}
for id in objects {
if !self.objects.contains_key(id) {
return Err(ArenaError::StaleHandle(*id));
}
if self.roots.values().any(|rooted| rooted == id) {
return Err(ArenaError::ObjectRooted(*id));
}
}
if !objects.is_empty() {
self.advance_mutation_epoch()?;
}
for id in objects {
self.objects.remove(id);
}
Ok(objects.to_vec())
}
pub fn apply_collection_at_epoch(
&mut self,
expected_epoch: u64,
weak: &[(ManagedId, EdgeId, ManagedId)],
ephemerons: &[(ManagedId, EdgeId, ManagedId, ManagedId)],
swept: &[ManagedId],
) -> Result<CollectionMutationReceipt, ArenaError>
where
T: ManagedObject,
{
if self.mutation_epoch != expected_epoch {
return Err(ArenaError::MutationEpochChanged {
expected: expected_epoch,
actual: self.mutation_epoch,
});
}
let kept = self
.kept_alive
.iter()
.filter_map(|(id, epoch)| (*epoch == expected_epoch).then_some(*id))
.collect::<std::collections::BTreeSet<_>>();
let actual_swept = swept
.iter()
.copied()
.filter(|id| !kept.contains(id))
.collect::<Vec<_>>();
for id in &actual_swept {
if !self.objects.contains_key(id) {
return Err(ArenaError::StaleHandle(*id));
}
if self.roots.values().any(|rooted| rooted == id) {
return Err(ArenaError::ObjectRooted(*id));
}
}
if !weak.is_empty() || !ephemerons.is_empty() || !actual_swept.is_empty() {
self.advance_mutation_epoch()?;
}
let mut cleared_weak = Vec::new();
for &(owner, edge, target) in weak {
if let Some(object) = self.objects.get_mut(&owner)
&& object.clear_weak_edge(edge, target)
{
cleared_weak.push((owner, edge));
}
}
let mut cleared_ephemerons = Vec::new();
for &(owner, edge, key, value) in ephemerons {
if let Some(object) = self.objects.get_mut(&owner)
&& object.clear_ephemeron_edge(edge, key, value)
{
cleared_ephemerons.push((owner, edge));
}
}
for id in &actual_swept {
self.objects.remove(id);
}
self.kept_alive
.retain(|id, epoch| self.objects.contains_key(id) && *epoch != expected_epoch);
Ok(CollectionMutationReceipt {
cleared_weak,
cleared_ephemerons,
swept: actual_swept,
})
}
pub fn safepoint<R>(
&mut self,
trace: impl FnOnce(&TraceSnapshot<'_, T>) -> R,
) -> Result<(R, SafepointReceipt), ArenaError>
where
T: ManagedObject,
{
let next = self
.next_safepoint
.checked_add(1)
.ok_or(ArenaError::IdentityExhausted)?;
let roots = self.roots.values().copied().collect::<Vec<_>>();
let snapshot = TraceSnapshot {
roots: roots.clone(),
kept_alive: self
.kept_alive
.iter()
.filter_map(|(id, epoch)| (*epoch == self.mutation_epoch).then_some(*id))
.collect(),
objects: &self.objects,
mutation_epoch: self.mutation_epoch,
};
let result = trace(&snapshot);
let receipt = SafepointReceipt {
sequence: self.next_safepoint,
roots,
objects: self.objects.keys().copied().collect(),
};
self.next_safepoint = next;
Ok((result, receipt))
}
pub fn teardown(&mut self) -> TeardownReceipt {
let receipt = TeardownReceipt {
objects: self.objects.keys().copied().collect(),
roots: self.roots.keys().copied().collect(),
};
if !self.objects.is_empty() || !self.roots.is_empty() {
self.mutation_epoch = self.mutation_epoch.saturating_add(1);
}
self.objects.clear();
self.roots.clear();
self.kept_alive.clear();
receipt
}
}