1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- ADA.CONTAINERS.HASH_TABLES.GENERIC_OPERATIONS --
9 -- Copyright (C) 2004 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, 59 Temple Place - Suite 330, Boston, --
20 -- MA 02111-1307, 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 -- This body needs commenting ???
34 with Ada
.Containers
.Prime_Numbers
;
35 with Ada
.Unchecked_Deallocation
;
37 with System
; use type System
.Address
;
39 package body Ada
.Containers
.Hash_Tables
.Generic_Operations
is
42 new Ada
.Unchecked_Deallocation
(Buckets_Type
, Buckets_Access
);
44 -----------------------
45 -- Local Subprograms --
46 -----------------------
49 (HT
: in out Hash_Table_Type
;
56 procedure Adjust
(HT
: in out Hash_Table_Type
) is
57 Src_Buckets
: constant Buckets_Access
:= HT
.Buckets
;
58 N
: constant Count_Type
:= HT
.Length
;
59 Src_Node
: Node_Access
;
60 Dst_Prev
: Node_Access
;
70 HT
.Buckets
:= new Buckets_Type
(Src_Buckets
'Range);
72 -- Probably we have to duplicate the Size (Src), too, in order
78 -- The only quirk is that we depend on the hash value of a dst key
79 -- to be the same as the src key from which it was copied.
80 -- If we relax the requirement that the hash value must be the
81 -- same, then of course we can't guarantee that following
82 -- assignment that Dst = Src is true ???
84 for Src_Index
in Src_Buckets
'Range loop
85 Src_Node
:= Src_Buckets
(Src_Index
);
87 if Src_Node
/= Null_Node
then
89 Dst_Node
: constant Node_Access
:= Copy_Node
(Src_Node
);
93 pragma Assert
(Index
(HT
, Dst_Node
) = Src_Index
);
96 HT
.Buckets
(Src_Index
) := Dst_Node
;
97 HT
.Length
:= HT
.Length
+ 1;
102 Src_Node
:= Next
(Src_Node
);
103 while Src_Node
/= Null_Node
loop
105 Dst_Node
: constant Node_Access
:= Copy_Node
(Src_Node
);
109 pragma Assert
(Index
(HT
, Dst_Node
) = Src_Index
);
112 Set_Next
(Node
=> Dst_Prev
, Next
=> Dst_Node
);
113 HT
.Length
:= HT
.Length
+ 1;
115 Dst_Prev
:= Dst_Node
;
118 Src_Node
:= Next
(Src_Node
);
123 pragma Assert
(HT
.Length
= N
);
130 function Capacity
(HT
: Hash_Table_Type
) return Count_Type
is
132 if HT
.Buckets
= null then
136 return HT
.Buckets
'Length;
143 procedure Clear
(HT
: in out Hash_Table_Type
) is
144 Index
: Hash_Type
:= 0;
148 while HT
.Length
> 0 loop
149 while HT
.Buckets
(Index
) = Null_Node
loop
154 Bucket
: Node_Access
renames HT
.Buckets
(Index
);
158 Bucket
:= Next
(Bucket
);
159 HT
.Length
:= HT
.Length
- 1;
161 exit when Bucket
= Null_Node
;
167 ---------------------------
168 -- Delete_Node_Sans_Free --
169 ---------------------------
171 procedure Delete_Node_Sans_Free
172 (HT
: in out Hash_Table_Type
;
175 pragma Assert
(X
/= Null_Node
);
182 if HT
.Length
= 0 then
186 Indx
:= Index
(HT
, X
);
187 Prev
:= HT
.Buckets
(Indx
);
189 if Prev
= Null_Node
then
194 HT
.Buckets
(Indx
) := Next
(Prev
);
195 HT
.Length
:= HT
.Length
- 1;
199 if HT
.Length
= 1 then
206 if Curr
= Null_Node
then
211 Set_Next
(Node
=> Prev
, Next
=> Next
(Curr
));
212 HT
.Length
:= HT
.Length
- 1;
218 end Delete_Node_Sans_Free
;
220 ---------------------
221 -- Ensure_Capacity --
222 ---------------------
224 procedure Ensure_Capacity
225 (HT
: in out Hash_Table_Type
;
232 if HT
.Length
= 0 then
235 elsif HT
.Length
< HT
.Buckets
'Length then
236 NN
:= Prime_Numbers
.To_Prime
(HT
.Length
);
238 -- ASSERT: NN >= HT.Length
240 if NN
< HT
.Buckets
'Length then
241 Rehash
(HT
, Size
=> NN
);
248 if HT
.Buckets
= null then
249 NN
:= Prime_Numbers
.To_Prime
(N
);
253 Rehash
(HT
, Size
=> NN
);
257 if N
<= HT
.Length
then
258 if HT
.Length
>= HT
.Buckets
'Length then
262 NN
:= Prime_Numbers
.To_Prime
(HT
.Length
);
264 -- ASSERT: NN >= HT.Length
266 if NN
< HT
.Buckets
'Length then
267 Rehash
(HT
, Size
=> NN
);
273 -- ASSERT: N > HT.Length
275 if N
= HT
.Buckets
'Length then
279 NN
:= Prime_Numbers
.To_Prime
(N
);
282 -- ASSERT: NN > HT.Length
284 if NN
/= HT
.Buckets
'Length then
285 Rehash
(HT
, Size
=> NN
);
293 procedure Finalize
(HT
: in out Hash_Table_Type
) is
303 function First
(HT
: Hash_Table_Type
) return Node_Access
is
307 if HT
.Length
= 0 then
311 Indx
:= HT
.Buckets
'First;
313 if HT
.Buckets
(Indx
) /= Null_Node
then
314 return HT
.Buckets
(Indx
);
321 ---------------------
322 -- Free_Hash_Table --
323 ---------------------
325 procedure Free_Hash_Table
(Buckets
: in out Buckets_Access
) is
329 if Buckets
= null then
333 for J
in Buckets
'Range loop
334 while Buckets
(J
) /= Null_Node
loop
336 Buckets
(J
) := Next
(Node
);
348 function Generic_Equal
349 (L
, R
: Hash_Table_Type
) return Boolean is
352 L_Node
: Node_Access
;
357 if L
'Address = R
'Address then
361 if L
.Length
/= R
.Length
then
372 L_Node
:= L
.Buckets
(L_Index
);
373 exit when L_Node
/= Null_Node
;
374 L_Index
:= L_Index
+ 1;
380 if not Find
(HT
=> R
, Key
=> L_Node
) then
386 L_Node
:= Next
(L_Node
);
388 if L_Node
= Null_Node
then
394 L_Index
:= L_Index
+ 1;
395 L_Node
:= L
.Buckets
(L_Index
);
396 exit when L_Node
/= Null_Node
;
402 -----------------------
403 -- Generic_Iteration --
404 -----------------------
406 procedure Generic_Iteration
(HT
: Hash_Table_Type
) is
411 or else HT
.Length
= 0
416 for Indx
in HT
.Buckets
'Range loop
417 Node
:= HT
.Buckets
(Indx
);
418 while Node
/= Null_Node
loop
423 end Generic_Iteration
;
429 procedure Generic_Read
430 (Stream
: access Root_Stream_Type
'Class;
431 HT
: out Hash_Table_Type
)
436 N
, M
: Count_Type
'Base;
439 -- As with the sorted set, it's not clear whether read is allowed to
440 -- have side effect if it fails. For now, we assume side effects are
441 -- allowed since it simplifies the algorithm ???
446 B
: Buckets_Access
:= HT
.Buckets
;
450 Free
(B
); -- can this fail???
453 Hash_Type
'Read (Stream
, Last
);
456 HT
.Buckets
:= new Buckets_Type
(0 .. Last
);
459 Count_Type
'Base'Read (Stream, N);
460 pragma Assert (N >= 0);
462 Hash_Type'Read (Stream, I);
463 pragma Assert (I in HT.Buckets'Range);
464 pragma Assert (HT.Buckets (I) = Null_Node);
466 Count_Type'Base'Read
(Stream
, M
);
467 pragma Assert
(M
>= 1);
468 pragma Assert
(M
<= N
);
470 HT
.Buckets
(I
) := New_Node
(Stream
);
471 pragma Assert
(HT
.Buckets
(I
) /= Null_Node
);
472 pragma Assert
(Next
(HT
.Buckets
(I
)) = Null_Node
);
476 HT
.Length
:= HT
.Length
+ 1;
478 for J
in Count_Type
range 2 .. M
loop
479 X
:= New_Node
(Stream
);
480 pragma Assert
(X
/= Null_Node
);
481 pragma Assert
(Next
(X
) = Null_Node
);
483 Set_Next
(Node
=> Y
, Next
=> X
);
486 HT
.Length
:= HT
.Length
+ 1;
497 procedure Generic_Write
498 (Stream
: access Root_Stream_Type
'Class;
499 HT
: Hash_Table_Type
)
505 if HT
.Buckets
= null then
506 Hash_Type
'Write (Stream
, 0);
508 Hash_Type
'Write (Stream
, HT
.Buckets
'Last);
511 Count_Type
'Base'Write (Stream, HT.Length);
513 if HT.Length = 0 then
517 for Indx in HT.Buckets'Range loop
518 X := HT.Buckets (Indx);
520 if X /= Null_Node then
524 exit when X = Null_Node;
528 Hash_Type'Write (Stream, Indx);
529 Count_Type'Base'Write
(Stream
, M
);
531 X
:= HT
.Buckets
(Indx
);
532 for J
in Count_Type
range 1 .. M
loop
537 pragma Assert
(X
= Null_Node
);
547 (Buckets
: Buckets_Type
;
548 Node
: Node_Access
) return Hash_Type
is
550 return Hash_Node
(Node
) mod Buckets
'Length;
554 (Hash_Table
: Hash_Table_Type
;
555 Node
: Node_Access
) return Hash_Type
is
557 return Index
(Hash_Table
.Buckets
.all, Node
);
564 procedure Move
(Target
, Source
: in out Hash_Table_Type
) is
566 if Target
'Address = Source
'Address then
570 if Target
.Length
> 0 then
571 raise Constraint_Error
;
574 Free
(Target
.Buckets
);
576 Target
.Buckets
:= Source
.Buckets
;
577 Source
.Buckets
:= null;
579 Target
.Length
:= Source
.Length
;
588 (HT
: Hash_Table_Type
;
589 Node
: Node_Access
) return Node_Access
591 Result
: Node_Access
:= Next
(Node
);
594 if Result
/= Null_Node
then
598 for Indx
in Index
(HT
, Node
) + 1 .. HT
.Buckets
'Last loop
599 Result
:= HT
.Buckets
(Indx
);
601 if Result
/= Null_Node
then
614 (HT
: in out Hash_Table_Type
;
617 subtype Buckets_Range
is Hash_Type
range 0 .. Size
- 1;
619 Dst_Buckets
: Buckets_Access
:= new Buckets_Type
(Buckets_Range
);
620 Src_Buckets
: Buckets_Access
:= HT
.Buckets
;
622 L
: Count_Type
renames HT
.Length
;
623 LL
: constant Count_Type
:= L
;
626 if Src_Buckets
= null then
627 pragma Assert
(L
= 0);
628 HT
.Buckets
:= Dst_Buckets
;
633 HT
.Buckets
:= Dst_Buckets
;
638 -- We might want to change this to iter from 1 .. L instead ???
640 for Src_Index
in Src_Buckets
'Range loop
643 Src_Bucket
: Node_Access
renames Src_Buckets
(Src_Index
);
645 while Src_Bucket
/= Null_Node
loop
647 Src_Node
: constant Node_Access
:= Src_Bucket
;
648 Dst_Index
: constant Hash_Type
:=
649 Index
(Dst_Buckets
.all, Src_Node
);
650 Dst_Bucket
: Node_Access
renames Dst_Buckets
(Dst_Index
);
652 Src_Bucket
:= Next
(Src_Node
);
653 Set_Next
(Src_Node
, Dst_Bucket
);
654 Dst_Bucket
:= Src_Node
;
657 pragma Assert
(L
> 0);
665 -- Not clear that we can deallocate the nodes,
666 -- because they may be designated by outstanding
667 -- iterators. Which means they're now lost... ???
669 -- for J in NB'Range loop
671 -- Dst : Node_Access renames NB (J);
674 -- while Dst /= Null_Node loop
676 -- Dst := Succ (Dst);
692 pragma Assert
(L
= 0);
694 HT
.Buckets
:= Dst_Buckets
;
700 end Ada
.Containers
.Hash_Tables
.Generic_Operations
;