1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- A D A . C O N T A I N E R S . H A S H E D _ S E T S --
9 -- Copyright (C) 2004-2012, 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
.Hashed_Sets
is
44 type Iterator
is limited new
45 Set_Iterator_Interfaces
.Forward_Iterator
with record
46 Container
: Set_Access
;
49 overriding
function First
(Object
: Iterator
) return Cursor
;
51 overriding
function Next
53 Position
: Cursor
) return Cursor
;
55 -----------------------
56 -- Local Subprograms --
57 -----------------------
59 procedure Assign
(Node
: Node_Access
; Item
: Element_Type
);
60 pragma Inline
(Assign
);
62 function Copy_Node
(Source
: Node_Access
) return Node_Access
;
63 pragma Inline
(Copy_Node
);
65 function Equivalent_Keys
67 Node
: Node_Access
) return Boolean;
68 pragma Inline
(Equivalent_Keys
);
70 function Find_Equal_Key
71 (R_HT
: Hash_Table_Type
;
72 L_Node
: Node_Access
) return Boolean;
74 function Find_Equivalent_Key
75 (R_HT
: Hash_Table_Type
;
76 L_Node
: Node_Access
) return Boolean;
78 procedure Free
(X
: in out Node_Access
);
80 function Hash_Node
(Node
: Node_Access
) return Hash_Type
;
81 pragma Inline
(Hash_Node
);
84 (HT
: in out Hash_Table_Type
;
85 New_Item
: Element_Type
;
86 Node
: out Node_Access
;
87 Inserted
: out Boolean);
90 (HT
: Hash_Table_Type
;
91 Key
: Node_Access
) return Boolean;
92 pragma Inline
(Is_In
);
94 function Next
(Node
: Node_Access
) return Node_Access
;
97 function Read_Node
(Stream
: not null access Root_Stream_Type
'Class)
99 pragma Inline
(Read_Node
);
101 procedure Set_Next
(Node
: Node_Access
; Next
: Node_Access
);
102 pragma Inline
(Set_Next
);
104 function Vet
(Position
: Cursor
) return Boolean;
107 (Stream
: not null access Root_Stream_Type
'Class;
109 pragma Inline
(Write_Node
);
111 --------------------------
112 -- Local Instantiations --
113 --------------------------
115 package HT_Ops
is new Hash_Tables
.Generic_Operations
116 (HT_Types
=> HT_Types
,
117 Hash_Node
=> Hash_Node
,
119 Set_Next
=> Set_Next
,
120 Copy_Node
=> Copy_Node
,
123 package Element_Keys
is new Hash_Tables
.Generic_Keys
124 (HT_Types
=> HT_Types
,
126 Set_Next
=> Set_Next
,
127 Key_Type
=> Element_Type
,
129 Equivalent_Keys
=> Equivalent_Keys
);
132 new HT_Ops
.Generic_Equal
(Find_Equal_Key
);
134 function Is_Equivalent
is
135 new HT_Ops
.Generic_Equal
(Find_Equivalent_Key
);
137 procedure Read_Nodes
is
138 new HT_Ops
.Generic_Read
(Read_Node
);
140 procedure Replace_Element
is
141 new Element_Keys
.Generic_Replace_Element
(Hash_Node
, Assign
);
143 procedure Write_Nodes
is
144 new HT_Ops
.Generic_Write
(Write_Node
);
150 function "=" (Left
, Right
: Set
) return Boolean is
152 return Is_Equal
(Left
.HT
, Right
.HT
);
159 procedure Adjust
(Container
: in out Set
) is
161 HT_Ops
.Adjust
(Container
.HT
);
164 procedure Adjust
(Control
: in out Reference_Control_Type
) is
166 if Control
.Container
/= null then
168 HT
: Hash_Table_Type
renames Control
.Container
.all.HT
;
169 B
: Natural renames HT
.Busy
;
170 L
: Natural renames HT
.Lock
;
182 procedure Assign
(Node
: Node_Access
; Item
: Element_Type
) is
184 Node
.Element
:= Item
;
187 procedure Assign
(Target
: in out Set
; Source
: Set
) is
189 if Target
'Address = Source
'Address then
194 Target
.Union
(Source
);
201 function Capacity
(Container
: Set
) return Count_Type
is
203 return HT_Ops
.Capacity
(Container
.HT
);
210 procedure Clear
(Container
: in out Set
) is
212 HT_Ops
.Clear
(Container
.HT
);
215 ------------------------
216 -- Constant_Reference --
217 ------------------------
219 function Constant_Reference
220 (Container
: aliased Set
;
221 Position
: Cursor
) return Constant_Reference_Type
224 if Position
.Container
= null then
225 raise Constraint_Error
with "Position cursor has no element";
228 if Position
.Container
/= Container
'Unrestricted_Access then
229 raise Program_Error
with
230 "Position cursor designates wrong container";
233 pragma Assert
(Vet
(Position
), "bad cursor in Constant_Reference");
236 HT
: Hash_Table_Type
renames Position
.Container
.all.HT
;
237 B
: Natural renames HT
.Busy
;
238 L
: Natural renames HT
.Lock
;
240 return R
: constant Constant_Reference_Type
:=
241 (Element
=> Position
.Node
.Element
'Access,
242 Control
=> (Controlled
with Container
'Unrestricted_Access))
248 end Constant_Reference
;
254 function Contains
(Container
: Set
; Item
: Element_Type
) return Boolean is
256 return Find
(Container
, Item
) /= No_Element
;
265 Capacity
: Count_Type
:= 0) return Set
273 elsif Capacity
>= Source
.Length
then
278 with "Requested capacity is less than Source length";
281 return Target
: Set
do
282 Target
.Reserve_Capacity
(C
);
283 Target
.Assign
(Source
);
291 function Copy_Node
(Source
: Node_Access
) return Node_Access
is
293 return new Node_Type
'(Element => Source.Element, Next => null);
301 (Container : in out Set;
307 Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
310 raise Constraint_Error with "attempt to delete element not in set";
317 (Container : in out Set;
318 Position : in out Cursor)
321 if Position.Node = null then
322 raise Constraint_Error with "Position cursor equals No_Element";
325 if Position.Container /= Container'Unrestricted_Access then
326 raise Program_Error with "Position cursor designates wrong set";
329 if Container.HT.Busy > 0 then
330 raise Program_Error with
331 "attempt to tamper with cursors (set is busy)";
334 pragma Assert (Vet (Position), "bad cursor in Delete");
336 HT_Ops.Delete_Node_Sans_Free (Container.HT, Position.Node);
338 Free (Position.Node);
339 Position.Container := null;
347 (Target : in out Set;
350 Tgt_Node : Node_Access;
353 if Target'Address = Source'Address then
358 if Source.HT.Length = 0 then
362 if Target.HT.Busy > 0 then
363 raise Program_Error with
364 "attempt to tamper with cursors (set is busy)";
367 if Source.HT.Length < Target.HT.Length then
369 Src_Node : Node_Access;
372 Src_Node := HT_Ops.First (Source.HT);
373 while Src_Node /= null loop
374 Tgt_Node := Element_Keys.Find (Target.HT, Src_Node.Element);
376 if Tgt_Node /= null then
377 HT_Ops.Delete_Node_Sans_Free (Target.HT, Tgt_Node);
381 Src_Node := HT_Ops.Next (Source.HT, Src_Node);
386 Tgt_Node := HT_Ops.First (Target.HT);
387 while Tgt_Node /= null loop
388 if Is_In (Source.HT, Tgt_Node) then
390 X : Node_Access := Tgt_Node;
392 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
393 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
398 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
404 function Difference (Left, Right : Set) return Set is
405 Buckets : HT_Types.Buckets_Access;
409 if Left'Address = Right'Address then
413 if Left.HT.Length = 0 then
417 if Right.HT.Length = 0 then
422 Size : constant Hash_Type := Prime_Numbers.To_Prime (Left.Length);
424 Buckets := HT_Ops.New_Buckets (Length => Size);
429 Iterate_Left : declare
430 procedure Process (L_Node : Node_Access);
433 new HT_Ops.Generic_Iteration (Process);
439 procedure Process (L_Node : Node_Access) is
441 if not Is_In (Right.HT, L_Node) then
443 J : constant Hash_Type :=
444 Hash (L_Node.Element) mod Buckets'Length;
446 Bucket : Node_Access renames Buckets (J);
449 Bucket := new Node_Type'(L_Node
.Element
, Bucket
);
452 Length
:= Length
+ 1;
456 -- Start of processing for Iterate_Left
462 HT_Ops
.Free_Hash_Table
(Buckets
);
466 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
473 function Element
(Position
: Cursor
) return Element_Type
is
475 if Position
.Node
= null then
476 raise Constraint_Error
with "Position cursor equals No_Element";
479 pragma Assert
(Vet
(Position
), "bad cursor in function Element");
481 return Position
.Node
.Element
;
484 ---------------------
485 -- Equivalent_Sets --
486 ---------------------
488 function Equivalent_Sets
(Left
, Right
: Set
) return Boolean is
490 return Is_Equivalent
(Left
.HT
, Right
.HT
);
493 -------------------------
494 -- Equivalent_Elements --
495 -------------------------
497 function Equivalent_Elements
(Left
, Right
: Cursor
)
500 if Left
.Node
= null then
501 raise Constraint_Error
with
502 "Left cursor of Equivalent_Elements equals No_Element";
505 if Right
.Node
= null then
506 raise Constraint_Error
with
507 "Right cursor of Equivalent_Elements equals No_Element";
510 pragma Assert
(Vet
(Left
), "bad Left cursor in Equivalent_Elements");
511 pragma Assert
(Vet
(Right
), "bad Right cursor in Equivalent_Elements");
513 return Equivalent_Elements
(Left
.Node
.Element
, Right
.Node
.Element
);
514 end Equivalent_Elements
;
516 function Equivalent_Elements
(Left
: Cursor
; Right
: Element_Type
)
519 if Left
.Node
= null then
520 raise Constraint_Error
with
521 "Left cursor of Equivalent_Elements equals No_Element";
524 pragma Assert
(Vet
(Left
), "Left cursor in Equivalent_Elements is bad");
526 return Equivalent_Elements
(Left
.Node
.Element
, Right
);
527 end Equivalent_Elements
;
529 function Equivalent_Elements
(Left
: Element_Type
; Right
: Cursor
)
532 if Right
.Node
= null then
533 raise Constraint_Error
with
534 "Right cursor of Equivalent_Elements equals No_Element";
539 "Right cursor of Equivalent_Elements is bad");
541 return Equivalent_Elements
(Left
, Right
.Node
.Element
);
542 end Equivalent_Elements
;
544 ---------------------
545 -- Equivalent_Keys --
546 ---------------------
548 function Equivalent_Keys
(Key
: Element_Type
; Node
: Node_Access
)
551 return Equivalent_Elements
(Key
, Node
.Element
);
559 (Container
: in out Set
;
564 Element_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Item
, X
);
572 procedure Finalize
(Container
: in out Set
) is
574 HT_Ops
.Finalize
(Container
.HT
);
577 procedure Finalize
(Control
: in out Reference_Control_Type
) is
579 if Control
.Container
/= null then
581 HT
: Hash_Table_Type
renames Control
.Container
.all.HT
;
582 B
: Natural renames HT
.Busy
;
583 L
: Natural renames HT
.Lock
;
589 Control
.Container
:= null;
599 Item
: Element_Type
) return Cursor
601 Node
: constant Node_Access
:= Element_Keys
.Find
(Container
.HT
, Item
);
608 return Cursor
'(Container'Unrestricted_Access, Node);
615 function Find_Equal_Key
616 (R_HT : Hash_Table_Type;
617 L_Node : Node_Access) return Boolean
619 R_Index : constant Hash_Type :=
620 Element_Keys.Index (R_HT, L_Node.Element);
622 R_Node : Node_Access := R_HT.Buckets (R_Index);
626 if R_Node = null then
630 if L_Node.Element = R_Node.Element then
634 R_Node := Next (R_Node);
638 -------------------------
639 -- Find_Equivalent_Key --
640 -------------------------
642 function Find_Equivalent_Key
643 (R_HT : Hash_Table_Type;
644 L_Node : Node_Access) return Boolean
646 R_Index : constant Hash_Type :=
647 Element_Keys.Index (R_HT, L_Node.Element);
649 R_Node : Node_Access := R_HT.Buckets (R_Index);
653 if R_Node = null then
657 if Equivalent_Elements (L_Node.Element, R_Node.Element) then
661 R_Node := Next (R_Node);
663 end Find_Equivalent_Key;
669 function First (Container : Set) return Cursor is
670 Node : constant Node_Access := HT_Ops.First (Container.HT);
677 return Cursor'(Container
'Unrestricted_Access, Node
);
680 function First
(Object
: Iterator
) return Cursor
is
682 return Object
.Container
.First
;
689 procedure Free
(X
: in out Node_Access
) is
690 procedure Deallocate
is
691 new Ada
.Unchecked_Deallocation
(Node_Type
, Node_Access
);
695 X
.Next
:= X
; -- detect mischief (in Vet)
704 function Has_Element
(Position
: Cursor
) return Boolean is
706 pragma Assert
(Vet
(Position
), "bad cursor in Has_Element");
707 return Position
.Node
/= null;
714 function Hash_Node
(Node
: Node_Access
) return Hash_Type
is
716 return Hash
(Node
.Element
);
724 (Container
: in out Set
;
725 New_Item
: Element_Type
)
731 Insert
(Container
, New_Item
, Position
, Inserted
);
734 if Container
.HT
.Lock
> 0 then
735 raise Program_Error
with
736 "attempt to tamper with elements (set is locked)";
739 Position
.Node
.Element
:= New_Item
;
748 (Container
: in out Set
;
749 New_Item
: Element_Type
;
750 Position
: out Cursor
;
751 Inserted
: out Boolean)
754 Insert
(Container
.HT
, New_Item
, Position
.Node
, Inserted
);
755 Position
.Container
:= Container
'Unchecked_Access;
759 (Container
: in out Set
;
760 New_Item
: Element_Type
)
763 pragma Unreferenced
(Position
);
768 Insert
(Container
, New_Item
, Position
, Inserted
);
771 raise Constraint_Error
with
772 "attempt to insert element already in set";
777 (HT
: in out Hash_Table_Type
;
778 New_Item
: Element_Type
;
779 Node
: out Node_Access
;
780 Inserted
: out Boolean)
782 function New_Node
(Next
: Node_Access
) return Node_Access
;
783 pragma Inline
(New_Node
);
785 procedure Local_Insert
is
786 new Element_Keys
.Generic_Conditional_Insert
(New_Node
);
792 function New_Node
(Next
: Node_Access
) return Node_Access
is
794 return new Node_Type
'(New_Item, Next);
797 -- Start of processing for Insert
800 if HT_Ops.Capacity (HT) = 0 then
801 HT_Ops.Reserve_Capacity (HT, 1);
804 Local_Insert (HT, New_Item, Node, Inserted);
807 and then HT.Length > HT_Ops.Capacity (HT)
809 HT_Ops.Reserve_Capacity (HT, HT.Length);
817 procedure Intersection
818 (Target : in out Set;
821 Tgt_Node : Node_Access;
824 if Target'Address = Source'Address then
828 if Source.HT.Length = 0 then
833 if Target.HT.Busy > 0 then
834 raise Program_Error with
835 "attempt to tamper with cursors (set is busy)";
838 Tgt_Node := HT_Ops.First (Target.HT);
839 while Tgt_Node /= null loop
840 if Is_In (Source.HT, Tgt_Node) then
841 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
845 X : Node_Access := Tgt_Node;
847 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
848 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
855 function Intersection (Left, Right : Set) return Set is
856 Buckets : HT_Types.Buckets_Access;
860 if Left'Address = Right'Address then
864 Length := Count_Type'Min (Left.Length, Right.Length);
871 Size : constant Hash_Type := Prime_Numbers.To_Prime (Length);
873 Buckets := HT_Ops.New_Buckets (Length => Size);
878 Iterate_Left : declare
879 procedure Process (L_Node : Node_Access);
882 new HT_Ops.Generic_Iteration (Process);
888 procedure Process (L_Node : Node_Access) is
890 if Is_In (Right.HT, L_Node) then
892 J : constant Hash_Type :=
893 Hash (L_Node.Element) mod Buckets'Length;
895 Bucket : Node_Access renames Buckets (J);
898 Bucket := new Node_Type'(L_Node
.Element
, Bucket
);
901 Length
:= Length
+ 1;
905 -- Start of processing for Iterate_Left
911 HT_Ops
.Free_Hash_Table
(Buckets
);
915 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
922 function Is_Empty
(Container
: Set
) return Boolean is
924 return Container
.HT
.Length
= 0;
931 function Is_In
(HT
: Hash_Table_Type
; Key
: Node_Access
) return Boolean is
933 return Element_Keys
.Find
(HT
, Key
.Element
) /= null;
940 function Is_Subset
(Subset
: Set
; Of_Set
: Set
) return Boolean is
941 Subset_Node
: Node_Access
;
944 if Subset
'Address = Of_Set
'Address then
948 if Subset
.Length
> Of_Set
.Length
then
952 Subset_Node
:= HT_Ops
.First
(Subset
.HT
);
953 while Subset_Node
/= null loop
954 if not Is_In
(Of_Set
.HT
, Subset_Node
) then
957 Subset_Node
:= HT_Ops
.Next
(Subset
.HT
, Subset_Node
);
969 Process
: not null access procedure (Position
: Cursor
))
971 procedure Process_Node
(Node
: Node_Access
);
972 pragma Inline
(Process_Node
);
975 new HT_Ops
.Generic_Iteration
(Process_Node
);
981 procedure Process_Node
(Node
: Node_Access
) is
983 Process
(Cursor
'(Container'Unrestricted_Access, Node));
986 B : Natural renames Container'Unrestricted_Access.HT.Busy;
988 -- Start of processing for Iterate
994 Iterate (Container.HT);
1005 (Container : Set) return Set_Iterator_Interfaces.Forward_Iterator'Class
1008 return Iterator'(Container
=> Container
'Unrestricted_Access);
1015 function Length
(Container
: Set
) return Count_Type
is
1017 return Container
.HT
.Length
;
1024 procedure Move
(Target
: in out Set
; Source
: in out Set
) is
1026 HT_Ops
.Move
(Target
=> Target
.HT
, Source
=> Source
.HT
);
1033 function Next
(Node
: Node_Access
) return Node_Access
is
1038 function Next
(Position
: Cursor
) return Cursor
is
1040 if Position
.Node
= null then
1044 pragma Assert
(Vet
(Position
), "bad cursor in Next");
1047 HT
: Hash_Table_Type
renames Position
.Container
.HT
;
1048 Node
: constant Node_Access
:= HT_Ops
.Next
(HT
, Position
.Node
);
1055 return Cursor
'(Position.Container, Node);
1059 procedure Next (Position : in out Cursor) is
1061 Position := Next (Position);
1066 Position : Cursor) return Cursor
1069 if Position.Container = null then
1073 if Position.Container /= Object.Container then
1074 raise Program_Error with
1075 "Position cursor of Next designates wrong set";
1078 return Next (Position);
1085 function Overlap (Left, Right : Set) return Boolean is
1086 Left_Node : Node_Access;
1089 if Right.Length = 0 then
1093 if Left'Address = Right'Address then
1097 Left_Node := HT_Ops.First (Left.HT);
1098 while Left_Node /= null loop
1099 if Is_In (Right.HT, Left_Node) then
1102 Left_Node := HT_Ops.Next (Left.HT, Left_Node);
1112 procedure Query_Element
1114 Process : not null access procedure (Element : Element_Type))
1117 if Position.Node = null then
1118 raise Constraint_Error with
1119 "Position cursor of Query_Element equals No_Element";
1122 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1125 HT : Hash_Table_Type renames Position.Container.HT;
1127 B : Natural renames HT.Busy;
1128 L : Natural renames HT.Lock;
1135 Process (Position.Node.Element);
1153 (Stream : not null access Root_Stream_Type'Class;
1154 Container : out Set)
1157 Read_Nodes (Stream, Container.HT);
1161 (Stream : not null access Root_Stream_Type'Class;
1165 raise Program_Error with "attempt to stream set cursor";
1169 (Stream : not null access Root_Stream_Type'Class;
1170 Item : out Constant_Reference_Type)
1173 raise Program_Error with "attempt to stream reference";
1180 function Read_Node (Stream : not null access Root_Stream_Type'Class)
1183 Node : Node_Access := new Node_Type;
1186 Element_Type'Read (Stream, Node.Element);
1199 (Container : in out Set;
1200 New_Item : Element_Type)
1202 Node : constant Node_Access :=
1203 Element_Keys.Find (Container.HT, New_Item);
1207 raise Constraint_Error with
1208 "attempt to replace element not in set";
1211 if Container.HT.Lock > 0 then
1212 raise Program_Error with
1213 "attempt to tamper with elements (set is locked)";
1216 Node.Element := New_Item;
1219 procedure Replace_Element
1220 (Container : in out Set;
1222 New_Item : Element_Type)
1225 if Position.Node = null then
1226 raise Constraint_Error with
1227 "Position cursor equals No_Element";
1230 if Position.Container /= Container'Unrestricted_Access then
1231 raise Program_Error with
1232 "Position cursor designates wrong set";
1235 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1237 Replace_Element (Container.HT, Position.Node, New_Item);
1238 end Replace_Element;
1240 ----------------------
1241 -- Reserve_Capacity --
1242 ----------------------
1244 procedure Reserve_Capacity
1245 (Container : in out Set;
1246 Capacity : Count_Type)
1249 HT_Ops.Reserve_Capacity (Container.HT, Capacity);
1250 end Reserve_Capacity;
1256 procedure Set_Next (Node : Node_Access; Next : Node_Access) is
1261 --------------------------
1262 -- Symmetric_Difference --
1263 --------------------------
1265 procedure Symmetric_Difference
1266 (Target : in out Set;
1270 if Target'Address = Source'Address then
1275 if Target.HT.Busy > 0 then
1276 raise Program_Error with
1277 "attempt to tamper with cursors (set is busy)";
1281 N : constant Count_Type := Target.Length + Source.Length;
1283 if N > HT_Ops.Capacity (Target.HT) then
1284 HT_Ops.Reserve_Capacity (Target.HT, N);
1288 if Target.Length = 0 then
1289 Iterate_Source_When_Empty_Target : declare
1290 procedure Process (Src_Node : Node_Access);
1292 procedure Iterate is
1293 new HT_Ops.Generic_Iteration (Process);
1299 procedure Process (Src_Node : Node_Access) is
1300 E : Element_Type renames Src_Node.Element;
1301 B : Buckets_Type renames Target.HT.Buckets.all;
1302 J : constant Hash_Type := Hash (E) mod B'Length;
1303 N : Count_Type renames Target.HT.Length;
1306 B (J) := new Node_Type'(E
, B
(J
));
1310 -- Start of processing for Iterate_Source_When_Empty_Target
1313 Iterate
(Source
.HT
);
1314 end Iterate_Source_When_Empty_Target
;
1317 Iterate_Source
: declare
1318 procedure Process
(Src_Node
: Node_Access
);
1320 procedure Iterate
is
1321 new HT_Ops
.Generic_Iteration
(Process
);
1327 procedure Process
(Src_Node
: Node_Access
) is
1328 E
: Element_Type
renames Src_Node
.Element
;
1329 B
: Buckets_Type
renames Target
.HT
.Buckets
.all;
1330 J
: constant Hash_Type
:= Hash
(E
) mod B
'Length;
1331 N
: Count_Type
renames Target
.HT
.Length
;
1334 if B
(J
) = null then
1335 B
(J
) := new Node_Type
'(E, null);
1338 elsif Equivalent_Elements (E, B (J).Element) then
1340 X : Node_Access := B (J);
1342 B (J) := B (J).Next;
1349 Prev : Node_Access := B (J);
1350 Curr : Node_Access := Prev.Next;
1353 while Curr /= null loop
1354 if Equivalent_Elements (E, Curr.Element) then
1355 Prev.Next := Curr.Next;
1365 B (J) := new Node_Type'(E
, B
(J
));
1371 -- Start of processing for Iterate_Source
1374 Iterate
(Source
.HT
);
1377 end Symmetric_Difference
;
1379 function Symmetric_Difference
(Left
, Right
: Set
) return Set
is
1380 Buckets
: HT_Types
.Buckets_Access
;
1381 Length
: Count_Type
;
1384 if Left
'Address = Right
'Address then
1388 if Right
.Length
= 0 then
1392 if Left
.Length
= 0 then
1397 Size
: constant Hash_Type
:=
1398 Prime_Numbers
.To_Prime
(Left
.Length
+ Right
.Length
);
1400 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
1405 Iterate_Left
: declare
1406 procedure Process
(L_Node
: Node_Access
);
1408 procedure Iterate
is
1409 new HT_Ops
.Generic_Iteration
(Process
);
1415 procedure Process
(L_Node
: Node_Access
) is
1417 if not Is_In
(Right
.HT
, L_Node
) then
1419 E
: Element_Type
renames L_Node
.Element
;
1420 J
: constant Hash_Type
:= Hash
(E
) mod Buckets
'Length;
1423 Buckets
(J
) := new Node_Type
'(E, Buckets (J));
1424 Length := Length + 1;
1429 -- Start of processing for Iterate_Left
1435 HT_Ops.Free_Hash_Table (Buckets);
1439 Iterate_Right : declare
1440 procedure Process (R_Node : Node_Access);
1442 procedure Iterate is
1443 new HT_Ops.Generic_Iteration (Process);
1449 procedure Process (R_Node : Node_Access) is
1451 if not Is_In (Left.HT, R_Node) then
1453 E : Element_Type renames R_Node.Element;
1454 J : constant Hash_Type := Hash (E) mod Buckets'Length;
1457 Buckets (J) := new Node_Type'(E
, Buckets
(J
));
1458 Length
:= Length
+ 1;
1463 -- Start of processing for Iterate_Right
1469 HT_Ops
.Free_Hash_Table
(Buckets
);
1473 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
1474 end Symmetric_Difference
;
1480 function To_Set
(New_Item
: Element_Type
) return Set
is
1481 HT
: Hash_Table_Type
;
1485 pragma Unreferenced
(Node
, Inserted
);
1488 Insert
(HT
, New_Item
, Node
, Inserted
);
1489 return Set
'(Controlled with HT);
1497 (Target : in out Set;
1500 procedure Process (Src_Node : Node_Access);
1502 procedure Iterate is
1503 new HT_Ops.Generic_Iteration (Process);
1509 procedure Process (Src_Node : Node_Access) is
1510 function New_Node (Next : Node_Access) return Node_Access;
1511 pragma Inline (New_Node);
1514 new Element_Keys.Generic_Conditional_Insert (New_Node);
1520 function New_Node (Next : Node_Access) return Node_Access is
1521 Node : constant Node_Access :=
1522 new Node_Type'(Src_Node
.Element
, Next
);
1527 Tgt_Node
: Node_Access
;
1529 pragma Unreferenced
(Tgt_Node
, Success
);
1531 -- Start of processing for Process
1534 Insert
(Target
.HT
, Src_Node
.Element
, Tgt_Node
, Success
);
1537 -- Start of processing for Union
1540 if Target
'Address = Source
'Address then
1544 if Target
.HT
.Busy
> 0 then
1545 raise Program_Error
with
1546 "attempt to tamper with cursors (set is busy)";
1550 N
: constant Count_Type
:= Target
.Length
+ Source
.Length
;
1552 if N
> HT_Ops
.Capacity
(Target
.HT
) then
1553 HT_Ops
.Reserve_Capacity
(Target
.HT
, N
);
1557 Iterate
(Source
.HT
);
1560 function Union
(Left
, Right
: Set
) return Set
is
1561 Buckets
: HT_Types
.Buckets_Access
;
1562 Length
: Count_Type
;
1565 if Left
'Address = Right
'Address then
1569 if Right
.Length
= 0 then
1573 if Left
.Length
= 0 then
1578 Size
: constant Hash_Type
:=
1579 Prime_Numbers
.To_Prime
(Left
.Length
+ Right
.Length
);
1581 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
1584 Iterate_Left
: declare
1585 procedure Process
(L_Node
: Node_Access
);
1587 procedure Iterate
is
1588 new HT_Ops
.Generic_Iteration
(Process
);
1594 procedure Process
(L_Node
: Node_Access
) is
1595 J
: constant Hash_Type
:=
1596 Hash
(L_Node
.Element
) mod Buckets
'Length;
1599 Buckets
(J
) := new Node_Type
'(L_Node.Element, Buckets (J));
1602 -- Start of processing for Iterate_Left
1608 HT_Ops.Free_Hash_Table (Buckets);
1612 Length := Left.Length;
1614 Iterate_Right : declare
1615 procedure Process (Src_Node : Node_Access);
1617 procedure Iterate is
1618 new HT_Ops.Generic_Iteration (Process);
1624 procedure Process (Src_Node : Node_Access) is
1625 J : constant Hash_Type :=
1626 Hash (Src_Node.Element) mod Buckets'Length;
1628 Tgt_Node : Node_Access := Buckets (J);
1631 while Tgt_Node /= null loop
1632 if Equivalent_Elements (Src_Node.Element, Tgt_Node.Element) then
1636 Tgt_Node := Next (Tgt_Node);
1639 Buckets (J) := new Node_Type'(Src_Node
.Element
, Buckets
(J
));
1640 Length
:= Length
+ 1;
1643 -- Start of processing for Iterate_Right
1649 HT_Ops
.Free_Hash_Table
(Buckets
);
1653 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
1660 function Vet
(Position
: Cursor
) return Boolean is
1662 if Position
.Node
= null then
1663 return Position
.Container
= null;
1666 if Position
.Container
= null then
1670 if Position
.Node
.Next
= Position
.Node
then
1675 HT
: Hash_Table_Type
renames Position
.Container
.HT
;
1679 if HT
.Length
= 0 then
1683 if HT
.Buckets
= null
1684 or else HT
.Buckets
'Length = 0
1689 X
:= HT
.Buckets
(Element_Keys
.Index
(HT
, Position
.Node
.Element
));
1691 for J
in 1 .. HT
.Length
loop
1692 if X
= Position
.Node
then
1700 if X
= X
.Next
then -- to prevent unnecessary looping
1716 (Stream
: not null access Root_Stream_Type
'Class;
1720 Write_Nodes
(Stream
, Container
.HT
);
1724 (Stream
: not null access Root_Stream_Type
'Class;
1728 raise Program_Error
with "attempt to stream set cursor";
1732 (Stream
: not null access Root_Stream_Type
'Class;
1733 Item
: Constant_Reference_Type
)
1736 raise Program_Error
with "attempt to stream reference";
1743 procedure Write_Node
1744 (Stream
: not null access Root_Stream_Type
'Class;
1748 Element_Type
'Write (Stream
, Node
.Element
);
1751 package body Generic_Keys
is
1753 -----------------------
1754 -- Local Subprograms --
1755 -----------------------
1757 function Equivalent_Key_Node
1759 Node
: Node_Access
) return Boolean;
1760 pragma Inline
(Equivalent_Key_Node
);
1762 --------------------------
1763 -- Local Instantiations --
1764 --------------------------
1767 new Hash_Tables
.Generic_Keys
1768 (HT_Types
=> HT_Types
,
1770 Set_Next
=> Set_Next
,
1771 Key_Type
=> Key_Type
,
1773 Equivalent_Keys
=> Equivalent_Key_Node
);
1775 ------------------------
1776 -- Constant_Reference --
1777 ------------------------
1779 function Constant_Reference
1780 (Container
: aliased Set
;
1781 Key
: Key_Type
) return Constant_Reference_Type
1783 Node
: constant Node_Access
:= Key_Keys
.Find
(Container
.HT
, Key
);
1787 raise Constraint_Error
with "Key not in set";
1791 HT
: Hash_Table_Type
renames Container
'Unrestricted_Access.all.HT
;
1792 B
: Natural renames HT
.Busy
;
1793 L
: Natural renames HT
.Lock
;
1795 return R
: constant Constant_Reference_Type
:=
1796 (Element
=> Node
.Element
'Access,
1797 Control
=> (Controlled
with Container
'Unrestricted_Access))
1803 end Constant_Reference
;
1811 Key
: Key_Type
) return Boolean
1814 return Find
(Container
, Key
) /= No_Element
;
1822 (Container
: in out Set
;
1828 Key_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Key
, X
);
1831 raise Constraint_Error
with "attempt to delete key not in set";
1843 Key
: Key_Type
) return Element_Type
1845 Node
: constant Node_Access
:= Key_Keys
.Find
(Container
.HT
, Key
);
1849 raise Constraint_Error
with "key not in map"; -- ??? "set"
1852 return Node
.Element
;
1855 -------------------------
1856 -- Equivalent_Key_Node --
1857 -------------------------
1859 function Equivalent_Key_Node
1861 Node
: Node_Access
) return Boolean
1864 return Equivalent_Keys
(Key
, Generic_Keys
.Key
(Node
.Element
));
1865 end Equivalent_Key_Node
;
1872 (Container
: in out Set
;
1877 Key_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Key
, X
);
1887 Key
: Key_Type
) return Cursor
1889 Node
: constant Node_Access
:= Key_Keys
.Find
(Container
.HT
, Key
);
1896 return Cursor
'(Container'Unrestricted_Access, Node);
1903 function Key (Position : Cursor) return Key_Type is
1905 if Position.Node = null then
1906 raise Constraint_Error with
1907 "Position cursor equals No_Element";
1910 pragma Assert (Vet (Position), "bad cursor in function Key");
1912 return Key (Position.Node.Element);
1920 (Stream : not null access Root_Stream_Type'Class;
1921 Item : out Reference_Type)
1924 raise Program_Error with "attempt to stream reference";
1927 ------------------------------
1928 -- Reference_Preserving_Key --
1929 ------------------------------
1931 function Reference_Preserving_Key
1932 (Container : aliased in out Set;
1933 Position : Cursor) return Reference_Type
1936 if Position.Container = null then
1937 raise Constraint_Error with "Position cursor has no element";
1940 if Position.Container /= Container'Unrestricted_Access then
1941 raise Program_Error with
1942 "Position cursor designates wrong container";
1947 "bad cursor in function Reference_Preserving_Key");
1949 -- Some form of finalization will be required in order to actually
1950 -- check that the key-part of the element designated by Position has
1953 return (Element => Position.Node.Element'Access);
1954 end Reference_Preserving_Key;
1956 function Reference_Preserving_Key
1957 (Container : aliased in out Set;
1958 Key : Key_Type) return Reference_Type
1960 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
1964 raise Constraint_Error with "Key not in set";
1967 -- Some form of finalization will be required in order to actually
1968 -- check that the key-part of the element designated by Key has not
1971 return (Element => Node.Element'Access);
1972 end Reference_Preserving_Key;
1979 (Container : in out Set;
1981 New_Item : Element_Type)
1983 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
1987 raise Constraint_Error with
1988 "attempt to replace key not in set";
1991 Replace_Element (Container.HT, Node, New_Item);
1994 -----------------------------------
1995 -- Update_Element_Preserving_Key --
1996 -----------------------------------
1998 procedure Update_Element_Preserving_Key
1999 (Container : in out Set;
2001 Process : not null access
2002 procedure (Element : in out Element_Type))
2004 HT : Hash_Table_Type renames Container.HT;
2008 if Position.Node = null then
2009 raise Constraint_Error with
2010 "Position cursor equals No_Element";
2013 if Position.Container /= Container'Unrestricted_Access then
2014 raise Program_Error with
2015 "Position cursor designates wrong set";
2018 if HT.Buckets = null
2019 or else HT.Buckets'Length = 0
2020 or else HT.Length = 0
2021 or else Position.Node.Next = Position.Node
2023 raise Program_Error with "Position cursor is bad (set is empty)";
2028 "bad cursor in Update_Element_Preserving_Key");
2030 Indx := HT_Ops.Index (HT, Position.Node);
2033 E : Element_Type renames Position.Node.Element;
2034 K : constant Key_Type := Key (E);
2036 B : Natural renames HT.Busy;
2037 L : Natural renames HT.Lock;
2055 if Equivalent_Keys (K, Key (E)) then
2056 pragma Assert (Hash (K) = Hash (E));
2061 if HT.Buckets (Indx) = Position.Node then
2062 HT.Buckets (Indx) := Position.Node.Next;
2066 Prev : Node_Access := HT.Buckets (Indx);
2069 while Prev.Next /= Position.Node loop
2073 raise Program_Error with
2074 "Position cursor is bad (node not found)";
2078 Prev.Next := Position.Node.Next;
2082 HT.Length := HT.Length - 1;
2085 X : Node_Access := Position.Node;
2091 raise Program_Error with "key was modified";
2092 end Update_Element_Preserving_Key;
2099 (Stream : not null access Root_Stream_Type'Class;
2100 Item : Reference_Type)
2103 raise Program_Error with "attempt to stream reference";
2108 end Ada.Containers.Hashed_Sets;