1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- ADA.CONTAINERS.HASH_TABLES.GENERIC_OPERATIONS --
9 -- Copyright (C) 2004-2014, Free Software Foundation, Inc. --
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 -- This unit was originally developed by Matthew J Heaney. --
28 ------------------------------------------------------------------------------
30 with Ada
.Containers
.Prime_Numbers
;
31 with Ada
.Unchecked_Deallocation
;
33 with System
; use type System
.Address
;
35 package body Ada
.Containers
.Hash_Tables
.Generic_Operations
is
37 type Buckets_Allocation
is access all Buckets_Type
;
38 -- Used for allocation and deallocation (see New_Buckets and Free_Buckets).
39 -- This is necessary because Buckets_Access has an empty storage pool.
45 procedure Adjust
(HT
: in out Hash_Table_Type
) is
46 Src_Buckets
: constant Buckets_Access
:= HT
.Buckets
;
47 N
: constant Count_Type
:= HT
.Length
;
48 Src_Node
: Node_Access
;
49 Dst_Prev
: Node_Access
;
59 -- Technically it isn't necessary to allocate the exact same length
60 -- buckets array, because our only requirement is that following
61 -- assignment the source and target containers compare equal (that is,
62 -- operator "=" returns True). We can satisfy this requirement with any
63 -- hash table length, but we decide here to match the length of the
64 -- source table. This has the benefit that when iterating, elements of
65 -- the target are delivered in the exact same order as for the source.
67 HT
.Buckets
:= New_Buckets
(Length
=> Src_Buckets
'Length);
69 for Src_Index
in Src_Buckets
'Range loop
70 Src_Node
:= Src_Buckets
(Src_Index
);
72 if Src_Node
/= null then
74 Dst_Node
: constant Node_Access
:= Copy_Node
(Src_Node
);
78 pragma Assert
(Checked_Index
(HT
, Dst_Node
) = Src_Index
);
81 HT
.Buckets
(Src_Index
) := Dst_Node
;
82 HT
.Length
:= HT
.Length
+ 1;
87 Src_Node
:= Next
(Src_Node
);
88 while Src_Node
/= null loop
90 Dst_Node
: constant Node_Access
:= Copy_Node
(Src_Node
);
94 pragma Assert
(Checked_Index
(HT
, Dst_Node
) = Src_Index
);
97 Set_Next
(Node
=> Dst_Prev
, Next
=> Dst_Node
);
98 HT
.Length
:= HT
.Length
+ 1;
100 Dst_Prev
:= Dst_Node
;
103 Src_Node
:= Next
(Src_Node
);
108 pragma Assert
(HT
.Length
= N
);
115 function Capacity
(HT
: Hash_Table_Type
) return Count_Type
is
117 if HT
.Buckets
= null then
121 return HT
.Buckets
'Length;
128 function Checked_Index
129 (Hash_Table
: aliased in out Hash_Table_Type
;
130 Buckets
: Buckets_Type
;
131 Node
: Node_Access
) return Hash_Type
135 B
: Natural renames Hash_Table
.Busy
;
136 L
: Natural renames Hash_Table
.Lock
;
142 Result
:= Index
(Buckets
, Node
);
157 function Checked_Index
158 (Hash_Table
: aliased in out Hash_Table_Type
;
159 Node
: Node_Access
) return Hash_Type
162 return Checked_Index
(Hash_Table
, Hash_Table
.Buckets
.all, Node
);
169 procedure Clear
(HT
: in out Hash_Table_Type
) is
170 Index
: Hash_Type
:= 0;
175 raise Program_Error
with
176 "attempt to tamper with cursors (container is busy)";
179 while HT
.Length
> 0 loop
180 while HT
.Buckets
(Index
) = null loop
185 Bucket
: Node_Access
renames HT
.Buckets
(Index
);
189 Bucket
:= Next
(Bucket
);
190 HT
.Length
:= HT
.Length
- 1;
192 exit when Bucket
= null;
198 --------------------------
199 -- Delete_Node_At_Index --
200 --------------------------
202 procedure Delete_Node_At_Index
203 (HT
: in out Hash_Table_Type
;
205 X
: in out Node_Access
)
211 Prev
:= HT
.Buckets
(Indx
);
214 HT
.Buckets
(Indx
) := Next
(Prev
);
215 HT
.Length
:= HT
.Length
- 1;
220 if HT
.Length
= 1 then
221 raise Program_Error
with
222 "attempt to delete node not in its proper hash bucket";
229 raise Program_Error
with
230 "attempt to delete node not in its proper hash bucket";
234 Set_Next
(Node
=> Prev
, Next
=> Next
(Curr
));
235 HT
.Length
:= HT
.Length
- 1;
242 end Delete_Node_At_Index
;
244 ---------------------------
245 -- Delete_Node_Sans_Free --
246 ---------------------------
248 procedure Delete_Node_Sans_Free
249 (HT
: in out Hash_Table_Type
;
252 pragma Assert
(X
/= null);
259 if HT
.Length
= 0 then
260 raise Program_Error
with
261 "attempt to delete node from empty hashed container";
264 Indx
:= Checked_Index
(HT
, X
);
265 Prev
:= HT
.Buckets
(Indx
);
268 raise Program_Error
with
269 "attempt to delete node from empty hash bucket";
273 HT
.Buckets
(Indx
) := Next
(Prev
);
274 HT
.Length
:= HT
.Length
- 1;
278 if HT
.Length
= 1 then
279 raise Program_Error
with
280 "attempt to delete node not in its proper hash bucket";
287 raise Program_Error
with
288 "attempt to delete node not in its proper hash bucket";
292 Set_Next
(Node
=> Prev
, Next
=> Next
(Curr
));
293 HT
.Length
:= HT
.Length
- 1;
299 end Delete_Node_Sans_Free
;
305 procedure Finalize
(HT
: in out Hash_Table_Type
) is
308 Free_Buckets
(HT
.Buckets
);
315 function First
(HT
: Hash_Table_Type
) return Node_Access
is
319 if HT
.Length
= 0 then
323 Indx
:= HT
.Buckets
'First;
325 if HT
.Buckets
(Indx
) /= null then
326 return HT
.Buckets
(Indx
);
337 procedure Free_Buckets
(Buckets
: in out Buckets_Access
) is
339 new Ada
.Unchecked_Deallocation
(Buckets_Type
, Buckets_Allocation
);
342 -- Buckets must have been created by New_Buckets. Here, we convert back
343 -- to the Buckets_Allocation type, and do the free on that.
345 Free
(Buckets_Allocation
(Buckets
));
348 ---------------------
349 -- Free_Hash_Table --
350 ---------------------
352 procedure Free_Hash_Table
(Buckets
: in out Buckets_Access
) is
356 if Buckets
= null then
360 for J
in Buckets
'Range loop
361 while Buckets
(J
) /= null loop
363 Buckets
(J
) := Next
(Node
);
368 Free_Buckets
(Buckets
);
375 function Generic_Equal
376 (L
, R
: Hash_Table_Type
) return Boolean
378 BL
: Natural renames L
'Unrestricted_Access.Busy
;
379 LL
: Natural renames L
'Unrestricted_Access.Lock
;
381 BR
: Natural renames R
'Unrestricted_Access.Busy
;
382 LR
: Natural renames R
'Unrestricted_Access.Lock
;
387 L_Node
: Node_Access
;
392 if L
'Address = R
'Address then
396 if L
.Length
/= R
.Length
then
404 -- Find the first node of hash table L
408 L_Node
:= L
.Buckets
(L_Index
);
409 exit when L_Node
/= null;
410 L_Index
:= L_Index
+ 1;
413 -- Per AI05-0022, the container implementation is required to detect
414 -- element tampering by a generic actual subprogram.
422 -- For each node of hash table L, search for an equivalent node in hash
427 if not Find
(HT
=> R
, Key
=> L_Node
) then
434 L_Node
:= Next
(L_Node
);
436 if L_Node
= null then
437 -- We have exhausted the nodes in this bucket
444 -- Find the next bucket
447 L_Index
:= L_Index
+ 1;
448 L_Node
:= L
.Buckets
(L_Index
);
449 exit when L_Node
/= null;
473 -----------------------
474 -- Generic_Iteration --
475 -----------------------
477 procedure Generic_Iteration
(HT
: Hash_Table_Type
) is
481 if HT
.Length
= 0 then
485 for Indx
in HT
.Buckets
'Range loop
486 Node
:= HT
.Buckets
(Indx
);
487 while Node
/= null loop
492 end Generic_Iteration
;
498 procedure Generic_Read
499 (Stream
: not null access Root_Stream_Type
'Class;
500 HT
: out Hash_Table_Type
)
508 Count_Type
'Base'Read (Stream, N);
511 raise Program_Error with "stream appears to be corrupt";
518 -- The RM does not specify whether or how the capacity changes when a
519 -- hash table is streamed in. Therefore we decide here to allocate a new
520 -- buckets array only when it's necessary to preserve representation
524 or else HT.Buckets'Length < N
526 Free_Buckets (HT.Buckets);
527 NN := Prime_Numbers.To_Prime (N);
528 HT.Buckets := New_Buckets (Length => NN);
533 Node : constant Node_Access := New_Node (Stream);
534 Indx : constant Hash_Type := Checked_Index (HT, Node);
535 B : Node_Access renames HT.Buckets (Indx);
537 Set_Next (Node => Node, Next => B);
541 HT.Length := HT.Length + 1;
549 procedure Generic_Write
550 (Stream : not null access Root_Stream_Type'Class;
551 HT : Hash_Table_Type)
553 procedure Write (Node : Node_Access);
554 pragma Inline (Write);
556 procedure Write is new Generic_Iteration (Write);
562 procedure Write (Node : Node_Access) is
564 Write (Stream, Node);
568 -- See Generic_Read for an explanation of why we do not stream out the
569 -- buckets array length too.
571 Count_Type'Base'Write
(Stream
, HT
.Length
);
580 (Buckets
: Buckets_Type
;
581 Node
: Node_Access
) return Hash_Type
is
583 return Hash_Node
(Node
) mod Buckets
'Length;
587 (Hash_Table
: Hash_Table_Type
;
588 Node
: Node_Access
) return Hash_Type
is
590 return Index
(Hash_Table
.Buckets
.all, Node
);
597 procedure Move
(Target
, Source
: in out Hash_Table_Type
) is
599 if Target
'Address = Source
'Address then
603 if Source
.Busy
> 0 then
604 raise Program_Error
with
605 "attempt to tamper with cursors (container is busy)";
611 Buckets
: constant Buckets_Access
:= Target
.Buckets
;
613 Target
.Buckets
:= Source
.Buckets
;
614 Source
.Buckets
:= Buckets
;
617 Target
.Length
:= Source
.Length
;
625 function New_Buckets
(Length
: Hash_Type
) return Buckets_Access
is
626 subtype Rng
is Hash_Type
range 0 .. Length
- 1;
629 -- Allocate in Buckets_Allocation'Storage_Pool, then convert to
632 return Buckets_Access
(Buckets_Allocation
'(new Buckets_Type (Rng)));
640 (HT : aliased in out Hash_Table_Type;
641 Node : Node_Access) return Node_Access
643 Result : Node_Access;
647 Result := Next (Node);
649 if Result /= null then
653 First := Checked_Index (HT, Node) + 1;
654 for Indx in First .. HT.Buckets'Last loop
655 Result := HT.Buckets (Indx);
657 if Result /= null then
665 ----------------------
666 -- Reserve_Capacity --
667 ----------------------
669 procedure Reserve_Capacity
670 (HT : in out Hash_Table_Type;
676 if HT.Buckets = null then
678 NN := Prime_Numbers.To_Prime (N);
679 HT.Buckets := New_Buckets (Length => NN);
685 if HT.Length = 0 then
687 -- This is the easy case. There are no nodes, so no rehashing is
688 -- necessary. All we need to do is allocate a new buckets array
689 -- having a length implied by the specified capacity. (We say
690 -- "implied by" because bucket arrays are always allocated with a
691 -- length that corresponds to a prime number.)
694 Free_Buckets (HT.Buckets);
698 if N = HT.Buckets'Length then
702 NN := Prime_Numbers.To_Prime (N);
704 if NN = HT.Buckets'Length then
709 X : Buckets_Access := HT.Buckets;
710 pragma Warnings (Off, X);
712 HT.Buckets := New_Buckets (Length => NN);
719 if N = HT.Buckets'Length then
723 if N < HT.Buckets'Length then
725 -- This is a request to contract the buckets array. The amount of
726 -- contraction is bounded in order to preserve the invariant that the
727 -- buckets array length is never smaller than the number of elements
728 -- (the load factor is 1).
730 if HT.Length >= HT.Buckets'Length then
734 NN := Prime_Numbers.To_Prime (HT.Length);
736 if NN >= HT.Buckets'Length then
741 NN := Prime_Numbers.To_Prime (Count_Type'Max (N, HT.Length));
743 if NN = HT.Buckets'Length then -- can't expand any more
749 raise Program_Error with
750 "attempt to tamper with cursors (container is busy)";
754 Dst_Buckets : Buckets_Access := New_Buckets (Length => NN);
755 Src_Buckets : Buckets_Access := HT.Buckets;
756 pragma Warnings (Off, Src_Buckets);
758 L : Count_Type renames HT.Length;
759 LL : constant Count_Type := L;
761 Src_Index : Hash_Type := Src_Buckets'First;
766 Src_Bucket : Node_Access renames Src_Buckets (Src_Index);
769 while Src_Bucket /= null loop
771 Src_Node : constant Node_Access := Src_Bucket;
773 Dst_Index : constant Hash_Type :=
774 Checked_Index (HT, Dst_Buckets.all, Src_Node);
776 Dst_Bucket : Node_Access renames Dst_Buckets (Dst_Index);
779 Src_Bucket := Next (Src_Node);
781 Set_Next (Src_Node, Dst_Bucket);
783 Dst_Bucket := Src_Node;
786 pragma Assert (L > 0);
793 -- If there's an error computing a hash value during a
794 -- rehash, then AI-302 says the nodes "become lost." The
795 -- issue is whether to actually deallocate these lost nodes,
796 -- since they might be designated by extant cursors. Here
797 -- we decide to deallocate the nodes, since it's better to
798 -- solve real problems (storage consumption) rather than
799 -- imaginary ones (the user might, or might not, dereference
800 -- a cursor designating a node that has been deallocated),
801 -- and because we have a way to vet a dangling cursor
802 -- reference anyway, and hence can actually detect the
805 for Dst_Index in Dst_Buckets'Range loop
807 B : Node_Access renames Dst_Buckets (Dst_Index);
818 Free_Buckets (Dst_Buckets);
819 raise Program_Error with
820 "hash function raised exception during rehash";
823 Src_Index := Src_Index + 1;
826 HT.Buckets := Dst_Buckets;
829 Free_Buckets (Src_Buckets);
831 end Reserve_Capacity;
833 end Ada.Containers.Hash_Tables.Generic_Operations;