pub mod node16;
pub mod node256;
pub mod node4;
pub mod node48;
#[cfg(feature = "persistent-artrie")]
pub mod atomic_ptr;
#[cfg(feature = "persistent-artrie")]
pub mod persistent_node;
pub use node16::Node16;
pub use node256::Node256;
pub use node4::Node4;
pub use node48::Node48;
#[cfg(feature = "persistent-artrie")]
pub use atomic_ptr::AtomicNodePtr;
#[cfg(feature = "persistent-artrie")]
pub use persistent_node::PersistentNode;
use std::sync::atomic::{AtomicU16, AtomicU64, AtomicU8, Ordering};
use super::swizzled_ptr::SwizzledPtr;
pub const MAX_PREFIX_LEN: usize = 12;
pub mod flags {
pub const IS_FINAL: u8 = 0b0000_0001;
pub const IS_DIRTY: u8 = 0b0000_0010;
pub const IS_LEAF: u8 = 0b0000_0100;
pub const HAS_DIRTY_DESCENDANTS: u8 = 0b0000_1000;
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct NodeHeader {
pub node_type: u8,
pub prefix_len: u8,
pub flags: u8,
pub _padding: u8,
pub num_children: u16,
pub _padding2: [u8; 2],
pub version: u64,
}
impl NodeHeader {
pub fn new(node_type: u8) -> Self {
Self {
node_type,
prefix_len: 0,
flags: 0,
_padding: 0,
num_children: 0,
_padding2: [0; 2],
version: 0,
}
}
#[inline]
pub fn is_final(&self) -> bool {
self.flags & flags::IS_FINAL != 0
}
#[inline]
pub fn set_final(&mut self, final_state: bool) {
if final_state {
self.flags |= flags::IS_FINAL;
} else {
self.flags &= !flags::IS_FINAL;
}
}
#[inline]
pub fn is_dirty(&self) -> bool {
self.flags & flags::IS_DIRTY != 0
}
#[inline]
pub fn set_dirty(&mut self, dirty: bool) {
if dirty {
self.flags |= flags::IS_DIRTY;
} else {
self.flags &= !flags::IS_DIRTY;
}
}
#[inline]
pub fn is_leaf(&self) -> bool {
self.flags & flags::IS_LEAF != 0
}
#[inline]
pub fn set_leaf(&mut self, leaf: bool) {
if leaf {
self.flags |= flags::IS_LEAF;
} else {
self.flags &= !flags::IS_LEAF;
}
}
#[inline]
pub fn has_dirty_descendants(&self) -> bool {
self.flags & flags::HAS_DIRTY_DESCENDANTS != 0
}
#[inline]
pub fn set_has_dirty_descendants(&mut self, has_dirty: bool) {
if has_dirty {
self.flags |= flags::HAS_DIRTY_DESCENDANTS;
} else {
self.flags &= !flags::HAS_DIRTY_DESCENDANTS;
}
}
#[inline]
pub fn needs_persistence(&self) -> bool {
(self.flags & (flags::IS_DIRTY | flags::HAS_DIRTY_DESCENDANTS)) != 0
}
#[inline]
pub fn clear_dirty_flags(&mut self) {
self.flags &= !(flags::IS_DIRTY | flags::HAS_DIRTY_DESCENDANTS);
}
#[inline]
pub fn increment_version(&mut self) {
self.version = self.version.wrapping_add(1);
}
#[inline]
pub fn check_version(&self, expected: u64) -> bool {
self.version == expected
}
#[inline]
pub fn version(&self) -> u64 {
self.version
}
}
impl Default for NodeHeader {
fn default() -> Self {
Self::new(0)
}
}
#[repr(C)]
pub struct AtomicNodeHeader {
pub node_type: u8,
pub prefix_len: u8,
flags: AtomicU8,
_padding: u8,
num_children: AtomicU16,
_padding2: [u8; 2],
version: AtomicU64,
}
impl AtomicNodeHeader {
pub fn new(node_type: u8) -> Self {
Self {
node_type,
prefix_len: 0,
flags: AtomicU8::new(0),
_padding: 0,
num_children: AtomicU16::new(0),
_padding2: [0; 2],
version: AtomicU64::new(0),
}
}
pub fn from_header(header: &NodeHeader) -> Self {
Self {
node_type: header.node_type,
prefix_len: header.prefix_len,
flags: AtomicU8::new(header.flags),
_padding: 0,
num_children: AtomicU16::new(header.num_children),
_padding2: [0; 2],
version: AtomicU64::new(header.version),
}
}
pub fn to_header(&self) -> NodeHeader {
NodeHeader {
node_type: self.node_type,
prefix_len: self.prefix_len,
flags: self.flags.load(Ordering::Acquire),
_padding: 0,
num_children: self.num_children.load(Ordering::Acquire),
_padding2: [0; 2],
version: self.version.load(Ordering::Acquire),
}
}
#[inline]
pub fn try_set_final(&self) -> bool {
let old = self.flags.fetch_or(flags::IS_FINAL, Ordering::AcqRel);
(old & flags::IS_FINAL) == 0
}
#[inline]
pub fn is_final(&self) -> bool {
(self.flags.load(Ordering::Acquire) & flags::IS_FINAL) != 0
}
#[inline]
pub fn set_dirty(&self) {
self.flags.fetch_or(flags::IS_DIRTY, Ordering::Release);
}
#[inline]
pub fn is_dirty(&self) -> bool {
(self.flags.load(Ordering::Acquire) & flags::IS_DIRTY) != 0
}
#[inline]
pub fn set_has_dirty_descendants(&self) {
self.flags
.fetch_or(flags::HAS_DIRTY_DESCENDANTS, Ordering::Release);
}
#[inline]
pub fn has_dirty_descendants(&self) -> bool {
(self.flags.load(Ordering::Acquire) & flags::HAS_DIRTY_DESCENDANTS) != 0
}
#[inline]
pub fn needs_persistence(&self) -> bool {
(self.flags.load(Ordering::Acquire) & (flags::IS_DIRTY | flags::HAS_DIRTY_DESCENDANTS)) != 0
}
#[inline]
pub fn clear_dirty_flags(&self) {
self.flags.fetch_and(
!(flags::IS_DIRTY | flags::HAS_DIRTY_DESCENDANTS),
Ordering::Release,
);
}
#[inline]
pub fn flags(&self) -> u8 {
self.flags.load(Ordering::Acquire)
}
#[inline]
pub fn num_children(&self) -> u16 {
self.num_children.load(Ordering::Acquire)
}
#[inline]
pub fn fetch_add_children(&self, count: u16) -> u16 {
self.num_children.fetch_add(count, Ordering::AcqRel)
}
#[inline]
pub fn fetch_sub_children(&self, count: u16) -> u16 {
self.num_children.fetch_sub(count, Ordering::AcqRel)
}
#[inline]
pub fn compare_exchange_children(&self, expected: u16, new: u16) -> Result<u16, u16> {
self.num_children
.compare_exchange(expected, new, Ordering::AcqRel, Ordering::Acquire)
}
#[inline]
pub fn version(&self) -> u64 {
self.version.load(Ordering::Acquire)
}
#[inline]
pub fn fetch_add_version(&self) -> u64 {
self.version.fetch_add(1, Ordering::AcqRel)
}
#[inline]
pub fn check_version(&self, expected: u64) -> bool {
self.version.load(Ordering::Acquire) == expected
}
#[inline]
pub fn begin_write(&self) -> u64 {
self.version.fetch_add(1, Ordering::AcqRel)
}
#[inline]
pub fn end_write(&self) {
self.version.fetch_add(1, Ordering::Release);
}
#[inline]
pub fn is_version_stable(&self) -> bool {
self.version.load(Ordering::Acquire) % 2 == 0
}
}
impl std::fmt::Debug for AtomicNodeHeader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AtomicNodeHeader")
.field("node_type", &self.node_type)
.field("prefix_len", &self.prefix_len)
.field("flags", &self.flags.load(Ordering::Relaxed))
.field("num_children", &self.num_children.load(Ordering::Relaxed))
.field("version", &self.version.load(Ordering::Relaxed))
.finish()
}
}
impl Clone for AtomicNodeHeader {
fn clone(&self) -> Self {
Self {
node_type: self.node_type,
prefix_len: self.prefix_len,
flags: AtomicU8::new(self.flags.load(Ordering::Acquire)),
_padding: 0,
num_children: AtomicU16::new(self.num_children.load(Ordering::Acquire)),
_padding2: [0; 2],
version: AtomicU64::new(self.version.load(Ordering::Acquire)),
}
}
}
impl Default for AtomicNodeHeader {
fn default() -> Self {
Self::new(0)
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct CompressedPrefix {
pub bytes: [u8; MAX_PREFIX_LEN],
}
impl CompressedPrefix {
pub const fn empty() -> Self {
Self {
bytes: [0; MAX_PREFIX_LEN],
}
}
pub fn from_bytes(bytes: &[u8]) -> Self {
assert!(
bytes.len() <= MAX_PREFIX_LEN,
"prefix too long: {} > {}",
bytes.len(),
MAX_PREFIX_LEN
);
let mut prefix = Self::empty();
prefix.bytes[..bytes.len()].copy_from_slice(bytes);
prefix
}
pub fn match_key(&self, key: &[u8], prefix_len: usize) -> usize {
let check_len = prefix_len.min(key.len()).min(MAX_PREFIX_LEN);
for i in 0..check_len {
if self.bytes[i] != key[i] {
return i;
}
}
check_len
}
pub fn as_slice(&self, len: usize) -> &[u8] {
&self.bytes[..len.min(MAX_PREFIX_LEN)]
}
}
impl Default for CompressedPrefix {
fn default() -> Self {
Self::empty()
}
}
#[derive(Debug, Clone)]
pub enum Node {
N4(Box<Node4>),
N16(Box<Node16>),
N48(Box<Node48>),
N256(Box<Node256>),
}
impl Node {
pub fn header(&self) -> &NodeHeader {
match self {
Node::N4(n) => &n.header,
Node::N16(n) => &n.header,
Node::N48(n) => &n.header,
Node::N256(n) => &n.header,
}
}
pub fn header_mut(&mut self) -> &mut NodeHeader {
match self {
Node::N4(n) => &mut n.header,
Node::N16(n) => &mut n.header,
Node::N48(n) => &mut n.header,
Node::N256(n) => &mut n.header,
}
}
pub fn prefix(&self) -> &CompressedPrefix {
match self {
Node::N4(n) => &n.prefix,
Node::N16(n) => &n.prefix,
Node::N48(n) => &n.prefix,
Node::N256(n) => &n.prefix,
}
}
pub fn prefix_mut(&mut self) -> &mut CompressedPrefix {
match self {
Node::N4(n) => &mut n.prefix,
Node::N16(n) => &mut n.prefix,
Node::N48(n) => &mut n.prefix,
Node::N256(n) => &mut n.prefix,
}
}
#[inline]
pub fn is_final(&self) -> bool {
self.header().is_final()
}
pub fn find_child(&self, key: u8) -> Option<&SwizzledPtr> {
match self {
Node::N4(n) => n.find_child(key),
Node::N16(n) => n.find_child(key),
Node::N48(n) => n.find_child(key),
Node::N256(n) => n.find_child(key),
}
}
pub fn find_child_mut(&mut self, key: u8) -> Option<&mut SwizzledPtr> {
match self {
Node::N4(n) => n.find_child_mut(key),
Node::N16(n) => n.find_child_mut(key),
Node::N48(n) => n.find_child_mut(key),
Node::N256(n) => n.find_child_mut(key),
}
}
pub fn iter_children(&self) -> Box<dyn Iterator<Item = (u8, &SwizzledPtr)> + '_> {
match self {
Node::N4(n) => Box::new(n.iter_children()),
Node::N16(n) => Box::new(n.iter_children()),
Node::N48(n) => Box::new(n.iter_children()),
Node::N256(n) => Box::new(n.iter_children()),
}
}
#[inline]
pub fn num_children(&self) -> usize {
self.header().num_children as usize
}
pub fn is_full(&self) -> bool {
match self {
Node::N4(n) => n.is_full(),
Node::N16(n) => n.is_full(),
Node::N48(n) => n.is_full(),
Node::N256(n) => n.is_full(),
}
}
pub fn should_grow(&self) -> bool {
self.is_full()
}
pub fn should_shrink(&self) -> bool {
match self {
Node::N4(_) => false,
Node::N16(n) => n.header.num_children <= 4,
Node::N48(n) => n.header.num_children <= 16,
Node::N256(n) => n.header.num_children <= 48,
}
}
pub fn new_node4() -> Self {
Node::N4(Box::new(Node4::new()))
}
pub fn new_node16() -> Self {
Node::N16(Box::new(Node16::new()))
}
pub fn new_node48() -> Self {
Node::N48(Box::new(Node48::new()))
}
pub fn new_node256() -> Self {
Node::N256(Box::new(Node256::new()))
}
pub fn grow(&self) -> Option<Node> {
match self {
Node::N4(n) => Some(Node::N16(Box::new(n.grow()))),
Node::N16(n) => Some(Node::N48(Box::new(n.grow()))),
Node::N48(n) => Some(Node::N256(Box::new(n.grow()))),
Node::N256(_) => None, }
}
pub fn shrink(&self) -> Option<Node> {
match self {
Node::N4(_) => None, Node::N16(n) => Some(Node::N4(Box::new(n.shrink()))),
Node::N48(n) => Some(Node::N16(Box::new(n.shrink()))),
Node::N256(n) => Some(Node::N48(Box::new(n.shrink()))),
}
}
pub fn add_child_growing(
&mut self,
key: u8,
child: SwizzledPtr,
) -> Result<Option<Node>, AddChildError> {
match self {
Node::N4(n) => {
if n.is_full() {
let mut grown = n.grow();
grown.add_child(key, child)?;
Ok(Some(Node::N16(Box::new(grown))))
} else {
n.add_child(key, child)?;
Ok(None)
}
}
Node::N16(n) => {
if n.is_full() {
let mut grown = n.grow();
grown.add_child(key, child)?;
Ok(Some(Node::N48(Box::new(grown))))
} else {
n.add_child(key, child)?;
Ok(None)
}
}
Node::N48(n) => {
if n.is_full() {
let mut grown = n.grow();
grown.add_child(key, child)?;
Ok(Some(Node::N256(Box::new(grown))))
} else {
n.add_child(key, child)?;
Ok(None)
}
}
Node::N256(n) => {
n.add_child(key, child)?;
Ok(None)
}
}
}
pub fn remove_child_shrinking(&mut self, key: u8) -> Option<(SwizzledPtr, Option<Node>)> {
match self {
Node::N4(n) => n.remove_child(key).map(|removed| (removed, None)),
Node::N16(n) => {
if let Some(removed) = n.remove_child(key) {
if n.header.num_children <= 4 {
Some((removed, Some(Node::N4(Box::new(n.shrink())))))
} else {
Some((removed, None))
}
} else {
None
}
}
Node::N48(n) => {
if let Some(removed) = n.remove_child(key) {
if n.header.num_children <= 16 {
Some((removed, Some(Node::N16(Box::new(n.shrink())))))
} else {
Some((removed, None))
}
} else {
None
}
}
Node::N256(n) => {
if let Some(removed) = n.remove_child(key) {
if n.header.num_children <= 48 {
Some((removed, Some(Node::N48(Box::new(n.shrink())))))
} else {
Some((removed, None))
}
} else {
None
}
}
}
}
}
pub trait ArtNode {
fn find_child(&self, key: u8) -> Option<&SwizzledPtr>;
fn find_child_mut(&mut self, key: u8) -> Option<&mut SwizzledPtr>;
fn add_child(&mut self, key: u8, child: SwizzledPtr) -> Result<(), AddChildError>;
fn remove_child(&mut self, key: u8) -> Option<SwizzledPtr>;
fn is_full(&self) -> bool;
fn iter_children(&self) -> impl Iterator<Item = (u8, &SwizzledPtr)>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddChildError {
NodeFull,
KeyExists,
}
impl std::fmt::Display for AddChildError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AddChildError::NodeFull => write!(f, "node is full"),
AddChildError::KeyExists => write!(f, "key already exists"),
}
}
}
impl std::error::Error for AddChildError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ChildStorage {
Direct,
Sequential {
arena_id: u32,
first_slot: u32,
},
}
impl ChildStorage {
#[inline]
pub fn is_direct(&self) -> bool {
matches!(self, ChildStorage::Direct)
}
#[inline]
pub fn is_sequential(&self) -> bool {
matches!(self, ChildStorage::Sequential { .. })
}
pub fn first_slot(&self) -> Option<u32> {
match self {
ChildStorage::Sequential { first_slot, .. } => Some(*first_slot),
ChildStorage::Direct => None,
}
}
pub fn arena_id(&self) -> Option<u32> {
match self {
ChildStorage::Sequential { arena_id, .. } => Some(*arena_id),
ChildStorage::Direct => None,
}
}
pub fn sequential(arena_id: u32, first_slot: u32) -> Self {
ChildStorage::Sequential {
arena_id,
first_slot,
}
}
}
impl Default for ChildStorage {
fn default() -> Self {
ChildStorage::Direct
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_node_header_flags() {
let mut header = NodeHeader::new(4);
assert!(!header.is_final());
assert!(!header.is_dirty());
assert!(!header.is_leaf());
header.set_final(true);
assert!(header.is_final());
header.set_dirty(true);
assert!(header.is_dirty());
header.set_leaf(true);
assert!(header.is_leaf());
header.set_final(false);
assert!(!header.is_final());
assert!(header.is_dirty()); }
#[test]
fn test_node_header_dirty_descendants_flag() {
let mut header = NodeHeader::new(4);
assert!(!header.has_dirty_descendants());
assert!(!header.is_dirty());
assert!(!header.needs_persistence());
header.set_has_dirty_descendants(true);
assert!(header.has_dirty_descendants());
assert!(!header.is_dirty());
assert!(header.needs_persistence());
header.set_dirty(true);
assert!(header.has_dirty_descendants());
assert!(header.is_dirty());
assert!(header.needs_persistence());
header.set_dirty(false);
assert!(header.has_dirty_descendants());
assert!(!header.is_dirty());
assert!(header.needs_persistence());
header.set_has_dirty_descendants(false);
assert!(!header.has_dirty_descendants());
assert!(!header.needs_persistence());
}
#[test]
fn test_node_header_clear_dirty_flags() {
let mut header = NodeHeader::new(4);
header.set_dirty(true);
header.set_has_dirty_descendants(true);
assert!(header.is_dirty());
assert!(header.has_dirty_descendants());
assert!(header.needs_persistence());
header.clear_dirty_flags();
assert!(!header.is_dirty());
assert!(!header.has_dirty_descendants());
assert!(!header.needs_persistence());
header.set_final(true);
header.set_leaf(true);
header.set_dirty(true);
header.set_has_dirty_descendants(true);
header.clear_dirty_flags();
assert!(header.is_final()); assert!(header.is_leaf()); assert!(!header.is_dirty());
assert!(!header.has_dirty_descendants());
}
#[test]
fn test_node_header_version() {
let mut header = NodeHeader::new(4);
assert_eq!(header.version, 0);
header.increment_version();
assert_eq!(header.version, 1);
header.increment_version();
assert_eq!(header.version, 2);
}
#[test]
fn test_compressed_prefix() {
let prefix = CompressedPrefix::from_bytes(b"hello");
assert_eq!(prefix.as_slice(5), b"hello");
assert_eq!(prefix.as_slice(3), b"hel");
}
#[test]
fn test_prefix_matching() {
let prefix = CompressedPrefix::from_bytes(b"hello");
assert_eq!(prefix.match_key(b"hello world", 5), 5);
assert_eq!(prefix.match_key(b"help", 5), 3);
assert_eq!(prefix.match_key(b"world", 5), 0);
assert_eq!(prefix.match_key(b"hel", 5), 3);
}
#[test]
#[should_panic(expected = "prefix too long")]
fn test_prefix_too_long() {
CompressedPrefix::from_bytes(b"this is way too long for the prefix");
}
#[test]
fn test_child_storage_direct() {
let storage = ChildStorage::Direct;
assert!(storage.is_direct());
assert!(!storage.is_sequential());
assert_eq!(storage.first_slot(), None);
assert_eq!(storage.arena_id(), None);
}
#[test]
fn test_child_storage_sequential() {
let storage = ChildStorage::sequential(5, 100);
assert!(!storage.is_direct());
assert!(storage.is_sequential());
assert_eq!(storage.arena_id(), Some(5));
assert_eq!(storage.first_slot(), Some(100));
}
#[test]
fn test_child_storage_default() {
let storage = ChildStorage::default();
assert!(storage.is_direct());
}
#[test]
fn test_child_storage_equality() {
let s1 = ChildStorage::sequential(1, 10);
let s2 = ChildStorage::sequential(1, 10);
let s3 = ChildStorage::sequential(1, 20);
let s4 = ChildStorage::Direct;
assert_eq!(s1, s2);
assert_ne!(s1, s3);
assert_ne!(s1, s4);
assert_eq!(s4, ChildStorage::Direct);
}
#[test]
fn test_atomic_header_new() {
let header = AtomicNodeHeader::new(4);
assert_eq!(header.node_type, 4);
assert_eq!(header.prefix_len, 0);
assert_eq!(header.flags(), 0);
assert_eq!(header.num_children(), 0);
assert_eq!(header.version(), 0);
}
#[test]
fn test_atomic_header_from_regular() {
let mut regular = NodeHeader::new(16);
regular.set_final(true);
regular.set_dirty(true);
regular.num_children = 5;
regular.version = 42;
let atomic = AtomicNodeHeader::from_header(®ular);
assert_eq!(atomic.node_type, 16);
assert!(atomic.is_final());
assert!(atomic.is_dirty());
assert_eq!(atomic.num_children(), 5);
assert_eq!(atomic.version(), 42);
}
#[test]
fn test_atomic_header_to_regular() {
let atomic = AtomicNodeHeader::new(48);
atomic.try_set_final();
atomic.fetch_add_children(3);
atomic.fetch_add_version();
let regular = atomic.to_header();
assert_eq!(regular.node_type, 48);
assert!(regular.is_final());
assert_eq!(regular.num_children, 3);
assert_eq!(regular.version, 1);
}
#[test]
fn test_atomic_header_try_set_final() {
let header = AtomicNodeHeader::new(4);
assert!(header.try_set_final());
assert!(header.is_final());
assert!(!header.try_set_final());
assert!(header.is_final());
}
#[test]
fn test_atomic_header_try_set_final_concurrent() {
use std::sync::Arc;
use std::thread;
let header = Arc::new(AtomicNodeHeader::new(4));
let num_threads = 10;
let handles: Vec<_> = (0..num_threads)
.map(|_| {
let h = Arc::clone(&header);
thread::spawn(move || h.try_set_final())
})
.collect();
let results: Vec<bool> = handles
.into_iter()
.map(|h| h.join().expect("thread should complete"))
.collect();
let winners = results.iter().filter(|&&x| x).count();
assert_eq!(winners, 1, "exactly one thread should win try_set_final");
assert!(header.is_final());
}
#[test]
fn test_atomic_header_dirty_flags() {
let header = AtomicNodeHeader::new(4);
assert!(!header.is_dirty());
assert!(!header.has_dirty_descendants());
assert!(!header.needs_persistence());
header.set_dirty();
assert!(header.is_dirty());
assert!(header.needs_persistence());
header.set_has_dirty_descendants();
assert!(header.has_dirty_descendants());
header.clear_dirty_flags();
assert!(!header.is_dirty());
assert!(!header.has_dirty_descendants());
assert!(!header.needs_persistence());
}
#[test]
fn test_atomic_header_children_ops() {
let header = AtomicNodeHeader::new(4);
assert_eq!(header.num_children(), 0);
let prev = header.fetch_add_children(5);
assert_eq!(prev, 0);
assert_eq!(header.num_children(), 5);
let prev = header.fetch_add_children(3);
assert_eq!(prev, 5);
assert_eq!(header.num_children(), 8);
let prev = header.fetch_sub_children(2);
assert_eq!(prev, 8);
assert_eq!(header.num_children(), 6);
}
#[test]
fn test_atomic_header_children_cas() {
let header = AtomicNodeHeader::new(4);
header.fetch_add_children(5);
let result = header.compare_exchange_children(3, 10);
assert_eq!(result, Err(5));
assert_eq!(header.num_children(), 5);
let result = header.compare_exchange_children(5, 10);
assert_eq!(result, Ok(5));
assert_eq!(header.num_children(), 10);
}
#[test]
fn test_atomic_header_version_ops() {
let header = AtomicNodeHeader::new(4);
assert_eq!(header.version(), 0);
assert!(header.is_version_stable());
let prev = header.begin_write();
assert_eq!(prev, 0);
assert_eq!(header.version(), 1);
assert!(!header.is_version_stable());
header.end_write();
assert_eq!(header.version(), 2);
assert!(header.is_version_stable()); }
#[test]
fn test_atomic_header_version_check() {
let header = AtomicNodeHeader::new(4);
assert!(header.check_version(0));
assert!(!header.check_version(1));
header.fetch_add_version();
assert!(!header.check_version(0));
assert!(header.check_version(1));
}
#[test]
fn test_atomic_header_clone() {
let header = AtomicNodeHeader::new(16);
header.try_set_final();
header.fetch_add_children(7);
header.fetch_add_version();
let cloned = header.clone();
assert_eq!(cloned.node_type, 16);
assert!(cloned.is_final());
assert_eq!(cloned.num_children(), 7);
assert_eq!(cloned.version(), 1);
cloned.fetch_add_children(3);
assert_eq!(header.num_children(), 7);
assert_eq!(cloned.num_children(), 10);
}
#[test]
fn test_atomic_header_debug() {
let header = AtomicNodeHeader::new(4);
let debug_str = format!("{:?}", header);
assert!(debug_str.contains("AtomicNodeHeader"));
assert!(debug_str.contains("node_type: 4"));
}
}