35 # include <tpl_dynArray.H> 36 # include <array_it.H> 38 # include <hash-dry.H> 40 # include <hash-fct.H> 42 using namespace Aleph;
80 template <
typename Key,
class Cmp = Aleph::equal_to<Key>>
82 :
public OhashCommon<ODhashTable<Key, Cmp>, Key>,
87 public StlAlephIterator<ODhashTable<Key, Cmp>>
89 friend class OhashCommon<ODhashTable<Key, Cmp>, Key>;
95 using Item_Type = Key;
97 using Hash_Fct = std::function<size_t(const Key &)>;
99 using Hash_Fct_Ptr = size_t (*) (
const Key&);
101 enum Status { EMPTY, BUSY, DELETED };
103 enum Probe { NO_PROBED, FIRST_PROBE, SECOND_PROBE, LINEAR_PROBE };
108 unsigned char status = EMPTY;
109 unsigned char probe_type = NO_PROBED;
110 unsigned int probe_counter = 0;
113 : key(), status(EMPTY), probe_type(NO_PROBED), probe_counter(0)
116 void reset() noexcept
119 probe_type = NO_PROBED;
125 return (status == EMPTY or status == DELETED or status == BUSY) and
126 (probe_type == NO_PROBED or probe_type == FIRST_PROBE or
127 probe_type == SECOND_PROBE or probe_type == LINEAR_PROBE);
130 friend ostream & operator << (ostream & out,
const Bucket & bucket)
133 switch (bucket.status)
135 case EMPTY: status =
"EMPTY";
break;
136 case BUSY: status =
"BUSY";
break;
137 case DELETED: status =
"DELETED";
break;
140 switch (bucket.probe_type)
142 case NO_PROBED: probe_type =
"NO_PROBED";
break;
143 case FIRST_PROBE: probe_type =
"FIRST_PROBE";
break;
144 case SECOND_PROBE: probe_type =
"SECOND_PROBE";
break;
145 case LINEAR_PROBE: probe_type =
"LINEAR_PROBE";
break;
147 return out <<
"Bucket at " << &bucket << endl
148 <<
"status = " << status << endl
149 <<
"probe_type = " << probe_type << endl
150 <<
"probe_counter = " << bucket.probe_counter;
154 Bucket * table =
nullptr;
155 Hash_Fct hash_fct =
nullptr;
156 Hash_Fct second_hash_fct =
nullptr;
171 Bucket * allocate_bucket(Bucket & bucket,
unsigned char probe_type) noexcept
173 assert(bucket.status != BUSY);
176 bucket.status = BUSY;
177 bucket.probe_type = probe_type;
178 bucket.probe_counter++;
183 void decrease_probe_counter(Bucket * bucket) noexcept
185 assert(bucket->status == BUSY or bucket->status == DELETED);
187 bucket->probe_counter--;
188 if (bucket->probe_counter == 0)
189 bucket->status = EMPTY;
192 void deallocate_bucket(Bucket * bucket) noexcept
194 assert(bucket->status == BUSY);
196 bucket->status = DELETED;
197 const Key & key = bucket->key;
199 const size_t i_fst = hash_fct(key) % len;
200 if (&table[i_fst] == bucket)
202 assert(Cmp () (table[i_fst].key, key));
203 assert(table[i_fst].probe_type == FIRST_PROBE);
207 const size_t i_snd = second_hash_fct(key) % len;
208 if (&table[i_snd] == bucket)
210 assert(Cmp () (table[i_snd].key, key));
211 assert(table[i_snd].probe_type == SECOND_PROBE);
212 decrease_probe_counter(&table[i_fst]);
216 decrease_probe_counter(&table[i_fst]);
217 decrease_probe_counter(&table[i_snd]);
219 for (index_forward(i); &table[i] != bucket; index_forward(i))
221 assert(table[i].status != EMPTY);
222 decrease_probe_counter(&table[i]);
224 assert(Cmp () (table[i].key, key));
225 assert(table[i].probe_type == LINEAR_PROBE);
229 decrease_probe_counter(bucket);
233 size_t index_forward(
size_t & i)
const noexcept
241 size_t index_backward(
size_t & i)
const noexcept
249 static Bucket * key_to_bucket(Key * rec) noexcept
251 Bucket * ret_val = 0;
252 const long offset = (long) &ret_val->key;
253 return (Bucket*) ((long) rec - offset);
256 bool is_valid_bucket(Bucket * bucket) noexcept
258 if (bucket < &table[0] or bucket >= &table[len])
261 const int offset_with_base = (
char*) bucket - (
char*) &table[0];
262 return offset_with_base %
sizeof(*bucket) == 0;
265 size_t bucket_to_index(Bucket * bucket) noexcept
267 assert(is_valid_bucket(bucket));
268 return bucket - &table[0];
273 const Cmp & get_compare()
const {
return cmp; }
275 Cmp & get_compare() {
return cmp; }
277 void swap(ODhashTable & other) noexcept
279 std::swap(table, other.table);
280 std::swap(hash_fct, other.hash_fct);
281 std::swap(second_hash_fct, other.second_hash_fct);
282 std::swap(cmp, other.cmp);
283 std::swap(N, other.N);
284 std::swap(len, other.len);
289 ODhashTable(
size_t __len, Hash_Fct __first_hash_fct,
290 Hash_Fct __second_hash_fct, Cmp __cmp,
291 float __lower_alpha,
float __upper_alpha,
bool __with_resize)
292 : table(
nullptr), hash_fct(__first_hash_fct),
293 second_hash_fct(__second_hash_fct), cmp(__cmp),
294 len(Primes::next_prime(__len)),
295 lower_alpha(__lower_alpha), upper_alpha(__upper_alpha),
296 N(0), with_resize(__with_resize)
298 table =
new Bucket[len];
313 Hash_Fct_Ptr first_hash_fct = Aleph::dft_hash_fct<Key>,
314 Hash_Fct_Ptr second_hash_fct = Aleph::snd_hash_fct<Key>,
316 float lower_alpha = hash_default_lower_alpha,
317 float upper_alpha = hash_default_upper_alpha,
318 bool with_resize =
true)
319 : ODhashTable(len, Hash_Fct(first_hash_fct), Hash_Fct(second_hash_fct),
320 cmp, lower_alpha, upper_alpha, with_resize) {}
325 if (table !=
nullptr)
329 ODhashTable(
const ODhashTable & other)
330 : ODhashTable(other.len, other.hash_fct, other.second_hash_fct, other.cmp,
331 other.lower_alpha, other.upper_alpha, other.with_resize)
333 assert(table !=
nullptr);
334 this->copy_from_table(other);
337 ODhashTable(ODhashTable && other) : ODhashTable(other)
339 assert(table !=
nullptr);
343 Special_Ctors(ODhashTable, Key);
345 ODhashTable & operator = (
const ODhashTable & other)
354 Bucket * new_table =
new Bucket [other.len];
359 hash_fct = other.hash_fct;
360 second_hash_fct = other.second_hash_fct;
362 lower_alpha = other.lower_alpha;
363 upper_alpha = other.upper_alpha;
364 with_resize = other.with_resize;
367 this->copy_from_table(other);
372 ODhashTable & operator = (ODhashTable && other) noexcept
378 OHASH_COMMON(ODhashTable);
383 Key *
search(
const Key & key)
const noexcept
385 const size_t i_fst = hash_fct(key) % len;
386 if (table[i_fst].status == EMPTY)
389 if (table[i_fst].status == BUSY and cmp(table[i_fst].key, key))
391 assert(table[i_fst].probe_type == FIRST_PROBE);
392 assert(table[i_fst].probe_counter > 0);
393 return &table[i_fst].key;
396 const size_t i_snd = second_hash_fct(key) % len;
398 if (table[i_snd].status == EMPTY)
401 if (table[i_snd].status == BUSY and cmp(table[i_snd].key, key))
403 assert(table[i_snd].probe_type == SECOND_PROBE);
404 assert(table[i_snd].probe_counter > 0);
405 return &table[i_snd].key;
410 for (
size_t count = 0; count < len; ++count)
413 switch (table[i].status)
416 assert(table[i].probe_counter == 0);
419 assert(table[i].probe_counter > 0);
420 if (cmp(table[i].key, key))
422 assert(table[i].probe_type == LINEAR_PROBE);
423 return &table[i].key;
427 assert(table[i].probe_counter > 0);
429 default: AH_ERROR(
"ODhashTable search: inconsistent bucket status");
436 Hash_Fct get_second_hash_fct()
const noexcept {
return second_hash_fct; }
438 void set_second_hash_fct(Hash_Fct fct) noexcept
440 second_hash_fct = fct;
443 void set_second_hash_fct(Hash_Fct_Ptr fct) noexcept
445 second_hash_fct = Hash_Fct(fct);
450 Bucket * allocate_bucket(
const Key & key) noexcept
454 const size_t i_fst = hash_fct(key) % len;
455 if (table[i_fst].status != BUSY)
456 return allocate_bucket(table[i_fst], FIRST_PROBE);
458 if (cmp(table[i_fst].key, key))
461 const size_t i_snd = second_hash_fct(key) % len;
462 if (table[i_snd].status != BUSY)
464 table[i_fst].probe_counter++;
465 return allocate_bucket(table[i_snd], SECOND_PROBE);
468 if (cmp(table[i_snd].key, key))
472 for (
size_t c = 0; c < len; ++c)
475 switch (table[i].status)
478 if (cmp(table[i].key, key))
480 for (
size_t k = 0; k < c; ++k)
483 table[i].probe_counter--;
490 table[i_fst].probe_counter++;
491 table[i_snd].probe_counter++;
492 return allocate_bucket(table[i], LINEAR_PROBE);
493 default: AH_ERROR(
"ODhashTable: Invalid bucket status");
break;
495 table[i].probe_counter++;
502 tuple<Bucket*, bool> hard_allocate_bucket(
const Key & key) noexcept
506 const size_t i_fst = hash_fct(key) % len;
507 if (table[i_fst].status != BUSY)
508 return tuple<Bucket*, bool>(allocate_bucket(table[i_fst], FIRST_PROBE),
511 if (cmp(table[i_fst].key, key))
512 return tuple<Bucket*, bool>(&table[i_fst],
true);
514 const size_t i_snd = second_hash_fct(key) % len;
515 if (table[i_snd].status != BUSY)
517 table[i_fst].probe_counter++;
518 return tuple<Bucket*, bool>(allocate_bucket(table[i_snd], SECOND_PROBE),
522 if (cmp(table[i_snd].key, key))
523 return tuple<Bucket*, bool>(&table[i_snd],
true);
526 for (
size_t c = 0; c < len; ++c)
529 switch (table[i].status)
532 if (cmp(table[i].key, key))
534 const size_t idx = i;
535 for (
size_t k = 0; k < c; ++k)
538 table[i].probe_counter--;
540 return tuple<Bucket*, bool>(&table[idx],
true);
545 table[i_fst].probe_counter++;
546 table[i_snd].probe_counter++;
547 return make_tuple(allocate_bucket(table[i], LINEAR_PROBE),
false);
548 default: AH_ERROR(
"ODhashTable: Invalid bucket status");
break;
550 table[i].probe_counter++;
553 return tuple<Bucket*, bool>((Bucket*)
nullptr,
false);
561 void remove_bucket(Bucket * bucket)
563 if (not is_valid_bucket(bucket))
564 throw std::invalid_argument(
"key pty does not belong to hash table");
566 if (bucket->status != BUSY)
567 throw std::domain_error(
"Bucket containing key is not BUSY");
569 deallocate_bucket(bucket);
573 bool process_entry_for_remove(Bucket * bucket,
const Key & key)
575 switch (bucket->status)
578 throw std::domain_error(
"Key not in hash table");
580 if (cmp(bucket->key, key))
582 bucket->status = DELETED;
583 decrease_probe_counter(bucket);
588 decrease_probe_counter(bucket);
590 default: AH_ERROR(
"Inconsistent bucket status");
597 void remove(
const Key & key)
602 const size_t i_fst = hash_fct(key) % len;
603 if (process_entry_for_remove(&table[i_fst], key))
606 const size_t i_snd = second_hash_fct(key) % len;
607 if (process_entry_for_remove(&table[i_snd], key))
611 for (
size_t c = 0; i < len; ++c)
614 if (process_entry_for_remove(&table[i], key))
626 using Stats =
typename OhashCommon<ODhashTable<Key, Cmp>, Key>::Stats;
631 size_t num_busy = 0, num_deleted = 0, num_empty = 0;
632 size_t max_len = std::numeric_limits<size_t>::min();
633 for (
size_t i = 0; i < len; ++i)
634 switch (table[i].status)
639 const Key & key = table[i].key;
641 const size_t i_fst = hash_fct(key) % len;
642 if (cmp(table[i_fst].key, key))
644 assert(table[i_fst].probe_type == FIRST_PROBE);
645 assert(table[i_fst].probe_counter > 0);
651 size_t i_snd = second_hash_fct(key) % len;
652 if (cmp(table[i_snd].key, key))
654 assert(table[i_snd].probe_type == SECOND_PROBE);
655 assert(table[i_snd].probe_counter > 0);
660 for (
size_t i = index_forward(i_snd);
true;
663 if (table[i].status == BUSY and cmp(table[i].key, key))
669 max_len = std::max(max_len, count);
670 update_stat_len(lens, count);
675 update_stat_len(lens, 0);
682 float avg = 0, sum = 0;
683 for (
size_t i = 0; i < lens.
size(); ++i)
691 for (
size_t i = 0; i < lens.
size(); ++i)
699 stats.num_busy = num_busy;
700 stats.num_deleted = num_deleted;
701 stats.num_empty = num_empty;
702 std::swap(lens, stats.lens);
705 stats.max_len = max_len;
712 template <
typename Key,
class Cmp = Aleph::equal_to<Key>>
736 # endif // TPL_ODHASH_H
ODhashTable(size_t len=Primes::DefaultPrime, Hash_Fct_Ptr first_hash_fct=Aleph::dft_hash_fct< Key >, Hash_Fct_Ptr second_hash_fct=Aleph::snd_hash_fct< Key >, Cmp cmp=Cmp(), float lower_alpha=hash_default_lower_alpha, float upper_alpha=hash_default_upper_alpha, bool with_resize=true)
Definition: tpl_odhash.H:312
size_t size() const noexcept
Definition: tpl_dynArray.H:641
~ODhashTable()
Libera toda la tabla hash.
Definition: tpl_odhash.H:323
Key * search(const Key &key) const noexcept
Definition: tpl_odhash.H:383
Definition: tpl_odhash.H:81
Definition: htlist.H:1323