1------------------------------------------------------------------------------
2--                                                                          --
3--                         GNAT RUN-TIME COMPONENTS                         --
4--                                                                          --
5--                 G N A T . D Y N A M I C _ H T A B L E S                  --
6--                                                                          --
7--                                 B o d y                                  --
8--                                                                          --
9--                     Copyright (C) 2002-2020, AdaCore                     --
10--                                                                          --
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.                                     --
17--                                                                          --
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.               --
21--                                                                          --
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/>.                                          --
26--                                                                          --
27-- GNAT was originally developed  by the GNAT team at  New York University. --
28-- Extensive contributions were provided by Ada Core Technologies Inc.      --
29--                                                                          --
30------------------------------------------------------------------------------
31
32with Ada.Unchecked_Deallocation;
33
34package body GNAT.Dynamic_HTables is
35
36   -------------------
37   -- Hash_Two_Keys --
38   -------------------
39
40   function Hash_Two_Keys
41     (Left  : Bucket_Range_Type;
42      Right : Bucket_Range_Type) return Bucket_Range_Type
43   is
44      Half : constant := 2 ** (Bucket_Range_Type'Size / 2);
45      Mask : constant := Half - 1;
46
47   begin
48      --  The hash is obtained in the following manner:
49      --
50      --    1) The low bits of Left are obtained, then shifted over to the high
51      --       bits position.
52      --
53      --    2) The low bits of Right are obtained
54      --
55      --  The results from 1) and 2) are or-ed to produce a value within the
56      --  range of Bucket_Range_Type.
57
58      return
59        ((Left  and Mask) * Half)
60            or
61         (Right and Mask);
62   end Hash_Two_Keys;
63
64   -------------------
65   -- Static_HTable --
66   -------------------
67
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
72      --  in table if any.
73
74      ---------
75      -- Get --
76      ---------
77
78      function Get (T : Instance; K : Key) return Elmt_Ptr is
79         Elmt : Elmt_Ptr;
80
81      begin
82         if T = null then
83            return Null_Ptr;
84         end if;
85
86         Elmt := T.Table (Hash (K));
87
88         loop
89            if Elmt = Null_Ptr then
90               return Null_Ptr;
91
92            elsif Equal (Get_Key (Elmt), K) then
93               return Elmt;
94
95            else
96               Elmt := Next (Elmt);
97            end if;
98         end loop;
99      end Get;
100
101      ---------------
102      -- Get_First --
103      ---------------
104
105      function Get_First (T : Instance) return Elmt_Ptr is
106      begin
107         if T = null then
108            return Null_Ptr;
109         end if;
110
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);
115      end Get_First;
116
117      --------------
118      -- Get_Next --
119      --------------
120
121      function Get_Next (T : Instance) return Elmt_Ptr is
122      begin
123         if T = null or else not T.Iterator_Started then
124            return Null_Ptr;
125         end if;
126
127         T.Iterator_Ptr := Next (T.Iterator_Ptr);
128         return Get_Non_Null (T);
129      end Get_Next;
130
131      ------------------
132      -- Get_Non_Null --
133      ------------------
134
135      function Get_Non_Null (T : Instance) return Elmt_Ptr is
136      begin
137         if T = null then
138            return Null_Ptr;
139         end if;
140
141         while T.Iterator_Ptr = Null_Ptr  loop
142            if T.Iterator_Index = T.Table'Last then
143               T.Iterator_Started := False;
144               return Null_Ptr;
145            end if;
146
147            T.Iterator_Index := T.Iterator_Index + 1;
148            T.Iterator_Ptr   := T.Table (T.Iterator_Index);
149         end loop;
150
151         return T.Iterator_Ptr;
152      end Get_Non_Null;
153
154      ------------
155      -- Remove --
156      ------------
157
158      procedure Remove  (T : Instance; K : Key) is
159         Index     : constant Header_Num := Hash (K);
160         Elmt      : Elmt_Ptr;
161         Next_Elmt : Elmt_Ptr;
162
163      begin
164         if T = null then
165            return;
166         end if;
167
168         Elmt := T.Table (Index);
169
170         if Elmt = Null_Ptr then
171            return;
172
173         elsif Equal (Get_Key (Elmt), K) then
174            T.Table (Index) := Next (Elmt);
175
176         else
177            loop
178               Next_Elmt := Next (Elmt);
179
180               if Next_Elmt = Null_Ptr then
181                  return;
182
183               elsif Equal (Get_Key (Next_Elmt), K) then
184                  Set_Next (Elmt, Next (Next_Elmt));
185                  return;
186
187               else
188                  Elmt := Next_Elmt;
189               end if;
190            end loop;
191         end if;
192      end Remove;
193
194      -----------
195      -- Reset --
196      -----------
197
198      procedure Reset (T : in out Instance) is
199         procedure Free is
200           new Ada.Unchecked_Deallocation (Instance_Data, Instance);
201
202      begin
203         if T = null then
204            return;
205         end if;
206
207         for J in T.Table'Range loop
208            T.Table (J) := Null_Ptr;
209         end loop;
210
211         Free (T);
212      end Reset;
213
214      ---------
215      -- Set --
216      ---------
217
218      procedure Set (T : in out Instance; E : Elmt_Ptr) is
219         Index : Header_Num;
220
221      begin
222         if T = null then
223            T := new Instance_Data;
224         end if;
225
226         Index := Hash (Get_Key (E));
227         Set_Next (E, T.Table (Index));
228         T.Table (Index) := E;
229      end Set;
230
231   end Static_HTable;
232
233   -------------------
234   -- Simple_HTable --
235   -------------------
236
237   package body Simple_HTable is
238      procedure Free is new
239        Ada.Unchecked_Deallocation (Element_Wrapper, Elmt_Ptr);
240
241      ---------
242      -- Get --
243      ---------
244
245      function Get (T : Instance; K : Key) return Element is
246         Tmp : Elmt_Ptr;
247
248      begin
249         if T = Nil then
250            return No_Element;
251         end if;
252
253         Tmp := Tab.Get (Tab.Instance (T), K);
254
255         if Tmp = null then
256            return No_Element;
257         else
258            return Tmp.E;
259         end if;
260      end Get;
261
262      ---------------
263      -- Get_First --
264      ---------------
265
266      function Get_First (T : Instance) return Element is
267         Tmp : constant Elmt_Ptr := Tab.Get_First (Tab.Instance (T));
268
269      begin
270         if Tmp = null then
271            return No_Element;
272         else
273            return Tmp.E;
274         end if;
275      end Get_First;
276
277      -------------------
278      -- Get_First_Key --
279      -------------------
280
281      function Get_First_Key (T : Instance) return Key_Option is
282         Tmp : constant Elmt_Ptr := Tab.Get_First (Tab.Instance (T));
283      begin
284         if Tmp = null then
285            return Key_Option'(Present => False);
286         else
287            return Key_Option'(Present => True, K => Tmp.all.K);
288         end if;
289      end Get_First_Key;
290
291      -------------
292      -- Get_Key --
293      -------------
294
295      function Get_Key (E : Elmt_Ptr) return Key is
296      begin
297         return E.K;
298      end Get_Key;
299
300      --------------
301      -- Get_Next --
302      --------------
303
304      function Get_Next (T : Instance) return Element is
305         Tmp : constant Elmt_Ptr := Tab.Get_Next (Tab.Instance (T));
306      begin
307         if Tmp = null then
308            return No_Element;
309         else
310            return Tmp.E;
311         end if;
312      end Get_Next;
313
314      ------------------
315      -- Get_Next_Key --
316      ------------------
317
318      function Get_Next_Key (T : Instance) return Key_Option is
319         Tmp : constant Elmt_Ptr := Tab.Get_Next (Tab.Instance (T));
320      begin
321         if Tmp = null then
322            return Key_Option'(Present => False);
323         else
324            return Key_Option'(Present => True, K => Tmp.all.K);
325         end if;
326      end Get_Next_Key;
327
328      ----------
329      -- Next --
330      ----------
331
332      function Next (E : Elmt_Ptr) return Elmt_Ptr is
333      begin
334         return E.Next;
335      end Next;
336
337      ------------
338      -- Remove --
339      ------------
340
341      procedure Remove  (T : Instance; K : Key) is
342         Tmp : Elmt_Ptr;
343
344      begin
345         Tmp := Tab.Get (Tab.Instance (T), K);
346
347         if Tmp /= null then
348            Tab.Remove (Tab.Instance (T), K);
349            Free (Tmp);
350         end if;
351      end Remove;
352
353      -----------
354      -- Reset --
355      -----------
356
357      procedure Reset (T : in out Instance) is
358         E1, E2 : Elmt_Ptr;
359
360      begin
361         E1 := Tab.Get_First (Tab.Instance (T));
362         while E1 /= null loop
363            E2 := Tab.Get_Next (Tab.Instance (T));
364            Free (E1);
365            E1 := E2;
366         end loop;
367
368         Tab.Reset (Tab.Instance (T));
369      end Reset;
370
371      ---------
372      -- Set --
373      ---------
374
375      procedure Set (T : in out Instance; K : Key; E : Element) is
376         Tmp : constant Elmt_Ptr := Tab.Get (Tab.Instance (T), K);
377      begin
378         if Tmp = null then
379            Tab.Set (Tab.Instance (T), new Element_Wrapper'(K, E, null));
380         else
381            Tmp.E := E;
382         end if;
383      end Set;
384
385      --------------
386      -- Set_Next --
387      --------------
388
389      procedure Set_Next (E : Elmt_Ptr; Next : Elmt_Ptr) is
390      begin
391         E.Next := Next;
392      end Set_Next;
393   end Simple_HTable;
394
395   -------------------------
396   -- Dynamic_Hash_Tables --
397   -------------------------
398
399   package body Dynamic_Hash_Tables is
400      Minimum_Size : constant Bucket_Range_Type := 8;
401      --  Minimum size of the buckets
402
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.
407
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.
412
413      procedure Delete_Node
414        (T   : Dynamic_Hash_Table;
415         Nod : Node_Ptr);
416      pragma Inline (Delete_Node);
417      --  Detach and delete node Nod from table T
418
419      procedure Destroy_Buckets (Bkts : Bucket_Table_Ptr);
420      pragma Inline (Destroy_Buckets);
421      --  Destroy all nodes within buckets Bkts
422
423      procedure Detach (Nod : Node_Ptr);
424      pragma Inline (Detach);
425      --  Detach node Nod from the bucket it resides in
426
427      procedure Ensure_Circular (Head : Node_Ptr);
428      pragma Inline (Ensure_Circular);
429      --  Ensure that dummy head Head is circular with respect to itself
430
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
434      --  the case.
435
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
439      --  the case.
440
441      function Find_Bucket
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.
447
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.
453
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;
459         Nod      : out Node_Ptr);
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.
465
466      procedure Free is
467        new Ada.Unchecked_Deallocation (Bucket_Table, Bucket_Table_Ptr);
468
469      procedure Free is
470        new Ada.Unchecked_Deallocation
471              (Dynamic_Hash_Table_Attributes, Dynamic_Hash_Table);
472
473      procedure Free is
474        new Ada.Unchecked_Deallocation (Node, Node_Ptr);
475
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
479
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.
484
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
488
489      procedure Lock (T : Dynamic_Hash_Table);
490      pragma Inline (Lock);
491      --  Lock all mutation functionality of hash table T
492
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.
499
500      procedure Prepend (Nod : Node_Ptr; Head : Node_Ptr);
501      pragma Inline (Prepend);
502      --  Insert node Nod immediately after dummy head Head
503
504      function Present (Bkts : Bucket_Table_Ptr) return Boolean;
505      pragma Inline (Present);
506      --  Determine whether buckets Bkts exist
507
508      function Present (Nod : Node_Ptr) return Boolean;
509      pragma Inline (Present);
510      --  Determine whether node Nod exists
511
512      procedure Unlock (T : Dynamic_Hash_Table);
513      pragma Inline (Unlock);
514      --  Unlock all mutation functionality of hash table T
515
516      --------------
517      -- Contains --
518      --------------
519
520      function Contains
521        (T   : Dynamic_Hash_Table;
522         Key : Key_Type) return Boolean
523      is
524         Head : Node_Ptr;
525         Nod  : Node_Ptr;
526
527      begin
528         Ensure_Created (T);
529
530         --  Obtain the dummy head of the bucket which should house the
531         --  key-value pair.
532
533         Head := Find_Bucket (T.Buckets, Key);
534
535         --  Try to find a node in the bucket which matches the key
536
537         Nod := Find_Node (Head, Key);
538
539         return Is_Valid (Nod, Head);
540      end Contains;
541
542      ------------
543      -- Create --
544      ------------
545
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
551
552         T : constant Dynamic_Hash_Table := new Dynamic_Hash_Table_Attributes;
553
554      begin
555         T.Buckets      := new Bucket_Table (0 .. Size - 1);
556         T.Initial_Size := Size;
557
558         return T;
559      end Create;
560
561      ------------
562      -- Delete --
563      ------------
564
565      procedure Delete
566        (T   : Dynamic_Hash_Table;
567         Key : Key_Type)
568      is
569         Head : Node_Ptr;
570         Nod  : Node_Ptr;
571
572      begin
573         Ensure_Created  (T);
574         Ensure_Unlocked (T);
575
576         --  Obtain the dummy head of the bucket which should house the
577         --  key-value pair.
578
579         Head := Find_Bucket (T.Buckets, Key);
580
581         --  Try to find a node in the bucket which matches the key
582
583         Nod := Find_Node (Head, Key);
584
585         --  If such a node exists, remove it from the bucket and deallocate it
586
587         if Is_Valid (Nod, Head) then
588            Delete_Node (T, Nod);
589         end if;
590      end Delete;
591
592      -----------------
593      -- Delete_Node --
594      -----------------
595
596      procedure Delete_Node
597        (T   : Dynamic_Hash_Table;
598         Nod : Node_Ptr)
599      is
600         procedure Compress;
601         pragma Inline (Compress);
602         --  Determine whether hash table T requires compression, and if so,
603         --  half its size.
604
605         --------------
606         -- Compress --
607         --------------
608
609         procedure Compress is
610            pragma Assert (Present (T));
611            pragma Assert (Present (T.Buckets));
612
613            Old_Size : constant Bucket_Range_Type := T.Buckets'Length;
614
615         begin
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.
618
619            if Load_Factor (T) < Compression_Threshold
620              and then Old_Size >= Safe_Compression_Size
621            then
622               Mutate_And_Rehash (T, Old_Size / Compression_Factor);
623            end if;
624         end Compress;
625
626         --  Local variables
627
628         Ref : Node_Ptr := Nod;
629
630      --  Start of processing for Delete_Node
631
632      begin
633         pragma Assert (Present (Ref));
634         pragma Assert (Present (T));
635
636         Detach (Ref);
637         Free   (Ref);
638
639         --  The number of key-value pairs is updated when the hash table
640         --  contains a valid node which represents the pair.
641
642         T.Pairs := T.Pairs - 1;
643
644         --  Compress the hash table if the load factor drops below the value
645         --  of Compression_Threshold.
646
647         Compress;
648      end Delete_Node;
649
650      -------------
651      -- Destroy --
652      -------------
653
654      procedure Destroy (T : in out Dynamic_Hash_Table) is
655      begin
656         Ensure_Created  (T);
657         Ensure_Unlocked (T);
658
659         --  Destroy all nodes in all buckets
660
661         Destroy_Buckets (T.Buckets);
662         Free (T.Buckets);
663         Free (T);
664      end Destroy;
665
666      ---------------------
667      -- Destroy_Buckets --
668      ---------------------
669
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
674
675         --------------------
676         -- Destroy_Bucket --
677         --------------------
678
679         procedure Destroy_Bucket (Head : Node_Ptr) is
680            Nod : Node_Ptr;
681
682         begin
683            --  Destroy all valid nodes which follow the dummy head
684
685            while Is_Valid (Head.Next, Head) loop
686               Nod := Head.Next;
687
688               --  Invoke the value destructor before deallocating the node
689
690               Destroy_Value (Nod.Value);
691
692               Detach (Nod);
693               Free   (Nod);
694            end loop;
695         end Destroy_Bucket;
696
697      --  Start of processing for Destroy_Buckets
698
699      begin
700         pragma Assert (Present (Bkts));
701
702         for Scan_Idx in Bkts'Range loop
703            Destroy_Bucket (Bkts (Scan_Idx)'Access);
704         end loop;
705      end Destroy_Buckets;
706
707      ------------
708      -- Detach --
709      ------------
710
711      procedure Detach (Nod : Node_Ptr) is
712         pragma Assert (Present (Nod));
713
714         Next : constant Node_Ptr := Nod.Next;
715         Prev : constant Node_Ptr := Nod.Prev;
716
717      begin
718         pragma Assert (Present (Next));
719         pragma Assert (Present (Prev));
720
721         Prev.Next := Next;  --  Prev ---> Next
722         Next.Prev := Prev;  --  Prev <--> Next
723
724         Nod.Next := null;
725         Nod.Prev := null;
726      end Detach;
727
728      ---------------------
729      -- Ensure_Circular --
730      ---------------------
731
732      procedure Ensure_Circular (Head : Node_Ptr) is
733         pragma Assert (Present (Head));
734
735      begin
736         if not Present (Head.Next) and then not Present (Head.Prev) then
737            Head.Next := Head;
738            Head.Prev := Head;
739         end if;
740      end Ensure_Circular;
741
742      --------------------
743      -- Ensure_Created --
744      --------------------
745
746      procedure Ensure_Created (T : Dynamic_Hash_Table) is
747      begin
748         if not Present (T) then
749            raise Not_Created;
750         end if;
751      end Ensure_Created;
752
753      ---------------------
754      -- Ensure_Unlocked --
755      ---------------------
756
757      procedure Ensure_Unlocked (T : Dynamic_Hash_Table) is
758      begin
759         pragma Assert (Present (T));
760
761         --  The hash table has at least one outstanding iterator
762
763         if T.Iterators > 0 then
764            raise Iterated;
765         end if;
766      end Ensure_Unlocked;
767
768      -----------------
769      -- Find_Bucket --
770      -----------------
771
772      function Find_Bucket
773        (Bkts : Bucket_Table_Ptr;
774         Key  : Key_Type) return Node_Ptr
775      is
776         pragma Assert (Present (Bkts));
777
778         Idx : constant Bucket_Range_Type := Hash (Key) mod Bkts'Length;
779
780      begin
781         return Bkts (Idx)'Access;
782      end Find_Bucket;
783
784      ---------------
785      -- Find_Node --
786      ---------------
787
788      function Find_Node (Head : Node_Ptr; Key : Key_Type) return Node_Ptr is
789         pragma Assert (Present (Head));
790
791         Nod : Node_Ptr;
792
793      begin
794         --  Traverse the nodes of the bucket, looking for a key-value pair
795         --  with the same key.
796
797         Nod := Head.Next;
798         while Is_Valid (Nod, Head) loop
799            if Nod.Key = Key then
800               return Nod;
801            end if;
802
803            Nod := Nod.Next;
804         end loop;
805
806         return null;
807      end Find_Node;
808
809      ----------------------
810      -- First_Valid_Node --
811      ----------------------
812
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;
818         Nod      : out Node_Ptr)
819      is
820         Head : Node_Ptr;
821
822      begin
823         pragma Assert (Present (T));
824         pragma Assert (Present (T.Buckets));
825
826         --  Assume that no valid node exists
827
828         Idx := 0;
829         Nod := null;
830
831         --  Examine the buckets of the hash table within the requested range,
832         --  looking for the first valid node.
833
834         for Scan_Idx in Low_Bkt .. High_Bkt loop
835            Head := T.Buckets (Scan_Idx)'Access;
836
837            --  The bucket contains at least one valid node, return the first
838            --  such node.
839
840            if Is_Valid (Head.Next, Head) then
841               Idx := Scan_Idx;
842               Nod := Head.Next;
843               return;
844            end if;
845         end loop;
846      end First_Valid_Node;
847
848      ---------
849      -- Get --
850      ---------
851
852      function Get
853        (T   : Dynamic_Hash_Table;
854         Key : Key_Type) return Value_Type
855      is
856         Head : Node_Ptr;
857         Nod  : Node_Ptr;
858
859      begin
860         Ensure_Created (T);
861
862         --  Obtain the dummy head of the bucket which should house the
863         --  key-value pair.
864
865         Head := Find_Bucket (T.Buckets, Key);
866
867         --  Try to find a node in the bucket which matches the key
868
869         Nod := Find_Node (Head, Key);
870
871         --  If such a node exists, return the value of the key-value pair
872
873         if Is_Valid (Nod, Head) then
874            return Nod.Value;
875         end if;
876
877         return No_Value;
878      end Get;
879
880      --------------
881      -- Has_Next --
882      --------------
883
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;
887
888      begin
889         pragma Assert (Present (T));
890
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.
894
895         if not Is_OK then
896            Unlock (T);
897         end if;
898
899         return Is_OK;
900      end Has_Next;
901
902      --------------
903      -- Is_Empty --
904      --------------
905
906      function Is_Empty (T : Dynamic_Hash_Table) return Boolean is
907      begin
908         Ensure_Created (T);
909
910         return T.Pairs = 0;
911      end Is_Empty;
912
913      --------------
914      -- Is_Valid --
915      --------------
916
917      function Is_Valid (Iter : Iterator) return Boolean is
918      begin
919         --  The invariant of Iterate and Next ensures that the iterator always
920         --  refers to a valid node if there exists one.
921
922         return Present (Iter.Curr_Nod);
923      end Is_Valid;
924
925      --------------
926      -- Is_Valid --
927      --------------
928
929      function Is_Valid (Nod : Node_Ptr; Head : Node_Ptr) return Boolean is
930      begin
931         --  A node is valid if it is non-null, and does not refer to the dummy
932         --  head of some bucket.
933
934         return Present (Nod) and then Nod /= Head;
935      end Is_Valid;
936
937      -------------
938      -- Iterate --
939      -------------
940
941      function Iterate (T : Dynamic_Hash_Table) return Iterator is
942         Iter : Iterator;
943
944      begin
945         Ensure_Created (T);
946         pragma Assert (Present (T.Buckets));
947
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
951         --  advance.
952
953         First_Valid_Node
954           (T        => T,
955            Low_Bkt  => T.Buckets'First,
956            High_Bkt => T.Buckets'Last,
957            Idx      => Iter.Curr_Idx,
958            Nod      => Iter.Curr_Nod);
959
960         --  Associate the iterator with the hash table to allow for future
961         --  mutation functionality unlocking.
962
963         Iter.Table := T;
964
965         --  Lock all mutation functionality of the hash table while it is
966         --  being iterated on.
967
968         Lock (T);
969
970         return Iter;
971      end Iterate;
972
973      -----------------
974      -- Load_Factor --
975      -----------------
976
977      function Load_Factor (T : Dynamic_Hash_Table) return Threshold_Type is
978         pragma Assert (Present (T));
979         pragma Assert (Present (T.Buckets));
980
981      begin
982         --  The load factor is the ratio of key-value pairs to buckets
983
984         return Threshold_Type (T.Pairs) / Threshold_Type (T.Buckets'Length);
985      end Load_Factor;
986
987      ----------
988      -- Lock --
989      ----------
990
991      procedure Lock (T : Dynamic_Hash_Table) is
992      begin
993         --  The hash table may be locked multiple times if multiple iterators
994         --  are operating over it.
995
996         T.Iterators := T.Iterators + 1;
997      end Lock;
998
999      -----------------------
1000      -- Mutate_And_Rehash --
1001      -----------------------
1002
1003      procedure Mutate_And_Rehash
1004        (T    : Dynamic_Hash_Table;
1005         Size : Bucket_Range_Type)
1006      is
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
1010
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
1014         --  into To.
1015
1016         procedure Rehash_Node (Nod : Node_Ptr; To : Bucket_Table_Ptr);
1017         pragma Inline (Rehash_Node);
1018         --  Rehash node Nod into To
1019
1020         ------------
1021         -- Rehash --
1022         ------------
1023
1024         procedure Rehash (From : Bucket_Table_Ptr; To : Bucket_Table_Ptr) is
1025         begin
1026            pragma Assert (Present (From));
1027            pragma Assert (Present (To));
1028
1029            for Scan_Idx in From'Range loop
1030               Rehash_Bucket (From (Scan_Idx)'Access, To);
1031            end loop;
1032         end Rehash;
1033
1034         -------------------
1035         -- Rehash_Bucket --
1036         -------------------
1037
1038         procedure Rehash_Bucket (Head : Node_Ptr; To : Bucket_Table_Ptr) is
1039            pragma Assert (Present (Head));
1040
1041            Nod : Node_Ptr;
1042
1043         begin
1044            --  Detach all nodes which follow the dummy head
1045
1046            while Is_Valid (Head.Next, Head) loop
1047               Nod := Head.Next;
1048
1049               Detach (Nod);
1050               Rehash_Node (Nod, To);
1051            end loop;
1052         end Rehash_Bucket;
1053
1054         -----------------
1055         -- Rehash_Node --
1056         -----------------
1057
1058         procedure Rehash_Node (Nod : Node_Ptr; To : Bucket_Table_Ptr) is
1059            pragma Assert (Present (Nod));
1060
1061            Head : Node_Ptr;
1062
1063         begin
1064            --  Obtain the dummy head of the bucket which should house the
1065            --  key-value pair.
1066
1067            Head := Find_Bucket (To, Nod.Key);
1068
1069            --  Ensure that the dummy head of an empty bucket is circular with
1070            --  respect to itself.
1071
1072            Ensure_Circular (Head);
1073
1074            --  Prepend the node to the bucket
1075
1076            Prepend (Nod, Head);
1077         end Rehash_Node;
1078
1079         --  Local declarations
1080
1081         Old_Bkts : Bucket_Table_Ptr;
1082
1083      --  Start of processing for Mutate_And_Rehash
1084
1085      begin
1086         pragma Assert (Present (T));
1087
1088         Old_Bkts  := T.Buckets;
1089         T.Buckets := new Bucket_Table (0 .. Size - 1);
1090
1091         --  Transfer and rehash all key-value pairs from the old buckets to
1092         --  the new buckets.
1093
1094         Rehash (From => Old_Bkts, To => T.Buckets);
1095         Free (Old_Bkts);
1096      end Mutate_And_Rehash;
1097
1098      ----------
1099      -- Next --
1100      ----------
1101
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;
1106         Head  : Node_Ptr;
1107
1108      begin
1109         pragma Assert (Present (T));
1110         pragma Assert (Present (T.Buckets));
1111
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.
1115
1116         if not Is_OK then
1117            Unlock (T);
1118            raise Iterator_Exhausted;
1119         end if;
1120
1121         --  Advance to the next node along the same bucket
1122
1123         Iter.Curr_Nod := Iter.Curr_Nod.Next;
1124         Head := T.Buckets (Iter.Curr_Idx)'Access;
1125
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.
1130
1131         if not Is_Valid (Iter.Curr_Nod, Head) then
1132            First_Valid_Node
1133              (T        => T,
1134               Low_Bkt  => Iter.Curr_Idx + 1,
1135               High_Bkt => T.Buckets'Last,
1136               Idx      => Iter.Curr_Idx,
1137               Nod      => Iter.Curr_Nod);
1138         end if;
1139
1140         Key := Saved.Key;
1141      end Next;
1142
1143      -------------
1144      -- Prepend --
1145      -------------
1146
1147      procedure Prepend (Nod : Node_Ptr; Head : Node_Ptr) is
1148         pragma Assert (Present (Nod));
1149         pragma Assert (Present (Head));
1150
1151         Next : constant Node_Ptr := Head.Next;
1152
1153      begin
1154         Head.Next := Nod;
1155         Next.Prev := Nod;
1156
1157         Nod.Next := Next;
1158         Nod.Prev := Head;
1159      end Prepend;
1160
1161      -------------
1162      -- Present --
1163      -------------
1164
1165      function Present (Bkts : Bucket_Table_Ptr) return Boolean is
1166      begin
1167         return Bkts /= null;
1168      end Present;
1169
1170      -------------
1171      -- Present --
1172      -------------
1173
1174      function Present (Nod : Node_Ptr) return Boolean is
1175      begin
1176         return Nod /= null;
1177      end Present;
1178
1179      -------------
1180      -- Present --
1181      -------------
1182
1183      function Present (T : Dynamic_Hash_Table) return Boolean is
1184      begin
1185         return T /= Nil;
1186      end Present;
1187
1188      ---------
1189      -- Put --
1190      ---------
1191
1192      procedure Put
1193        (T     : Dynamic_Hash_Table;
1194         Key   : Key_Type;
1195         Value : Value_Type)
1196      is
1197         procedure Expand;
1198         pragma Inline (Expand);
1199         --  Determine whether hash table T requires expansion, and if so,
1200         --  double its size.
1201
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.
1207
1208         ------------
1209         -- Expand --
1210         ------------
1211
1212         procedure Expand is
1213            pragma Assert (Present (T));
1214            pragma Assert (Present (T.Buckets));
1215
1216            Old_Size : constant Bucket_Range_Type := T.Buckets'Length;
1217
1218         begin
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.
1221
1222            if Load_Factor (T) > Expansion_Threshold
1223              and then Old_Size <= Safe_Expansion_Size
1224            then
1225               Mutate_And_Rehash (T, Old_Size * Expansion_Factor);
1226            end if;
1227         end Expand;
1228
1229         ------------------------
1230         -- Prepend_Or_Replace --
1231         ------------------------
1232
1233         procedure Prepend_Or_Replace (Head : Node_Ptr) is
1234            pragma Assert (Present (Head));
1235
1236            Nod : Node_Ptr;
1237
1238         begin
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.
1242
1243            Nod := Head.Next;
1244            while Is_Valid (Nod, Head) loop
1245               if Nod.Key = Key then
1246                  Nod.Value := Value;
1247                  return;
1248               end if;
1249
1250               Nod := Nod.Next;
1251            end loop;
1252
1253            --  At this point the bucket is either empty, or none of the nodes
1254            --  match key Key. Prepend a new key-value pair.
1255
1256            Nod := new Node'(Key, Value, null, null);
1257
1258            Prepend (Nod, Head);
1259
1260            --  The number of key-value pairs must be updated for a prepend,
1261            --  never for a replace.
1262
1263            T.Pairs := T.Pairs + 1;
1264         end Prepend_Or_Replace;
1265
1266         --  Local variables
1267
1268         Head : Node_Ptr;
1269
1270      --  Start of processing for Put
1271
1272      begin
1273         Ensure_Created  (T);
1274         Ensure_Unlocked (T);
1275
1276         --  Obtain the dummy head of the bucket which should house the
1277         --  key-value pair.
1278
1279         Head := Find_Bucket (T.Buckets, Key);
1280
1281         --  Ensure that the dummy head of an empty bucket is circular with
1282         --  respect to itself.
1283
1284         Ensure_Circular (Head);
1285
1286         --  In case the bucket already contains a node with the same key,
1287         --  replace its value, otherwise prepend a new key-value pair.
1288
1289         Prepend_Or_Replace (Head);
1290
1291         --  Expand the hash table if the ratio of pairs to buckets goes over
1292         --  Expansion_Threshold.
1293
1294         Expand;
1295      end Put;
1296
1297      -----------
1298      -- Reset --
1299      -----------
1300
1301      procedure Reset (T : Dynamic_Hash_Table) is
1302      begin
1303         Ensure_Created  (T);
1304         Ensure_Unlocked (T);
1305
1306         --  Destroy all nodes in all buckets
1307
1308         Destroy_Buckets (T.Buckets);
1309         Free (T.Buckets);
1310
1311         --  Recreate the buckets using the original size from creation time
1312
1313         T.Buckets := new Bucket_Table (0 .. T.Initial_Size - 1);
1314         T.Pairs   := 0;
1315      end Reset;
1316
1317      ----------
1318      -- Size --
1319      ----------
1320
1321      function Size (T : Dynamic_Hash_Table) return Natural is
1322      begin
1323         Ensure_Created (T);
1324
1325         return T.Pairs;
1326      end Size;
1327
1328      ------------
1329      -- Unlock --
1330      ------------
1331
1332      procedure Unlock (T : Dynamic_Hash_Table) is
1333      begin
1334         --  The hash table may be locked multiple times if multiple iterators
1335         --  are operating over it.
1336
1337         T.Iterators := T.Iterators - 1;
1338      end Unlock;
1339   end Dynamic_Hash_Tables;
1340
1341end GNAT.Dynamic_HTables;
1342