use dashmap::DashMap;
use std::sync::atomic::{AtomicUsize, Ordering};
#[derive(Debug)]
pub struct ShardedMappings {
id_to_idx: DashMap<u64, usize>,
idx_to_id: DashMap<usize, u64>,
next_idx: AtomicUsize,
}
impl Default for ShardedMappings {
fn default() -> Self {
Self::new()
}
}
impl ShardedMappings {
#[must_use]
pub fn new() -> Self {
Self {
id_to_idx: DashMap::new(),
idx_to_id: DashMap::new(),
next_idx: AtomicUsize::new(0),
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
id_to_idx: DashMap::with_capacity(capacity),
idx_to_id: DashMap::with_capacity(capacity),
next_idx: AtomicUsize::new(0),
}
}
pub fn register(&self, id: u64) -> Option<usize> {
use dashmap::mapref::entry::Entry;
match self.id_to_idx.entry(id) {
Entry::Occupied(_) => None,
Entry::Vacant(entry) => Some(self.allocate_and_map(entry, id)),
}
}
pub fn register_or_replace(&self, id: u64) -> (usize, Option<usize>) {
use dashmap::mapref::entry::Entry;
match self.id_to_idx.entry(id) {
Entry::Occupied(mut entry) => {
let old_idx = *entry.get();
let new_idx = self.next_idx.fetch_add(1, Ordering::Relaxed);
entry.insert(new_idx);
self.idx_to_id.remove(&old_idx);
self.idx_to_id.insert(new_idx, id);
(new_idx, Some(old_idx))
}
Entry::Vacant(entry) => (self.allocate_and_map(entry, id), None),
}
}
fn allocate_and_map(
&self,
entry: dashmap::mapref::entry::VacantEntry<'_, u64, usize>,
id: u64,
) -> usize {
let idx = self.next_idx.fetch_add(1, Ordering::Relaxed);
entry.insert(idx);
self.idx_to_id.insert(idx, id);
idx
}
#[allow(dead_code)] pub fn register_batch(&self, ids: &[u64]) -> Vec<(u64, usize)> {
let mut results = Vec::with_capacity(ids.len());
for &id in ids {
if let Some(idx) = self.register(id) {
results.push((id, idx));
}
}
results
}
pub fn restore(&self, id: u64, idx: usize) {
self.id_to_idx.insert(id, idx);
self.idx_to_id.insert(idx, id);
}
pub fn remove(&self, id: u64) -> Option<usize> {
if let Some((_, idx)) = self.id_to_idx.remove(&id) {
self.idx_to_id.remove(&idx);
Some(idx)
} else {
None
}
}
#[must_use]
pub fn get_idx(&self, id: u64) -> Option<usize> {
self.id_to_idx.get(&id).map(|r| *r)
}
#[must_use]
pub fn get_id(&self, idx: usize) -> Option<u64> {
self.idx_to_id.get(&idx).map(|r| *r)
}
#[must_use]
pub fn len(&self) -> usize {
self.id_to_idx.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.id_to_idx.is_empty()
}
#[must_use]
pub fn contains(&self, id: u64) -> bool {
self.id_to_idx.contains_key(&id)
}
pub fn iter(&self) -> impl Iterator<Item = (u64, usize)> + '_ {
self.id_to_idx.iter().map(|r| (*r.key(), *r.value()))
}
#[must_use]
pub fn next_idx(&self) -> usize {
self.next_idx.load(std::sync::atomic::Ordering::Relaxed)
}
pub fn clear(&self) {
self.id_to_idx.clear();
self.idx_to_id.clear();
self.next_idx.store(0, std::sync::atomic::Ordering::Relaxed);
}
#[must_use]
pub fn from_parts(
id_to_idx: std::collections::HashMap<u64, usize>,
idx_to_id: std::collections::HashMap<usize, u64>,
next_idx: usize,
) -> Self {
let sharded_id_to_idx = DashMap::with_capacity(id_to_idx.len());
let sharded_idx_to_id = DashMap::with_capacity(idx_to_id.len());
for (id, idx) in id_to_idx {
sharded_id_to_idx.insert(id, idx);
}
for (idx, id) in idx_to_id {
sharded_idx_to_id.insert(idx, id);
}
Self {
id_to_idx: sharded_id_to_idx,
idx_to_id: sharded_idx_to_id,
next_idx: AtomicUsize::new(next_idx),
}
}
#[must_use]
pub fn as_parts(
&self,
) -> (
std::collections::HashMap<u64, usize>,
std::collections::HashMap<usize, u64>,
usize,
) {
let id_to_idx: std::collections::HashMap<u64, usize> = self
.id_to_idx
.iter()
.map(|r| (*r.key(), *r.value()))
.collect();
let idx_to_id: std::collections::HashMap<usize, u64> = self
.idx_to_id
.iter()
.map(|r| (*r.key(), *r.value()))
.collect();
let next_idx = self.next_idx.load(Ordering::SeqCst);
(id_to_idx, idx_to_id, next_idx)
}
}