mod __test__;
use std::{
cell::UnsafeCell,
collections::HashMap,
sync::atomic::{AtomicU16, Ordering},
sync::{Arc, RwLock},
};
#[derive(Debug)]
struct LocalCache {
target_cache: [(u64, u16); 64],
message_cache: [(u64, u16); 64],
file_cache: [(u64, u16); 64],
kv_cache: [(u64, u16); 64],
target_counter: u8,
message_counter: u8,
file_counter: u8,
kv_counter: u8,
}
impl LocalCache {
fn new() -> Self {
Self {
target_cache: [(0, 0); 64],
message_cache: [(0, 0); 64],
file_cache: [(0, 0); 64],
kv_cache: [(0, 0); 64],
target_counter: 0,
message_counter: 0,
file_counter: 0,
kv_counter: 0,
}
}
fn get_target(&self, hash: u64) -> Option<u16> {
self
.target_cache
.iter()
.find(|(h, _)| *h == hash)
.map(|(_, id)| *id)
}
fn put_target(&mut self, hash: u64, id: u16) {
let idx = self.target_counter as usize % 8;
self.target_cache[idx] = (hash, id);
self.target_counter = self.target_counter.wrapping_add(1);
}
fn get_message(&self, hash: u64) -> Option<u16> {
self
.message_cache
.iter()
.find(|(h, _)| *h == hash)
.map(|(_, id)| *id)
}
fn get_file(&self, hash: u64) -> Option<u16> {
self
.file_cache
.iter()
.find(|(h, _)| *h == hash)
.map(|(_, id)| *id)
}
fn put_message(&mut self, hash: u64, id: u16) {
let idx = self.message_counter as usize % 16;
self.message_cache[idx] = (hash, id);
self.message_counter = self.message_counter.wrapping_add(1);
}
fn put_file(&mut self, hash: u64, id: u16) {
let idx = self.file_counter as usize % 8;
self.file_cache[idx] = (hash, id);
self.file_counter = self.file_counter.wrapping_add(1);
}
fn get_kv(&self, hash: u64) -> Option<u16> {
self
.kv_cache
.iter()
.find(|(h, _)| *h == hash)
.map(|(_, id)| *id)
}
fn put_kv(&mut self, hash: u64, id: u16) {
let idx = self.kv_counter as usize % 8;
self.kv_cache[idx] = (hash, id);
self.kv_counter = self.kv_counter.wrapping_add(1);
}
}
thread_local! {
static LOCAL_CACHE: UnsafeCell<LocalCache> = UnsafeCell::new(LocalCache::new());
}
#[derive(Debug)]
pub struct StringInterner {
targets: RwLock<Vec<Arc<str>>>,
messages: RwLock<Vec<Arc<str>>>,
files: RwLock<Vec<Arc<str>>>,
kvs: RwLock<Vec<Arc<smallvec::SmallVec<[u8; 128]>>>>,
target_lookup: RwLock<HashMap<u64, u16>>,
message_lookup: RwLock<HashMap<u64, u16>>,
file_lookup: RwLock<HashMap<u64, u16>>,
kv_lookup: RwLock<HashMap<u64, u16>>,
target_count: AtomicU16,
message_count: AtomicU16,
file_count: AtomicU16,
kv_count: AtomicU16,
}
impl StringInterner {
pub fn new() -> Self {
let mut targets = Vec::with_capacity(256);
let mut messages = Vec::with_capacity(4096);
let mut files = Vec::with_capacity(512);
let mut kvs = Vec::with_capacity(512);
targets.push(Arc::from(""));
messages.push(Arc::from(""));
files.push(Arc::from(""));
kvs.push(Arc::from(smallvec::SmallVec::new()));
Self {
targets: RwLock::new(targets),
messages: RwLock::new(messages),
files: RwLock::new(files),
kvs: RwLock::new(kvs),
target_lookup: RwLock::new(HashMap::with_capacity(256)),
message_lookup: RwLock::new(HashMap::with_capacity(4096)),
file_lookup: RwLock::new(HashMap::with_capacity(512)),
kv_lookup: RwLock::new(HashMap::with_capacity(512)),
target_count: AtomicU16::new(1),
message_count: AtomicU16::new(1),
file_count: AtomicU16::new(1),
kv_count: AtomicU16::new(1),
}
}
#[inline]
pub fn intern_target(&self, string: &str) -> u16 {
let hash = self.fast_hash(string);
LOCAL_CACHE.with(|cache| {
let cache_ptr = cache.get();
unsafe {
if let Some(id) = (*cache_ptr).get_target(hash) {
return id;
}
}
let id = self.intern_string_slow(
string,
&self.targets,
&self.target_lookup,
&self.target_count,
);
unsafe {
(*cache_ptr).put_target(hash, id);
}
id
})
}
#[inline]
pub fn intern_message(&self, string: &str) -> u16 {
let hash = self.fast_hash(string);
LOCAL_CACHE.with(|cache| {
let cache_ptr = cache.get();
unsafe {
if let Some(id) = (*cache_ptr).get_message(hash) {
return id;
}
}
let id = self.intern_string_slow(
string,
&self.messages,
&self.message_lookup,
&self.message_count,
);
unsafe {
(*cache_ptr).put_message(hash, id);
}
id
})
}
#[inline]
pub fn intern_file(&self, string: &str) -> u16 {
let hash = self.fast_hash(string);
LOCAL_CACHE.with(|cache| {
let cache_ptr = cache.get();
unsafe {
if let Some(id) = (*cache_ptr).get_file(hash) {
return id;
}
}
let id = self.intern_string_slow(string, &self.files, &self.file_lookup, &self.file_count);
unsafe {
(*cache_ptr).put_file(hash, id);
}
id
})
}
#[inline]
pub fn intern_kv(&self, buf: smallvec::SmallVec<[u8; 128]>) -> u16 {
let hash = self.fast_hash_smallvec(&buf);
LOCAL_CACHE.with(|cache| {
let cache_ptr = cache.get();
unsafe {
if let Some(id) = (*cache_ptr).get_kv(hash) {
return id;
}
}
let id = self.intern_string_slow_smallvec(buf, &self.kvs, &self.kv_lookup, &self.kv_count);
unsafe {
(*cache_ptr).put_kv(hash, id);
}
id
})
}
#[cold]
fn intern_string_slow_smallvec(
&self,
string: smallvec::SmallVec<[u8; 128]>,
storage: &RwLock<Vec<Arc<smallvec::SmallVec<[u8; 128]>>>>,
lookup: &RwLock<HashMap<u64, u16>>,
counter: &AtomicU16,
) -> u16 {
let hash = self.fast_hash_smallvec(&string);
if let Ok(lookup_guard) = lookup.read() {
if let Some(&id) = lookup_guard.get(&hash) {
return id;
}
}
let mut lookup_guard = lookup.write().unwrap();
if let Some(&id) = lookup_guard.get(&hash) {
return id;
}
let mut storage_guard = storage.write().unwrap();
let id = storage_guard.len() as u16;
if id == u16::MAX {
return 0;
}
storage_guard.push(Arc::from(string));
lookup_guard.insert(hash, id);
counter.store(id + 1, Ordering::Relaxed);
id
}
#[cold]
fn intern_string_slow(
&self,
string: &str,
storage: &RwLock<Vec<Arc<str>>>,
lookup: &RwLock<HashMap<u64, u16>>,
counter: &AtomicU16,
) -> u16 {
let hash = self.fast_hash(string);
if let Ok(lookup_guard) = lookup.read() {
if let Some(&id) = lookup_guard.get(&hash) {
return id;
}
}
let mut lookup_guard = lookup.write().unwrap();
if let Some(&id) = lookup_guard.get(&hash) {
return id;
}
let mut storage_guard = storage.write().unwrap();
let id = storage_guard.len() as u16;
if id == u16::MAX {
return 0;
}
storage_guard.push(Arc::from(string));
lookup_guard.insert(hash, id);
counter.store(id + 1, Ordering::Relaxed);
id
}
pub fn get_file(&self, id: u16) -> Option<Arc<str>> {
self.files.read().unwrap().get(id as usize).cloned()
}
pub fn get_target(&self, id: u16) -> Option<Arc<str>> {
self.targets.read().unwrap().get(id as usize).cloned()
}
pub fn get_message(&self, id: u16) -> Option<Arc<str>> {
self.messages.read().unwrap().get(id as usize).cloned()
}
pub fn get_kv(&self, id: u16) -> Option<Arc<smallvec::SmallVec<[u8; 128]>>> {
self.kvs.read().unwrap().get(id as usize).cloned()
}
pub fn stats(&self) -> (usize, usize, usize) {
(
self.target_count.load(Ordering::Relaxed) as usize,
self.message_count.load(Ordering::Relaxed) as usize,
self.kv_count.load(Ordering::Relaxed) as usize,
)
}
#[inline]
fn fast_hash_smallvec(&self, s: &smallvec::SmallVec<[u8; 128]>) -> u64 {
let mut hash = 0xcbf29ce484222325u64; let bytes: &[u8] = s.as_slice();
let chunks = bytes.chunks_exact(8);
let remainder = chunks.remainder();
for chunk in chunks {
let chunk_u64 = u64::from_le_bytes(chunk.try_into().unwrap());
hash ^= chunk_u64;
hash = hash.wrapping_mul(0x100000001b3);
}
for &byte in remainder {
hash ^= byte as u64;
hash = hash.wrapping_mul(0x100000001b3);
}
hash
}
#[inline]
fn fast_hash(&self, s: &str) -> u64 {
let mut hash = 0xcbf29ce484222325u64; let bytes = s.as_bytes();
let chunks = bytes.chunks_exact(8);
let remainder = chunks.remainder();
for chunk in chunks {
let chunk_u64 = unsafe { std::ptr::read_unaligned(chunk.as_ptr() as *const u64) };
hash ^= chunk_u64;
hash = hash.wrapping_mul(0x100000001b3); }
for &byte in remainder {
hash ^= byte as u64;
hash = hash.wrapping_mul(0x100000001b3);
}
hash
}
}
impl Default for StringInterner {
fn default() -> Self {
Self::new()
}
}