use crate::error::{CapError, CapResult};
use crate::DEFAULT_CAP_TABLE_CAPACITY;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DerivationNode {
pub is_valid: bool,
pub depth: u8,
pub epoch: u64,
pub first_child: u32,
pub next_sibling: u32,
pub parent_index: u32,
}
impl DerivationNode {
#[inline]
#[must_use]
pub const fn empty() -> Self {
Self {
is_valid: false,
depth: 0,
epoch: 0,
first_child: u32::MAX,
next_sibling: u32::MAX,
parent_index: u32::MAX,
}
}
#[inline]
#[must_use]
pub const fn new_root(epoch: u64) -> Self {
Self {
is_valid: true,
depth: 0,
epoch,
first_child: u32::MAX,
next_sibling: u32::MAX,
parent_index: u32::MAX,
}
}
#[inline]
#[must_use]
pub const fn new_child(depth: u8, epoch: u64) -> Self {
Self {
is_valid: true,
depth,
epoch,
first_child: u32::MAX,
next_sibling: u32::MAX,
parent_index: u32::MAX,
}
}
#[inline]
#[must_use]
pub const fn has_children(&self) -> bool {
self.first_child != u32::MAX
}
}
impl Default for DerivationNode {
fn default() -> Self {
Self::empty()
}
}
pub struct DerivationTree<const N: usize = DEFAULT_CAP_TABLE_CAPACITY> {
nodes: [DerivationNode; N],
count: usize,
}
impl<const N: usize> DerivationTree<N> {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
nodes: [DerivationNode::empty(); N],
count: 0,
}
}
#[inline]
#[must_use]
pub const fn len(&self) -> usize {
self.count
}
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.count == 0
}
pub fn add_root(&mut self, index: u32, epoch: u64) -> CapResult<()> {
let idx = index as usize;
if idx >= N {
return Err(CapError::TreeFull);
}
self.nodes[idx] = DerivationNode::new_root(epoch);
self.count += 1;
Ok(())
}
pub fn add_child(
&mut self,
parent_index: u32,
child_index: u32,
depth: u8,
epoch: u64,
) -> CapResult<()> {
let pidx = parent_index as usize;
let cidx = child_index as usize;
if pidx >= N || cidx >= N {
return Err(CapError::TreeFull);
}
if !self.nodes[pidx].is_valid {
return Err(CapError::Revoked);
}
let mut child = DerivationNode::new_child(depth, epoch);
child.next_sibling = self.nodes[pidx].first_child;
child.parent_index = parent_index;
self.nodes[pidx].first_child = child_index;
self.nodes[cidx] = child;
self.count += 1;
Ok(())
}
pub fn revoke(&mut self, index: u32) -> CapResult<usize> {
let idx = index as usize;
if idx >= N {
return Err(CapError::InvalidHandle);
}
if !self.nodes[idx].is_valid {
return Err(CapError::Revoked);
}
Ok(self.revoke_subtree(index))
}
pub fn depth(&self, index: u32) -> CapResult<u8> {
let idx = index as usize;
if idx >= N || !self.nodes[idx].is_valid {
return Err(CapError::InvalidHandle);
}
Ok(self.nodes[idx].depth)
}
#[must_use]
pub fn is_valid(&self, index: u32) -> bool {
let idx = index as usize;
idx < N && self.nodes[idx].is_valid
}
#[must_use]
pub fn get(&self, index: u32) -> Option<&DerivationNode> {
let idx = index as usize;
if idx < N && self.nodes[idx].is_valid {
Some(&self.nodes[idx])
} else {
None
}
}
#[must_use]
pub fn collect_subtree(&self, index: u32) -> [u32; N] {
let mut result = [u32::MAX; N];
let mut result_count = 0;
let mut stack = [u32::MAX; N];
let mut stack_top = 0;
let idx = index as usize;
if idx < N && self.nodes[idx].is_valid {
stack[stack_top] = index;
stack_top += 1;
}
while stack_top > 0 {
stack_top -= 1;
let current = stack[stack_top];
let cidx = current as usize;
if cidx >= N || !self.nodes[cidx].is_valid {
continue;
}
if result_count < N {
result[result_count] = current;
result_count += 1;
}
let mut child = self.nodes[cidx].first_child;
while child != u32::MAX {
let child_idx = child as usize;
if child_idx >= N {
break;
}
if self.nodes[child_idx].is_valid && stack_top < N {
stack[stack_top] = child;
stack_top += 1;
}
child = self.nodes[child_idx].next_sibling;
}
}
result
}
#[must_use]
pub fn find_parent(&self, child_index: u32) -> Option<u32> {
let cidx = child_index as usize;
if cidx >= N || !self.nodes[cidx].is_valid {
return None;
}
if self.nodes[cidx].depth == 0 {
return None;
}
let pidx = self.nodes[cidx].parent_index;
if pidx != u32::MAX {
let pi = pidx as usize;
if pi < N && self.nodes[pi].is_valid {
return Some(pidx);
}
}
for i in 0..N {
if !self.nodes[i].is_valid {
continue;
}
let mut cursor = self.nodes[i].first_child;
while cursor != u32::MAX {
if cursor == child_index {
return Some(u32::try_from(i).unwrap_or(u32::MAX));
}
let c = cursor as usize;
if c >= N {
break;
}
cursor = self.nodes[c].next_sibling;
}
}
None
}
fn revoke_subtree(&mut self, index: u32) -> usize {
let mut stack = [u32::MAX; N];
let mut stack_top: usize = 0;
let mut count: usize = 0;
let root_idx = index as usize;
if root_idx >= N || !self.nodes[root_idx].is_valid {
return 0;
}
stack[stack_top] = index;
stack_top += 1;
while stack_top > 0 {
stack_top -= 1;
let current = stack[stack_top];
let cidx = current as usize;
if cidx >= N || !self.nodes[cidx].is_valid {
continue;
}
self.nodes[cidx].is_valid = false;
self.count = self.count.saturating_sub(1);
count += 1;
let mut child = self.nodes[cidx].first_child;
while child != u32::MAX {
let child_idx = child as usize;
if child_idx >= N {
break;
}
if self.nodes[child_idx].is_valid && stack_top < N {
stack[stack_top] = child;
stack_top += 1;
}
child = self.nodes[child_idx].next_sibling;
}
}
count
}
}
impl<const N: usize> Default for DerivationTree<N> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_root() {
let mut tree = DerivationTree::<64>::new();
tree.add_root(0, 1).unwrap();
assert_eq!(tree.len(), 1);
assert!(tree.is_valid(0));
assert_eq!(tree.depth(0).unwrap(), 0);
}
#[test]
fn test_add_child() {
let mut tree = DerivationTree::<64>::new();
tree.add_root(0, 1).unwrap();
tree.add_child(0, 1, 1, 1).unwrap();
assert_eq!(tree.len(), 2);
assert!(tree.is_valid(1));
assert_eq!(tree.depth(1).unwrap(), 1);
assert!(tree.get(0).unwrap().has_children());
}
#[test]
fn test_revoke_subtree() {
let mut tree = DerivationTree::<64>::new();
tree.add_root(0, 1).unwrap();
tree.add_child(0, 1, 1, 1).unwrap();
tree.add_child(0, 2, 1, 1).unwrap();
tree.add_child(1, 3, 2, 1).unwrap();
let revoked = tree.revoke(0).unwrap();
assert_eq!(revoked, 4);
assert_eq!(tree.len(), 0);
}
#[test]
fn test_partial_revoke() {
let mut tree = DerivationTree::<64>::new();
tree.add_root(0, 1).unwrap();
tree.add_child(0, 1, 1, 1).unwrap();
tree.add_child(0, 2, 1, 1).unwrap();
tree.add_child(1, 3, 2, 1).unwrap();
let revoked = tree.revoke(1).unwrap();
assert_eq!(revoked, 2);
assert!(tree.is_valid(0));
assert!(!tree.is_valid(1));
assert!(tree.is_valid(2));
assert!(!tree.is_valid(3));
}
#[test]
fn test_add_child_to_revoked_parent() {
let mut tree = DerivationTree::<64>::new();
tree.add_root(0, 1).unwrap();
tree.revoke(0).unwrap();
assert_eq!(tree.add_child(0, 1, 1, 1), Err(CapError::Revoked));
}
#[test]
fn test_out_of_bounds() {
let mut tree = DerivationTree::<4>::new();
assert_eq!(tree.add_root(10, 1), Err(CapError::TreeFull));
}
}