#[cfg(not(test))]
use alloc::vec::Vec;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use core::alloc::Layout;
use core::ptr::NonNull;
const WORDS_PER_BLOCK: usize = 8;
#[allow(dead_code)]
const BITS_PER_BLOCK: usize = WORDS_PER_BLOCK * 64;
const BLOCKS_PER_SUPERBLOCK: usize = 1 << 23;
const CACHE_LINE_SIZE: usize = 64;
#[allow(dead_code)]
const ENTRIES_PER_CACHE_LINE: usize = CACHE_LINE_SIZE / 16;
#[derive(Debug)]
struct CacheAlignedL1L2 {
ptr: NonNull<u128>,
len: usize,
}
impl CacheAlignedL1L2 {
fn empty() -> Self {
Self {
ptr: NonNull::dangling(),
len: 0,
}
}
fn builder(capacity: usize) -> CacheAlignedL1L2Builder {
CacheAlignedL1L2Builder::with_capacity(capacity)
}
#[allow(dead_code)]
fn from_vec(data: Vec<u128>) -> Self {
if data.is_empty() {
return Self::empty();
}
let len = data.len();
let layout = Layout::from_size_align(len * 16, CACHE_LINE_SIZE).expect("layout error");
let ptr = unsafe { alloc::alloc::alloc(layout) as *mut u128 };
if ptr.is_null() {
alloc::alloc::handle_alloc_error(layout);
}
unsafe {
core::ptr::copy_nonoverlapping(data.as_ptr(), ptr, len);
}
Self {
ptr: NonNull::new(ptr).unwrap(),
len,
}
}
#[inline]
#[allow(dead_code)]
fn len(&self) -> usize {
self.len
}
#[inline]
#[allow(dead_code)]
fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
fn as_slice(&self) -> &[u128] {
if self.len == 0 {
&[]
} else {
unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
}
}
}
impl Clone for CacheAlignedL1L2 {
fn clone(&self) -> Self {
if self.len == 0 {
return Self::empty();
}
let layout = Layout::from_size_align(self.len * 16, CACHE_LINE_SIZE).expect("layout error");
let ptr = unsafe { alloc::alloc::alloc(layout) as *mut u128 };
if ptr.is_null() {
alloc::alloc::handle_alloc_error(layout);
}
unsafe {
core::ptr::copy_nonoverlapping(self.ptr.as_ptr(), ptr, self.len);
}
Self {
ptr: NonNull::new(ptr).unwrap(),
len: self.len,
}
}
}
impl Drop for CacheAlignedL1L2 {
fn drop(&mut self) {
if self.len > 0 {
let layout =
Layout::from_size_align(self.len * 16, CACHE_LINE_SIZE).expect("layout error");
unsafe {
alloc::alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout);
}
}
}
}
impl Default for CacheAlignedL1L2 {
fn default() -> Self {
Self::empty()
}
}
unsafe impl Send for CacheAlignedL1L2 {}
unsafe impl Sync for CacheAlignedL1L2 {}
struct CacheAlignedL1L2Builder {
ptr: NonNull<u128>,
len: usize,
capacity: usize,
}
impl CacheAlignedL1L2Builder {
fn with_capacity(capacity: usize) -> Self {
if capacity == 0 {
return Self {
ptr: NonNull::dangling(),
len: 0,
capacity: 0,
};
}
let layout = Layout::from_size_align(capacity * 16, CACHE_LINE_SIZE).expect("layout error");
let ptr = unsafe { alloc::alloc::alloc(layout) as *mut u128 };
if ptr.is_null() {
alloc::alloc::handle_alloc_error(layout);
}
Self {
ptr: NonNull::new(ptr).unwrap(),
len: 0,
capacity,
}
}
#[inline]
fn push(&mut self, value: u128) {
debug_assert!(
self.len < self.capacity,
"CacheAlignedL1L2Builder overflow: len={}, capacity={}",
self.len,
self.capacity
);
unsafe {
self.ptr.as_ptr().add(self.len).write(value);
}
self.len += 1;
}
fn build(self) -> CacheAlignedL1L2 {
if self.len == 0 {
if self.capacity > 0 {
let layout =
Layout::from_size_align(self.capacity * 16, CACHE_LINE_SIZE).expect("layout");
unsafe {
alloc::alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout);
}
}
return CacheAlignedL1L2::empty();
}
if self.len == self.capacity {
let result = CacheAlignedL1L2 {
ptr: self.ptr,
len: self.len,
};
core::mem::forget(self);
return result;
}
let new_layout =
Layout::from_size_align(self.len * 16, CACHE_LINE_SIZE).expect("layout error");
let new_ptr = unsafe { alloc::alloc::alloc(new_layout) as *mut u128 };
if new_ptr.is_null() {
alloc::alloc::handle_alloc_error(new_layout);
}
unsafe {
core::ptr::copy_nonoverlapping(self.ptr.as_ptr(), new_ptr, self.len);
}
let old_layout =
Layout::from_size_align(self.capacity * 16, CACHE_LINE_SIZE).expect("layout");
unsafe {
alloc::alloc::dealloc(self.ptr.as_ptr() as *mut u8, old_layout);
}
let result = CacheAlignedL1L2 {
ptr: NonNull::new(new_ptr).unwrap(),
len: self.len,
};
core::mem::forget(self);
result
}
}
impl Drop for CacheAlignedL1L2Builder {
fn drop(&mut self) {
if self.capacity > 0 {
let layout =
Layout::from_size_align(self.capacity * 16, CACHE_LINE_SIZE).expect("layout error");
unsafe {
alloc::alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout);
}
}
}
}
#[cfg(feature = "serde")]
impl Serialize for CacheAlignedL1L2 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.as_slice().serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for CacheAlignedL1L2 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let data: Vec<u128> = Vec::deserialize(deserializer)?;
Ok(Self::from_vec(data))
}
}
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RankDirectory {
l0: Vec<u64>,
l1_l2: CacheAlignedL1L2,
}
impl RankDirectory {
pub fn empty() -> Self {
Self {
l0: Vec::new(),
l1_l2: CacheAlignedL1L2::default(),
}
}
pub fn build(words: &[u64]) -> Self {
if words.is_empty() {
return Self::empty();
}
let num_blocks = words.len().div_ceil(WORDS_PER_BLOCK);
let mut l0 = Vec::new();
let mut l1_l2_builder = CacheAlignedL1L2::builder(num_blocks);
let mut cumulative_rank: u64 = 0;
let mut l0_base: u64 = 0;
for block_idx in 0..num_blocks {
if block_idx > 0 && block_idx % BLOCKS_PER_SUPERBLOCK == 0 {
l0.push(cumulative_rank);
l0_base = cumulative_rank;
}
let block_start = block_idx * WORDS_PER_BLOCK;
let block_end = (block_start + WORDS_PER_BLOCK).min(words.len());
let l1_rank = (cumulative_rank - l0_base) as u32;
let mut l2_offsets = [0u16; 7];
let mut block_cumulative: u16 = 0;
for (i, word_idx) in (block_start..block_end).enumerate() {
if i > 0 && i < 8 {
l2_offsets[i - 1] = block_cumulative;
}
block_cumulative += words[word_idx].count_ones() as u16;
}
let mut entry: u128 = l1_rank as u128;
for (i, &offset) in l2_offsets.iter().enumerate() {
entry |= (offset as u128) << (32 + i * 9);
}
l1_l2_builder.push(entry);
cumulative_rank += block_cumulative as u64;
}
Self {
l0,
l1_l2: l1_l2_builder.build(),
}
}
#[inline]
pub fn rank_at_word(&self, word_idx: usize) -> usize {
let data = self.l1_l2.as_slice();
if data.is_empty() {
return 0;
}
let block_idx = word_idx / WORDS_PER_BLOCK;
let word_in_block = word_idx % WORDS_PER_BLOCK;
let block_idx = block_idx.min(data.len() - 1);
let l0_idx = block_idx / BLOCKS_PER_SUPERBLOCK;
let l0_rank = if l0_idx > 0 && l0_idx <= self.l0.len() {
self.l0[l0_idx - 1]
} else {
0
};
let entry = data[block_idx];
let l1_rank = (entry & 0xFFFF_FFFF) as u64;
let l2_rank = if word_in_block == 0 {
0
} else {
let l2_idx = word_in_block - 1;
let shift = 32 + l2_idx * 9;
((entry >> shift) & 0x1FF) as u64
};
(l0_rank + l1_rank + l2_rank) as usize
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_directory() {
let dir = RankDirectory::build(&[]);
assert_eq!(dir.rank_at_word(0), 0);
}
#[test]
fn test_single_word() {
let words = vec![0b1010_1010u64]; let dir = RankDirectory::build(&words);
assert_eq!(dir.rank_at_word(0), 0);
}
#[test]
fn test_multiple_words_single_block() {
let words: Vec<u64> = vec![0xFF; 8]; let dir = RankDirectory::build(&words);
assert_eq!(dir.rank_at_word(0), 0);
assert_eq!(dir.rank_at_word(1), 8);
assert_eq!(dir.rank_at_word(2), 16);
assert_eq!(dir.rank_at_word(7), 56);
}
#[test]
fn test_multiple_blocks() {
let words: Vec<u64> = vec![u64::MAX; 16]; let dir = RankDirectory::build(&words);
assert_eq!(dir.rank_at_word(0), 0);
assert_eq!(dir.rank_at_word(1), 64);
assert_eq!(dir.rank_at_word(7), 64 * 7);
assert_eq!(dir.rank_at_word(8), 64 * 8);
assert_eq!(dir.rank_at_word(9), 64 * 9);
}
#[test]
fn test_sparse_words() {
let words: Vec<u64> = vec![1; 16];
let dir = RankDirectory::build(&words);
assert_eq!(dir.rank_at_word(0), 0);
assert_eq!(dir.rank_at_word(1), 1);
assert_eq!(dir.rank_at_word(8), 8);
assert_eq!(dir.rank_at_word(15), 15);
}
#[test]
fn test_partial_block() {
let words: Vec<u64> = vec![0xFF; 5];
let dir = RankDirectory::build(&words);
assert_eq!(dir.rank_at_word(0), 0);
assert_eq!(dir.rank_at_word(1), 8);
assert_eq!(dir.rank_at_word(4), 32);
}
#[test]
fn test_l2_offset_storage() {
let mut words = vec![0u64; 8];
words[0] = 0b1111; words[1] = 0b11111111; words[2] = 0b11;
let dir = RankDirectory::build(&words);
assert_eq!(dir.rank_at_word(0), 0);
assert_eq!(dir.rank_at_word(1), 4);
assert_eq!(dir.rank_at_word(2), 12); assert_eq!(dir.rank_at_word(3), 14); }
#[test]
fn test_large_cumulative_rank() {
let words: Vec<u64> = vec![u64::MAX; 8];
let dir = RankDirectory::build(&words);
assert_eq!(dir.rank_at_word(7), 64 * 7); }
mod builder_tests {
use super::*;
#[test]
fn test_builder_empty() {
let builder = CacheAlignedL1L2::builder(0);
let result = builder.build();
assert!(result.is_empty());
assert_eq!(result.len(), 0);
}
#[test]
fn test_builder_single_element() {
let mut builder = CacheAlignedL1L2::builder(1);
builder.push(42);
let result = builder.build();
assert_eq!(result.len(), 1);
assert_eq!(result.as_slice(), &[42]);
}
#[test]
fn test_builder_multiple_elements() {
let mut builder = CacheAlignedL1L2::builder(4);
builder.push(1);
builder.push(2);
builder.push(3);
builder.push(4);
let result = builder.build();
assert_eq!(result.len(), 4);
assert_eq!(result.as_slice(), &[1, 2, 3, 4]);
}
#[test]
fn test_builder_partial_capacity() {
let mut builder = CacheAlignedL1L2::builder(10);
builder.push(100);
builder.push(200);
builder.push(300);
let result = builder.build();
assert_eq!(result.len(), 3);
assert_eq!(result.as_slice(), &[100, 200, 300]);
}
#[test]
fn test_builder_matches_from_vec() {
let values: Vec<u128> = (0..16).map(|i| i * 1000).collect();
let from_vec_result = CacheAlignedL1L2::from_vec(values.clone());
let mut builder = CacheAlignedL1L2::builder(values.len());
for &v in &values {
builder.push(v);
}
let builder_result = builder.build();
assert_eq!(from_vec_result.len(), builder_result.len());
assert_eq!(from_vec_result.as_slice(), builder_result.as_slice());
}
#[test]
fn test_builder_alignment() {
let mut builder = CacheAlignedL1L2::builder(8);
for i in 0..8 {
builder.push(i as u128);
}
let result = builder.build();
let ptr_addr = result.ptr.as_ptr() as usize;
assert_eq!(
ptr_addr % CACHE_LINE_SIZE,
0,
"Pointer should be cache-line aligned"
);
}
#[test]
fn test_builder_large_values() {
let mut builder = CacheAlignedL1L2::builder(3);
builder.push(u128::MAX);
builder.push(u128::MAX / 2);
builder.push(1);
let result = builder.build();
assert_eq!(result.as_slice(), &[u128::MAX, u128::MAX / 2, 1]);
}
#[test]
fn test_builder_drop_without_build() {
let mut builder = CacheAlignedL1L2::builder(100);
builder.push(1);
builder.push(2);
drop(builder);
}
}
}