use alloc::vec::Vec;
use hashbrown::HashTable;
use crate::sym::{LOCAL_MASK, Sym};
pub(crate) struct ShardWrite {
dedup: HashTable<Sym>,
offsets: Vec<u32>,
buffer: Vec<u8>,
}
struct StorageRollback<'a> {
offsets: &'a mut Vec<u32>,
buffer: &'a mut Vec<u8>,
local: usize,
}
impl Drop for StorageRollback<'_> {
fn drop(&mut self) {
self.offsets.truncate(self.local + 1);
self.buffer.truncate(self.offsets[self.local] as usize);
}
}
impl ShardWrite {
pub(crate) fn with_capacity(strings: usize, bytes: usize) -> Self {
let mut offsets = Vec::with_capacity(strings.saturating_add(1));
offsets.push(0);
Self {
dedup: HashTable::with_capacity(strings),
offsets,
buffer: Vec::with_capacity(bytes),
}
}
#[inline]
pub(crate) fn len(&self) -> usize {
self.offsets.len() - 1
}
#[inline]
fn str_at<'a>(offsets: &[u32], buffer: &'a [u8], local: usize) -> &'a str {
crate::storage::str_at(offsets, buffer, local)
}
pub(crate) fn insert_new(&mut self, idx: usize, h: u64, s: &str, rehash: impl Fn(&[u8]) -> u64) -> Sym {
let local0 = self.offsets.len() - 1;
assert!(local0 < LOCAL_MASK as usize, "internity: shard {idx} capacity exceeded");
#[expect(
clippy::cast_possible_truncation,
reason = "local0 is asserted above to fit in LOCAL_MASK (< 2^26)"
)]
let local0_u32 = local0 as u32;
let buffer_len = self.buffer.len();
let end = buffer_len
.checked_add(s.len())
.and_then(|n| u32::try_from(n).ok())
.expect("internity: shard bytes exceed u32");
let storage = StorageRollback {
offsets: &mut self.offsets,
buffer: &mut self.buffer,
local: local0,
};
storage.buffer.extend_from_slice(s.as_bytes());
storage.offsets.push(end);
let sym = Sym::pack(idx, local0_u32 + 1);
let offsets = &*storage.offsets;
let buffer = &*storage.buffer;
self.dedup.insert_unique(h, sym, |&sym| {
rehash(Self::str_at(offsets, buffer, sym.local() as usize).as_bytes())
});
core::mem::forget(storage);
sym
}
#[inline]
pub(crate) fn get(&self, h: u64, s: &str) -> Option<Sym> {
let offsets = &self.offsets;
let buffer = &self.buffer;
self.dedup
.find(h, |&sym| Self::str_at(offsets, buffer, sym.local() as usize) == s)
.copied()
}
#[inline]
pub(crate) fn get_bytes(&self, h: u64, bytes: &[u8]) -> Option<Sym> {
let offsets = &self.offsets;
let buffer = &self.buffer;
self.dedup
.find(h, |&sym| Self::str_at(offsets, buffer, sym.local() as usize).as_bytes() == bytes)
.copied()
}
pub(crate) fn into_parts(self) -> (Vec<u32>, Vec<u8>) {
(self.offsets, self.buffer)
}
pub(crate) fn parts(&self) -> (&[u32], &[u8]) {
(&self.offsets, &self.buffer)
}
#[cfg(test)]
pub(crate) fn capacities(&self) -> (usize, usize, usize) {
(self.dedup.capacity(), self.offsets.capacity(), self.buffer.capacity())
}
}