1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- ADA.CONTAINERS.HASH_TABLES.GENERIC_OPERATIONS --
9 -- Copyright (C) 2004-2008, 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 2, 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. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, USA. --
22 -- As a special exception, if other files instantiate generics from this --
23 -- unit, or you link this unit with other files to produce an executable, --
24 -- this unit does not by itself cause the resulting executable to be --
25 -- covered by the GNU General Public License. This exception does not --
26 -- however invalidate any other reasons why the executable file might be --
27 -- covered by the GNU Public License. --
29 -- This unit was originally developed by Matthew J Heaney. --
30 ------------------------------------------------------------------------------
32 with Ada.Containers.Prime_Numbers;
33 with Ada.Unchecked_Deallocation;
35 with System; use type System.Address;
37 package body Ada.Containers.Hash_Tables.Generic_Operations is
39 type Buckets_Allocation is access all Buckets_Type;
40 -- Used for allocation and deallocation (see New_Buckets and Free_Buckets).
41 -- This is necessary because Buckets_Access has an empty storage pool.
47 procedure Adjust (HT : in out Hash_Table_Type) is
48 Src_Buckets : constant Buckets_Access := HT.Buckets;
49 N : constant Count_Type := HT.Length;
50 Src_Node : Node_Access;
51 Dst_Prev : Node_Access;
61 -- Technically it isn't necessary to allocate the exact same length
62 -- buckets array, because our only requirement is that following
63 -- assignment the source and target containers compare equal (that is,
64 -- operator "=" returns True). We can satisfy this requirement with any
65 -- hash table length, but we decide here to match the length of the
66 -- source table. This has the benefit that when iterating, elements of
67 -- the target are delivered in the exact same order as for the source.
69 HT.Buckets := New_Buckets (Length => Src_Buckets'Length);
71 for Src_Index in Src_Buckets'Range loop
72 Src_Node := Src_Buckets (Src_Index);
74 if Src_Node /= null then
76 Dst_Node : constant Node_Access := Copy_Node (Src_Node);
80 pragma Assert (Index (HT, Dst_Node) = Src_Index);
83 HT.Buckets (Src_Index) := Dst_Node;
84 HT.Length := HT.Length + 1;
89 Src_Node := Next (Src_Node);
90 while Src_Node /= null loop
92 Dst_Node : constant Node_Access := Copy_Node (Src_Node);
96 pragma Assert (Index (HT, Dst_Node) = Src_Index);
99 Set_Next (Node => Dst_Prev, Next => Dst_Node);
100 HT.Length := HT.Length + 1;
102 Dst_Prev := Dst_Node;
105 Src_Node := Next (Src_Node);
110 pragma Assert (HT.Length = N);
117 function Capacity (HT : Hash_Table_Type) return Count_Type is
119 if HT.Buckets = null then
123 return HT.Buckets'Length;
130 procedure Clear (HT : in out Hash_Table_Type) is
131 Index : Hash_Type := 0;
136 raise Program_Error with
137 "attempt to tamper with elements (container is busy)";
140 while HT.Length > 0 loop
141 while HT.Buckets (Index) = null loop
146 Bucket : Node_Access renames HT.Buckets (Index);
150 Bucket := Next (Bucket);
151 HT.Length := HT.Length - 1;
153 exit when Bucket = null;
159 ---------------------------
160 -- Delete_Node_Sans_Free --
161 ---------------------------
163 procedure Delete_Node_Sans_Free
164 (HT : in out Hash_Table_Type;
167 pragma Assert (X /= null);
174 if HT.Length = 0 then
175 raise Program_Error with
176 "attempt to delete node from empty hashed container";
179 Indx := Index (HT, X);
180 Prev := HT.Buckets (Indx);
183 raise Program_Error with
184 "attempt to delete node from empty hash bucket";
188 HT.Buckets (Indx) := Next (Prev);
189 HT.Length := HT.Length - 1;
193 if HT.Length = 1 then
194 raise Program_Error with
195 "attempt to delete node not in its proper hash bucket";
202 raise Program_Error with
203 "attempt to delete node not in its proper hash bucket";
207 Set_Next (Node => Prev, Next => Next (Curr));
208 HT.Length := HT.Length - 1;
214 end Delete_Node_Sans_Free;
220 procedure Finalize (HT : in out Hash_Table_Type) is
223 Free_Buckets (HT.Buckets);
230 function First (HT : Hash_Table_Type) return Node_Access is
234 if HT.Length = 0 then
238 Indx := HT.Buckets'First;
240 if HT.Buckets (Indx) /= null then
241 return HT.Buckets (Indx);
252 procedure Free_Buckets (Buckets : in out Buckets_Access) is
254 new Ada.Unchecked_Deallocation (Buckets_Type, Buckets_Allocation);
257 -- Buckets must have been created by New_Buckets. Here, we convert back
258 -- to the Buckets_Allocation type, and do the free on that.
260 Free (Buckets_Allocation (Buckets));
263 ---------------------
264 -- Free_Hash_Table --
265 ---------------------
267 procedure Free_Hash_Table (Buckets : in out Buckets_Access) is
271 if Buckets = null then
275 for J in Buckets'Range loop
276 while Buckets (J) /= null loop
278 Buckets (J) := Next (Node);
283 Free_Buckets (Buckets);
290 function Generic_Equal
291 (L, R : Hash_Table_Type) return Boolean
294 L_Node : Node_Access;
299 if L'Address = R'Address then
303 if L.Length /= R.Length then
311 -- Find the first node of hash table L
315 L_Node := L.Buckets (L_Index);
316 exit when L_Node /= null;
317 L_Index := L_Index + 1;
320 -- For each node of hash table L, search for an equivalent node in hash
325 if not Find (HT => R, Key => L_Node) then
331 L_Node := Next (L_Node);
333 if L_Node = null then
334 -- We have exhausted the nodes in this bucket
340 -- Find the next bucket
343 L_Index := L_Index + 1;
344 L_Node := L.Buckets (L_Index);
345 exit when L_Node /= null;
351 -----------------------
352 -- Generic_Iteration --
353 -----------------------
355 procedure Generic_Iteration (HT : Hash_Table_Type) is
359 if HT.Length = 0 then
363 for Indx in HT.Buckets'Range loop
364 Node := HT.Buckets (Indx);
365 while Node /= null loop
370 end Generic_Iteration;
376 procedure Generic_Read
377 (Stream : not null access Root_Stream_Type'Class;
378 HT : out Hash_Table_Type)
386 Count_Type'Base'Read (Stream, N);
389 raise Program_Error with "stream appears to be corrupt";
396 -- The RM does not specify whether or how the capacity changes when a
397 -- hash table is streamed in. Therefore we decide here to allocate a new
398 -- buckets array only when it's necessary to preserve representation
402 or else HT.Buckets'Length < N
404 Free_Buckets (HT.Buckets);
405 NN := Prime_Numbers.To_Prime (N);
406 HT.Buckets := New_Buckets (Length => NN);
411 Node : constant Node_Access := New_Node (Stream);
412 Indx : constant Hash_Type := Index (HT, Node);
413 B : Node_Access renames HT.Buckets (Indx);
415 Set_Next (Node => Node, Next => B);
419 HT.Length := HT.Length + 1;
427 procedure Generic_Write
428 (Stream : not null access Root_Stream_Type'Class;
429 HT : Hash_Table_Type)
431 procedure Write (Node : Node_Access);
432 pragma Inline (Write);
434 procedure Write is new Generic_Iteration (Write);
440 procedure Write (Node : Node_Access) is
442 Write (Stream, Node);
446 -- See Generic_Read for an explanation of why we do not stream out the
447 -- buckets array length too.
449 Count_Type'Base'Write (Stream, HT.Length);
458 (Buckets : Buckets_Type;
459 Node : Node_Access) return Hash_Type is
461 return Hash_Node (Node) mod Buckets'Length;
465 (Hash_Table : Hash_Table_Type;
466 Node : Node_Access) return Hash_Type is
468 return Index (Hash_Table.Buckets.all, Node);
475 procedure Move (Target, Source : in out Hash_Table_Type) is
477 if Target'Address = Source'Address then
481 if Source.Busy > 0 then
482 raise Program_Error with
483 "attempt to tamper with elements (container is busy)";
489 Buckets : constant Buckets_Access := Target.Buckets;
491 Target.Buckets := Source.Buckets;
492 Source.Buckets := Buckets;
495 Target.Length := Source.Length;
503 function New_Buckets (Length : Hash_Type) return Buckets_Access is
504 subtype Rng is Hash_Type range 0 .. Length - 1;
507 -- Allocate in Buckets_Allocation'Storage_Pool, then convert to
510 return Buckets_Access (Buckets_Allocation'(new Buckets_Type (Rng)));
518 (HT : Hash_Table_Type;
519 Node : Node_Access) return Node_Access
521 Result : Node_Access := Next (Node);
524 if Result /= null then
528 for Indx in Index (HT, Node) + 1 .. HT.Buckets'Last loop
529 Result := HT.Buckets (Indx);
531 if Result /= null then
539 ----------------------
540 -- Reserve_Capacity --
541 ----------------------
543 procedure Reserve_Capacity
544 (HT : in out Hash_Table_Type;
550 if HT.Buckets = null then
552 NN := Prime_Numbers.To_Prime (N);
553 HT.Buckets := New_Buckets (Length => NN);
559 if HT.Length = 0 then
561 -- This is the easy case. There are no nodes, so no rehashing is
562 -- necessary. All we need to do is allocate a new buckets array
563 -- having a length implied by the specified capacity. (We say
564 -- "implied by" because bucket arrays are always allocated with a
565 -- length that corresponds to a prime number.)
568 Free_Buckets (HT.Buckets);
572 if N = HT.Buckets'Length then
576 NN := Prime_Numbers.To_Prime (N);
578 if NN = HT.Buckets'Length then
583 X : Buckets_Access := HT.Buckets;
584 pragma Warnings (Off, X);
586 HT.Buckets := New_Buckets (Length => NN);
593 if N = HT.Buckets'Length then
597 if N < HT.Buckets'Length then
599 -- This is a request to contract the buckets array. The amount of
600 -- contraction is bounded in order to preserve the invariant that the
601 -- buckets array length is never smaller than the number of elements
602 -- (the load factor is 1).
604 if HT.Length >= HT.Buckets'Length then
608 NN := Prime_Numbers.To_Prime (HT.Length);
610 if NN >= HT.Buckets'Length then
615 NN := Prime_Numbers.To_Prime (Count_Type'Max (N, HT.Length));
617 if NN = HT.Buckets'Length then -- can't expand any more
623 raise Program_Error with
624 "attempt to tamper with elements (container is busy)";
628 Dst_Buckets : Buckets_Access := New_Buckets (Length => NN);
629 Src_Buckets : Buckets_Access := HT.Buckets;
630 pragma Warnings (Off, Src_Buckets);
632 L : Count_Type renames HT.Length;
633 LL : constant Count_Type := L;
635 Src_Index : Hash_Type := Src_Buckets'First;
640 Src_Bucket : Node_Access renames Src_Buckets (Src_Index);
643 while Src_Bucket /= null loop
645 Src_Node : constant Node_Access := Src_Bucket;
647 Dst_Index : constant Hash_Type :=
648 Index (Dst_Buckets.all, Src_Node);
650 Dst_Bucket : Node_Access renames Dst_Buckets (Dst_Index);
653 Src_Bucket := Next (Src_Node);
655 Set_Next (Src_Node, Dst_Bucket);
657 Dst_Bucket := Src_Node;
660 pragma Assert (L > 0);
665 -- If there's an error computing a hash value during a
666 -- rehash, then AI-302 says the nodes "become lost." The
667 -- issue is whether to actually deallocate these lost nodes,
668 -- since they might be designated by extant cursors. Here
669 -- we decide to deallocate the nodes, since it's better to
670 -- solve real problems (storage consumption) rather than
671 -- imaginary ones (the user might, or might not, dereference
672 -- a cursor designating a node that has been deallocated),
673 -- and because we have a way to vet a dangling cursor
674 -- reference anyway, and hence can actually detect the
677 for Dst_Index in Dst_Buckets'Range loop
679 B : Node_Access renames Dst_Buckets (Dst_Index);
690 Free_Buckets (Dst_Buckets);
691 raise Program_Error with
692 "hash function raised exception during rehash";
695 Src_Index := Src_Index + 1;
698 HT.Buckets := Dst_Buckets;
701 Free_Buckets (Src_Buckets);
703 end Reserve_Capacity;
705 end Ada.Containers.Hash_Tables.Generic_Operations;