mod codec;
mod config;
mod key;
mod lookup;
pub mod manager;
mod memory;
pub use config::IdIndexerConfig;
pub use key::IdKey;
pub use lookup::PkLookup;
pub use manager::{stripe_index, IdManager};
pub use memory::IdIndexMemoryBreakdown;
pub const PK_DELTA_ANCHOR_THRESHOLD: usize = 8192;
#[derive(Debug, Clone)]
pub struct IdIndexer {
manager: std::sync::Arc<IdManager>,
}
impl IdIndexer {
pub fn new() -> Self {
Self::with_config(IdIndexerConfig::default())
}
pub fn with_capacity(capacity: usize) -> Self {
Self::with_config(IdIndexerConfig::default().with_initial_capacity(capacity))
}
pub fn with_config(config: IdIndexerConfig) -> Self {
Self {
manager: std::sync::Arc::new(IdManager::with_config(config.clone())),
}
}
pub fn insert(&self, key: IdKey) -> linkrs_core::error::StorageResult<u32> {
self.manager.insert(key)
}
pub fn reserve_next(&self) -> linkrs_core::error::StorageResult<u32> {
self.manager.reserve_next()
}
pub fn register_reserved(&self, key: IdKey, id: u32) -> linkrs_core::error::StorageResult<()> {
self.manager.register_reserved(key, id)
}
pub fn release_reserved(&self, id: u32) {
self.manager.release_reserved(id);
}
pub fn try_reclaim(&self, id: u32) -> bool {
self.manager.try_reclaim(id)
}
pub fn reserve(&self, additional: usize) {
self.manager.reserve(additional);
}
pub fn get_index(&self, key: &IdKey) -> Option<u32> {
self.manager.get_id(key)
}
pub fn get_key(&self, index: u32) -> Option<IdKey> {
self.manager.get_key(index)
}
pub fn len(&self) -> usize {
self.manager.len()
}
pub fn next_index(&self) -> u32 {
self.manager.next_index()
}
pub fn remove(&self, key: &IdKey) -> Option<u32> {
self.manager.remove(key)
}
pub fn lookup(&self, key: &IdKey, is_visible: impl Fn(u32) -> bool) -> PkLookup {
self.manager.lookup(key, is_visible)
}
pub fn delta_len(&self) -> usize {
self.manager.delta_len()
}
pub fn take_baseline_invalidated(&self) -> bool {
self.manager.take_baseline_invalidated()
}
pub fn reuse_count(&self) -> u64 {
self.manager.reuse_count()
}
pub fn baseline_invalidated(&self) -> bool {
self.manager.baseline_invalidated()
}
pub fn free_depth(&self) -> usize {
self.manager.free_depth()
}
pub fn hole_ratio(&self) -> f64 {
self.manager.hole_ratio()
}
pub fn memory_breakdown(&self) -> IdIndexMemoryBreakdown {
memory::memory_breakdown(&self.manager)
}
pub fn clear_index_delta(&self) {
self.manager.clear_index_delta()
}
pub fn serialize_delta(&self) -> Vec<u8> {
let core = self.manager.core.lock();
codec::serialize_delta(&core)
}
pub fn deserialize_delta(
data: &[u8],
) -> linkrs_core::error::StorageResult<Vec<(u8, u32, IdKey)>> {
codec::deserialize_delta(data)
}
pub fn apply_delta_entries(
&self,
entries: &[(u8, u32, IdKey)],
) -> linkrs_core::error::StorageResult<()> {
let mut guards: Vec<_> = self.manager.stripes.iter().map(|s| s.lock()).collect();
let mut core = self.manager.core.lock();
for (op, id, key) in entries {
match op {
0 => {
key::validate_key_shape(key)?;
let stripe = stripe_index(key);
if let Some(existing) = guards[stripe].get(key).copied() {
if existing != *id {
return Err(linkrs_core::error::StorageError::deserialize_error(
format!(
"pk delta insert diverges for {:?}: baseline {} vs delta {}",
key, existing, id
),
));
}
continue;
}
while core.keys.len() <= *id as usize {
core.keys.push(None);
}
core.keys[*id as usize] = Some(key.clone());
guards[stripe].insert(key.clone(), *id);
core.live_ids.insert(*id);
}
1 => {
let stripe = stripe_index(key);
if let Some(idx) = guards[stripe].remove(key) {
if (idx as usize) < core.keys.len() {
core.keys[idx as usize] = None;
}
core.live_ids.remove(&idx);
core.push_free(idx);
}
}
_ => {
return Err(linkrs_core::error::StorageError::deserialize_error(
format!("pk delta unknown op {}", op),
));
}
}
}
Ok(())
}
pub fn iter(&self) -> Vec<(IdKey, u32)> {
self.manager.iter()
}
pub fn live_ids(&self) -> Vec<u32> {
self.manager.live_ids()
}
pub fn memory_size(&self) -> usize {
self.manager.memory_size() + std::mem::size_of::<Self>()
}
pub fn compact(
&self,
) -> linkrs_core::error::StorageResult<std::collections::HashMap<u32, u32>> {
let mapping = self.compute_compact_mapping();
if mapping.is_empty() {
self.manager.core.lock().clear_free();
return Ok(std::collections::HashMap::new());
}
let mut guards: Vec<_> = self.manager.stripes.iter().map(|s| s.lock()).collect();
let mut core = self.manager.core.lock();
let mut entries: Vec<(u32, IdKey)> = Vec::new();
for map in guards.iter() {
entries.extend(map.iter().map(|(key, &idx)| (idx, key.clone())));
}
entries.sort_by_key(|(old_id, _)| *old_id);
rebuild_with_mapping_locked(&mut core, &mut guards, &entries)?;
core.clear_free();
core.delta_log.clear();
core.baseline_invalidated = true;
Ok(mapping)
}
pub fn compute_compact_mapping(&self) -> std::collections::HashMap<u32, u32> {
self.manager.compute_compact_mapping()
}
pub fn serialize(&self) -> Vec<u8> {
let core = self.manager.core.lock();
codec::serialize(&core)
}
pub fn snapshot_bytes(&self) -> Vec<u8> {
self.serialize()
}
pub fn restore_snapshot(&self, bytes: &[u8]) -> linkrs_core::error::StorageResult<()> {
let fresh = IdManager::deserialize(bytes)?;
let mut guards: Vec<_> = self.manager.stripes.iter().map(|s| s.lock()).collect();
let mut core = self.manager.core.lock();
let fresh_guards: Vec<_> = fresh.stripes.iter().map(|s| s.lock()).collect();
let fresh_core = fresh.core.lock();
for (dst, src) in guards.iter_mut().zip(fresh_guards.iter()) {
**dst = (**src).clone();
}
core.clone_from_fresh(&fresh_core);
Ok(())
}
pub fn deserialize(data: &[u8]) -> linkrs_core::error::StorageResult<Self> {
let manager = IdManager::deserialize(data)?;
Ok(Self {
manager: std::sync::Arc::new(manager),
})
}
}
impl Default for IdIndexer {
fn default() -> Self {
Self::new()
}
}
fn rebuild_with_mapping_locked(
core: &mut manager::SharedCore,
guards: &mut [parking_lot::MutexGuard<'_, std::collections::HashMap<IdKey, u32>>],
entries: &[(u32, IdKey)],
) -> linkrs_core::error::StorageResult<()> {
let mut new_keys = vec![None; entries.len()];
for (new_id, (_, key)) in entries.iter().enumerate() {
new_keys[new_id] = Some(key.clone());
}
for map in guards.iter_mut() {
map.clear();
}
for (new_id, (_, key)) in entries.iter().enumerate() {
let stripe = stripe_index(key);
guards[stripe].insert(key.clone(), new_id as u32);
}
core.keys = new_keys;
core.live_ids = (0..entries.len() as u32).collect();
core.clear_free();
Ok(())
}
#[cfg(test)]
mod tests;