1use std::{ops::Deref, sync::LazyLock};
2
3use hashbrown::{HashTable, hash_table::Entry};
4use parking_lot::{Mutex, MutexGuard};
5use triomphe::Arc;
6
7type LockedShard = HashTable<Arc<[u8]>>;
8type Shard = Mutex<LockedShard>;
9
10#[derive(Debug, Default, Clone, Copy)]
11pub struct MemoryUsage {
12 pub len: usize,
13 pub capacity: usize,
14}
15
16pub(crate) struct ShardedSet {
17 pub(crate) shift: usize,
18 pub(crate) hash_builder: ahash::RandomState,
19 pub(crate) shards: Box<[Shard]>,
20}
21
22impl ShardedSet {
23 fn get_hash_and_shard(&self, value: &[u8]) -> (u64, MutexGuard<LockedShard>) {
24 let hash = self.hash_builder.hash_one(value);
26 let idx = ((hash << 7) >> self.shift) as usize;
28 let shard = self.shards[idx].lock();
29 (hash, shard)
30 }
31
32 fn hasher(&self, value: &Arc<[u8]>) -> u64 {
33 self.hash_builder.hash_one(value.deref())
34 }
35
36 pub(crate) fn get_from_existing_ref(&self, value: &[u8]) -> Option<Arc<[u8]>> {
37 let (hash, shard) = self.get_hash_and_shard(value);
38 shard
39 .find(hash, |o| std::ptr::addr_eq(o.as_ptr(), value.as_ptr()))
40 .cloned()
41 }
42
43 pub(crate) fn get_or_insert(&self, value: &[u8]) -> Arc<[u8]> {
44 let (hash, mut shard) = self.get_hash_and_shard(value);
45
46 shard
47 .entry(hash, |o| o.deref() == value, |o| self.hasher(o))
48 .or_insert_with(|| Arc::from(value))
49 .get()
50 .clone()
51 }
52
53 pub(crate) fn remove_if_needed(&self, value: &Arc<[u8]>) {
56 const MINIMUM_STRONG_COUNT: usize = 2;
57
58 if Arc::strong_count(value) > MINIMUM_STRONG_COUNT {
59 return;
60 }
61
62 let (hash, mut shard) = self.get_hash_and_shard(value);
63
64 let entry = shard.entry(
65 hash,
66 |o| std::ptr::addr_eq(o.as_ptr(), value.as_ptr()),
67 |o| self.hasher(o),
68 );
69
70 if let Entry::Occupied(entry) = entry
71 && Arc::strong_count(entry.get()) <= MINIMUM_STRONG_COUNT
72 {
73 entry.remove();
74 }
75 }
76
77 pub(crate) fn is_empty(&self) -> bool {
78 self.len() == 0
79 }
80
81 pub(crate) fn len(&self) -> usize {
82 self.shards.iter().map(|o| o.lock().len()).sum()
83 }
84
85 pub(crate) fn capacity(&self) -> usize {
86 self.shards.iter().map(|o| o.lock().capacity()).sum()
87 }
88
89 pub(crate) fn get_memory_usage(&self) -> MemoryUsage {
90 self.shards
91 .iter()
92 .map(|o| {
93 let o = o.lock();
94 MemoryUsage {
95 len: o.len(),
96 capacity: o.capacity(),
97 }
98 })
99 .reduce(|acc, o| MemoryUsage {
100 len: acc.len + o.len,
101 capacity: acc.capacity + o.capacity,
102 })
103 .unwrap_or_default()
104 }
105
106 pub(crate) fn shrink_to_fit(&self) {
107 for shard in self.shards.iter() {
108 shard.lock().shrink_to_fit(|o| self.hasher(o));
109 }
110 }
111}
112
113impl Default for ShardedSet {
114 fn default() -> Self {
115 static DEFAULT_SHARDS_COUNT: LazyLock<usize> = LazyLock::new(|| {
117 (std::thread::available_parallelism().map_or(1, usize::from) * 4).next_power_of_two()
118 });
119
120 let shift =
122 (std::mem::size_of::<usize>() * 8) - DEFAULT_SHARDS_COUNT.trailing_zeros() as usize;
123
124 Self {
125 shift,
126 hash_builder: Default::default(),
127 shards: (0..*DEFAULT_SHARDS_COUNT)
128 .map(|_| Default::default())
129 .collect(),
130 }
131 }
132}
133
134pub(crate) static POOL: LazyLock<ShardedSet> = LazyLock::new(Default::default);
135
136pub fn is_empty() -> bool {
137 POOL.is_empty()
138}
139
140pub fn len() -> usize {
141 POOL.len()
142}
143
144pub fn capacity() -> usize {
145 POOL.capacity()
146}
147
148pub fn get_memory_usage() -> MemoryUsage {
149 POOL.get_memory_usage()
150}
151
152pub fn shrink_to_fit() {
153 POOL.shrink_to_fit();
154}