20#ifndef REVERSE_PURGE_HASH_MAP_IMPL_HPP_
21#define REVERSE_PURGE_HASH_MAP_IMPL_HPP_
28#include "MurmurHash3.h"
33template<
typename K,
typename V,
typename H,
typename E,
typename A>
34constexpr uint32_t reverse_purge_hash_map<K, V, H, E, A>::MAX_SAMPLE_SIZE;
36template<
typename K,
typename V,
typename H,
typename E,
typename A>
37reverse_purge_hash_map<K, V, H, E, A>::reverse_purge_hash_map(uint8_t lg_cur_size, uint8_t lg_max_size,
38 const E& equal,
const A& allocator):
41lg_cur_size_(lg_cur_size),
42lg_max_size_(lg_max_size),
44keys_(allocator_.allocate(1ULL << lg_cur_size)),
48 AllocV av(allocator_);
49 values_ = av.allocate(1ULL << lg_cur_size);
50 AllocU16 au16(allocator_);
51 states_ = au16.allocate(1ULL << lg_cur_size);
52 std::fill(states_, states_ + (1ULL << lg_cur_size),
static_cast<uint16_t
>(0));
55template<
typename K,
typename V,
typename H,
typename E,
typename A>
56reverse_purge_hash_map<K, V, H, E, A>::reverse_purge_hash_map(
const reverse_purge_hash_map<K, V, H, E, A>& other):
58allocator_(other.allocator_),
59lg_cur_size_(other.lg_cur_size_),
60lg_max_size_(other.lg_max_size_),
61num_active_(other.num_active_),
62keys_(allocator_.allocate(1ULL << lg_cur_size_)),
66 AllocV av(allocator_);
67 values_ = av.allocate(1ULL << lg_cur_size_);
68 AllocU16 au16(allocator_);
69 states_ = au16.allocate(1ULL << lg_cur_size_);
70 const uint32_t size = 1 << lg_cur_size_;
71 if (num_active_ > 0) {
72 auto num = num_active_;
73 for (uint32_t i = 0; i < size; i++) {
74 if (other.states_[i] > 0) {
75 new (&keys_[i]) K(other.keys_[i]);
76 values_[i] = other.values_[i];
77 if (--num == 0) {
break; }
81 std::copy(other.states_, other.states_ + size, states_);
84template<
typename K,
typename V,
typename H,
typename E,
typename A>
85reverse_purge_hash_map<K, V, H, E, A>::reverse_purge_hash_map(reverse_purge_hash_map<K, V, H, E, A>&& other)
noexcept:
86equal_(std::move(other.equal_)),
87allocator_(std::move(other.allocator_)),
88lg_cur_size_(other.lg_cur_size_),
89lg_max_size_(other.lg_max_size_),
90num_active_(other.num_active_),
95 std::swap(keys_, other.keys_);
96 std::swap(values_, other.values_);
97 std::swap(states_, other.states_);
98 other.num_active_ = 0;
101template<
typename K,
typename V,
typename H,
typename E,
typename A>
102reverse_purge_hash_map<K, V, H, E, A>::~reverse_purge_hash_map() {
103 const uint32_t size = 1 << lg_cur_size_;
104 if (num_active_ > 0) {
105 for (uint32_t i = 0; i < size; i++) {
108 if (--num_active_ == 0) {
break; }
112 if (keys_ !=
nullptr) {
113 allocator_.deallocate(keys_, size);
115 if (values_ !=
nullptr) {
116 AllocV av(allocator_);
117 av.deallocate(values_, size);
119 if (states_ !=
nullptr) {
120 AllocU16 au16(allocator_);
121 au16.deallocate(states_, size);
125template<
typename K,
typename V,
typename H,
typename E,
typename A>
126reverse_purge_hash_map<K, V, H, E, A>& reverse_purge_hash_map<K, V, H, E, A>::operator=(
const reverse_purge_hash_map<K, V, H, E, A>& other) {
127 reverse_purge_hash_map copy(other);
128 std::swap(equal_, copy.equal_);
129 std::swap(allocator_, copy.allocator_);
130 std::swap(lg_cur_size_, copy.lg_cur_size_);
131 std::swap(lg_max_size_, copy.lg_max_size_);
132 std::swap(num_active_, copy.num_active_);
133 std::swap(keys_, copy.keys_);
134 std::swap(values_, copy.values_);
135 std::swap(states_, copy.states_);
139template<
typename K,
typename V,
typename H,
typename E,
typename A>
140reverse_purge_hash_map<K, V, H, E, A>& reverse_purge_hash_map<K, V, H, E, A>::operator=(reverse_purge_hash_map<K, V, H, E, A>&& other) {
141 std::swap(equal_, other.equal_);
142 std::swap(allocator_, other.allocator_);
143 std::swap(lg_cur_size_, other.lg_cur_size_);
144 std::swap(lg_max_size_, other.lg_max_size_);
145 std::swap(num_active_, other.num_active_);
146 std::swap(keys_, other.keys_);
147 std::swap(values_, other.values_);
148 std::swap(states_, other.states_);
152template<
typename K,
typename V,
typename H,
typename E,
typename A>
153template<
typename FwdK>
154V reverse_purge_hash_map<K, V, H, E, A>::adjust_or_insert(FwdK&& key, V value) {
155 const uint32_t num_active_before = num_active_;
156 const uint32_t index = internal_adjust_or_insert(key, value);
157 if (num_active_ > num_active_before) {
158 new (&keys_[index]) K(std::forward<FwdK>(key));
159 return resize_or_purge_if_needed();
164template<
typename K,
typename V,
typename H,
typename E,
typename A>
165V reverse_purge_hash_map<K, V, H, E, A>::get(
const K& key)
const {
166 const uint32_t mask = (1 << lg_cur_size_) - 1;
167 uint32_t probe = fmix64(H()(key)) & mask;
168 while (is_active(probe)) {
169 if (E()(keys_[probe], key)) {
return values_[probe]; }
170 probe = (probe + 1) & mask;
175template<
typename K,
typename V,
typename H,
typename E,
typename A>
176uint8_t reverse_purge_hash_map<K, V, H, E, A>::get_lg_cur_size()
const {
180template<
typename K,
typename V,
typename H,
typename E,
typename A>
181uint8_t reverse_purge_hash_map<K, V, H, E, A>::get_lg_max_size()
const {
185template<
typename K,
typename V,
typename H,
typename E,
typename A>
186uint32_t reverse_purge_hash_map<K, V, H, E, A>::get_capacity()
const {
187 return static_cast<uint32_t
>((1 << lg_cur_size_) * LOAD_FACTOR);
190template<
typename K,
typename V,
typename H,
typename E,
typename A>
191uint32_t reverse_purge_hash_map<K, V, H, E, A>::get_num_active()
const {
195template<
typename K,
typename V,
typename H,
typename E,
typename A>
196const A& reverse_purge_hash_map<K, V, H, E, A>::get_allocator()
const {
200template<
typename K,
typename V,
typename H,
typename E,
typename A>
201const E& reverse_purge_hash_map<K, V, H, E, A>::get_equal()
const {
205template<
typename K,
typename V,
typename H,
typename E,
typename A>
206typename reverse_purge_hash_map<K, V, H, E, A>::iterator reverse_purge_hash_map<K, V, H, E, A>::begin()
const {
207 const uint32_t size = 1 << lg_cur_size_;
209 while (i < size && !is_active(i)) i++;
210 return reverse_purge_hash_map<K, V, H, E, A>::iterator(
this, i, 0);
213template<
typename K,
typename V,
typename H,
typename E,
typename A>
214typename reverse_purge_hash_map<K, V, H, E, A>::iterator reverse_purge_hash_map<K, V, H, E, A>::end()
const {
215 return reverse_purge_hash_map<K, V, H, E, A>::iterator(
this, 1 << lg_cur_size_, num_active_);
218template<
typename K,
typename V,
typename H,
typename E,
typename A>
219bool reverse_purge_hash_map<K, V, H, E, A>::is_active(uint32_t index)
const {
220 return states_[index] > 0;
223template<
typename K,
typename V,
typename H,
typename E,
typename A>
224void reverse_purge_hash_map<K, V, H, E, A>::subtract_and_keep_positive_only(V amount) {
227 uint32_t first_probe = (1 << lg_cur_size_) - 1;
228 while (is_active(first_probe)) first_probe--;
231 for (uint32_t probe = first_probe; probe-- > 0;) {
232 if (is_active(probe)) {
233 if (values_[probe] <= amount) {
237 values_[probe] -= amount;
242 for (uint32_t probe = (1 << lg_cur_size_); probe-- > first_probe;) {
243 if (is_active(probe)) {
244 if (values_[probe] <= amount) {
248 values_[probe] -= amount;
254template<
typename K,
typename V,
typename H,
typename E,
typename A>
255void reverse_purge_hash_map<K, V, H, E, A>::hash_delete(uint32_t delete_index) {
259 states_[delete_index] = 0;
260 keys_[delete_index].~K();
262 const uint32_t mask = (1 << lg_cur_size_) - 1;
263 uint32_t probe = (delete_index + drift) & mask;
265 while (is_active(probe)) {
266 if (states_[probe] > drift) {
268 new (&keys_[delete_index]) K(std::move(keys_[probe]));
269 values_[delete_index] = values_[probe];
270 states_[delete_index] = states_[probe] - drift;
274 delete_index = probe;
276 probe = (probe + 1) & mask;
279 if (drift >= DRIFT_LIMIT) {
throw std::logic_error(
"drift: " + std::to_string(drift) +
" >= DRIFT_LIMIT"); }
283template<
typename K,
typename V,
typename H,
typename E,
typename A>
284uint32_t reverse_purge_hash_map<K, V, H, E, A>::internal_adjust_or_insert(
const K& key, V value) {
285 const uint32_t mask = (1 << lg_cur_size_) - 1;
286 uint32_t index = fmix64(H()(key)) & mask;
288 while (is_active(index)) {
289 if (E()(keys_[index], key)) {
291 values_[index] += value;
294 index = (index + 1) & mask;
297 if (drift >= DRIFT_LIMIT) {
throw std::logic_error(
"drift limit reached"); }
300 if (num_active_ > get_capacity()) {
301 throw std::logic_error(
"num_active " + std::to_string(num_active_) +
" > capacity " + std::to_string(get_capacity()));
303 values_[index] = value;
304 states_[index] = drift;
309template<
typename K,
typename V,
typename H,
typename E,
typename A>
310V reverse_purge_hash_map<K, V, H, E, A>::resize_or_purge_if_needed() {
311 if (num_active_ > get_capacity()) {
312 if (lg_cur_size_ < lg_max_size_) {
313 resize(lg_cur_size_ + 1);
315 const V offset = purge();
316 if (num_active_ > get_capacity()) {
317 throw std::logic_error(
"purge did not reduce number of active items");
325template<
typename K,
typename V,
typename H,
typename E,
typename A>
326void reverse_purge_hash_map<K, V, H, E, A>::resize(uint8_t lg_new_size) {
327 const uint32_t old_size = 1 << lg_cur_size_;
329 V* old_values = values_;
330 uint16_t* old_states = states_;
331 const uint32_t new_size = 1 << lg_new_size;
332 keys_ = allocator_.allocate(new_size);
333 AllocV av(allocator_);
334 values_ = av.allocate(new_size);
335 AllocU16 au16(allocator_);
336 states_ = au16.allocate(new_size);
337 std::fill(states_, states_ + new_size,
static_cast<uint16_t
>(0));
339 lg_cur_size_ = lg_new_size;
340 for (uint32_t i = 0; i < old_size; i++) {
341 if (old_states[i] > 0) {
342 adjust_or_insert(std::move(old_keys[i]), old_values[i]);
346 allocator_.deallocate(old_keys, old_size);
347 av.deallocate(old_values, old_size);
348 au16.deallocate(old_states, old_size);
351template<
typename K,
typename V,
typename H,
typename E,
typename A>
352V reverse_purge_hash_map<K, V, H, E, A>::purge() {
353 const uint32_t limit = std::min(MAX_SAMPLE_SIZE, num_active_);
354 uint32_t num_samples = 0;
356 AllocV av(allocator_);
357 V* samples = av.allocate(limit);
358 while (num_samples < limit) {
360 samples[num_samples++] = values_[i];
364 std::nth_element(samples, samples+ (num_samples / 2), samples + num_samples);
365 const V median = samples[num_samples / 2];
366 av.deallocate(samples, limit);
367 subtract_and_keep_positive_only(median);
DataSketches namespace.
Definition binomial_bounds.hpp:38