Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
pointer_table_test.cc
Go to the documentation of this file.
1
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
46#include <gtest/gtest.h>
47#include <vector>
48#include <set>
49#include <random>
50#include <algorithm>
51#include <stdexcept>
52#include <pointer_table.H>
53
54using namespace std;
55using namespace testing;
56
57// =============================================================================
58// Basic Construction Tests
59// =============================================================================
60
62{
63protected:
64 void SetUp() override {}
65};
66
68{
69 Pointer_Table table;
70
71 EXPECT_EQ(table.size(), 0);
72 EXPECT_EQ(table.busies(), 0);
73 EXPECT_EQ(table.frees(), 0);
74 EXPECT_EQ(table.get_heap_index(), 0);
75 EXPECT_EQ(table.get_threshold(), 0);
76 EXPECT_TRUE(table.is_empty());
77}
78
80{
81 Pointer_Table table(100);
82
83 EXPECT_EQ(table.busies(), 0);
84 EXPECT_EQ(table.frees(), 0);
85 EXPECT_EQ(table.get_heap_index(), 0);
86 EXPECT_EQ(table.get_threshold(), 100);
87 EXPECT_TRUE(table.is_empty());
88}
89
97
105
106// =============================================================================
107// Basic Insert Tests
108// =============================================================================
109
110class PointerTableInsertTest : public Test
111{
112protected:
114 int dummy1 = 1, dummy2 = 2, dummy3 = 3;
115};
116
118{
119 long idx = table.insert_pointer(&dummy1);
120
121 EXPECT_EQ(idx, 0);
122 EXPECT_EQ(table.busies(), 1);
123 EXPECT_EQ(table.get_heap_index(), 1);
124 EXPECT_FALSE(table.is_empty());
125}
126
128{
129 long idx1 = table.insert_pointer(&dummy1);
130 long idx2 = table.insert_pointer(&dummy2);
131 long idx3 = table.insert_pointer(&dummy3);
132
133 EXPECT_EQ(idx1, 0);
134 EXPECT_EQ(idx2, 1);
135 EXPECT_EQ(idx3, 2);
136 EXPECT_EQ(table.busies(), 3);
137 EXPECT_EQ(table.get_heap_index(), 3);
138}
139
141{
142 long idx1 = table.insert_pointer(&dummy1);
143 long idx2 = table.insert_pointer(&dummy1);
144
146 EXPECT_EQ(table.busies(), 2);
147}
148
150{
151 vector<int> data(1000);
153
154 for (int i = 0; i < 1000; ++i)
155 {
156 data[i] = i;
157 indices.push_back(table.insert_pointer(&data[i]));
158 }
159
160 EXPECT_EQ(table.busies(), 1000);
161 EXPECT_EQ(table.get_heap_index(), 1000);
162
163 for (int i = 0; i < 1000; ++i)
164 EXPECT_EQ(indices[i], i);
165}
166
167// =============================================================================
168// Basic Remove Tests
169// =============================================================================
170
171class PointerTableRemoveTest : public Test
172{
173protected:
175 int dummy1 = 1, dummy2 = 2, dummy3 = 3;
176};
177
179{
180 long idx = table.insert_pointer(&dummy1);
181 table.remove_pointer(idx);
182
183 EXPECT_EQ(table.busies(), 0);
184 EXPECT_EQ(table.get_heap_index(), 0);
185 EXPECT_TRUE(table.is_empty());
186}
187
189{
190 long idx1 = table.insert_pointer(&dummy1);
191 long idx2 = table.insert_pointer(&dummy2);
192 long idx3 = table.insert_pointer(&dummy3);
193 (void)idx1;
194 (void)idx3;
195
196 table.remove_pointer(idx2);
197
198 EXPECT_EQ(table.busies(), 2);
199 EXPECT_EQ(table.frees(), 1);
200 EXPECT_EQ(table.get_heap_index(), 3);
201}
202
204{
205 long idx1 = table.insert_pointer(&dummy1);
206 long idx2 = table.insert_pointer(&dummy2);
207 long idx3 = table.insert_pointer(&dummy3);
208 (void)idx1;
209 (void)idx2;
210
211 table.remove_pointer(idx3);
212
213 EXPECT_EQ(table.busies(), 2);
214 EXPECT_EQ(table.frees(), 0); // No free entry - heap contracted
215 EXPECT_EQ(table.get_heap_index(), 2);
216}
217
219{
220 long idx1 = table.insert_pointer(&dummy1);
221 long idx2 = table.insert_pointer(&dummy2);
222 long idx3 = table.insert_pointer(&dummy3);
223 (void)idx2;
224 (void)idx3;
225
226 table.remove_pointer(idx1);
227
228 EXPECT_EQ(table.busies(), 2);
229 EXPECT_EQ(table.frees(), 1);
230 EXPECT_EQ(table.get_heap_index(), 3);
231}
232
234{
235 long idx1 = table.insert_pointer(&dummy1);
236 long idx2 = table.insert_pointer(&dummy2);
237 long idx3 = table.insert_pointer(&dummy3);
238
239 table.remove_pointer(idx1);
240 table.remove_pointer(idx2);
241 table.remove_pointer(idx3);
242
243 EXPECT_EQ(table.busies(), 0);
244 EXPECT_EQ(table.frees(), 0);
245 EXPECT_EQ(table.get_heap_index(), 0);
246 EXPECT_TRUE(table.is_empty());
247}
248
250{
251 table.insert_pointer(&dummy1);
252
253 EXPECT_THROW(table.remove_pointer(-1), std::range_error);
254 EXPECT_THROW(table.remove_pointer(100), std::range_error);
255 EXPECT_THROW(table.remove_pointer(1), std::range_error);
256}
257
259{
260 long idx = table.insert_pointer(&dummy1);
261 table.insert_pointer(&dummy2);
262 table.remove_pointer(idx);
263
264 EXPECT_THROW(table.remove_pointer(idx), std::domain_error);
265}
266
267// =============================================================================
268// Index Recycling Tests
269// =============================================================================
270
271class PointerTableRecyclingTest : public Test
272{
273protected:
275 int dummy1 = 1, dummy2 = 2, dummy3 = 3, dummy4 = 4;
276};
277
279{
280 long idx1 = table.insert_pointer(&dummy1);
281 long idx2 = table.insert_pointer(&dummy2);
282 (void)idx2;
283
284 table.remove_pointer(idx1);
285 long idx3 = table.insert_pointer(&dummy3);
286
287 EXPECT_EQ(idx3, idx1); // Should reuse index 0
288 EXPECT_EQ(table.busies(), 2);
289 EXPECT_EQ(table.frees(), 0);
290}
291
293{
294 long idx1 = table.insert_pointer(&dummy1);
295 long idx2 = table.insert_pointer(&dummy2);
296 long idx3 = table.insert_pointer(&dummy3);
297 (void)idx3;
298
299 table.remove_pointer(idx1);
300 table.remove_pointer(idx2);
301
302 EXPECT_EQ(table.frees(), 2);
303
304 long idx4 = table.insert_pointer(&dummy4);
306 EXPECT_EQ(table.frees(), 1);
307}
308
310{
311 vector<int> data(100);
313
314 for (int i = 0; i < 100; ++i)
315 {
316 data[i] = i;
317 indices.push_back(table.insert_pointer(&data[i]));
318 }
319
320 // Remove every other element
322 for (int i = 0; i < 100; i += 2)
323 {
324 table.remove_pointer(indices[i]);
326 }
327
328 EXPECT_EQ(table.busies(), 50);
329
330 // Insert 50 more - should reuse all freed indices
331 for (int i = 0; i < 50; ++i)
332 {
333 long new_idx = table.insert_pointer(&data[i]);
334 EXPECT_TRUE(removed.count(new_idx) > 0 or new_idx >= 100);
335 }
336
337 // Now heap should not have grown
338 EXPECT_EQ(table.get_heap_index(), 100);
339}
340
341// =============================================================================
342// Critical Bug Fix Test: Heap Contraction with Free Table Cleanup
343// =============================================================================
344
346{
347protected:
349 int d1 = 1, d2 = 2, d3 = 3, d4 = 4, d5 = 5;
350};
351
353{
354 long idx1 = table.insert_pointer(&d1);
355 long idx2 = table.insert_pointer(&d2);
356 long idx3 = table.insert_pointer(&d3);
357 (void)idx1;
358 (void)idx2;
359
360 // Remove from top - heap should contract
361 table.remove_pointer(idx3);
362
363 EXPECT_EQ(table.get_heap_index(), 2);
364 EXPECT_EQ(table.busies(), 2);
365 EXPECT_EQ(table.frees(), 0);
366}
367
369{
370 // This test verifies the critical bug fix:
371 // When heap contracts, free_table must be cleaned of invalid indices
372
373 long idx1 = table.insert_pointer(&d1);
374 long idx2 = table.insert_pointer(&d2);
375 long idx3 = table.insert_pointer(&d3);
376 long idx4 = table.insert_pointer(&d4);
377 long idx5 = table.insert_pointer(&d5);
378 (void)idx1;
379 (void)idx3;
380
381 // State: [d1, d2, d3, d4, d5], heap_index=5, free_table=[]
382
383 // Remove middle elements - they go to free_table
384 table.remove_pointer(idx2);
385 table.remove_pointer(idx4);
386
387 // State: [d1, null, d3, null, d5], heap_index=5, free_table=[1, 3]
388 EXPECT_EQ(table.frees(), 2);
389
390 // Remove the top element - heap should contract to index 3
391 // and free_table should be cleaned of index 3 (which is now >= heap_index)
392 table.remove_pointer(idx5);
393
394 // State should be: [d1, null, d3], heap_index=3, free_table=[1]
395 EXPECT_EQ(table.get_heap_index(), 3);
396 EXPECT_EQ(table.frees(), 1); // Only index 1 should remain
397 EXPECT_EQ(table.busies(), 2);
398
399 // Now insert a new pointer - it should get index 1 (recycled)
400 int d6 = 6;
401 long idx6 = table.insert_pointer(&d6);
402 EXPECT_EQ(idx6, 1);
403 EXPECT_EQ(table.frees(), 0);
404}
405
407{
408 // Create: [d1, d2, d3, d4, d5]
409 long idx1 = table.insert_pointer(&d1);
410 long idx2 = table.insert_pointer(&d2);
411 long idx3 = table.insert_pointer(&d3);
412 long idx4 = table.insert_pointer(&d4);
413 long idx5 = table.insert_pointer(&d5);
414 (void)idx1;
415
416 // Remove all but the first
417 table.remove_pointer(idx2); // free_table = [1]
418 table.remove_pointer(idx3); // free_table = [1, 2]
419 table.remove_pointer(idx4); // free_table = [1, 2, 3]
420
421 EXPECT_EQ(table.frees(), 3);
422
423 // Remove top - should contract to heap_index=1 and clean ALL free_table
424 table.remove_pointer(idx5);
425
426 // heap_index should be 1 (only d1 remains)
427 EXPECT_EQ(table.get_heap_index(), 1);
428 EXPECT_EQ(table.frees(), 0); // All free indices were >= 1, so removed
429 EXPECT_EQ(table.busies(), 1);
430}
431
433{
434 long idx1 = table.insert_pointer(&d1);
435 long idx2 = table.insert_pointer(&d2);
436 long idx3 = table.insert_pointer(&d3);
437 long idx4 = table.insert_pointer(&d4);
438 (void)idx1;
439
440 // Remove idx2 (goes to free_table)
441 table.remove_pointer(idx2);
442
443 // Remove idx4 (top) - contracts heap to 3
444 table.remove_pointer(idx4);
445
446 // Remove idx3 (now top) - contracts heap to 1
447 // Since idx2 was in free_table and is still valid (<1? no, it's 1 which is >=1)
448 // Actually idx2=1, and new heap_index=1, so idx2 should be removed from free_table
449 table.remove_pointer(idx3);
450
451 EXPECT_EQ(table.get_heap_index(), 1);
452 EXPECT_EQ(table.frees(), 0); // idx2=1 is now invalid
453 EXPECT_EQ(table.busies(), 1);
454}
455
456// =============================================================================
457// Verify Pointer Tests
458// =============================================================================
459
460class PointerTableVerifyTest : public Test
461{
462protected:
464 int dummy1 = 1, dummy2 = 2;
465};
466
468{
469 long idx = table.insert_pointer(&dummy1);
470 void* result = table.verify_pointer(idx, &dummy1);
471
472 EXPECT_EQ(result, &dummy1);
473}
474
476{
477 long idx = table.insert_pointer(&dummy1);
478
479 EXPECT_THROW(table.verify_pointer(idx, &dummy2), std::domain_error);
480}
481
483{
484 table.insert_pointer(&dummy1);
485
486 EXPECT_THROW(table.verify_pointer(-1, &dummy1), std::range_error);
487 EXPECT_THROW(table.verify_pointer(100, &dummy1), std::range_error);
488}
489
491{
492 long idx = table.insert_pointer(&dummy1);
493 table.insert_pointer(&dummy2); // Keep heap from contracting
494 table.remove_pointer(idx);
495
496 // Index is valid but pointer is nullptr (different from expected)
497 EXPECT_THROW(table.verify_pointer(idx, &dummy1), std::domain_error);
498}
499
500// =============================================================================
501// Get Pointer Tests
502// =============================================================================
503
504class PointerTableGetTest : public Test
505{
506protected:
508 int dummy1 = 1, dummy2 = 2, dummy3 = 3;
509};
510
512{
513 long idx = table.insert_pointer(&dummy1);
514 void* result = table.get_pointer(idx);
515
516 EXPECT_EQ(result, &dummy1);
517}
518
520{
521 long idx1 = table.insert_pointer(&dummy1);
522 long idx2 = table.insert_pointer(&dummy2);
523 long idx3 = table.insert_pointer(&dummy3);
524
525 EXPECT_EQ(table.get_pointer(idx1), &dummy1);
526 EXPECT_EQ(table.get_pointer(idx2), &dummy2);
527 EXPECT_EQ(table.get_pointer(idx3), &dummy3);
528}
529
531{
532 table.insert_pointer(&dummy1);
533
534 EXPECT_THROW(table.get_pointer(-1), std::range_error);
535 EXPECT_THROW(table.get_pointer(100), std::range_error);
536}
537
539{
540 long idx = table.insert_pointer(&dummy1);
541 table.insert_pointer(&dummy2);
542 table.remove_pointer(idx);
543
544 void* result = table.get_pointer(idx);
545 EXPECT_EQ(result, nullptr);
546}
547
548// =============================================================================
549// Clear Tests
550// =============================================================================
551
552class PointerTableClearTest : public Test
553{
554protected:
555 int d1 = 1, d2 = 2, d3 = 3;
556};
557
559{
560 Pointer_Table table;
561 table.clear();
562
563 EXPECT_TRUE(table.is_empty());
564 EXPECT_EQ(table.busies(), 0);
565 EXPECT_EQ(table.frees(), 0);
566}
567
569{
570 Pointer_Table table;
571 table.insert_pointer(&d1);
572 table.insert_pointer(&d2);
573 table.insert_pointer(&d3);
574
575 table.clear();
576
577 EXPECT_TRUE(table.is_empty());
578 EXPECT_EQ(table.busies(), 0);
579 EXPECT_EQ(table.frees(), 0);
580 EXPECT_EQ(table.get_heap_index(), 0);
581}
582
584{
585 Pointer_Table table(100);
586 table.insert_pointer(&d1);
587 table.insert_pointer(&d2);
588
589 table.clear();
590
591 EXPECT_EQ(table.get_threshold(), 100);
592 EXPECT_TRUE(table.is_empty());
593}
594
596{
597 Pointer_Table table;
598 long idx1 = table.insert_pointer(&d1);
599 table.insert_pointer(&d2);
600 table.insert_pointer(&d3);
601 table.remove_pointer(idx1); // Creates free entry
602
603 EXPECT_EQ(table.frees(), 1);
604
605 table.clear();
606
607 EXPECT_EQ(table.frees(), 0);
608 EXPECT_TRUE(table.is_empty());
609}
610
611// =============================================================================
612// Threshold Behavior Tests
613// =============================================================================
614
615class PointerTableThresholdTest : public Test
616{
617protected:
618 int d1 = 1, d2 = 2;
619};
620
622{
623 Pointer_Table table(10);
624
625 for (int i = 0; i < 20; ++i)
626 table.insert_pointer(&d1);
627
628 // Remove all but one
629 for (int i = 19; i >= 1; --i)
630 table.remove_pointer(i);
631
632 // Should maintain threshold size
633 EXPECT_GE(table.size(), 0); // Implementation may vary
634 EXPECT_EQ(table.get_threshold(), 10);
635}
636
638{
639 Pointer_Table table(0);
640
641 for (int i = 0; i < 100; ++i)
642 table.insert_pointer(&d1);
643
644 // Remove all
645 for (int i = 99; i >= 0; --i)
646 table.remove_pointer(i);
647
648 EXPECT_TRUE(table.is_empty());
649 EXPECT_EQ(table.get_heap_index(), 0);
650}
651
652// =============================================================================
653// Edge Cases Tests
654// =============================================================================
655
656class PointerTableEdgeCasesTest : public Test
657{
658protected:
660};
661
663{
664 int d = 1;
665 long idx = table.insert_pointer(&d);
666 table.remove_pointer(idx);
667
668 EXPECT_TRUE(table.is_empty());
669 EXPECT_EQ(table.get_heap_index(), 0);
670 EXPECT_EQ(table.frees(), 0);
671}
672
674{
675 int d1 = 1;
676
677 for (int i = 0; i < 100; ++i)
678 {
679 long idx = table.insert_pointer(&d1);
680 EXPECT_EQ(idx, 0); // Should always be 0 due to recycling
681 table.remove_pointer(idx);
682 EXPECT_TRUE(table.is_empty());
683 }
684}
685
687{
688 vector<int> data(100);
690
691 for (int i = 0; i < 100; ++i)
692 {
693 data[i] = i;
694 indices.push_back(table.insert_pointer(&data[i]));
695 }
696
697 // Remove in reverse order - heap should contract each time
698 for (int i = 99; i >= 0; --i)
699 {
700 table.remove_pointer(indices[i]);
701 EXPECT_EQ(table.get_heap_index(), i);
702 EXPECT_EQ(table.frees(), 0); // No free entries when removing from top
703 }
704
705 EXPECT_TRUE(table.is_empty());
706}
707
709{
710 vector<int> data(100);
712
713 for (int i = 0; i < 100; ++i)
714 {
715 data[i] = i;
716 indices.push_back(table.insert_pointer(&data[i]));
717 }
718
719 // Remove in forward order
720 for (int i = 0; i < 99; ++i)
721 {
722 table.remove_pointer(indices[i]);
723 EXPECT_EQ(table.frees(), i + 1);
724 }
725
726 // Remove last - should trigger full cleanup
727 table.remove_pointer(indices[99]);
728 EXPECT_TRUE(table.is_empty());
729 EXPECT_EQ(table.frees(), 0);
730}
731
732// =============================================================================
733// Stress Tests
734// =============================================================================
735
736class PointerTableStressTest : public Test
737{
738protected:
740 mt19937 rng{42}; // Fixed seed for reproducibility
741};
742
744{
745 vector<int> data(10000);
747
748 for (int i = 0; i < 10000; ++i)
749 data[i] = i;
750
751 uniform_int_distribution<int> op_dist(0, 2); // 0=insert, 1=remove, 2=verify
752
753 for (int iteration = 0; iteration < 50000; ++iteration)
754 {
755 int op = op_dist(rng);
756
757 if (op == 0 or active_indices.empty())
758 {
759 // Insert
760 int data_idx = rng() % 10000;
761 long idx = table.insert_pointer(&data[data_idx]);
763 }
764 else if (op == 1)
765 {
766 // Remove
767 auto it = active_indices.begin();
768 advance(it, rng() % active_indices.size());
769 long idx = *it;
770 table.remove_pointer(idx);
771 active_indices.erase(it);
772 }
773 else
774 {
775 // Verify - get pointer at random active index
776 auto it = active_indices.begin();
777 advance(it, rng() % active_indices.size());
778 long idx = *it;
779 void* ptr = table.get_pointer(idx);
780 EXPECT_NE(ptr, nullptr);
781 }
782
783 // Invariant check
784 EXPECT_EQ(table.busies(), static_cast<long>(active_indices.size()));
785 }
786}
787
789{
790 vector<int> data(1000);
791 for (int i = 0; i < 1000; ++i)
792 data[i] = i;
793
794 // Insert 1000 elements
796 for (int i = 0; i < 1000; ++i)
797 indices.push_back(table.insert_pointer(&data[i]));
798
799 // Remove odd indices
800 for (int i = 1; i < 1000; i += 2)
801 table.remove_pointer(indices[i]);
802
803 EXPECT_EQ(table.busies(), 500);
804 EXPECT_EQ(table.frees(), 499); // One less because last removed might contract
805
806 // Insert 500 more - should reuse freed indices
807 for (int i = 0; i < 500; ++i)
808 {
809 long new_idx = table.insert_pointer(&data[i]);
810 // Should be recycled
811 EXPECT_LT(new_idx, 1000);
812 }
813
814 EXPECT_EQ(table.busies(), 1000);
815 EXPECT_LE(table.get_heap_index(), 1000);
816}
817
819{
820 vector<int> data(100000);
821 for (int i = 0; i < 100000; ++i)
822 data[i] = i;
823
824 // Grow to 100k
826 for (int i = 0; i < 100000; ++i)
827 indices.push_back(table.insert_pointer(&data[i]));
828
829 EXPECT_EQ(table.busies(), 100000);
830 EXPECT_EQ(table.get_heap_index(), 100000);
831
832 // Shrink to 0
833 for (int i = 99999; i >= 0; --i)
834 table.remove_pointer(indices[i]);
835
836 EXPECT_TRUE(table.is_empty());
837 EXPECT_EQ(table.get_heap_index(), 0);
838 EXPECT_EQ(table.frees(), 0);
839}
840
842{
843 // This test specifically stresses the heap contraction with free_table cleanup
844 vector<int> data(1000);
845 for (int i = 0; i < 1000; ++i)
846 data[i] = i;
847
848 for (int round = 0; round < 10; ++round)
849 {
850 // Insert all
852 for (int i = 0; i < 1000; ++i)
853 indices.push_back(table.insert_pointer(&data[i]));
854
855 // Remove in a pattern that creates fragmentation then contracts
856 // Remove first 500 (creates free entries)
857 for (int i = 0; i < 500; ++i)
858 table.remove_pointer(indices[i]);
859
860 EXPECT_EQ(table.frees(), 500);
861
862 // Remove from the end (triggers contraction and cleanup)
863 for (int i = 999; i >= 500; --i)
864 table.remove_pointer(indices[i]);
865
866 // All should be cleaned
867 EXPECT_TRUE(table.is_empty());
868 EXPECT_EQ(table.frees(), 0);
869 EXPECT_EQ(table.get_heap_index(), 0);
870 }
871}
872
873// =============================================================================
874// Consistency Tests
875// =============================================================================
876
878{
879protected:
881};
882
884{
885 vector<int> data(100);
886 set<long> active;
887
888 for (int i = 0; i < 100; ++i)
889 data[i] = i;
890
891 for (int iter = 0; iter < 1000; ++iter)
892 {
893 if (rand() % 2 == 0 or active.empty())
894 {
895 long idx = table.insert_pointer(&data[rand() % 100]);
896 active.insert(idx);
897 }
898 else
899 {
900 auto it = active.begin();
901 advance(it, rand() % active.size());
902 table.remove_pointer(*it);
903 active.erase(it);
904 }
905
906 // Consistency check
907 EXPECT_EQ(table.busies(), static_cast<long>(active.size()));
908 EXPECT_EQ(table.busies() + table.frees(), table.get_heap_index());
909 }
910}
911
913{
914 vector<int> data(500);
915 for (int i = 0; i < 500; ++i)
916 data[i] = i;
917
919 for (int i = 0; i < 500; ++i)
920 indices.push_back(table.insert_pointer(&data[i]));
921
922 // Random removals
924
925 for (int i = 0; i < 500; ++i)
926 {
927 table.remove_pointer(indices[i]);
928
929 // heap_index should always equal busies + frees
930 EXPECT_EQ(table.get_heap_index(), table.busies() + table.frees());
931 }
932}
933
934// =============================================================================
935// Null Index Constant Tests
936// =============================================================================
937
943
944// =============================================================================
945// Const Correctness Tests
946// =============================================================================
947
948class PointerTableConstTest : public Test
949{
950protected:
951 int d1 = 1, d2 = 2;
952};
953
955{
956 Pointer_Table table;
957 long idx = table.insert_pointer(&d1);
958 table.insert_pointer(&d2);
959
960 const Pointer_Table& const_ref = table;
961
962 EXPECT_EQ(const_ref.size(), table.size());
963 EXPECT_EQ(const_ref.busies(), table.busies());
964 EXPECT_EQ(const_ref.frees(), table.frees());
965 EXPECT_EQ(const_ref.get_heap_index(), table.get_heap_index());
966 EXPECT_EQ(const_ref.get_threshold(), table.get_threshold());
968 EXPECT_EQ(const_ref.get_pointer(idx), &d1);
969 EXPECT_EQ(const_ref.verify_pointer(idx, &d1), &d1);
970}
971
972// =============================================================================
973// Memory Pattern Tests
974// =============================================================================
975
977{
978protected:
980};
981
983{
984 vector<int> data(100);
985 for (int i = 0; i < 100; ++i)
986 data[i] = i;
987
988 for (int cycle = 0; cycle < 10; ++cycle)
989 {
990 // Grow
992 for (int i = 0; i < 100; ++i)
993 indices.push_back(table.insert_pointer(&data[i]));
994
995 // Shrink to half
996 for (int i = 99; i >= 50; --i)
997 table.remove_pointer(indices[i]);
998
999 EXPECT_EQ(table.busies(), 50);
1000 EXPECT_EQ(table.get_heap_index(), 50);
1001
1002 // Shrink rest
1003 for (int i = 49; i >= 0; --i)
1004 table.remove_pointer(indices[i]);
1005
1006 EXPECT_TRUE(table.is_empty());
1007 }
1008}
1009
1011{
1012 vector<int> data(1000);
1013 for (int i = 0; i < 1000; ++i)
1014 data[i] = i;
1015
1017 for (int i = 0; i < 1000; ++i)
1018 indices.push_back(table.insert_pointer(&data[i]));
1019
1020 // Create maximum fragmentation: remove every other element
1021 for (int i = 0; i < 1000; i += 2)
1022 table.remove_pointer(indices[i]);
1023
1024 EXPECT_EQ(table.busies(), 500);
1025
1026 // Now remove the rest in reverse order
1027 for (int i = 999; i >= 1; i -= 2)
1028 {
1029 table.remove_pointer(indices[i]);
1030
1031 // Verify consistency
1032 EXPECT_EQ(table.busies() + table.frees(), table.get_heap_index());
1033 }
1034
1035 EXPECT_TRUE(table.is_empty());
1036}
1037
1038// =============================================================================
1039// Specific Bug Regression Tests
1040// =============================================================================
1041
1043{
1044protected:
1046};
1047
1049{
1050 // Regression test for the bug where free_table was not cleaned
1051 // when heap contracted, leading to invalid indices being reused
1052
1053 int d1 = 1, d2 = 2, d3 = 3, d4 = 4, d5 = 5, d6 = 6;
1054
1055 // Setup: [d1, d2, d3, d4, d5]
1056 long i1 = table.insert_pointer(&d1);
1057 long i2 = table.insert_pointer(&d2);
1058 long i3 = table.insert_pointer(&d3);
1059 long i4 = table.insert_pointer(&d4);
1060 long i5 = table.insert_pointer(&d5);
1061 (void)i1;
1062 (void)i3;
1063
1064 // Remove i2 and i4 - they go to free_table
1065 table.remove_pointer(i2); // free_table = [1]
1066 table.remove_pointer(i4); // free_table = [1, 3]
1067
1068 // Remove i5 (top) - heap contracts, i3 should be removed from free_table
1069 table.remove_pointer(i5);
1070
1071 // Now heap_index should be 3, and free_table should only contain [1]
1072 EXPECT_EQ(table.get_heap_index(), 3);
1073 EXPECT_EQ(table.frees(), 1);
1074
1075 // Insert new element - should get index 1 (the only valid free index)
1076 long i6 = table.insert_pointer(&d6);
1077 EXPECT_EQ(i6, 1);
1078
1079 // Verify the pointer at index 1 is d6
1080 EXPECT_EQ(table.get_pointer(i6), &d6);
1081 EXPECT_EQ(table.verify_pointer(i6, &d6), &d6);
1082}
1083
1085{
1086 int d1 = 1, d2 = 2, d3 = 3, d4 = 4;
1087
1088 table.insert_pointer(&d1);
1089 long i2 = table.insert_pointer(&d2);
1090 long i3 = table.insert_pointer(&d3);
1091 long i4 = table.insert_pointer(&d4);
1092
1093 // Remove middle ones
1094 table.remove_pointer(i2);
1095 table.remove_pointer(i3);
1096
1097 EXPECT_EQ(table.frees(), 2);
1098
1099 // Remove top - should contract and clean free_table
1100 table.remove_pointer(i4);
1101
1102 // heap_index should be 1, and all free indices (1, 2) should be cleaned
1103 EXPECT_EQ(table.get_heap_index(), 1);
1104 EXPECT_EQ(table.frees(), 0);
1105 EXPECT_EQ(table.busies(), 1);
1106}
1107
1108// =============================================================================
1109// Main
1110// =============================================================================
1111
1112int main(int argc, char** argv)
1113{
1115 return RUN_ALL_TESTS();
1116}
int main()
T & insert(const T &item)
Insert a new item by copy.
Definition htlist.H:1502
void empty() noexcept
empty the list
Definition htlist.H:1689
constexpr bool is_empty() const noexcept
Return true if list is empty.
Definition htlist.H:523
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
A dynamic table for managing void pointers with index recycling.
long frees() const noexcept
Returns the number of recyclable indices.
void remove_pointer(long i)
Removes the pointer at the given index.
long get_threshold() const noexcept
Returns the threshold size.
long busies() const noexcept
Returns the number of pointers currently stored.
bool is_empty() const noexcept
Checks if the table is empty.
long get_heap_index() const noexcept
Returns the current heap index (high water mark).
long size() const noexcept
Returns the current capacity of the pointer table.
static constexpr long Null_Index
Sentinel value indicating an invalid or null index.
long insert_pointer(void *ptr)
Inserts a pointer and returns its assigned index.
void clear()
Clears all pointers from the table.
iterator end() noexcept
Return an STL-compatible end iterator.
iterator begin() noexcept
Return an STL-compatible iterator to the first element.
#define TEST(name)
static mt19937 rng
auto shuffle(const C< T > &c)
Randomly shuffle a sequence.
DynList< T > maps(const C &c, Op op)
Classic map operation.
STL namespace.
Pointer table with index reuse for efficient pointer management.
TEST_F(PointerTableConstructionTest, DefaultConstructor)