Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_odhash.H
Go to the documentation of this file.
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
101# ifndef TPL_ODHASH_H
102# define TPL_ODHASH_H
103
104# include <iostream>
105# include <cstddef>
106# include <cstdint>
107
108# include <primes.H>
109# include <dlink.H>
110# include <tpl_dynArray.H>
111# include <array_it.H>
112# include <ahDry.H>
113# include <hash-dry.H>
114# include <hashDry.H>
115# include <hash-fct.H>
116# include <ah-errors.H>
117
118using namespace Aleph;
119
120namespace Aleph
121{
122# ifdef N
123# define NBACKUP N
124# undef N
125# endif
126
127# ifdef M
128# define MBACKUP M
129# undef M
130# endif
131
132
172 template <typename Key, class Cmp = Aleph::equal_to<Key>>
174 : public OhashCommon<ODhashTable<Key, Cmp>, Key>,
175 public GenericTraverse<ODhashTable<Key, Cmp>>,
176 public LocateFunctions<ODhashTable<Key, Cmp>, Key>,
177 public FunctionalMethods<ODhashTable<Key, Cmp>, Key>,
178 public EqualToMethod<ODhashTable<Key, Cmp>>,
179 public StlAlephIterator<ODhashTable<Key, Cmp>>
180 {
181 friend class OhashCommon<ODhashTable<Key, Cmp>, Key>;
182
183 public:
184 using Key_Type = Key;
185
186 using Item_Type = Key;
187
188 using Hash_Fct = std::function<size_t(const Key &)>;
189
190 using Hash_Fct_Ptr = size_t (*)(const Key &);
191
193
195
196 struct Bucket
197 {
198 Key key;
199 unsigned char status = EMPTY; // status EMPTY, DELETED o BUSY
200 unsigned char probe_type = NO_PROBED; // FIRST_PROBE SECOND_PROBE LINEAR_PROBE
201 unsigned int probe_counter = 0;
202
205 { /* empty */
206 }
207
208 void reset() noexcept // put all as when constructed
209 {
210 status = EMPTY;
212 probe_counter = 0;
213 }
214
221
222 friend std::ostream &operator <<(std::ostream & out, const Bucket & bucket)
223 {
224 std::string status_str;
225 switch (bucket.status)
226 {
227 case EMPTY: status_str = "EMPTY";
228 break;
229 case BUSY: status_str = "BUSY";
230 break;
231 case DELETED: status_str = "DELETED";
232 break;
233 }
234 std::string probe_type_str;
235 switch (bucket.probe_type)
236 {
237 case NO_PROBED: probe_type_str = "NO_PROBED";
238 break;
239 case FIRST_PROBE: probe_type_str = "FIRST_PROBE";
240 break;
241 case SECOND_PROBE: probe_type_str = "SECOND_PROBE";
242 break;
243 case LINEAR_PROBE: probe_type_str = "LINEAR_PROBE";
244 break;
245 }
246 return out << "Bucket at " << &bucket << '\n'
247 << "status = " << status_str << '\n'
248 << "probe_type = " << probe_type_str << '\n'
249 << "probe_counter = " << bucket.probe_counter;
250 }
251 };
252
253 Bucket *table = nullptr; //bucket arrangement
254 Hash_Fct hash_fct = nullptr; // first hash function
255 Hash_Fct second_hash_fct = nullptr; // second hash function
256
257 Cmp cmp;
258
259 protected:
260 size_t len; // table size
263
264 private:
265 size_t N; // number of buckets occupied
267
268 Bucket * allocate_bucket(Bucket & bucket, unsigned char probe_type) noexcept
269 {
270 assert(bucket.status != BUSY);
271
272 ++N;
273 bucket.status = BUSY;
274 bucket.probe_type = probe_type;
275 ++bucket.probe_counter;
276
277 return &bucket;
278 }
279
280 void decrease_probe_counter(Bucket *bucket) noexcept
281 {
282 assert(bucket->status == BUSY or bucket->status == DELETED);
283
284 --bucket->probe_counter;
285 if (bucket->probe_counter == 0) // <mark EMPTY on the bucket?
286 bucket->status = EMPTY;
287 }
288
289 void deallocate_bucket(Bucket *bucket) noexcept
290 {
291 deallocate_bucket_with_record(bucket, [](Bucket *) {});
292 }
293
294 size_t index_forward(size_t & i) const noexcept
295 {
296 assert(i < len);
297 if (++i == len)
298 i = 0;
299 return i;
300 }
301
302 size_t index_backward(size_t & i) const noexcept
303 {
304 assert(i < len);
305 if (i-- == 0)
306 i = len - 1;
307 return i;
308 }
309
310 [[nodiscard]] bool is_valid_bucket(Bucket *bucket) const noexcept
311 {
312 if (table == nullptr)
313 return false;
314
315 const auto begin = reinterpret_cast<std::uintptr_t>(&table[0]);
316 const auto end = reinterpret_cast<std::uintptr_t>(&table[len]);
317 const auto addr = reinterpret_cast<std::uintptr_t>(bucket);
318
320 return false;
321
322 const auto offset_with_base =
323 static_cast<std::ptrdiff_t>(addr - begin);
324 return offset_with_base % sizeof(*bucket) == 0;
325 }
326
327 [[nodiscard]] size_t bucket_to_index(Bucket *bucket) const noexcept
328 {
329 assert(is_valid_bucket(bucket));
330 return bucket - &table[0];
331 }
332
334 [[nodiscard]] bool is_key_in_table(const Key & key) const noexcept
335 {
336 if (table == nullptr)
337 return false;
338
339 const auto addr = reinterpret_cast<std::uintptr_t>(&key);
340 const auto table_begin = reinterpret_cast<std::uintptr_t>(&table[0]);
341 const auto table_end = reinterpret_cast<std::uintptr_t>(&table[len]);
342
344 return false;
345
346 constexpr auto key_offset = offsetof(Bucket, key);
347 return ((addr - table_begin - key_offset) % sizeof(Bucket)) == 0;
348 }
349
351 [[nodiscard]] Bucket * key_in_table_to_bucket(const Key & key) noexcept
352 {
354 return key_to_bucket(const_cast<Key *>(&key));
355 }
356
359 template <typename RecordBucket>
361 RecordBucket && record_bucket) noexcept
362 {
363 assert(bucket->status == BUSY);
364
365 bucket->status = DELETED;
366 const Key & key = bucket->key;
367
368 const size_t i_fst = hash_fct(key) % len;
369 if (&table[i_fst] == bucket)
370 {
371 assert(Cmp () (table[i_fst].key, key));
372 assert(table[i_fst].probe_type == FIRST_PROBE);
373 }
374 else
375 {
376 const size_t i_snd = second_hash_fct(key) % len;
377 if (&table[i_snd] == bucket)
378 {
379 assert(Cmp () (table[i_snd].key, key));
380 assert(table[i_snd].probe_type == SECOND_PROBE);
383 }
384 else
385 {
390 size_t i = i_snd;
391 for (index_forward(i); &table[i] != bucket; index_forward(i))
392 {
393 assert(table[i].status != EMPTY);
394 record_bucket(&table[i]);
396 }
397 assert(Cmp () (table[i].key, key));
398 assert(table[i].probe_type == LINEAR_PROBE);
399 }
400 }
401
403 --N;
404 }
405
406 public:
407 [[nodiscard]] static Bucket * key_to_bucket(Key *rec) noexcept
408 {
409 const auto base = reinterpret_cast<std::uintptr_t>(rec);
410 const auto offset = offsetof(Bucket, key);
411 return reinterpret_cast<Bucket *>(base - offset);
412 }
413
414 [[nodiscard]] constexpr const Cmp &get_compare() const noexcept { return cmp; }
415
416 [[nodiscard]] constexpr Cmp &get_compare() noexcept { return cmp; }
417
418 void swap(ODhashTable & other) noexcept
419 {
420 std::swap(table, other.table);
421 std::swap(hash_fct, other.hash_fct);
422 std::swap(second_hash_fct, other.second_hash_fct);
423 std::swap(cmp, other.cmp);
424 std::swap(N, other.N);
425 std::swap(len, other.len);
426 }
427
428 protected:
431 const float lower, const float upper, const bool resize)
434 len(Primes::next_prime(l)),
436 N(0), with_resize(resize)
437 {
438 table = new Bucket[len];
439 }
440
441 public:
475
478 {
479 if (table != nullptr)
480 delete [] table;
481 }
482
490
493 {
494 assert(table != nullptr);
495 swap(other);
496 }
497
499
501 {
502 if (this == &other)
503 return *this;
504
505 if (len > other.N)
506 this->clean_table();
507 else
508 {
509 auto *new_table = new Bucket [other.len];
510 delete [] table;
512 N = 0;
513 len = other.len;
514 hash_fct = other.hash_fct;
515 second_hash_fct = other.second_hash_fct;
516 cmp = other.cmp;
517 lower_alpha = other.lower_alpha;
518 upper_alpha = other.upper_alpha;
519 with_resize = other.with_resize;
520 }
521
522 this->copy_from_table(other);
523
524 return *this;
525 }
526
528 {
529 swap(other);
530 return *this;
531 }
532
534
537 [[nodiscard]] Key * search(const Key & key) const noexcept
538 {
539 const size_t i_fst = hash_fct(key) % len; // 1st probe (1st hash function)
540 if (table[i_fst].status == EMPTY) [[unlikely]]
541 return nullptr;
542
543 if (table[i_fst].status == BUSY and cmp(table[i_fst].key, key)) [[likely]]
544 {
545 assert(table[i_fst].probe_type == FIRST_PROBE);
546 assert(table[i_fst].probe_counter > 0);
547 return &table[i_fst].key;
548 }
549
550 // Early exit: if BUSY with different key and probe_counter == 1,
551 // no other key with this first hash can exist in the table
552 if (table[i_fst].status == BUSY and table[i_fst].probe_counter == 1)
553 return nullptr;
554
555 const size_t i_snd = second_hash_fct(key) % len; // 2nd probe
556
557 // If both hashes collide to same index, skip directly to linear probing
558 if (i_fst == i_snd) [[unlikely]]
559 goto linear_probe;
560
561 if (table[i_snd].status == EMPTY)
562 return nullptr;
563
564 if (table[i_snd].status == BUSY and cmp(table[i_snd].key, key))
565 {
566 assert(table[i_snd].probe_type == SECOND_PROBE);
567 assert(table[i_snd].probe_counter > 0);
568 return &table[i_snd].key;
569 }
570
571 // Early exit: if BUSY with different key and probe_counter == 1,
572 // no other key passed through this bucket
573 if (table[i_snd].status == BUSY and table[i_snd].probe_counter == 1)
574 return nullptr;
575
577 size_t i = i_snd;
578 // Linear polling from index of 2nd hash function
579 for (size_t count = 0; count < len; ++count)
580 {
581 index_forward(i);
582 // Prefetch next bucket for better cache performance
583 __builtin_prefetch(&table[(i + 1 < len) ? i + 1 : 0], 0, 1);
584 switch (table[i].status)
585 {
586 case EMPTY:
587 assert(table[i].probe_counter == 0);
588 return nullptr;
589 case BUSY:
590 assert(table[i].probe_counter > 0);
591 if (cmp(table[i].key, key)) [[unlikely]]
592 {
593 assert(table[i].probe_type == LINEAR_PROBE);
594 return &table[i].key;
595 }
596 break;
597 case DELETED:
598 assert(table[i].probe_counter > 0);
599 break;
600 default: AH_ERROR("ODhashTable search: inconsistent bucket status");
601 }
602 }
603
604 return nullptr;
605 }
606
608
610 {
612 }
613
618
619 private:
620 Bucket * allocate_bucket(const Key & key) noexcept
621 {
622 assert(N < len);
623
624 const size_t i_fst = hash_fct(key) % len;
625
626 // EMPTY at first probe: safe to insert, no collision chain
627 if (table[i_fst].status == EMPTY)
629
630 // BUSY at first probe: check for duplicate
631 if (table[i_fst].status == BUSY && cmp(table[i_fst].key, key))
632 return nullptr;
633
634 const size_t i_snd = second_hash_fct(key) % len;
635
636 // EMPTY at second probe: safe to insert if first wasn't the key
637 if (table[i_snd].status == EMPTY)
638 {
639 // If first was DELETED, use it; otherwise use second
640 if (table[i_fst].status == DELETED)
644 }
645
646 // BUSY at second probe: check for duplicate
647 if (table[i_snd].status == BUSY && cmp(table[i_snd].key, key))
648 return nullptr;
649
650 // Need to do linear probing - search for duplicate and find insertion point
651 Bucket *candidate = nullptr;
652 size_t candidate_idx = 0;
653
654 // Check if first or second are candidates (DELETED)
655 if (table[i_fst].status == DELETED)
656 {
658 candidate_idx = 0; // Marker: use first probe slot
659 }
660 else if (table[i_snd].status == DELETED)
661 {
663 candidate_idx = 1; // Marker: use second probe slot
664 }
665
666 size_t i = i_snd;
667 for (size_t c = 0; c < len; ++c)
668 {
669 index_forward(i);
670 // Prefetch next bucket for better cache performance
671 __builtin_prefetch(&table[(i + 1 < len) ? i + 1 : 0], 0, 1);
672
673 if (table[i].status == EMPTY)
674 {
675 // End of collision chain - no duplicate, insert
676 if (candidate)
677 {
678 // Insert at the first DELETED slot found
679 if (candidate_idx == 0) // First probe slot
681 if (candidate_idx == 1) // Second probe slot
682 {
685 }
686 // Linear probe slot - need to update all counters
689 size_t j = i_snd;
690 for (size_t k = 0; k < candidate_idx - 2; ++k)
691 {
692 index_forward(j);
693 ++table[j].probe_counter;
694 }
696 }
697 // No DELETED found, insert at this EMPTY slot
700 // Increment probe_counter for all buckets between i_snd and i (exclusive)
701 size_t j = i_snd;
702 for (size_t k = 0; k < c; ++k)
703 {
704 index_forward(j);
705 ++table[j].probe_counter;
706 }
708 }
709
710 if (table[i].status == BUSY && cmp(table[i].key, key))
711 return nullptr; // Duplicate found
712
713 if (table[i].status == DELETED && candidate == nullptr)
714 {
715 candidate = &table[i];
716 candidate_idx = c + 2; // Store position (2 = after first and second)
717 }
718 }
719
720 // Table full, but might have a DELETED slot
721 if (candidate)
722 {
723 if (candidate_idx == 0)
725 if (candidate_idx == 1)
726 {
729 }
732 size_t j = i_snd;
733 for (size_t k = 0; k < candidate_idx - 2; ++k)
734 {
735 index_forward(j);
736 ++table[j].probe_counter;
737 }
739 }
740
741 return nullptr;
742 }
743
744 // search key. If found return (bucket_ptr, true), otherwise allocates and
745 // returns (new_bucket_ptr, false)
746 std::tuple<Bucket *, bool> hard_allocate_bucket(const Key & key) noexcept
747 {
748 assert(N < len);
749
750 Bucket *candidate = nullptr;
751 size_t candidate_linear_steps = 0; // How many linear steps to reach candidate
752
753 const size_t i_fst = hash_fct(key) % len;
754 switch (table[i_fst].status)
755 {
756 case EMPTY:
757 return {allocate_bucket(table[i_fst], FIRST_PROBE), false};
758 case DELETED:
760 // candidate at FIRST_PROBE, no linear steps needed
761 break;
762 case BUSY:
763 if (cmp(table[i_fst].key, key))
764 return {&table[i_fst], true}; // found existing
765 break;
766 }
767
768 const size_t i_snd = second_hash_fct(key) % len;
769 switch (table[i_snd].status)
770 {
771 case EMPTY:
772 if (candidate) // candidate is at i_fst (FIRST_PROBE)
773 return {allocate_bucket(*candidate, FIRST_PROBE), false};
775 return {allocate_bucket(table[i_snd], SECOND_PROBE), false};
776 case DELETED:
777 if (candidate == nullptr)
778 {
780 // candidate at SECOND_PROBE, no linear steps needed
781 }
782 break;
783 case BUSY:
784 if (cmp(table[i_snd].key, key))
785 return {&table[i_snd], true}; // found existing
786 break;
787 }
788
789 // Determine if candidate is at i_fst or i_snd (for probe_type later)
790 const bool candidate_at_first = (candidate == &table[i_fst]);
791 const bool candidate_at_second = (candidate == &table[i_snd]);
792
793 size_t i = i_snd;
794 for (size_t c = 0; c < len; ++c)
795 {
796 index_forward(i);
797 // Prefetch next bucket for better cache performance
798 __builtin_prefetch(&table[(i + 1 < len) ? i + 1 : 0], 0, 1);
799 switch (table[i].status)
800 {
801 case BUSY:
802 if (cmp(table[i].key, key))
803 return {&table[i], true}; // found existing
804 break;
805 case DELETED:
806 if (candidate == nullptr)
807 {
808 candidate = &table[i];
809 candidate_linear_steps = c + 1; // Steps from i_snd
810 }
811 break;
812 case EMPTY:
813 {
814 // End of collision chain - insert at candidate or here
815 if (candidate)
816 {
818 return {allocate_bucket(*candidate, FIRST_PROBE), false};
819
821
823 return {allocate_bucket(*candidate, SECOND_PROBE), false};
824
825 // Candidate is in LINEAR_PROBE region
827 // Increment all buckets between i_snd and candidate
828 size_t j = i_snd;
829 for (size_t k = 0; k < candidate_linear_steps - 1; ++k)
830 {
831 index_forward(j);
832 ++table[j].probe_counter;
833 }
834 return {allocate_bucket(*candidate, LINEAR_PROBE), false};
835 }
836 // No candidate found, insert at this EMPTY slot
839 // Increment all buckets between i_snd and here
840 size_t j = i_snd;
841 for (size_t k = 0; k < c; ++k)
842 {
843 index_forward(j);
844 ++table[j].probe_counter;
845 }
846 return {allocate_bucket(table[i], LINEAR_PROBE), false};
847 }
848 default: AH_ERROR("ODhashTable: Invalid bucket status");
849 break;
850 }
851 }
852
853 // All slots checked (full table), use candidate if found
854 if (candidate)
855 {
857 return {allocate_bucket(*candidate, FIRST_PROBE), false};
858
860
862 return {allocate_bucket(*candidate, SECOND_PROBE), false};
863
864 // Candidate is in LINEAR_PROBE region
866 size_t j = i_snd;
867 for (size_t k = 0; k < candidate_linear_steps - 1; ++k)
868 {
869 index_forward(j);
870 ++table[j].probe_counter;
871 }
872 return {allocate_bucket(*candidate, LINEAR_PROBE), false};
873 }
874
875 return {nullptr, false};
876 }
877
883 void remove_bucket(Bucket *bucket)
884 {
885 ah_invalid_argument_if(not is_valid_bucket(bucket)) << "key pty does not belong to hash table";
886 ah_domain_error_if(bucket->status != BUSY) << "Bucket containing key is not BUSY";
887
888 deallocate_bucket(bucket);
889 }
890
891 public:
897 void remove(const Key & key)
898 {
899 // Fast path: if key is a reference to a key inside the table
900 if (is_key_in_table(key)) [[likely]]
901 {
902 Bucket *bucket = key_in_table_to_bucket(key);
903 ah_domain_error_if(bucket->status != BUSY)
904 << "Bucket containing key is not BUSY";
905 deallocate_bucket(bucket);
906 return;
907 }
908
909 // External key: search for it (without modifying probe_counters)
910 const size_t i_fst = hash_fct(key) % len;
911
912 // Check first probe position
913 ah_domain_error_if(table[i_fst].status == EMPTY) << "Key not in hash table";
914
915 if (table[i_fst].status == BUSY and cmp(table[i_fst].key, key)) [[likely]]
916 {
918 return;
919 }
920
921 // Early exit: if BUSY with different key and probe_counter == 1,
922 // no other key with this first hash can exist in the table
923 ah_domain_error_if(table[i_fst].status == BUSY and table[i_fst].probe_counter == 1)
924 << "Key not in hash table";
925
926 const size_t i_snd = second_hash_fct(key) % len;
927
928 // If both hashes collide to same index, skip directly to linear probing
929 if (i_fst == i_snd) [[unlikely]]
931
932 // Check second probe position
933 ah_domain_error_if(table[i_snd].status == EMPTY) << "Key not in hash table";
934
935 if (table[i_snd].status == BUSY and cmp(table[i_snd].key, key))
936 {
938 return;
939 }
940
941 // Early exit: if BUSY with different key and probe_counter == 1,
942 // no other key passed through this bucket
943 ah_domain_error_if(table[i_snd].status == BUSY and table[i_snd].probe_counter == 1)
944 << "Key not in hash table";
945
947 // Linear probing (search only, no modifications until key found)
948 size_t i = i_snd;
949 for (size_t c = 0; c < len; ++c)
950 {
951 index_forward(i);
952 // Prefetch next bucket for better cache performance
953 __builtin_prefetch(&table[(i + 1 < len) ? i + 1 : 0], 0, 1);
954
955 switch (table[i].status)
956 {
957 case EMPTY:
958 ah_domain_error() << "Key not in hash table";
959 break; // unreachable, but silences -Wimplicit-fallthrough
960
961 case BUSY:
962 if (cmp(table[i].key, key)) [[unlikely]]
963 {
965 return;
966 }
967 break;
968
969 case DELETED:
970 break;
971 }
972 }
973
974 ah_domain_error() << "Key not in hash table";
975 }
976
978
980 {
981 DynArray<size_t> lens;
982 size_t num_busy = 0, num_deleted = 0, num_empty = 0;
983 size_t max_len = std::numeric_limits<size_t>::min();
984 for (size_t i = 0; i < len; ++i)
985 switch (table[i].status)
986 {
987 case BUSY:
988 {
989 ++num_busy;
990 const Key & key = table[i].key;
991 size_t count = 1;
992 const size_t i_fst = hash_fct(key) % len;
993 if (cmp(table[i_fst].key, key))
994 {
995 assert(table[i_fst].probe_type == FIRST_PROBE);
996 assert(table[i_fst].probe_counter > 0);;
997 }
998 else
999 {
1000 ++count;
1001 size_t i_snd = second_hash_fct(key) % len;
1002 if (cmp(table[i_snd].key, key))
1003 {
1004 assert(table[i_snd].probe_type == SECOND_PROBE);
1005 assert(table[i_snd].probe_counter > 0);;
1006 }
1007 else
1008 {
1009 for (size_t i = index_forward(i_snd); true;
1010 index_forward(i))
1011 {
1012 if (table[i].status == BUSY and cmp(table[i].key, key))
1013 break;
1014 ++count;
1015 }
1016 }
1017 }
1018 max_len = std::max(max_len, count);
1019 update_stat_len(lens, count);
1020 break;
1021 }
1022 case EMPTY:
1023 ++num_empty;
1024 update_stat_len(lens, 0);
1025 break;
1026 case DELETED:
1027 ++num_deleted;
1028 break;
1029 }
1030
1031 float avg = 0, sum = 0;
1032 for (size_t i = 0; i < lens.size(); ++i)
1033 {
1034 avg += lens(i) * i;
1035 sum += lens(i);
1036 }
1037
1038 avg /= sum;
1039 float var = 0;
1040 for (size_t i = 0; i < lens.size(); ++i)
1041 {
1042 const float s = i - avg;
1043 var += lens(i) * s * s;
1044 }
1045 var /= sum;
1046
1047 Stats stats;
1048 stats.num_busy = num_busy;
1049 stats.num_deleted = num_deleted;
1050 stats.num_empty = num_empty;
1051 std::swap(lens, stats.lens);
1052 stats.avg = avg;
1053 stats.var = var;
1054 stats.max_len = max_len;
1055
1056 return stats;
1057 }
1058 };
1059
1060
1061 template <typename Key, class Cmp = Aleph::equal_to<Key>>
1063
1064
1065# ifdef NBACKUP
1066# define N NBACKUP
1067# undef NBACKUP
1068# endif
1069
1070# ifdef MBACKUP
1071# define M MBACKUP
1072# undef MBACKUP
1073# endif
1074
1075# undef EMPTY
1076# undef BUSY
1077# undef DELETED
1078# undef NO_PROBED
1079# undef FIRST_PROBE
1080# undef SECOND_PROBE
1081# undef LINEAR_PROBE
1082} // end namespace Aleph
1083
1084# endif // TPL_ODHASH_H
Exception handling system with formatted messages for Aleph-w.
#define ah_domain_error()
Throws std::domain_error unconditionally.
Definition ah-errors.H:554
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:522
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:639
#define AH_ERROR(format, args...)
Print an error message (always enabled).
Definition ahDefs.H:271
DRY (Don't Repeat Yourself) utilities and macros.
#define Special_Ctors(Set_Type, Type)
Generates special constructors for containers.
Definition ahDry.H:113
Iterator wrapper for C++ raw arrays and circular buffers.
size_t size() const noexcept
Return the current dimension of array.
Open addressing hash table with double hashing collision resolution.
Definition tpl_odhash.H:180
Bucket * key_in_table_to_bucket(const Key &key) noexcept
Converts a key reference inside the table to its containing bucket.
Definition tpl_odhash.H:351
static Bucket * key_to_bucket(Key *rec) noexcept
Definition tpl_odhash.H:407
typename OhashCommon< ODhashTable< Key, Cmp >, Key >::Stats Stats
Definition tpl_odhash.H:977
size_t bucket_to_index(Bucket *bucket) const noexcept
Definition tpl_odhash.H:327
void deallocate_bucket_with_record(Bucket *bucket, RecordBucket &&record_bucket) noexcept
Template version of deallocate_bucket that accepts a callback to record modified buckets before decre...
Definition tpl_odhash.H:360
bool is_key_in_table(const Key &key) const noexcept
Returns true if the key reference points to a key inside the table.
Definition tpl_odhash.H:334
size_t(*)(const Key &) Hash_Fct_Ptr
Definition tpl_odhash.H:190
size_t index_backward(size_t &i) const noexcept
Definition tpl_odhash.H:302
void set_second_hash_fct(Hash_Fct fct) noexcept
Definition tpl_odhash.H:609
Hash_Fct second_hash_fct
Definition tpl_odhash.H:255
Key * search(const Key &key) const noexcept
searches the table for the key.
Definition tpl_odhash.H:537
void decrease_probe_counter(Bucket *bucket) noexcept
Definition tpl_odhash.H:280
constexpr Hash_Fct get_second_hash_fct() const noexcept
Definition tpl_odhash.H:607
~ODhashTable()
Free the whole hash table.
Definition tpl_odhash.H:477
std::function< size_t(const Key &)> Hash_Fct
Definition tpl_odhash.H:188
void swap(ODhashTable &other) noexcept
Definition tpl_odhash.H:418
ODhashTable(const size_t l, Hash_Fct fst_hash_fct, Hash_Fct snd_hash_fct, Cmp cpm_fct, const float lower, const float upper, const bool resize)
Definition tpl_odhash.H:429
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)
Instantiate a closed hash table with collision resolution by double hashing.
Definition tpl_odhash.H:466
constexpr Cmp & get_compare() noexcept
Definition tpl_odhash.H:416
ODhashTable(const ODhashTable &other)
Definition tpl_odhash.H:483
void remove_bucket(Bucket *bucket)
Delete the record from the table.
Definition tpl_odhash.H:883
bool is_valid_bucket(Bucket *bucket) const noexcept
Definition tpl_odhash.H:310
ODhashTable(ODhashTable &&other) noexcept
Definition tpl_odhash.H:491
void remove(const Key &key)
Remove a key from the hash table.
Definition tpl_odhash.H:897
Bucket * allocate_bucket(const Key &key) noexcept
Definition tpl_odhash.H:620
void set_second_hash_fct(Hash_Fct_Ptr fct) noexcept
Definition tpl_odhash.H:614
size_t index_forward(size_t &i) const noexcept
Definition tpl_odhash.H:294
static void update_stat_len(DynArray< size_t > &lens, size_t i)
Definition tpl_odhash.H:533
void deallocate_bucket(Bucket *bucket) noexcept
Definition tpl_odhash.H:289
constexpr const Cmp & get_compare() const noexcept
Definition tpl_odhash.H:414
Bucket * allocate_bucket(Bucket &bucket, unsigned char probe_type) noexcept
Definition tpl_odhash.H:268
std::tuple< Bucket *, bool > hard_allocate_bucket(const Key &key) noexcept
Definition tpl_odhash.H:746
Stats stats() const
Definition tpl_odhash.H:979
ODhashTable & operator=(const ODhashTable &other)
Definition tpl_odhash.H:500
Equality test for containers.
Definition ah-dry.H:1534
Common methods to the Aleph-w ( ) containers.
Definition ah-dry.H:548
Aleph::DynList< __T > maps(Operation &op) const
Map the elements of the container.
Definition ah-dry.H:904
Common sequential searching methods on containers.
Definition ah-dry.H:164
LocateFunctions< Container, Type > * base() const
Definition ah-dry.H:172
CRTP mixin providing common operations for open addressing hash tables.
Definition hashDry.H:101
size_t resize(size_t new_size)
Resizes the hash table to a new capacity.
Definition hashDry.H:524
void clean_table()
Removes all entries from the table without deallocating storage.
Definition hashDry.H:166
void copy_from_table(const HashTbl &other)
Copies all entries from another hash table.
Definition hashDry.H:143
Mixin that adds STL begin()/end() and cbegin()/cend() to Aleph containers.
iterator end() noexcept
Return an STL-compatible end iterator.
iterator begin() noexcept
Return an STL-compatible iterator to the first element.
Common hash table utilities and base classes.
#define OHASH_COMMON(class_name)
Definition hash-dry.H:48
Collection of general-purpose hash functions.
Common operations for open addressing hash tables (CRTP mixin).
const long double offset[]
Offset values indexed by symbol string length (bounded by MAX_OFFSET_INDEX)
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
size_t snd_hash_fct(const Key &key) noexcept
Definition hash-fct.H:937
const float hash_default_upper_alpha
Definition hash-dry.C:40
const float hash_default_lower_alpha
Definition hash-dry.C:38
DynList< T > maps(const C &c, Op op)
Classic map operation.
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
const unsigned long DefaultPrime
Default prime number used when no specific size is requested.
Definition primes.C:381
Prime number utilities for hash tables and mathematical operations.
constexpr bool valid() const noexcept
Definition tpl_odhash.H:215
friend std::ostream & operator<<(std::ostream &out, const Bucket &bucket)
Definition tpl_odhash.H:222
Generic traversal of the container through its iterator.
Definition ah-dry.H:65
Lazy and scalable dynamic array implementation.
DynList< int > l