use std::cell::Cell;
use bumpalo::Bump;
use crate::ids::NameId;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct NsRef(pub u32);
impl Default for NsRef {
fn default() -> Self {
Self::NULL
}
}
impl NsRef {
pub const NULL: NsRef = NsRef(u32::MAX);
#[inline]
pub fn is_null(self) -> bool {
self == Self::NULL
}
}
pub const NS_PAGE_SHIFT: u32 = 12;
pub const NS_PAGE_SIZE: u32 = 1 << NS_PAGE_SHIFT;
pub const NS_PAGE_MASK: u32 = NS_PAGE_SIZE - 1;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct NamespaceNode {
pub prefix: NameId,
pub namespace_uri: NameId,
pub next: NsRef,
}
impl Default for NamespaceNode {
fn default() -> Self {
Self {
prefix: NameId(0),
namespace_uri: NameId(0),
next: NsRef::NULL,
}
}
}
impl NamespaceNode {
pub fn new(prefix: NameId, namespace_uri: NameId, next: NsRef) -> Self {
Self {
prefix,
namespace_uri,
next,
}
}
}
pub struct NamespacePageFactory<'a> {
arena: &'a Bump,
pages: Vec<&'a [Cell<NamespaceNode>]>,
len: u32,
}
#[inline]
fn page_of(ns_ref: NsRef) -> u32 {
ns_ref.0 >> NS_PAGE_SHIFT
}
#[inline]
fn slot_of(ns_ref: NsRef) -> u32 {
ns_ref.0 & NS_PAGE_MASK
}
impl<'a> NamespacePageFactory<'a> {
pub fn new(arena: &'a Bump) -> Self {
let first_page = Self::make_page(arena);
Self {
arena,
pages: vec![first_page],
len: 0,
}
}
pub fn alloc(&mut self) -> Option<NsRef> {
let idx = self.len;
if idx == u32::MAX {
return None;
}
self.len = idx + 1;
if idx > 0 && slot_of(NsRef(idx)) == 0 {
self.allocate_page();
}
Some(NsRef(idx))
}
#[inline]
pub fn get(&self, ns_ref: NsRef) -> NamespaceNode {
let page = page_of(ns_ref) as usize;
let slot = slot_of(ns_ref) as usize;
self.pages[page][slot].get()
}
#[inline]
pub fn set(&self, ns_ref: NsRef, node: NamespaceNode) {
let page = page_of(ns_ref) as usize;
let slot = slot_of(ns_ref) as usize;
self.pages[page][slot].set(node);
}
#[inline]
pub fn len(&self) -> u32 {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn iter_chain(&self, head: NsRef) -> NamespaceChain<'_, 'a> {
NamespaceChain {
factory: self,
cursor: head,
}
}
fn allocate_page(&mut self) {
let page = Self::make_page(self.arena);
self.pages.push(page);
}
fn make_page(arena: &Bump) -> &[Cell<NamespaceNode>] {
arena.alloc_slice_fill_with(NS_PAGE_SIZE as usize, |_| {
Cell::new(NamespaceNode::default())
})
}
}
pub struct NamespaceChain<'f, 'a> {
factory: &'f NamespacePageFactory<'a>,
cursor: NsRef,
}
impl Iterator for NamespaceChain<'_, '_> {
type Item = (NameId, NameId);
fn next(&mut self) -> Option<Self::Item> {
if self.cursor.is_null() {
return None;
}
let node = self.factory.get(self.cursor);
self.cursor = node.next;
Some((node.prefix, node.namespace_uri))
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem;
#[test]
fn nsref_null_is_u32_max() {
assert_eq!(NsRef::NULL.0, u32::MAX);
}
#[test]
fn nsref_default_is_null() {
assert_eq!(NsRef::default(), NsRef::NULL);
assert!(NsRef::default().is_null());
}
#[test]
fn nsref_is_null() {
assert!(NsRef::NULL.is_null());
assert!(!NsRef(0).is_null());
assert!(!NsRef(42).is_null());
}
#[test]
fn end_of_chain_is_null() {
let node = NamespaceNode::new(NameId(1), NameId(2), NsRef::NULL);
assert!(node.next.is_null());
}
#[test]
fn namespace_node_is_12_bytes() {
assert_eq!(mem::size_of::<NamespaceNode>(), 12);
}
#[test]
fn sequential_alloc() {
let arena = Bump::new();
let mut factory = NamespacePageFactory::new(&arena);
for expected in 0..10u32 {
let ns_ref = factory.alloc().unwrap();
assert_eq!(ns_ref.0, expected);
}
}
#[test]
fn set_get_round_trip() {
let arena = Bump::new();
let mut factory = NamespacePageFactory::new(&arena);
let r = factory.alloc().unwrap();
let node = NamespaceNode::new(NameId(10), NameId(20), NsRef::NULL);
factory.set(r, node);
assert_eq!(factory.get(r), node);
}
#[test]
fn cross_page_allocation() {
let arena = Bump::new();
let mut factory = NamespacePageFactory::new(&arena);
let count = NS_PAGE_SIZE + 1;
for i in 0..count {
let r = factory.alloc().unwrap();
assert_eq!(r.0, i);
factory.set(
r,
NamespaceNode::new(NameId(i), NameId(i + 1000), NsRef::NULL),
);
}
assert_eq!(factory.len(), count);
let last_first = NsRef(NS_PAGE_SIZE - 1);
assert_eq!(factory.get(last_first).prefix, NameId(NS_PAGE_SIZE - 1));
let first_second = NsRef(NS_PAGE_SIZE);
assert_eq!(factory.get(first_second).prefix, NameId(NS_PAGE_SIZE));
}
#[test]
fn single_node_chain() {
let arena = Bump::new();
let mut factory = NamespacePageFactory::new(&arena);
let r = factory.alloc().unwrap();
factory.set(r, NamespaceNode::new(NameId(1), NameId(2), NsRef::NULL));
let items: Vec<_> = factory.iter_chain(r).collect();
assert_eq!(items, vec![(NameId(1), NameId(2))]);
}
#[test]
fn two_node_chain() {
let arena = Bump::new();
let mut factory = NamespacePageFactory::new(&arena);
let r1 = factory.alloc().unwrap();
let r0 = factory.alloc().unwrap();
factory.set(r1, NamespaceNode::new(NameId(3), NameId(4), NsRef::NULL));
factory.set(r0, NamespaceNode::new(NameId(1), NameId(2), r1));
let items: Vec<_> = factory.iter_chain(r0).collect();
assert_eq!(items, vec![(NameId(1), NameId(2)), (NameId(3), NameId(4))]);
}
#[test]
fn multi_node_chain() {
let arena = Bump::new();
let mut factory = NamespacePageFactory::new(&arena);
let r2 = factory.alloc().unwrap();
let r1 = factory.alloc().unwrap();
let r0 = factory.alloc().unwrap();
factory.set(r2, NamespaceNode::new(NameId(5), NameId(6), NsRef::NULL));
factory.set(r1, NamespaceNode::new(NameId(3), NameId(4), r2));
factory.set(r0, NamespaceNode::new(NameId(1), NameId(2), r1));
let items: Vec<_> = factory.iter_chain(r0).collect();
assert_eq!(
items,
vec![
(NameId(1), NameId(2)),
(NameId(3), NameId(4)),
(NameId(5), NameId(6)),
]
);
}
#[test]
fn null_chain_yields_empty() {
let arena = Bump::new();
let factory = NamespacePageFactory::new(&arena);
let items: Vec<_> = factory.iter_chain(NsRef::NULL).collect();
assert!(items.is_empty());
}
#[test]
fn len_and_is_empty() {
let arena = Bump::new();
let mut factory = NamespacePageFactory::new(&arena);
assert_eq!(factory.len(), 0);
assert!(factory.is_empty());
factory.alloc().unwrap();
assert_eq!(factory.len(), 1);
assert!(!factory.is_empty());
}
}