1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- G N A T . D Y N A M I C _ H T A B L E S --
9 -- Copyright (C) 2002-2024, AdaCore --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 with Ada
.Unchecked_Deallocation
;
34 package body GNAT
.Dynamic_HTables
is
40 function Hash_Two_Keys
41 (Left
: Bucket_Range_Type
;
42 Right
: Bucket_Range_Type
) return Bucket_Range_Type
44 Half
: constant := 2 ** (Bucket_Range_Type
'Size / 2);
45 Mask
: constant := Half
- 1;
48 -- The hash is obtained in the following manner:
50 -- 1) The low bits of Left are obtained, then shifted over to the high
53 -- 2) The low bits of Right are obtained
55 -- The results from 1) and 2) are or-ed to produce a value within the
56 -- range of Bucket_Range_Type.
59 (Left
and Mask
) * Half
68 package body Static_HTable
is
69 function Get_Non_Null
(T
: Instance
) return Elmt_Ptr
;
70 -- Returns Null_Ptr if Iterator_Started is False or if the Table is
71 -- empty. Returns Iterator_Ptr if non null, or the next non null element
78 function Get
(T
: Instance
; K
: Key
) return Elmt_Ptr
is
86 Elmt
:= T
.Table
(Hash
(K
));
89 if Elmt
= Null_Ptr
then
92 elsif Equal
(Get_Key
(Elmt
), K
) then
105 function Get_First
(T
: Instance
) return Elmt_Ptr
is
111 T
.Iterator_Started
:= True;
112 T
.Iterator_Index
:= T
.Table
'First;
113 T
.Iterator_Ptr
:= T
.Table
(T
.Iterator_Index
);
114 return Get_Non_Null
(T
);
121 function Get_Next
(T
: Instance
) return Elmt_Ptr
is
123 if T
= null or else not T
.Iterator_Started
then
127 T
.Iterator_Ptr
:= Next
(T
.Iterator_Ptr
);
128 return Get_Non_Null
(T
);
135 function Get_Non_Null
(T
: Instance
) return Elmt_Ptr
is
141 while T
.Iterator_Ptr
= Null_Ptr
loop
142 if T
.Iterator_Index
= T
.Table
'Last then
143 T
.Iterator_Started
:= False;
147 T
.Iterator_Index
:= T
.Iterator_Index
+ 1;
148 T
.Iterator_Ptr
:= T
.Table
(T
.Iterator_Index
);
151 return T
.Iterator_Ptr
;
158 procedure Remove
(T
: Instance
; K
: Key
) is
159 Index
: constant Header_Num
:= Hash
(K
);
161 Next_Elmt
: Elmt_Ptr
;
168 Elmt
:= T
.Table
(Index
);
170 if Elmt
= Null_Ptr
then
173 elsif Equal
(Get_Key
(Elmt
), K
) then
174 T
.Table
(Index
) := Next
(Elmt
);
178 Next_Elmt
:= Next
(Elmt
);
180 if Next_Elmt
= Null_Ptr
then
183 elsif Equal
(Get_Key
(Next_Elmt
), K
) then
184 Set_Next
(Elmt
, Next
(Next_Elmt
));
198 procedure Reset
(T
: in out Instance
) is
200 new Ada
.Unchecked_Deallocation
(Instance_Data
, Instance
);
207 for J
in T
.Table
'Range loop
208 T
.Table
(J
) := Null_Ptr
;
218 procedure Set
(T
: in out Instance
; E
: Elmt_Ptr
) is
223 T
:= new Instance_Data
;
226 Index
:= Hash
(Get_Key
(E
));
227 Set_Next
(E
, T
.Table
(Index
));
228 T
.Table
(Index
) := E
;
237 package body Simple_HTable
is
238 procedure Free
is new
239 Ada
.Unchecked_Deallocation
(Element_Wrapper
, Elmt_Ptr
);
245 function Get
(T
: Instance
; K
: Key
) return Element
is
253 Tmp
:= Tab
.Get
(Tab
.Instance
(T
), K
);
266 function Get_First
(T
: Instance
) return Element
is
267 Tmp
: constant Elmt_Ptr
:= Tab
.Get_First
(Tab
.Instance
(T
));
281 function Get_First_Key
(T
: Instance
) return Key_Option
is
282 Tmp
: constant Elmt_Ptr
:= Tab
.Get_First
(Tab
.Instance
(T
));
285 return Key_Option
'(Present => False);
287 return Key_Option'(Present
=> True, K
=> Tmp
.all.K
);
295 function Get_Key
(E
: Elmt_Ptr
) return Key
is
304 function Get_Next
(T
: Instance
) return Element
is
305 Tmp
: constant Elmt_Ptr
:= Tab
.Get_Next
(Tab
.Instance
(T
));
318 function Get_Next_Key
(T
: Instance
) return Key_Option
is
319 Tmp
: constant Elmt_Ptr
:= Tab
.Get_Next
(Tab
.Instance
(T
));
322 return Key_Option
'(Present => False);
324 return Key_Option'(Present
=> True, K
=> Tmp
.all.K
);
332 function Next
(E
: Elmt_Ptr
) return Elmt_Ptr
is
341 procedure Remove
(T
: Instance
; K
: Key
) is
345 Tmp
:= Tab
.Get
(Tab
.Instance
(T
), K
);
348 Tab
.Remove
(Tab
.Instance
(T
), K
);
357 procedure Reset
(T
: in out Instance
) is
361 E1
:= Tab
.Get_First
(Tab
.Instance
(T
));
362 while E1
/= null loop
363 E2
:= Tab
.Get_Next
(Tab
.Instance
(T
));
368 Tab
.Reset
(Tab
.Instance
(T
));
375 procedure Set
(T
: in out Instance
; K
: Key
; E
: Element
) is
376 Tmp
: constant Elmt_Ptr
:= Tab
.Get
(Tab
.Instance
(T
), K
);
379 Tab
.Set
(Tab
.Instance
(T
), new Element_Wrapper
'(K, E, null));
389 procedure Set_Next (E : Elmt_Ptr; Next : Elmt_Ptr) is
395 -------------------------
396 -- Dynamic_Hash_Tables --
397 -------------------------
399 package body Dynamic_Hash_Tables is
400 Minimum_Size : constant Bucket_Range_Type := 8;
401 -- Minimum size of the buckets
403 Safe_Compression_Size : constant Bucket_Range_Type :=
404 Minimum_Size * Compression_Factor;
405 -- Maximum safe size for hash table compression. Beyond this size, a
406 -- compression will violate the minimum size constraint on the buckets.
408 Safe_Expansion_Size : constant Bucket_Range_Type :=
409 Bucket_Range_Type'Last / Expansion_Factor;
410 -- Maximum safe size for hash table expansion. Beyond this size, an
411 -- expansion will overflow the buckets.
413 procedure Delete_Node
414 (T : Dynamic_Hash_Table;
416 pragma Inline (Delete_Node);
417 -- Detach and delete node Nod from table T
419 procedure Destroy_Buckets (Bkts : Bucket_Table_Ptr);
420 pragma Inline (Destroy_Buckets);
421 -- Destroy all nodes within buckets Bkts
423 procedure Detach (Nod : Node_Ptr);
424 pragma Inline (Detach);
425 -- Detach node Nod from the bucket it resides in
427 procedure Ensure_Circular (Head : Node_Ptr);
428 pragma Inline (Ensure_Circular);
429 -- Ensure that dummy head Head is circular with respect to itself
431 procedure Ensure_Created (T : Dynamic_Hash_Table);
432 pragma Inline (Ensure_Created);
433 -- Verify that hash table T is created. Raise Not_Created if this is not
436 procedure Ensure_Unlocked (T : Dynamic_Hash_Table);
437 pragma Inline (Ensure_Unlocked);
438 -- Verify that hash table T is unlocked. Raise Iterated if this is not
442 (Bkts : Bucket_Table_Ptr;
443 Key : Key_Type) return Node_Ptr;
444 pragma Inline (Find_Bucket);
445 -- Find the bucket among buckets Bkts which corresponds to key Key, and
446 -- return its dummy head.
448 function Find_Node (Head : Node_Ptr; Key : Key_Type) return Node_Ptr;
449 pragma Inline (Find_Node);
450 -- Traverse a bucket indicated by dummy head Head to determine whether
451 -- there exists a node with key Key. If such a node exists, return it,
452 -- otherwise return null.
454 procedure First_Valid_Node
455 (T : Dynamic_Hash_Table;
456 Low_Bkt : Bucket_Range_Type;
457 High_Bkt : Bucket_Range_Type;
458 Idx : out Bucket_Range_Type;
460 pragma Inline (First_Valid_Node);
461 -- Find the first valid node in the buckets of hash table T constrained
462 -- by the range Low_Bkt .. High_Bkt. If such a node exists, return its
463 -- bucket index in Idx and reference in Nod. If no such node exists,
464 -- Idx is set to 0 and Nod to null.
467 new Ada.Unchecked_Deallocation (Bucket_Table, Bucket_Table_Ptr);
470 new Ada.Unchecked_Deallocation
471 (Dynamic_Hash_Table_Attributes, Dynamic_Hash_Table);
474 new Ada.Unchecked_Deallocation (Node, Node_Ptr);
476 function Is_Valid (Iter : Iterator) return Boolean;
477 pragma Inline (Is_Valid);
478 -- Determine whether iterator Iter refers to a valid key-value pair
480 function Is_Valid (Nod : Node_Ptr; Head : Node_Ptr) return Boolean;
481 pragma Inline (Is_Valid);
482 -- Determine whether node Nod is non-null and does not refer to dummy
483 -- head Head, thus making it valid.
485 function Load_Factor (T : Dynamic_Hash_Table) return Threshold_Type;
486 pragma Inline (Load_Factor);
487 -- Calculate the load factor of hash table T
489 procedure Lock (T : Dynamic_Hash_Table);
490 pragma Inline (Lock);
491 -- Lock all mutation functionality of hash table T
493 procedure Mutate_And_Rehash
494 (T : Dynamic_Hash_Table;
495 Size : Bucket_Range_Type);
496 pragma Inline (Mutate_And_Rehash);
497 -- Replace the buckets of hash table T with a new set of buckets of size
498 -- Size. Rehash all key-value pairs from the old to the new buckets.
500 procedure Prepend (Nod : Node_Ptr; Head : Node_Ptr);
501 pragma Inline (Prepend);
502 -- Insert node Nod immediately after dummy head Head
504 function Present (Bkts : Bucket_Table_Ptr) return Boolean;
505 pragma Inline (Present);
506 -- Determine whether buckets Bkts exist
508 function Present (Nod : Node_Ptr) return Boolean;
509 pragma Inline (Present);
510 -- Determine whether node Nod exists
512 procedure Unlock (T : Dynamic_Hash_Table);
513 pragma Inline (Unlock);
514 -- Unlock all mutation functionality of hash table T
521 (T : Dynamic_Hash_Table;
522 Key : Key_Type) return Boolean
530 -- Obtain the dummy head of the bucket which should house the
533 Head := Find_Bucket (T.Buckets, Key);
535 -- Try to find a node in the bucket which matches the key
537 Nod := Find_Node (Head, Key);
539 return Is_Valid (Nod, Head);
546 function Create (Initial_Size : Positive) return Dynamic_Hash_Table is
547 Size : constant Bucket_Range_Type :=
548 Bucket_Range_Type'Max
549 (Bucket_Range_Type (Initial_Size), Minimum_Size);
550 -- Ensure that the buckets meet a minimum size
552 T : constant Dynamic_Hash_Table := new Dynamic_Hash_Table_Attributes;
555 T.Buckets := new Bucket_Table (0 .. Size - 1);
556 T.Initial_Size := Size;
566 (T : Dynamic_Hash_Table;
576 -- Obtain the dummy head of the bucket which should house the
579 Head := Find_Bucket (T.Buckets, Key);
581 -- Try to find a node in the bucket which matches the key
583 Nod := Find_Node (Head, Key);
585 -- If such a node exists, remove it from the bucket and deallocate it
587 if Is_Valid (Nod, Head) then
588 Delete_Node (T, Nod);
596 procedure Delete_Node
597 (T : Dynamic_Hash_Table;
601 pragma Inline (Compress);
602 -- Determine whether hash table T requires compression, and if so,
609 procedure Compress is
610 pragma Assert (Present (T));
611 pragma Assert (Present (T.Buckets));
613 Old_Size : constant Bucket_Range_Type := T.Buckets'Length;
616 -- The ratio of pairs to buckets is under the desited threshold.
617 -- Compress the hash table only when there is still room to do so.
619 if Load_Factor (T) < Compression_Threshold
620 and then Old_Size >= Safe_Compression_Size
622 Mutate_And_Rehash (T, Old_Size / Compression_Factor);
628 Ref : Node_Ptr := Nod;
630 -- Start of processing for Delete_Node
633 pragma Assert (Present (Ref));
634 pragma Assert (Present (T));
639 -- The number of key-value pairs is updated when the hash table
640 -- contains a valid node which represents the pair.
642 T.Pairs := T.Pairs - 1;
644 -- Compress the hash table if the load factor drops below the value
645 -- of Compression_Threshold.
654 procedure Destroy (T : in out Dynamic_Hash_Table) is
659 -- Destroy all nodes in all buckets
661 Destroy_Buckets (T.Buckets);
666 ---------------------
667 -- Destroy_Buckets --
668 ---------------------
670 procedure Destroy_Buckets (Bkts : Bucket_Table_Ptr) is
671 procedure Destroy_Bucket (Head : Node_Ptr);
672 pragma Inline (Destroy_Bucket);
673 -- Destroy all nodes in a bucket with dummy head Head
679 procedure Destroy_Bucket (Head : Node_Ptr) is
683 -- Destroy all valid nodes which follow the dummy head
685 while Is_Valid (Head.Next, Head) loop
688 -- Invoke the value destructor before deallocating the node
690 Destroy_Value (Nod.Value);
697 -- Start of processing for Destroy_Buckets
700 pragma Assert (Present (Bkts));
702 for Scan_Idx in Bkts'Range loop
703 Destroy_Bucket (Bkts (Scan_Idx)'Access);
711 procedure Detach (Nod : Node_Ptr) is
712 pragma Assert (Present (Nod));
714 Next : constant Node_Ptr := Nod.Next;
715 Prev : constant Node_Ptr := Nod.Prev;
718 pragma Assert (Present (Next));
719 pragma Assert (Present (Prev));
721 Prev.Next := Next; -- Prev ---> Next
722 Next.Prev := Prev; -- Prev <--> Next
728 ---------------------
729 -- Ensure_Circular --
730 ---------------------
732 procedure Ensure_Circular (Head : Node_Ptr) is
733 pragma Assert (Present (Head));
736 if not Present (Head.Next) and then not Present (Head.Prev) then
746 procedure Ensure_Created (T : Dynamic_Hash_Table) is
748 if not Present (T) then
753 ---------------------
754 -- Ensure_Unlocked --
755 ---------------------
757 procedure Ensure_Unlocked (T : Dynamic_Hash_Table) is
759 pragma Assert (Present (T));
761 -- The hash table has at least one outstanding iterator
763 if T.Iterators > 0 then
773 (Bkts : Bucket_Table_Ptr;
774 Key : Key_Type) return Node_Ptr
776 pragma Assert (Present (Bkts));
778 Idx : constant Bucket_Range_Type := Hash (Key) mod Bkts'Length;
781 return Bkts (Idx)'Access;
788 function Find_Node (Head : Node_Ptr; Key : Key_Type) return Node_Ptr is
789 pragma Assert (Present (Head));
794 -- Traverse the nodes of the bucket, looking for a key-value pair
795 -- with the same key.
798 while Is_Valid (Nod, Head) loop
799 if Nod.Key = Key then
809 ----------------------
810 -- First_Valid_Node --
811 ----------------------
813 procedure First_Valid_Node
814 (T : Dynamic_Hash_Table;
815 Low_Bkt : Bucket_Range_Type;
816 High_Bkt : Bucket_Range_Type;
817 Idx : out Bucket_Range_Type;
823 pragma Assert (Present (T));
824 pragma Assert (Present (T.Buckets));
826 -- Assume that no valid node exists
831 -- Examine the buckets of the hash table within the requested range,
832 -- looking for the first valid node.
834 for Scan_Idx in Low_Bkt .. High_Bkt loop
835 Head := T.Buckets (Scan_Idx)'Access;
837 -- The bucket contains at least one valid node, return the first
840 if Is_Valid (Head.Next, Head) then
846 end First_Valid_Node;
853 (T : Dynamic_Hash_Table;
854 Key : Key_Type) return Value_Type
862 -- Obtain the dummy head of the bucket which should house the
865 Head := Find_Bucket (T.Buckets, Key);
867 -- Try to find a node in the bucket which matches the key
869 Nod := Find_Node (Head, Key);
871 -- If such a node exists, return the value of the key-value pair
873 if Is_Valid (Nod, Head) then
884 function Has_Next (Iter : Iterator) return Boolean is
885 Is_OK : constant Boolean := Is_Valid (Iter);
886 T : constant Dynamic_Hash_Table := Iter.Table;
889 pragma Assert (Present (T));
891 -- The iterator is no longer valid which indicates that it has been
892 -- exhausted. Unlock all mutation functionality of the hash table
893 -- because the iterator cannot be advanced any further.
906 function Is_Empty (T : Dynamic_Hash_Table) return Boolean is
917 function Is_Valid (Iter : Iterator) return Boolean is
919 -- The invariant of Iterate and Next ensures that the iterator always
920 -- refers to a valid node if there exists one.
922 return Present (Iter.Curr_Nod);
929 function Is_Valid (Nod : Node_Ptr; Head : Node_Ptr) return Boolean is
931 -- A node is valid if it is non-null, and does not refer to the dummy
932 -- head of some bucket.
934 return Present (Nod) and then Nod /= Head;
941 function Iterate (T : Dynamic_Hash_Table) return Iterator is
946 pragma Assert (Present (T.Buckets));
948 -- Initialize the iterator to reference the first valid node in
949 -- the full range of hash table buckets. If no such node exists,
950 -- the iterator is left in a state which does not allow it to
955 Low_Bkt => T.Buckets'First,
956 High_Bkt => T.Buckets'Last,
957 Idx => Iter.Curr_Idx,
958 Nod => Iter.Curr_Nod);
960 -- Associate the iterator with the hash table to allow for future
961 -- mutation functionality unlocking.
965 -- Lock all mutation functionality of the hash table while it is
966 -- being iterated on.
977 function Load_Factor (T : Dynamic_Hash_Table) return Threshold_Type is
978 pragma Assert (Present (T));
979 pragma Assert (Present (T.Buckets));
982 -- The load factor is the ratio of key-value pairs to buckets
984 return Threshold_Type (T.Pairs) / Threshold_Type (T.Buckets'Length);
991 procedure Lock (T : Dynamic_Hash_Table) is
993 -- The hash table may be locked multiple times if multiple iterators
994 -- are operating over it.
996 T.Iterators := T.Iterators + 1;
999 -----------------------
1000 -- Mutate_And_Rehash --
1001 -----------------------
1003 procedure Mutate_And_Rehash
1004 (T : Dynamic_Hash_Table;
1005 Size : Bucket_Range_Type)
1007 procedure Rehash (From : Bucket_Table_Ptr; To : Bucket_Table_Ptr);
1008 pragma Inline (Rehash);
1009 -- Remove all nodes from buckets From and rehash them into buckets To
1011 procedure Rehash_Bucket (Head : Node_Ptr; To : Bucket_Table_Ptr);
1012 pragma Inline (Rehash_Bucket);
1013 -- Detach all nodes starting from dummy head Head and rehash them
1016 procedure Rehash_Node (Nod : Node_Ptr; To : Bucket_Table_Ptr);
1017 pragma Inline (Rehash_Node);
1018 -- Rehash node Nod into To
1024 procedure Rehash (From : Bucket_Table_Ptr; To : Bucket_Table_Ptr) is
1026 pragma Assert (Present (From));
1027 pragma Assert (Present (To));
1029 for Scan_Idx in From'Range loop
1030 Rehash_Bucket (From (Scan_Idx)'Access, To);
1038 procedure Rehash_Bucket (Head : Node_Ptr; To : Bucket_Table_Ptr) is
1039 pragma Assert (Present (Head));
1044 -- Detach all nodes which follow the dummy head
1046 while Is_Valid (Head.Next, Head) loop
1050 Rehash_Node (Nod, To);
1058 procedure Rehash_Node (Nod : Node_Ptr; To : Bucket_Table_Ptr) is
1059 pragma Assert (Present (Nod));
1064 -- Obtain the dummy head of the bucket which should house the
1067 Head := Find_Bucket (To, Nod.Key);
1069 -- Ensure that the dummy head of an empty bucket is circular with
1070 -- respect to itself.
1072 Ensure_Circular (Head);
1074 -- Prepend the node to the bucket
1076 Prepend (Nod, Head);
1079 -- Local declarations
1081 Old_Bkts : Bucket_Table_Ptr;
1083 -- Start of processing for Mutate_And_Rehash
1086 pragma Assert (Present (T));
1088 Old_Bkts := T.Buckets;
1089 T.Buckets := new Bucket_Table (0 .. Size - 1);
1091 -- Transfer and rehash all key-value pairs from the old buckets to
1094 Rehash (From => Old_Bkts, To => T.Buckets);
1096 end Mutate_And_Rehash;
1102 procedure Next (Iter : in out Iterator; Key : out Key_Type) is
1103 Is_OK : constant Boolean := Is_Valid (Iter);
1104 Saved : constant Node_Ptr := Iter.Curr_Nod;
1105 T : constant Dynamic_Hash_Table := Iter.Table;
1109 pragma Assert (Present (T));
1110 pragma Assert (Present (T.Buckets));
1112 -- The iterator is no longer valid which indicates that it has been
1113 -- exhausted. Unlock all mutation functionality of the hash table as
1114 -- the iterator cannot be advanced any further.
1118 raise Iterator_Exhausted;
1121 -- Advance to the next node along the same bucket
1123 Iter.Curr_Nod := Iter.Curr_Nod.Next;
1124 Head := T.Buckets (Iter.Curr_Idx)'Access;
1126 -- If the new node is no longer valid, then this indicates that the
1127 -- current bucket has been exhausted. Advance to the next valid node
1128 -- within the remaining range of buckets. If no such node exists, the
1129 -- iterator is left in a state which does not allow it to advance.
1131 if not Is_Valid (Iter.Curr_Nod, Head) then
1134 Low_Bkt => Iter.Curr_Idx + 1,
1135 High_Bkt => T.Buckets'Last,
1136 Idx => Iter.Curr_Idx,
1137 Nod => Iter.Curr_Nod);
1147 procedure Prepend (Nod : Node_Ptr; Head : Node_Ptr) is
1148 pragma Assert (Present (Nod));
1149 pragma Assert (Present (Head));
1151 Next : constant Node_Ptr := Head.Next;
1165 function Present (Bkts : Bucket_Table_Ptr) return Boolean is
1167 return Bkts /= null;
1174 function Present (Nod : Node_Ptr) return Boolean is
1183 function Present (T : Dynamic_Hash_Table) return Boolean is
1193 (T : Dynamic_Hash_Table;
1198 pragma Inline (Expand);
1199 -- Determine whether hash table T requires expansion, and if so,
1202 procedure Prepend_Or_Replace (Head : Node_Ptr);
1203 pragma Inline (Prepend_Or_Replace);
1204 -- Update the value of a node within a bucket with dummy head Head
1205 -- whose key is Key to Value. If there is no such node, prepend a new
1206 -- key-value pair to the bucket.
1213 pragma Assert (Present (T));
1214 pragma Assert (Present (T.Buckets));
1216 Old_Size : constant Bucket_Range_Type := T.Buckets'Length;
1219 -- The ratio of pairs to buckets is over the desited threshold.
1220 -- Expand the hash table only when there is still room to do so.
1222 if Load_Factor (T) > Expansion_Threshold
1223 and then Old_Size <= Safe_Expansion_Size
1225 Mutate_And_Rehash (T, Old_Size * Expansion_Factor);
1229 ------------------------
1230 -- Prepend_Or_Replace --
1231 ------------------------
1233 procedure Prepend_Or_Replace (Head : Node_Ptr) is
1234 pragma Assert (Present (Head));
1239 -- If the bucket containst at least one valid node, then there is
1240 -- a chance that a node with the same key as Key exists. If this
1241 -- is the case, the value of that node must be updated.
1244 while Is_Valid (Nod, Head) loop
1245 if Nod.Key = Key then
1253 -- At this point the bucket is either empty, or none of the nodes
1254 -- match key Key. Prepend a new key-value pair.
1256 Nod := new Node'(Key
, Value
, null, null);
1258 Prepend
(Nod
, Head
);
1260 -- The number of key-value pairs must be updated for a prepend,
1261 -- never for a replace.
1263 T
.Pairs
:= T
.Pairs
+ 1;
1264 end Prepend_Or_Replace
;
1270 -- Start of processing for Put
1274 Ensure_Unlocked
(T
);
1276 -- Obtain the dummy head of the bucket which should house the
1279 Head
:= Find_Bucket
(T
.Buckets
, Key
);
1281 -- Ensure that the dummy head of an empty bucket is circular with
1282 -- respect to itself.
1284 Ensure_Circular
(Head
);
1286 -- In case the bucket already contains a node with the same key,
1287 -- replace its value, otherwise prepend a new key-value pair.
1289 Prepend_Or_Replace
(Head
);
1291 -- Expand the hash table if the ratio of pairs to buckets goes over
1292 -- Expansion_Threshold.
1301 procedure Reset
(T
: Dynamic_Hash_Table
) is
1304 Ensure_Unlocked
(T
);
1306 -- Destroy all nodes in all buckets
1308 Destroy_Buckets
(T
.Buckets
);
1311 -- Recreate the buckets using the original size from creation time
1313 T
.Buckets
:= new Bucket_Table
(0 .. T
.Initial_Size
- 1);
1321 function Size
(T
: Dynamic_Hash_Table
) return Natural is
1332 procedure Unlock
(T
: Dynamic_Hash_Table
) is
1334 -- The hash table may be locked multiple times if multiple iterators
1335 -- are operating over it.
1337 T
.Iterators
:= T
.Iterators
- 1;
1339 end Dynamic_Hash_Tables
;
1341 end GNAT
.Dynamic_HTables
;