use std::{
hint,
mem,
slice,
str,
sync::atomic::{self, AtomicPtr},
};
use super::{StringId, PAGE_SIZE};
pub(super) const DATA_OFFSET: usize = 6 + mem::size_of::<AtomicPtr<Entry>>();
pub const MAX_STRING_LENGTH: usize = PAGE_SIZE - DATA_OFFSET;
#[repr(C)]
pub(super) struct Entry {
pub(super) next: AtomicPtr<Entry>,
pub(super) id: Option<StringId>,
pub(super) len: u16,
pub(super) data: [u8; MAX_STRING_LENGTH],
}
impl Entry {
pub(super) fn id(&self) -> StringId {
self.id.unwrap_or_else(|| {
debug_assert!(false, "Entry was not initialized");
unsafe { hint::unreachable_unchecked() }
})
}
pub(super) fn next(&self) -> &AtomicPtr<Self> {
&self.next
}
pub(super) fn len(&self) -> u16 {
self.len
}
pub(super) fn is_empty(&self) -> bool {
self.len() == 0
}
pub(super) fn as_str(&self) -> &str {
unsafe {
let slice = slice::from_raw_parts(self.data.as_ptr(), self.len as usize);
str::from_utf8_unchecked(slice)
}
}
pub(super) fn iter(&self) -> impl Iterator<Item = &Self> {
Entries {
current: Some(self),
}
}
}
struct Entries<'a> {
current: Option<&'a Entry>,
}
impl<'a> Iterator for Entries<'a> {
type Item = &'a Entry;
fn next(&mut self) -> Option<Self::Item> {
self.current.map(|current| {
let next = current.next().load(atomic::Ordering::Acquire);
self.current = if next.is_null() {
None
} else {
unsafe { Some(&*next) }
};
current
})
}
}