use core::mem::MaybeUninit;
pub(crate) trait LeaseFacet: Copy + Default {
type Context<'ctx>;
fn on_commit<'ctx>(&self, context: &mut Self::Context<'ctx>);
fn on_rollback<'ctx>(&self, context: &mut Self::Context<'ctx>);
}
pub(crate) trait LeaseChildStorage<Id: Copy>: Copy {
const CAPACITY: usize;
fn empty() -> Self;
fn get(&self, idx: usize) -> Option<Id>;
fn set(&mut self, idx: usize, id: Id);
}
#[derive(Clone, Copy)]
pub(crate) struct InlineLeaseChildStorage<Id: Copy, const CAPACITY: usize> {
slots: [Option<Id>; CAPACITY],
}
impl<Id: Copy, const CAPACITY: usize> LeaseChildStorage<Id>
for InlineLeaseChildStorage<Id, CAPACITY>
{
const CAPACITY: usize = CAPACITY;
#[inline]
fn empty() -> Self {
Self {
slots: [None; CAPACITY],
}
}
#[inline]
fn get(&self, idx: usize) -> Option<Id> {
self.slots.get(idx).copied().flatten()
}
#[inline]
fn set(&mut self, idx: usize, id: Id) {
self.slots[idx] = Some(id);
}
}
pub(crate) trait LeaseSpec: Sized {
type NodeId: Copy + Eq + Ord + core::fmt::Debug;
type Facet: LeaseFacet;
type ChildStorage: LeaseChildStorage<Self::NodeId>;
type NodeStorage<'graph>: LeaseNodeStorage<'graph, Self>
where
Self: 'graph;
const MAX_NODES: usize;
const MAX_CHILDREN: usize;
}
impl LeaseFacet for () {
type Context<'ctx> = ();
fn on_commit<'ctx>(&self, _context: &mut Self::Context<'ctx>) {}
fn on_rollback<'ctx>(&self, _context: &mut Self::Context<'ctx>) {}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum GraphState {
Active,
Committed,
RolledBack,
}
pub(crate) struct NodeData<'graph, S: LeaseSpec> {
id: S::NodeId,
facet: S::Facet,
context: <<S as LeaseSpec>::Facet as LeaseFacet>::Context<'graph>,
children: S::ChildStorage,
child_count: usize,
}
impl<'graph, S: LeaseSpec> NodeData<'graph, S> {
fn new(
id: S::NodeId,
facet: S::Facet,
context: <<S as LeaseSpec>::Facet as LeaseFacet>::Context<'graph>,
) -> Self {
Self {
id,
facet,
context,
children: S::ChildStorage::empty(),
child_count: 0,
}
}
fn add_child(&mut self, child_id: S::NodeId) -> Result<(), LeaseGraphError> {
if self.child_count >= S::MAX_CHILDREN || self.child_count >= S::ChildStorage::CAPACITY {
return Err(LeaseGraphError::TooManyChildren);
}
self.children.set(self.child_count, child_id);
self.child_count += 1;
Ok(())
}
}
pub(crate) trait LeaseNodeStorage<'graph, S: LeaseSpec> {
const CAPACITY: usize;
unsafe fn init_empty(dst: *mut Self);
unsafe fn write(&mut self, idx: usize, node: NodeData<'graph, S>);
unsafe fn read(&mut self, idx: usize) -> NodeData<'graph, S>;
fn get(&self, idx: usize) -> Option<&NodeData<'graph, S>>;
fn get_mut(&mut self, idx: usize) -> Option<&mut NodeData<'graph, S>>;
}
pub(crate) struct InlineLeaseNodeStorage<'graph, S: LeaseSpec, const CAPACITY: usize> {
slots: [MaybeUninit<NodeData<'graph, S>>; CAPACITY],
}
impl<'graph, S: LeaseSpec, const CAPACITY: usize> LeaseNodeStorage<'graph, S>
for InlineLeaseNodeStorage<'graph, S, CAPACITY>
{
const CAPACITY: usize = CAPACITY;
#[inline]
unsafe fn init_empty(dst: *mut Self) {
unsafe {
let slots =
core::ptr::addr_of_mut!((*dst).slots).cast::<MaybeUninit<NodeData<'graph, S>>>();
let mut i = 0;
while i < CAPACITY {
slots.add(i).write(MaybeUninit::uninit());
i += 1;
}
}
}
#[inline]
unsafe fn write(&mut self, idx: usize, node: NodeData<'graph, S>) {
debug_assert!(idx < CAPACITY, "lease graph node index out of bounds");
unsafe {
self.slots.get_unchecked_mut(idx).write(node);
}
}
#[inline]
unsafe fn read(&mut self, idx: usize) -> NodeData<'graph, S> {
debug_assert!(idx < CAPACITY, "lease graph node index out of bounds");
unsafe { self.slots.get_unchecked(idx).assume_init_read() }
}
#[inline]
fn get(&self, idx: usize) -> Option<&NodeData<'graph, S>> {
if idx >= CAPACITY {
return None;
}
Some(
unsafe { self.slots.get_unchecked(idx).assume_init_ref() },
)
}
#[inline]
fn get_mut(&mut self, idx: usize) -> Option<&mut NodeData<'graph, S>> {
if idx >= CAPACITY {
return None;
}
Some(
unsafe { self.slots.get_unchecked_mut(idx).assume_init_mut() },
)
}
}
#[cfg(test)]
pub(crate) struct ChildIter<'a, 'graph, S: LeaseSpec> {
node: &'a NodeData<'graph, S>,
index: usize,
}
#[cfg(test)]
impl<'a, 'graph, S: LeaseSpec> ChildIter<'a, 'graph, S> {
fn new(node: &'a NodeData<'graph, S>) -> Self {
Self { node, index: 0 }
}
}
#[cfg(test)]
impl<'a, 'graph, S: LeaseSpec> Iterator for ChildIter<'a, 'graph, S> {
type Item = S::NodeId;
fn next(&mut self) -> Option<Self::Item> {
while self.index < self.node.child_count {
let slot = self.index;
self.index += 1;
if let Some(id) = self.node.children.get(slot) {
return Some(id);
}
}
None
}
}
pub(crate) struct FacetHandle<'a, 'graph, S: LeaseSpec> {
facet: S::Facet,
context: &'a mut <<S as LeaseSpec>::Facet as LeaseFacet>::Context<'graph>,
}
impl<'a, 'graph, S: LeaseSpec> FacetHandle<'a, 'graph, S> {
#[inline]
pub(crate) fn context(
&mut self,
) -> &mut <<S as LeaseSpec>::Facet as LeaseFacet>::Context<'graph> {
let _ = self.facet;
self.context
}
#[inline]
#[cfg(test)]
pub(crate) fn with<R>(
self,
f: impl FnOnce(S::Facet, &mut <<S as LeaseSpec>::Facet as LeaseFacet>::Context<'graph>) -> R,
) -> R {
let FacetHandle { facet, context } = self;
f(facet, context)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum LeaseGraphError {
GraphFull,
TooManyChildren,
NodeNotFound,
DuplicateId,
AlreadyCommitted,
AlreadyRolledBack,
}
pub(crate) struct LeaseGraph<'graph, S: LeaseSpec + 'graph> {
nodes: S::NodeStorage<'graph>,
node_count: usize,
root_id: S::NodeId,
state: GraphState,
}
impl<'graph, S: LeaseSpec + 'graph> LeaseGraph<'graph, S> {
pub(crate) unsafe fn init_new(
dst: *mut Self,
root_id: S::NodeId,
root_facet: S::Facet,
root_context: <<S as LeaseSpec>::Facet as LeaseFacet>::Context<'graph>,
) {
debug_assert!(S::MAX_NODES > 0, "LeaseGraph requires MAX_NODES > 0");
debug_assert!(S::MAX_CHILDREN > 0, "LeaseGraph requires MAX_CHILDREN > 0");
debug_assert!(
S::MAX_NODES == <S::NodeStorage<'graph> as LeaseNodeStorage<'graph, S>>::CAPACITY,
"LeaseGraph node storage must match LeaseSpec capacity"
);
debug_assert!(
S::MAX_CHILDREN == <S::ChildStorage as LeaseChildStorage<S::NodeId>>::CAPACITY,
"LeaseGraph child storage must match LeaseSpec capacity"
);
unsafe {
S::NodeStorage::init_empty(core::ptr::addr_of_mut!((*dst).nodes));
core::ptr::addr_of_mut!((*dst).node_count).write(1);
core::ptr::addr_of_mut!((*dst).root_id).write(root_id);
core::ptr::addr_of_mut!((*dst).state).write(GraphState::Active);
let nodes = &mut *core::ptr::addr_of_mut!((*dst).nodes);
nodes.write(0, NodeData::new(root_id, root_facet, root_context));
}
}
pub(crate) fn root_id(&self) -> S::NodeId {
self.root_id
}
fn find_node(&self, id: S::NodeId) -> Option<usize> {
let mut idx = 0usize;
while idx < self.node_count {
let node = self
.nodes
.get(idx)
.expect("active lease graph stores a dense initialized prefix");
if node.id == id {
return Some(idx);
}
idx += 1;
}
None
}
fn find_node_with_taken_mask(&self, id: S::NodeId, taken_mask: usize) -> Option<usize> {
debug_assert!(
S::MAX_NODES <= usize::BITS as usize,
"lease graph traversal mask must cover all node slots"
);
let mut idx = 0usize;
while idx < self.node_count {
if (taken_mask & (1usize << idx)) != 0 {
idx += 1;
continue;
}
let node = self
.nodes
.get(idx)
.expect("active lease graph stores a dense initialized prefix");
if node.id == id {
return Some(idx);
}
idx += 1;
}
None
}
pub(crate) fn handle_mut(&mut self, id: S::NodeId) -> Option<FacetHandle<'_, 'graph, S>> {
if self.state != GraphState::Active {
return None;
}
let idx = self.find_node(id)?;
let node = self.nodes.get_mut(idx)?;
Some(FacetHandle {
facet: node.facet,
context: &mut node.context,
})
}
#[inline]
#[cfg(test)]
pub(crate) fn children(&self, id: S::NodeId) -> Option<ChildIter<'_, 'graph, S>> {
let idx = self.find_node(id)?;
let node = self.nodes.get(idx)?;
Some(ChildIter::new(node))
}
pub(crate) fn root_handle_mut(&mut self) -> FacetHandle<'_, 'graph, S> {
self.handle_mut(self.root_id)
.expect("root node exists and graph is active")
}
pub(crate) fn add_child(
&mut self,
parent_id: S::NodeId,
child_id: S::NodeId,
child_facet: S::Facet,
child_context: <<S as LeaseSpec>::Facet as LeaseFacet>::Context<'graph>,
) -> Result<(), LeaseGraphError> {
if self.state == GraphState::Committed {
return Err(LeaseGraphError::AlreadyCommitted);
}
if self.state == GraphState::RolledBack {
return Err(LeaseGraphError::AlreadyRolledBack);
}
if self.find_node(child_id).is_some() {
return Err(LeaseGraphError::DuplicateId);
}
let parent_idx = self
.find_node(parent_id)
.ok_or(LeaseGraphError::NodeNotFound)?;
if self.node_count >= S::MAX_NODES
|| self.node_count >= <S::NodeStorage<'graph> as LeaseNodeStorage<'graph, S>>::CAPACITY
{
return Err(LeaseGraphError::GraphFull);
}
{
self.nodes
.get_mut(parent_idx)
.expect("active lease graph stores a dense initialized prefix")
.add_child(child_id)?;
unsafe {
self.nodes.write(
self.node_count,
NodeData::new(child_id, child_facet, child_context),
);
}
}
self.node_count += 1;
Ok(())
}
#[cfg(test)]
pub(crate) fn navigate_mut(
&mut self,
path: &[S::NodeId],
) -> Option<FacetHandle<'_, 'graph, S>> {
if path.is_empty() || path[0] != self.root_id {
return None;
}
let mut current = self.root_id;
for &next_id in &path[1..] {
let idx = self.find_node(current)?;
let node = self.nodes.get(idx)?;
let mut found = false;
let mut child_idx = 0usize;
while child_idx < node.child_count {
if node.children.get(child_idx) == Some(next_id) {
found = true;
break;
}
child_idx += 1;
}
if !found {
return None;
}
current = next_id;
}
let idx = self.find_node(current)?;
let node = self.nodes.get_mut(idx)?;
Some(FacetHandle {
facet: node.facet,
context: &mut node.context,
})
}
pub(crate) fn commit(&mut self) {
if self.state != GraphState::Active {
return;
}
self.state = GraphState::Committed;
let mut taken_mask = 0usize;
self.commit_node_recursive(self.root_id, &mut taken_mask);
self.node_count = 0;
}
fn commit_node_recursive(&mut self, id: S::NodeId, taken_mask: &mut usize) {
let idx = match self.find_node_with_taken_mask(id, *taken_mask) {
Some(i) => i,
None => return,
};
let node = self
.nodes
.get(idx)
.expect("active lease graph stores a dense initialized prefix");
let children = node.children;
let child_count = node.child_count;
let mut child_idx = 0usize;
while child_idx < child_count {
if let Some(child_id) = children.get(child_idx) {
self.commit_node_recursive(child_id, taken_mask);
}
child_idx += 1;
}
*taken_mask |= 1usize << idx;
let mut node = unsafe { self.nodes.read(idx) };
node.facet.on_commit(&mut node.context);
}
pub(crate) fn rollback(&mut self) {
if self.state != GraphState::Active {
return;
}
self.state = GraphState::RolledBack;
let mut taken_mask = 0usize;
self.rollback_node_recursive(self.root_id, &mut taken_mask);
self.node_count = 0;
}
fn rollback_node_recursive(&mut self, id: S::NodeId, taken_mask: &mut usize) {
let idx = match self.find_node_with_taken_mask(id, *taken_mask) {
Some(i) => i,
None => return,
};
let node = self
.nodes
.get(idx)
.expect("active lease graph stores a dense initialized prefix");
let children = node.children;
let child_count = node.child_count;
let mut child_idx = 0usize;
while child_idx < child_count {
if let Some(child_id) = children.get(child_idx) {
self.rollback_node_recursive(child_id, taken_mask);
}
child_idx += 1;
}
*taken_mask |= 1usize << idx;
let mut node = unsafe { self.nodes.read(idx) };
node.facet.on_rollback(&mut node.context);
}
}
#[cfg(all(test, hibana_repo_tests))]
mod tests;