1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- ADA.CONTAINERS.INDEFINITE_HASHED_SETS --
9 -- Copyright (C) 2004-2010, 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
.Unchecked_Deallocation
;
32 with Ada
.Containers
.Hash_Tables
.Generic_Operations
;
33 pragma Elaborate_All
(Ada
.Containers
.Hash_Tables
.Generic_Operations
);
35 with Ada
.Containers
.Hash_Tables
.Generic_Keys
;
36 pragma Elaborate_All
(Ada
.Containers
.Hash_Tables
.Generic_Keys
);
38 with Ada
.Containers
.Prime_Numbers
;
40 with System
; use type System
.Address
;
42 package body Ada
.Containers
.Indefinite_Hashed_Sets
is
44 -----------------------
45 -- Local Subprograms --
46 -----------------------
48 procedure Assign
(Node
: Node_Access
; Item
: Element_Type
);
49 pragma Inline
(Assign
);
51 function Copy_Node
(Source
: Node_Access
) return Node_Access
;
52 pragma Inline
(Copy_Node
);
54 function Equivalent_Keys
56 Node
: Node_Access
) return Boolean;
57 pragma Inline
(Equivalent_Keys
);
59 function Find_Equal_Key
60 (R_HT
: Hash_Table_Type
;
61 L_Node
: Node_Access
) return Boolean;
63 function Find_Equivalent_Key
64 (R_HT
: Hash_Table_Type
;
65 L_Node
: Node_Access
) return Boolean;
67 procedure Free
(X
: in out Node_Access
);
69 function Hash_Node
(Node
: Node_Access
) return Hash_Type
;
70 pragma Inline
(Hash_Node
);
73 (HT
: in out Hash_Table_Type
;
74 New_Item
: Element_Type
;
75 Node
: out Node_Access
;
76 Inserted
: out Boolean);
78 function Is_In
(HT
: Hash_Table_Type
; Key
: Node_Access
) return Boolean;
79 pragma Inline
(Is_In
);
81 function Next
(Node
: Node_Access
) return Node_Access
;
84 function Read_Node
(Stream
: not null access Root_Stream_Type
'Class)
86 pragma Inline
(Read_Node
);
88 procedure Set_Next
(Node
: Node_Access
; Next
: Node_Access
);
89 pragma Inline
(Set_Next
);
91 function Vet
(Position
: Cursor
) return Boolean;
94 (Stream
: not null access Root_Stream_Type
'Class;
96 pragma Inline
(Write_Node
);
98 --------------------------
99 -- Local Instantiations --
100 --------------------------
102 procedure Free_Element
is
103 new Ada
.Unchecked_Deallocation
(Element_Type
, Element_Access
);
105 package HT_Ops
is new Hash_Tables
.Generic_Operations
106 (HT_Types
=> HT_Types
,
107 Hash_Node
=> Hash_Node
,
109 Set_Next
=> Set_Next
,
110 Copy_Node
=> Copy_Node
,
113 package Element_Keys
is new Hash_Tables
.Generic_Keys
114 (HT_Types
=> HT_Types
,
116 Set_Next
=> Set_Next
,
117 Key_Type
=> Element_Type
,
119 Equivalent_Keys
=> Equivalent_Keys
);
122 new HT_Ops
.Generic_Equal
(Find_Equal_Key
);
124 function Is_Equivalent
is
125 new HT_Ops
.Generic_Equal
(Find_Equivalent_Key
);
127 procedure Read_Nodes
is
128 new HT_Ops
.Generic_Read
(Read_Node
);
130 procedure Replace_Element
is
131 new Element_Keys
.Generic_Replace_Element
(Hash_Node
, Assign
);
133 procedure Write_Nodes
is
134 new HT_Ops
.Generic_Write
(Write_Node
);
140 function "=" (Left
, Right
: Set
) return Boolean is
142 return Is_Equal
(Left
.HT
, Right
.HT
);
149 procedure Adjust
(Container
: in out Set
) is
151 HT_Ops
.Adjust
(Container
.HT
);
158 procedure Assign
(Node
: Node_Access
; Item
: Element_Type
) is
159 X
: Element_Access
:= Node
.Element
;
161 Node
.Element
:= new Element_Type
'(Item);
169 function Capacity (Container : Set) return Count_Type is
171 return HT_Ops.Capacity (Container.HT);
178 procedure Clear (Container : in out Set) is
180 HT_Ops.Clear (Container.HT);
187 function Contains (Container : Set; Item : Element_Type) return Boolean is
189 return Find (Container, Item) /= No_Element;
196 function Copy_Node (Source : Node_Access) return Node_Access is
197 E : Element_Access := new Element_Type'(Source
.Element
.all);
199 return new Node_Type
'(Element => E, Next => null);
211 (Container : in out Set;
217 Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
220 raise Constraint_Error with "attempt to delete element not in set";
227 (Container : in out Set;
228 Position : in out Cursor)
231 if Position.Node = null then
232 raise Constraint_Error with "Position cursor equals No_Element";
235 if Position.Node.Element = null then
236 raise Program_Error with "Position cursor is bad";
239 if Position.Container /= Container'Unrestricted_Access then
240 raise Program_Error with "Position cursor designates wrong set";
243 if Container.HT.Busy > 0 then
244 raise Program_Error with
245 "attempt to tamper with cursors (set is busy)";
248 pragma Assert (Vet (Position), "Position cursor is bad");
250 HT_Ops.Delete_Node_Sans_Free (Container.HT, Position.Node);
252 Free (Position.Node);
253 Position.Container := null;
261 (Target : in out Set;
264 Tgt_Node : Node_Access;
267 if Target'Address = Source'Address then
272 if Source.HT.Length = 0 then
276 if Target.HT.Busy > 0 then
277 raise Program_Error with
278 "attempt to tamper with cursors (set is busy)";
281 if Source.HT.Length < Target.HT.Length then
283 Src_Node : Node_Access;
286 Src_Node := HT_Ops.First (Source.HT);
287 while Src_Node /= null loop
288 Tgt_Node := Element_Keys.Find (Target.HT, Src_Node.Element.all);
290 if Tgt_Node /= null then
291 HT_Ops.Delete_Node_Sans_Free (Target.HT, Tgt_Node);
295 Src_Node := HT_Ops.Next (Source.HT, Src_Node);
300 Tgt_Node := HT_Ops.First (Target.HT);
301 while Tgt_Node /= null loop
302 if Is_In (Source.HT, Tgt_Node) then
304 X : Node_Access := Tgt_Node;
306 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
307 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
312 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
318 function Difference (Left, Right : Set) return Set is
319 Buckets : HT_Types.Buckets_Access;
323 if Left'Address = Right'Address then
327 if Left.Length = 0 then
331 if Right.Length = 0 then
336 Size : constant Hash_Type := Prime_Numbers.To_Prime (Left.Length);
338 Buckets := HT_Ops.New_Buckets (Length => Size);
343 Iterate_Left : declare
344 procedure Process (L_Node : Node_Access);
347 new HT_Ops.Generic_Iteration (Process);
353 procedure Process (L_Node : Node_Access) is
355 if not Is_In (Right.HT, L_Node) then
357 Src : Element_Type renames L_Node.Element.all;
358 Indx : constant Hash_Type := Hash (Src) mod Buckets'Length;
359 Bucket : Node_Access renames Buckets (Indx);
360 Tgt : Element_Access := new Element_Type'(Src
);
362 Bucket
:= new Node_Type
'(Tgt, Bucket);
369 Length := Length + 1;
373 -- Start of processing for Iterate_Left
379 HT_Ops.Free_Hash_Table (Buckets);
383 return (Controlled with HT => (Buckets, Length, 0, 0));
390 function Element (Position : Cursor) return Element_Type is
392 if Position.Node = null then
393 raise Constraint_Error with "Position cursor of equals No_Element";
396 if Position.Node.Element = null then -- handle dangling reference
397 raise Program_Error with "Position cursor is bad";
400 pragma Assert (Vet (Position), "bad cursor in function Element");
402 return Position.Node.Element.all;
405 ---------------------
406 -- Equivalent_Sets --
407 ---------------------
409 function Equivalent_Sets (Left, Right : Set) return Boolean is
411 return Is_Equivalent (Left.HT, Right.HT);
414 -------------------------
415 -- Equivalent_Elements --
416 -------------------------
418 function Equivalent_Elements (Left, Right : Cursor)
421 if Left.Node = null then
422 raise Constraint_Error with
423 "Left cursor of Equivalent_Elements equals No_Element";
426 if Right.Node = null then
427 raise Constraint_Error with
428 "Right cursor of Equivalent_Elements equals No_Element";
431 if Left.Node.Element = null then
432 raise Program_Error with
433 "Left cursor of Equivalent_Elements is bad";
436 if Right.Node.Element = null then
437 raise Program_Error with
438 "Right cursor of Equivalent_Elements is bad";
441 pragma Assert (Vet (Left), "bad Left cursor in Equivalent_Elements");
442 pragma Assert (Vet (Right), "bad Right cursor in Equivalent_Elements");
444 return Equivalent_Elements
445 (Left.Node.Element.all,
446 Right.Node.Element.all);
447 end Equivalent_Elements;
449 function Equivalent_Elements (Left : Cursor; Right : Element_Type)
452 if Left.Node = null then
453 raise Constraint_Error with
454 "Left cursor of Equivalent_Elements equals No_Element";
457 if Left.Node.Element = null then
458 raise Program_Error with
459 "Left cursor of Equivalent_Elements is bad";
462 pragma Assert (Vet (Left), "bad Left cursor in Equivalent_Elements");
464 return Equivalent_Elements (Left.Node.Element.all, Right);
465 end Equivalent_Elements;
467 function Equivalent_Elements (Left : Element_Type; Right : Cursor)
470 if Right.Node = null then
471 raise Constraint_Error with
472 "Right cursor of Equivalent_Elements equals No_Element";
475 if Right.Node.Element = null then
476 raise Program_Error with
477 "Right cursor of Equivalent_Elements is bad";
480 pragma Assert (Vet (Right), "bad Right cursor in Equivalent_Elements");
482 return Equivalent_Elements (Left, Right.Node.Element.all);
483 end Equivalent_Elements;
485 ---------------------
486 -- Equivalent_Keys --
487 ---------------------
489 function Equivalent_Keys (Key : Element_Type; Node : Node_Access)
492 return Equivalent_Elements (Key, Node.Element.all);
500 (Container : in out Set;
505 Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
513 procedure Finalize (Container : in out Set) is
515 HT_Ops.Finalize (Container.HT);
524 Item : Element_Type) return Cursor
526 Node : constant Node_Access := Element_Keys.Find (Container.HT, Item);
533 return Cursor'(Container
'Unrestricted_Access, Node
);
540 function Find_Equal_Key
541 (R_HT
: Hash_Table_Type
;
542 L_Node
: Node_Access
) return Boolean
544 R_Index
: constant Hash_Type
:=
545 Element_Keys
.Index
(R_HT
, L_Node
.Element
.all);
547 R_Node
: Node_Access
:= R_HT
.Buckets
(R_Index
);
551 if R_Node
= null then
555 if L_Node
.Element
.all = R_Node
.Element
.all then
559 R_Node
:= Next
(R_Node
);
563 -------------------------
564 -- Find_Equivalent_Key --
565 -------------------------
567 function Find_Equivalent_Key
568 (R_HT
: Hash_Table_Type
;
569 L_Node
: Node_Access
) return Boolean
571 R_Index
: constant Hash_Type
:=
572 Element_Keys
.Index
(R_HT
, L_Node
.Element
.all);
574 R_Node
: Node_Access
:= R_HT
.Buckets
(R_Index
);
578 if R_Node
= null then
582 if Equivalent_Elements
(L_Node
.Element
.all, R_Node
.Element
.all) then
586 R_Node
:= Next
(R_Node
);
588 end Find_Equivalent_Key
;
594 function First
(Container
: Set
) return Cursor
is
595 Node
: constant Node_Access
:= HT_Ops
.First
(Container
.HT
);
602 return Cursor
'(Container'Unrestricted_Access, Node);
609 procedure Free (X : in out Node_Access) is
610 procedure Deallocate is
611 new Ada.Unchecked_Deallocation (Node_Type, Node_Access);
618 X.Next := X; -- detect mischief (in Vet)
621 Free_Element (X.Element);
636 function Has_Element (Position : Cursor) return Boolean is
638 pragma Assert (Vet (Position), "bad cursor in Has_Element");
639 return Position.Node /= null;
646 function Hash_Node (Node : Node_Access) return Hash_Type is
648 return Hash (Node.Element.all);
656 (Container : in out Set;
657 New_Item : Element_Type)
665 Insert (Container, New_Item, Position, Inserted);
668 if Container.HT.Lock > 0 then
669 raise Program_Error with
670 "attempt to tamper with elements (set is locked)";
673 X := Position.Node.Element;
675 Position.Node.Element := new Element_Type'(New_Item
);
686 (Container
: in out Set
;
687 New_Item
: Element_Type
;
688 Position
: out Cursor
;
689 Inserted
: out Boolean)
692 Insert
(Container
.HT
, New_Item
, Position
.Node
, Inserted
);
693 Position
.Container
:= Container
'Unchecked_Access;
697 (Container
: in out Set
;
698 New_Item
: Element_Type
)
701 pragma Unreferenced
(Position
);
706 Insert
(Container
, New_Item
, Position
, Inserted
);
709 raise Constraint_Error
with
710 "attempt to insert element already in set";
715 (HT
: in out Hash_Table_Type
;
716 New_Item
: Element_Type
;
717 Node
: out Node_Access
;
718 Inserted
: out Boolean)
720 function New_Node
(Next
: Node_Access
) return Node_Access
;
721 pragma Inline
(New_Node
);
723 procedure Local_Insert
is
724 new Element_Keys
.Generic_Conditional_Insert
(New_Node
);
730 function New_Node
(Next
: Node_Access
) return Node_Access
is
731 Element
: Element_Access
:= new Element_Type
'(New_Item);
734 return new Node_Type'(Element
, Next
);
737 Free_Element
(Element
);
741 -- Start of processing for Insert
744 if HT_Ops
.Capacity
(HT
) = 0 then
745 HT_Ops
.Reserve_Capacity
(HT
, 1);
748 Local_Insert
(HT
, New_Item
, Node
, Inserted
);
751 and then HT
.Length
> HT_Ops
.Capacity
(HT
)
753 HT_Ops
.Reserve_Capacity
(HT
, HT
.Length
);
761 procedure Intersection
762 (Target
: in out Set
;
765 Tgt_Node
: Node_Access
;
768 if Target
'Address = Source
'Address then
772 if Source
.Length
= 0 then
777 if Target
.HT
.Busy
> 0 then
778 raise Program_Error
with
779 "attempt to tamper with cursors (set is busy)";
782 Tgt_Node
:= HT_Ops
.First
(Target
.HT
);
783 while Tgt_Node
/= null loop
784 if Is_In
(Source
.HT
, Tgt_Node
) then
785 Tgt_Node
:= HT_Ops
.Next
(Target
.HT
, Tgt_Node
);
789 X
: Node_Access
:= Tgt_Node
;
791 Tgt_Node
:= HT_Ops
.Next
(Target
.HT
, Tgt_Node
);
792 HT_Ops
.Delete_Node_Sans_Free
(Target
.HT
, X
);
799 function Intersection
(Left
, Right
: Set
) return Set
is
800 Buckets
: HT_Types
.Buckets_Access
;
804 if Left
'Address = Right
'Address then
808 Length
:= Count_Type
'Min (Left
.Length
, Right
.Length
);
815 Size
: constant Hash_Type
:= Prime_Numbers
.To_Prime
(Length
);
817 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
822 Iterate_Left
: declare
823 procedure Process
(L_Node
: Node_Access
);
826 new HT_Ops
.Generic_Iteration
(Process
);
832 procedure Process
(L_Node
: Node_Access
) is
834 if Is_In
(Right
.HT
, L_Node
) then
836 Src
: Element_Type
renames L_Node
.Element
.all;
838 Indx
: constant Hash_Type
:= Hash
(Src
) mod Buckets
'Length;
840 Bucket
: Node_Access
renames Buckets
(Indx
);
842 Tgt
: Element_Access
:= new Element_Type
'(Src);
845 Bucket := new Node_Type'(Tgt
, Bucket
);
852 Length
:= Length
+ 1;
856 -- Start of processing for Iterate_Left
862 HT_Ops
.Free_Hash_Table
(Buckets
);
866 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
873 function Is_Empty
(Container
: Set
) return Boolean is
875 return Container
.HT
.Length
= 0;
882 function Is_In
(HT
: Hash_Table_Type
; Key
: Node_Access
) return Boolean is
884 return Element_Keys
.Find
(HT
, Key
.Element
.all) /= null;
893 Of_Set
: Set
) return Boolean
895 Subset_Node
: Node_Access
;
898 if Subset
'Address = Of_Set
'Address then
902 if Subset
.Length
> Of_Set
.Length
then
906 Subset_Node
:= HT_Ops
.First
(Subset
.HT
);
907 while Subset_Node
/= null loop
908 if not Is_In
(Of_Set
.HT
, Subset_Node
) then
912 Subset_Node
:= HT_Ops
.Next
(Subset
.HT
, Subset_Node
);
924 Process
: not null access procedure (Position
: Cursor
))
926 procedure Process_Node
(Node
: Node_Access
);
927 pragma Inline
(Process_Node
);
930 new HT_Ops
.Generic_Iteration
(Process_Node
);
936 procedure Process_Node
(Node
: Node_Access
) is
938 Process
(Cursor
'(Container'Unrestricted_Access, Node));
941 B : Natural renames Container'Unrestricted_Access.HT.Busy;
943 -- Start of processing for Iterate
949 Iterate (Container.HT);
963 function Length (Container : Set) return Count_Type is
965 return Container.HT.Length;
972 procedure Move (Target : in out Set; Source : in out Set) is
974 HT_Ops.Move (Target => Target.HT, Source => Source.HT);
981 function Next (Node : Node_Access) return Node_Access is
986 function Next (Position : Cursor) return Cursor is
988 if Position.Node = null then
992 if Position.Node.Element = null then
993 raise Program_Error with "bad cursor in Next";
996 pragma Assert (Vet (Position), "bad cursor in Next");
999 HT : Hash_Table_Type renames Position.Container.HT;
1000 Node : constant Node_Access := HT_Ops.Next (HT, Position.Node);
1007 return Cursor'(Position
.Container
, Node
);
1011 procedure Next
(Position
: in out Cursor
) is
1013 Position
:= Next
(Position
);
1020 function Overlap
(Left
, Right
: Set
) return Boolean is
1021 Left_Node
: Node_Access
;
1024 if Right
.Length
= 0 then
1028 if Left
'Address = Right
'Address then
1032 Left_Node
:= HT_Ops
.First
(Left
.HT
);
1033 while Left_Node
/= null loop
1034 if Is_In
(Right
.HT
, Left_Node
) then
1038 Left_Node
:= HT_Ops
.Next
(Left
.HT
, Left_Node
);
1048 procedure Query_Element
1050 Process
: not null access procedure (Element
: Element_Type
))
1053 if Position
.Node
= null then
1054 raise Constraint_Error
with
1055 "Position cursor of Query_Element equals No_Element";
1058 if Position
.Node
.Element
= null then
1059 raise Program_Error
with "bad cursor in Query_Element";
1062 pragma Assert
(Vet
(Position
), "bad cursor in Query_Element");
1065 HT
: Hash_Table_Type
renames
1066 Position
.Container
'Unrestricted_Access.all.HT
;
1068 B
: Natural renames HT
.Busy
;
1069 L
: Natural renames HT
.Lock
;
1076 Process
(Position
.Node
.Element
.all);
1094 (Stream
: not null access Root_Stream_Type
'Class;
1095 Container
: out Set
)
1098 Read_Nodes
(Stream
, Container
.HT
);
1102 (Stream
: not null access Root_Stream_Type
'Class;
1106 raise Program_Error
with "attempt to stream set cursor";
1114 (Stream
: not null access Root_Stream_Type
'Class) return Node_Access
1116 X
: Element_Access
:= new Element_Type
'(Element_Type'Input (Stream));
1119 return new Node_Type'(X
, null);
1131 (Container
: in out Set
;
1132 New_Item
: Element_Type
)
1134 Node
: constant Node_Access
:=
1135 Element_Keys
.Find
(Container
.HT
, New_Item
);
1138 pragma Warnings
(Off
, X
);
1142 raise Constraint_Error
with
1143 "attempt to replace element not in set";
1146 if Container
.HT
.Lock
> 0 then
1147 raise Program_Error
with
1148 "attempt to tamper with elements (set is locked)";
1153 Node
.Element
:= new Element_Type
'(New_Item);
1158 ---------------------
1159 -- Replace_Element --
1160 ---------------------
1162 procedure Replace_Element
1163 (Container : in out Set;
1165 New_Item : Element_Type)
1168 if Position.Node = null then
1169 raise Constraint_Error with "Position cursor equals No_Element";
1172 if Position.Node.Element = null then
1173 raise Program_Error with "bad cursor in Replace_Element";
1176 if Position.Container /= Container'Unrestricted_Access then
1177 raise Program_Error with
1178 "Position cursor designates wrong set";
1181 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1183 Replace_Element (Container.HT, Position.Node, New_Item);
1184 end Replace_Element;
1186 ----------------------
1187 -- Reserve_Capacity --
1188 ----------------------
1190 procedure Reserve_Capacity
1191 (Container : in out Set;
1192 Capacity : Count_Type)
1195 HT_Ops.Reserve_Capacity (Container.HT, Capacity);
1196 end Reserve_Capacity;
1202 procedure Set_Next (Node : Node_Access; Next : Node_Access) is
1207 --------------------------
1208 -- Symmetric_Difference --
1209 --------------------------
1211 procedure Symmetric_Difference
1212 (Target : in out Set;
1216 if Target'Address = Source'Address then
1221 if Target.HT.Busy > 0 then
1222 raise Program_Error with
1223 "attempt to tamper with cursors (set is busy)";
1227 N : constant Count_Type := Target.Length + Source.Length;
1229 if N > HT_Ops.Capacity (Target.HT) then
1230 HT_Ops.Reserve_Capacity (Target.HT, N);
1234 if Target.Length = 0 then
1235 Iterate_Source_When_Empty_Target : declare
1236 procedure Process (Src_Node : Node_Access);
1238 procedure Iterate is
1239 new HT_Ops.Generic_Iteration (Process);
1245 procedure Process (Src_Node : Node_Access) is
1246 E : Element_Type renames Src_Node.Element.all;
1247 B : Buckets_Type renames Target.HT.Buckets.all;
1248 J : constant Hash_Type := Hash (E) mod B'Length;
1249 N : Count_Type renames Target.HT.Length;
1253 X : Element_Access := new Element_Type'(E
);
1255 B
(J
) := new Node_Type
'(X, B (J));
1265 -- Start of processing for Iterate_Source_When_Empty_Target
1268 Iterate (Source.HT);
1269 end Iterate_Source_When_Empty_Target;
1272 Iterate_Source : declare
1273 procedure Process (Src_Node : Node_Access);
1275 procedure Iterate is
1276 new HT_Ops.Generic_Iteration (Process);
1282 procedure Process (Src_Node : Node_Access) is
1283 E : Element_Type renames Src_Node.Element.all;
1284 B : Buckets_Type renames Target.HT.Buckets.all;
1285 J : constant Hash_Type := Hash (E) mod B'Length;
1286 N : Count_Type renames Target.HT.Length;
1289 if B (J) = null then
1291 X : Element_Access := new Element_Type'(E
);
1293 B
(J
) := new Node_Type
'(X, null);
1302 elsif Equivalent_Elements (E, B (J).Element.all) then
1304 X : Node_Access := B (J);
1306 B (J) := B (J).Next;
1313 Prev : Node_Access := B (J);
1314 Curr : Node_Access := Prev.Next;
1317 while Curr /= null loop
1318 if Equivalent_Elements (E, Curr.Element.all) then
1319 Prev.Next := Curr.Next;
1330 X : Element_Access := new Element_Type'(E
);
1332 B
(J
) := new Node_Type
'(X, B (J));
1344 -- Start of processing for Iterate_Source
1347 Iterate (Source.HT);
1350 end Symmetric_Difference;
1352 function Symmetric_Difference (Left, Right : Set) return Set is
1353 Buckets : HT_Types.Buckets_Access;
1354 Length : Count_Type;
1357 if Left'Address = Right'Address then
1361 if Right.Length = 0 then
1365 if Left.Length = 0 then
1370 Size : constant Hash_Type :=
1371 Prime_Numbers.To_Prime (Left.Length + Right.Length);
1373 Buckets := HT_Ops.New_Buckets (Length => Size);
1378 Iterate_Left : declare
1379 procedure Process (L_Node : Node_Access);
1381 procedure Iterate is
1382 new HT_Ops.Generic_Iteration (Process);
1388 procedure Process (L_Node : Node_Access) is
1390 if not Is_In (Right.HT, L_Node) then
1392 E : Element_Type renames L_Node.Element.all;
1393 J : constant Hash_Type := Hash (E) mod Buckets'Length;
1397 X : Element_Access := new Element_Type'(E
);
1399 Buckets
(J
) := new Node_Type
'(X, Buckets (J));
1406 Length := Length + 1;
1411 -- Start of processing for Iterate_Left
1417 HT_Ops.Free_Hash_Table (Buckets);
1421 Iterate_Right : declare
1422 procedure Process (R_Node : Node_Access);
1424 procedure Iterate is
1425 new HT_Ops.Generic_Iteration (Process);
1431 procedure Process (R_Node : Node_Access) is
1433 if not Is_In (Left.HT, R_Node) then
1435 E : Element_Type renames R_Node.Element.all;
1436 J : constant Hash_Type := Hash (E) mod Buckets'Length;
1440 X : Element_Access := new Element_Type'(E
);
1442 Buckets
(J
) := new Node_Type
'(X, Buckets (J));
1449 Length := Length + 1;
1454 -- Start of processing for Iterate_Right
1460 HT_Ops.Free_Hash_Table (Buckets);
1464 return (Controlled with HT => (Buckets, Length, 0, 0));
1465 end Symmetric_Difference;
1471 function To_Set (New_Item : Element_Type) return Set is
1472 HT : Hash_Table_Type;
1476 pragma Unreferenced (Node, Inserted);
1479 Insert (HT, New_Item, Node, Inserted);
1480 return Set'(Controlled
with HT
);
1488 (Target
: in out Set
;
1491 procedure Process
(Src_Node
: Node_Access
);
1493 procedure Iterate
is
1494 new HT_Ops
.Generic_Iteration
(Process
);
1500 procedure Process
(Src_Node
: Node_Access
) is
1501 Src
: Element_Type
renames Src_Node
.Element
.all;
1503 function New_Node
(Next
: Node_Access
) return Node_Access
;
1504 pragma Inline
(New_Node
);
1507 new Element_Keys
.Generic_Conditional_Insert
(New_Node
);
1513 function New_Node
(Next
: Node_Access
) return Node_Access
is
1514 Tgt
: Element_Access
:= new Element_Type
'(Src);
1517 return new Node_Type'(Tgt
, Next
);
1524 Tgt_Node
: Node_Access
;
1526 pragma Unreferenced
(Tgt_Node
, Success
);
1528 -- Start of processing for Process
1531 Insert
(Target
.HT
, Src
, Tgt_Node
, Success
);
1534 -- Start of processing for Union
1537 if Target
'Address = Source
'Address then
1541 if Target
.HT
.Busy
> 0 then
1542 raise Program_Error
with
1543 "attempt to tamper with cursors (set is busy)";
1547 N
: constant Count_Type
:= Target
.Length
+ Source
.Length
;
1549 if N
> HT_Ops
.Capacity
(Target
.HT
) then
1550 HT_Ops
.Reserve_Capacity
(Target
.HT
, N
);
1554 Iterate
(Source
.HT
);
1557 function Union
(Left
, Right
: Set
) return Set
is
1558 Buckets
: HT_Types
.Buckets_Access
;
1559 Length
: Count_Type
;
1562 if Left
'Address = Right
'Address then
1566 if Right
.Length
= 0 then
1570 if Left
.Length
= 0 then
1575 Size
: constant Hash_Type
:=
1576 Prime_Numbers
.To_Prime
(Left
.Length
+ Right
.Length
);
1578 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
1581 Iterate_Left
: declare
1582 procedure Process
(L_Node
: Node_Access
);
1584 procedure Iterate
is
1585 new HT_Ops
.Generic_Iteration
(Process
);
1591 procedure Process
(L_Node
: Node_Access
) is
1592 Src
: Element_Type
renames L_Node
.Element
.all;
1594 J
: constant Hash_Type
:= Hash
(Src
) mod Buckets
'Length;
1596 Bucket
: Node_Access
renames Buckets
(J
);
1598 Tgt
: Element_Access
:= new Element_Type
'(Src);
1601 Bucket := new Node_Type'(Tgt
, Bucket
);
1608 -- Start of processing for Process
1614 HT_Ops
.Free_Hash_Table
(Buckets
);
1618 Length
:= Left
.Length
;
1620 Iterate_Right
: declare
1621 procedure Process
(Src_Node
: Node_Access
);
1623 procedure Iterate
is
1624 new HT_Ops
.Generic_Iteration
(Process
);
1630 procedure Process
(Src_Node
: Node_Access
) is
1631 Src
: Element_Type
renames Src_Node
.Element
.all;
1632 Idx
: constant Hash_Type
:= Hash
(Src
) mod Buckets
'Length;
1634 Tgt_Node
: Node_Access
:= Buckets
(Idx
);
1637 while Tgt_Node
/= null loop
1638 if Equivalent_Elements
(Src
, Tgt_Node
.Element
.all) then
1641 Tgt_Node
:= Next
(Tgt_Node
);
1645 Tgt
: Element_Access
:= new Element_Type
'(Src);
1647 Buckets (Idx) := new Node_Type'(Tgt
, Buckets
(Idx
));
1654 Length
:= Length
+ 1;
1657 -- Start of processing for Iterate_Right
1663 HT_Ops
.Free_Hash_Table
(Buckets
);
1667 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
1674 function Vet
(Position
: Cursor
) return Boolean is
1676 if Position
.Node
= null then
1677 return Position
.Container
= null;
1680 if Position
.Container
= null then
1684 if Position
.Node
.Next
= Position
.Node
then
1688 if Position
.Node
.Element
= null then
1693 HT
: Hash_Table_Type
renames Position
.Container
.HT
;
1697 if HT
.Length
= 0 then
1701 if HT
.Buckets
= null
1702 or else HT
.Buckets
'Length = 0
1707 X
:= HT
.Buckets
(Element_Keys
.Index
(HT
, Position
.Node
.Element
.all));
1709 for J
in 1 .. HT
.Length
loop
1710 if X
= Position
.Node
then
1718 if X
= X
.Next
then -- to prevent unnecessary looping
1734 (Stream
: not null access Root_Stream_Type
'Class;
1738 Write_Nodes
(Stream
, Container
.HT
);
1742 (Stream
: not null access Root_Stream_Type
'Class;
1746 raise Program_Error
with "attempt to stream set cursor";
1753 procedure Write_Node
1754 (Stream
: not null access Root_Stream_Type
'Class;
1758 Element_Type
'Output (Stream
, Node
.Element
.all);
1761 package body Generic_Keys
is
1763 -----------------------
1764 -- Local Subprograms --
1765 -----------------------
1767 function Equivalent_Key_Node
1769 Node
: Node_Access
) return Boolean;
1770 pragma Inline
(Equivalent_Key_Node
);
1772 --------------------------
1773 -- Local Instantiations --
1774 --------------------------
1777 new Hash_Tables
.Generic_Keys
1778 (HT_Types
=> HT_Types
,
1780 Set_Next
=> Set_Next
,
1781 Key_Type
=> Key_Type
,
1783 Equivalent_Keys
=> Equivalent_Key_Node
);
1791 Key
: Key_Type
) return Boolean
1794 return Find
(Container
, Key
) /= No_Element
;
1802 (Container
: in out Set
;
1808 Key_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Key
, X
);
1811 raise Constraint_Error
with "key not in map";
1823 Key
: Key_Type
) return Element_Type
1825 Node
: constant Node_Access
:= Key_Keys
.Find
(Container
.HT
, Key
);
1829 raise Constraint_Error
with "key not in map";
1832 return Node
.Element
.all;
1835 -------------------------
1836 -- Equivalent_Key_Node --
1837 -------------------------
1839 function Equivalent_Key_Node
1841 Node
: Node_Access
) return Boolean is
1843 return Equivalent_Keys
(Key
, Generic_Keys
.Key
(Node
.Element
.all));
1844 end Equivalent_Key_Node
;
1851 (Container
: in out Set
;
1856 Key_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Key
, X
);
1866 Key
: Key_Type
) return Cursor
1868 Node
: constant Node_Access
:= Key_Keys
.Find
(Container
.HT
, Key
);
1875 return Cursor
'(Container'Unrestricted_Access, Node);
1882 function Key (Position : Cursor) return Key_Type is
1884 if Position.Node = null then
1885 raise Constraint_Error with
1886 "Position cursor equals No_Element";
1889 if Position.Node.Element = null then
1890 raise Program_Error with "Position cursor is bad";
1893 pragma Assert (Vet (Position), "bad cursor in function Key");
1895 return Key (Position.Node.Element.all);
1903 (Container : in out Set;
1905 New_Item : Element_Type)
1907 Node : constant Node_Access :=
1908 Key_Keys.Find (Container.HT, Key);
1912 raise Constraint_Error with
1913 "attempt to replace key not in set";
1916 Replace_Element (Container.HT, Node, New_Item);
1919 procedure Update_Element_Preserving_Key
1920 (Container : in out Set;
1922 Process : not null access
1923 procedure (Element : in out Element_Type))
1925 HT : Hash_Table_Type renames Container.HT;
1929 if Position.Node = null then
1930 raise Constraint_Error with
1931 "Position cursor equals No_Element";
1934 if Position.Node.Element = null
1935 or else Position.Node.Next = Position.Node
1937 raise Program_Error with "Position cursor is bad";
1940 if Position.Container /= Container'Unrestricted_Access then
1941 raise Program_Error with
1942 "Position cursor designates wrong set";
1945 if HT.Buckets = null
1946 or else HT.Buckets'Length = 0
1947 or else HT.Length = 0
1949 raise Program_Error with "Position cursor is bad (set is empty)";
1954 "bad cursor in Update_Element_Preserving_Key");
1956 Indx := HT_Ops.Index (HT, Position.Node);
1959 E : Element_Type renames Position.Node.Element.all;
1960 K : constant Key_Type := Key (E);
1962 B : Natural renames HT.Busy;
1963 L : Natural renames HT.Lock;
1981 if Equivalent_Keys (K, Key (E)) then
1982 pragma Assert (Hash (K) = Hash (E));
1987 if HT.Buckets (Indx) = Position.Node then
1988 HT.Buckets (Indx) := Position.Node.Next;
1992 Prev : Node_Access := HT.Buckets (Indx);
1995 while Prev.Next /= Position.Node loop
1999 raise Program_Error with
2000 "Position cursor is bad (node not found)";
2004 Prev.Next := Position.Node.Next;
2008 HT.Length := HT.Length - 1;
2011 X : Node_Access := Position.Node;
2017 raise Program_Error with "key was modified";
2018 end Update_Element_Preserving_Key;
2022 end Ada.Containers.Indefinite_Hashed_Sets;