1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- ADA.CONTAINERS.INDEFINITE_HASHED_SETS --
9 -- Copyright (C) 2004-2011, 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 type Iterator is new Limited_Controlled and
45 Set_Iterator_Interfaces.Forward_Iterator with
47 Container : Set_Access;
50 overriding procedure Finalize (Object : in out Iterator);
52 overriding function First (Object : Iterator) return Cursor;
54 overriding function Next
56 Position : Cursor) return Cursor;
58 -----------------------
59 -- Local Subprograms --
60 -----------------------
62 procedure Assign (Node : Node_Access; Item : Element_Type);
63 pragma Inline (Assign);
65 function Copy_Node (Source : Node_Access) return Node_Access;
66 pragma Inline (Copy_Node);
68 function Equivalent_Keys
70 Node : Node_Access) return Boolean;
71 pragma Inline (Equivalent_Keys);
73 function Find_Equal_Key
74 (R_HT : Hash_Table_Type;
75 L_Node : Node_Access) return Boolean;
77 function Find_Equivalent_Key
78 (R_HT : Hash_Table_Type;
79 L_Node : Node_Access) return Boolean;
81 procedure Free (X : in out Node_Access);
83 function Hash_Node (Node : Node_Access) return Hash_Type;
84 pragma Inline (Hash_Node);
87 (HT : in out Hash_Table_Type;
88 New_Item : Element_Type;
89 Node : out Node_Access;
90 Inserted : out Boolean);
92 function Is_In (HT : Hash_Table_Type; Key : Node_Access) return Boolean;
93 pragma Inline (Is_In);
95 function Next (Node : Node_Access) return Node_Access;
98 function Read_Node (Stream : not null access Root_Stream_Type'Class)
100 pragma Inline (Read_Node);
102 procedure Set_Next (Node : Node_Access; Next : Node_Access);
103 pragma Inline (Set_Next);
105 function Vet (Position : Cursor) return Boolean;
108 (Stream : not null access Root_Stream_Type'Class;
110 pragma Inline (Write_Node);
112 --------------------------
113 -- Local Instantiations --
114 --------------------------
116 procedure Free_Element is
117 new Ada.Unchecked_Deallocation (Element_Type, Element_Access);
119 package HT_Ops is new Hash_Tables.Generic_Operations
120 (HT_Types => HT_Types,
121 Hash_Node => Hash_Node,
123 Set_Next => Set_Next,
124 Copy_Node => Copy_Node,
127 package Element_Keys is new Hash_Tables.Generic_Keys
128 (HT_Types => HT_Types,
130 Set_Next => Set_Next,
131 Key_Type => Element_Type,
133 Equivalent_Keys => Equivalent_Keys);
136 new HT_Ops.Generic_Equal (Find_Equal_Key);
138 function Is_Equivalent is
139 new HT_Ops.Generic_Equal (Find_Equivalent_Key);
141 procedure Read_Nodes is
142 new HT_Ops.Generic_Read (Read_Node);
144 procedure Replace_Element is
145 new Element_Keys.Generic_Replace_Element (Hash_Node, Assign);
147 procedure Write_Nodes is
148 new HT_Ops.Generic_Write (Write_Node);
154 function "=" (Left, Right : Set) return Boolean is
156 return Is_Equal (Left.HT, Right.HT);
163 procedure Adjust (Container : in out Set) is
165 HT_Ops.Adjust (Container.HT);
172 procedure Assign (Node : Node_Access; Item : Element_Type) is
173 X : Element_Access := Node.Element;
175 Node.Element := new Element_Type'(Item);
179 procedure Assign (Target : in out Set; Source : Set) is
181 if Target'Address = Source'Address then
186 Target.Union (Source);
193 function Capacity (Container : Set) return Count_Type is
195 return HT_Ops.Capacity (Container.HT);
202 procedure Clear (Container : in out Set) is
204 HT_Ops.Clear (Container.HT);
207 ------------------------
208 -- Constant_Reference --
209 ------------------------
211 function Constant_Reference
212 (Container : aliased Set;
213 Position : Cursor) return Constant_Reference_Type
216 if Position.Container = null then
217 raise Constraint_Error with "Position cursor has no element";
220 if Position.Container /= Container'Unrestricted_Access then
221 raise Program_Error with
222 "Position cursor designates wrong container";
225 if Position.Node.Element = null then
226 raise Program_Error with "Node has no element";
229 pragma Assert (Vet (Position), "bad cursor in Constant_Reference");
231 return (Element => Position.Node.Element.all'Access);
232 end Constant_Reference;
238 function Contains (Container : Set; Item : Element_Type) return Boolean is
240 return Find (Container, Item) /= No_Element;
249 Capacity : Count_Type := 0) return Set
257 elsif Capacity >= Source.Length then
262 with "Requested capacity is less than Source length";
265 return Target : Set do
266 Target.Reserve_Capacity (C);
267 Target.Assign (Source);
275 function Copy_Node (Source : Node_Access) return Node_Access is
276 E : Element_Access := new Element_Type'(Source.Element.all);
278 return new Node_Type'(Element => E, Next => null);
290 (Container : in out Set;
296 Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
299 raise Constraint_Error with "attempt to delete element not in set";
306 (Container : in out Set;
307 Position : in out Cursor)
310 if Position.Node = null then
311 raise Constraint_Error with "Position cursor equals No_Element";
314 if Position.Node.Element = null then
315 raise Program_Error with "Position cursor is bad";
318 if Position.Container /= Container'Unrestricted_Access then
319 raise Program_Error with "Position cursor designates wrong set";
322 if Container.HT.Busy > 0 then
323 raise Program_Error with
324 "attempt to tamper with cursors (set is busy)";
327 pragma Assert (Vet (Position), "Position cursor is bad");
329 HT_Ops.Delete_Node_Sans_Free (Container.HT, Position.Node);
331 Free (Position.Node);
332 Position.Container := null;
340 (Target : in out Set;
343 Tgt_Node : Node_Access;
346 if Target'Address = Source'Address then
351 if Source.HT.Length = 0 then
355 if Target.HT.Busy > 0 then
356 raise Program_Error with
357 "attempt to tamper with cursors (set is busy)";
360 if Source.HT.Length < Target.HT.Length then
362 Src_Node : Node_Access;
365 Src_Node := HT_Ops.First (Source.HT);
366 while Src_Node /= null loop
367 Tgt_Node := Element_Keys.Find (Target.HT, Src_Node.Element.all);
369 if Tgt_Node /= null then
370 HT_Ops.Delete_Node_Sans_Free (Target.HT, Tgt_Node);
374 Src_Node := HT_Ops.Next (Source.HT, Src_Node);
379 Tgt_Node := HT_Ops.First (Target.HT);
380 while Tgt_Node /= null loop
381 if Is_In (Source.HT, Tgt_Node) then
383 X : Node_Access := Tgt_Node;
385 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
386 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
391 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
397 function Difference (Left, Right : Set) return Set is
398 Buckets : HT_Types.Buckets_Access;
402 if Left'Address = Right'Address then
406 if Left.Length = 0 then
410 if Right.Length = 0 then
415 Size : constant Hash_Type := Prime_Numbers.To_Prime (Left.Length);
417 Buckets := HT_Ops.New_Buckets (Length => Size);
422 Iterate_Left : declare
423 procedure Process (L_Node : Node_Access);
426 new HT_Ops.Generic_Iteration (Process);
432 procedure Process (L_Node : Node_Access) is
434 if not Is_In (Right.HT, L_Node) then
436 Src : Element_Type renames L_Node.Element.all;
437 Indx : constant Hash_Type := Hash (Src) mod Buckets'Length;
438 Bucket : Node_Access renames Buckets (Indx);
439 Tgt : Element_Access := new Element_Type'(Src);
441 Bucket := new Node_Type'(Tgt, Bucket);
448 Length := Length + 1;
452 -- Start of processing for Iterate_Left
458 HT_Ops.Free_Hash_Table (Buckets);
462 return (Controlled with HT => (Buckets, Length, 0, 0));
469 function Element (Position : Cursor) return Element_Type is
471 if Position.Node = null then
472 raise Constraint_Error with "Position cursor of equals No_Element";
475 if Position.Node.Element = null then -- handle dangling reference
476 raise Program_Error with "Position cursor is bad";
479 pragma Assert (Vet (Position), "bad cursor in function Element");
481 return Position.Node.Element.all;
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) return Boolean is
499 if Left.Node = null then
500 raise Constraint_Error with
501 "Left cursor of Equivalent_Elements equals No_Element";
504 if Right.Node = null then
505 raise Constraint_Error with
506 "Right cursor of Equivalent_Elements equals No_Element";
509 if Left.Node.Element = null then
510 raise Program_Error with
511 "Left cursor of Equivalent_Elements is bad";
514 if Right.Node.Element = null then
515 raise Program_Error with
516 "Right cursor of Equivalent_Elements is bad";
519 pragma Assert (Vet (Left), "bad Left cursor in Equivalent_Elements");
520 pragma Assert (Vet (Right), "bad Right cursor in Equivalent_Elements");
522 return Equivalent_Elements
523 (Left.Node.Element.all,
524 Right.Node.Element.all);
525 end Equivalent_Elements;
527 function Equivalent_Elements
529 Right : Element_Type) return Boolean
532 if Left.Node = null then
533 raise Constraint_Error with
534 "Left cursor of Equivalent_Elements equals No_Element";
537 if Left.Node.Element = null then
538 raise Program_Error with
539 "Left cursor of Equivalent_Elements is bad";
542 pragma Assert (Vet (Left), "bad Left cursor in Equivalent_Elements");
544 return Equivalent_Elements (Left.Node.Element.all, Right);
545 end Equivalent_Elements;
547 function Equivalent_Elements
548 (Left : Element_Type;
549 Right : Cursor) return Boolean
552 if Right.Node = null then
553 raise Constraint_Error with
554 "Right cursor of Equivalent_Elements equals No_Element";
557 if Right.Node.Element = null then
558 raise Program_Error with
559 "Right cursor of Equivalent_Elements is bad";
562 pragma Assert (Vet (Right), "bad Right cursor in Equivalent_Elements");
564 return Equivalent_Elements (Left, Right.Node.Element.all);
565 end Equivalent_Elements;
567 ---------------------
568 -- Equivalent_Keys --
569 ---------------------
571 function Equivalent_Keys
573 Node : Node_Access) return Boolean
576 return Equivalent_Elements (Key, Node.Element.all);
584 (Container : in out Set;
589 Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
597 procedure Finalize (Container : in out Set) is
599 HT_Ops.Finalize (Container.HT);
602 procedure Finalize (Object : in out Iterator) is
604 if Object.Container /= null then
606 B : Natural renames Object.Container.all.HT.Busy;
619 Item : Element_Type) return Cursor
621 Node : constant Node_Access := Element_Keys.Find (Container.HT, Item);
623 return (if Node = null then No_Element
624 else Cursor'(Container'Unrestricted_Access, Node));
631 function Find_Equal_Key
632 (R_HT : Hash_Table_Type;
633 L_Node : Node_Access) return Boolean
635 R_Index : constant Hash_Type :=
636 Element_Keys.Index (R_HT, L_Node.Element.all);
638 R_Node : Node_Access := R_HT.Buckets (R_Index);
642 if R_Node = null then
646 if L_Node.Element.all = R_Node.Element.all then
650 R_Node := Next (R_Node);
654 -------------------------
655 -- Find_Equivalent_Key --
656 -------------------------
658 function Find_Equivalent_Key
659 (R_HT : Hash_Table_Type;
660 L_Node : Node_Access) return Boolean
662 R_Index : constant Hash_Type :=
663 Element_Keys.Index (R_HT, L_Node.Element.all);
665 R_Node : Node_Access := R_HT.Buckets (R_Index);
669 if R_Node = null then
673 if Equivalent_Elements (L_Node.Element.all, R_Node.Element.all) then
677 R_Node := Next (R_Node);
679 end Find_Equivalent_Key;
685 function First (Container : Set) return Cursor is
686 Node : constant Node_Access := HT_Ops.First (Container.HT);
688 return (if Node = null then No_Element
689 else Cursor'(Container'Unrestricted_Access, Node));
692 function First (Object : Iterator) return Cursor is
694 return Object.Container.First;
701 procedure Free (X : in out Node_Access) is
702 procedure Deallocate is
703 new Ada.Unchecked_Deallocation (Node_Type, Node_Access);
710 X.Next := X; -- detect mischief (in Vet)
713 Free_Element (X.Element);
728 function Has_Element (Position : Cursor) return Boolean is
730 pragma Assert (Vet (Position), "bad cursor in Has_Element");
731 return Position.Node /= null;
738 function Hash_Node (Node : Node_Access) return Hash_Type is
740 return Hash (Node.Element.all);
748 (Container : in out Set;
749 New_Item : Element_Type)
757 Insert (Container, New_Item, Position, Inserted);
760 if Container.HT.Lock > 0 then
761 raise Program_Error with
762 "attempt to tamper with elements (set is locked)";
765 X := Position.Node.Element;
767 Position.Node.Element := new Element_Type'(New_Item);
778 (Container : in out Set;
779 New_Item : Element_Type;
780 Position : out Cursor;
781 Inserted : out Boolean)
784 Insert (Container.HT, New_Item, Position.Node, Inserted);
785 Position.Container := Container'Unchecked_Access;
789 (Container : in out Set;
790 New_Item : Element_Type)
793 pragma Unreferenced (Position);
798 Insert (Container, New_Item, Position, Inserted);
801 raise Constraint_Error with
802 "attempt to insert element already in set";
807 (HT : in out Hash_Table_Type;
808 New_Item : Element_Type;
809 Node : out Node_Access;
810 Inserted : out Boolean)
812 function New_Node (Next : Node_Access) return Node_Access;
813 pragma Inline (New_Node);
815 procedure Local_Insert is
816 new Element_Keys.Generic_Conditional_Insert (New_Node);
822 function New_Node (Next : Node_Access) return Node_Access is
823 Element : Element_Access := new Element_Type'(New_Item);
825 return new Node_Type'(Element, Next);
828 Free_Element (Element);
832 -- Start of processing for Insert
835 if HT_Ops.Capacity (HT) = 0 then
836 HT_Ops.Reserve_Capacity (HT, 1);
839 Local_Insert (HT, New_Item, Node, Inserted);
842 and then HT.Length > HT_Ops.Capacity (HT)
844 HT_Ops.Reserve_Capacity (HT, HT.Length);
852 procedure Intersection
853 (Target : in out Set;
856 Tgt_Node : Node_Access;
859 if Target'Address = Source'Address then
863 if Source.Length = 0 then
868 if Target.HT.Busy > 0 then
869 raise Program_Error with
870 "attempt to tamper with cursors (set is busy)";
873 Tgt_Node := HT_Ops.First (Target.HT);
874 while Tgt_Node /= null loop
875 if Is_In (Source.HT, Tgt_Node) then
876 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
880 X : Node_Access := Tgt_Node;
882 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
883 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
890 function Intersection (Left, Right : Set) return Set is
891 Buckets : HT_Types.Buckets_Access;
895 if Left'Address = Right'Address then
899 Length := Count_Type'Min (Left.Length, Right.Length);
906 Size : constant Hash_Type := Prime_Numbers.To_Prime (Length);
908 Buckets := HT_Ops.New_Buckets (Length => Size);
913 Iterate_Left : declare
914 procedure Process (L_Node : Node_Access);
917 new HT_Ops.Generic_Iteration (Process);
923 procedure Process (L_Node : Node_Access) is
925 if Is_In (Right.HT, L_Node) then
927 Src : Element_Type renames L_Node.Element.all;
929 Indx : constant Hash_Type := Hash (Src) mod Buckets'Length;
931 Bucket : Node_Access renames Buckets (Indx);
933 Tgt : Element_Access := new Element_Type'(Src);
936 Bucket := new Node_Type'(Tgt, Bucket);
943 Length := Length + 1;
947 -- Start of processing for Iterate_Left
953 HT_Ops.Free_Hash_Table (Buckets);
957 return (Controlled with HT => (Buckets, Length, 0, 0));
964 function Is_Empty (Container : Set) return Boolean is
966 return Container.HT.Length = 0;
973 function Is_In (HT : Hash_Table_Type; Key : Node_Access) return Boolean is
975 return Element_Keys.Find (HT, Key.Element.all) /= null;
984 Of_Set : Set) return Boolean
986 Subset_Node : Node_Access;
989 if Subset'Address = Of_Set'Address then
993 if Subset.Length > Of_Set.Length then
997 Subset_Node := HT_Ops.First (Subset.HT);
998 while Subset_Node /= null loop
999 if not Is_In (Of_Set.HT, Subset_Node) then
1003 Subset_Node := HT_Ops.Next (Subset.HT, Subset_Node);
1015 Process : not null access procedure (Position : Cursor))
1017 procedure Process_Node (Node : Node_Access);
1018 pragma Inline (Process_Node);
1020 procedure Iterate is
1021 new HT_Ops.Generic_Iteration (Process_Node);
1027 procedure Process_Node (Node : Node_Access) is
1029 Process (Cursor'(Container'Unrestricted_Access, Node));
1032 B : Natural renames Container'Unrestricted_Access.all.HT.Busy;
1034 -- Start of processing for Iterate
1040 Iterate (Container.HT);
1050 function Iterate (Container : Set)
1051 return Set_Iterator_Interfaces.Forward_Iterator'Class
1053 B : Natural renames Container'Unrestricted_Access.all.HT.Busy;
1055 return It : constant Iterator :=
1056 Iterator'(Limited_Controlled with
1057 Container => Container'Unrestricted_Access)
1067 function Length (Container : Set) return Count_Type is
1069 return Container.HT.Length;
1076 procedure Move (Target : in out Set; Source : in out Set) is
1078 HT_Ops.Move (Target => Target.HT, Source => Source.HT);
1085 function Next (Node : Node_Access) return Node_Access is
1090 function Next (Position : Cursor) return Cursor is
1092 if Position.Node = null then
1096 if Position.Node.Element = null then
1097 raise Program_Error with "bad cursor in Next";
1100 pragma Assert (Vet (Position), "bad cursor in Next");
1103 HT : Hash_Table_Type renames Position.Container.HT;
1104 Node : constant Node_Access := HT_Ops.Next (HT, Position.Node);
1106 return (if Node = null then No_Element
1107 else Cursor'(Position.Container, Node));
1111 procedure Next (Position : in out Cursor) is
1113 Position := Next (Position);
1118 Position : Cursor) return Cursor
1121 if Position.Container = null then
1125 if Position.Container /= Object.Container then
1126 raise Program_Error with
1127 "Position cursor of Next designates wrong set";
1130 return Next (Position);
1137 function Overlap (Left, Right : Set) return Boolean is
1138 Left_Node : Node_Access;
1141 if Right.Length = 0 then
1145 if Left'Address = Right'Address then
1149 Left_Node := HT_Ops.First (Left.HT);
1150 while Left_Node /= null loop
1151 if Is_In (Right.HT, Left_Node) then
1155 Left_Node := HT_Ops.Next (Left.HT, Left_Node);
1165 procedure Query_Element
1167 Process : not null access procedure (Element : Element_Type))
1170 if Position.Node = null then
1171 raise Constraint_Error with
1172 "Position cursor of Query_Element equals No_Element";
1175 if Position.Node.Element = null then
1176 raise Program_Error with "bad cursor in Query_Element";
1179 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1182 HT : Hash_Table_Type renames
1183 Position.Container'Unrestricted_Access.all.HT;
1185 B : Natural renames HT.Busy;
1186 L : Natural renames HT.Lock;
1193 Process (Position.Node.Element.all);
1211 (Stream : not null access Root_Stream_Type'Class;
1212 Container : out Set)
1215 Read_Nodes (Stream, Container.HT);
1219 (Stream : not null access Root_Stream_Type'Class;
1223 raise Program_Error with "attempt to stream set cursor";
1227 (Stream : not null access Root_Stream_Type'Class;
1228 Item : out Constant_Reference_Type)
1231 raise Program_Error with "attempt to stream reference";
1239 (Stream : not null access Root_Stream_Type'Class) return Node_Access
1241 X : Element_Access := new Element_Type'(Element_Type'Input (Stream));
1243 return new Node_Type'(X, null);
1255 (Container : in out Set;
1256 New_Item : Element_Type)
1258 Node : constant Node_Access :=
1259 Element_Keys.Find (Container.HT, New_Item);
1262 pragma Warnings (Off, X);
1266 raise Constraint_Error with
1267 "attempt to replace element not in set";
1270 if Container.HT.Lock > 0 then
1271 raise Program_Error with
1272 "attempt to tamper with elements (set is locked)";
1277 Node.Element := new Element_Type'(New_Item);
1282 ---------------------
1283 -- Replace_Element --
1284 ---------------------
1286 procedure Replace_Element
1287 (Container : in out Set;
1289 New_Item : Element_Type)
1292 if Position.Node = null then
1293 raise Constraint_Error with "Position cursor equals No_Element";
1296 if Position.Node.Element = null then
1297 raise Program_Error with "bad cursor in Replace_Element";
1300 if Position.Container /= Container'Unrestricted_Access then
1301 raise Program_Error with
1302 "Position cursor designates wrong set";
1305 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1307 Replace_Element (Container.HT, Position.Node, New_Item);
1308 end Replace_Element;
1310 ----------------------
1311 -- Reserve_Capacity --
1312 ----------------------
1314 procedure Reserve_Capacity
1315 (Container : in out Set;
1316 Capacity : Count_Type)
1319 HT_Ops.Reserve_Capacity (Container.HT, Capacity);
1320 end Reserve_Capacity;
1326 procedure Set_Next (Node : Node_Access; Next : Node_Access) is
1331 --------------------------
1332 -- Symmetric_Difference --
1333 --------------------------
1335 procedure Symmetric_Difference
1336 (Target : in out Set;
1340 if Target'Address = Source'Address then
1345 if Target.HT.Busy > 0 then
1346 raise Program_Error with
1347 "attempt to tamper with cursors (set is busy)";
1351 N : constant Count_Type := Target.Length + Source.Length;
1353 if N > HT_Ops.Capacity (Target.HT) then
1354 HT_Ops.Reserve_Capacity (Target.HT, N);
1358 if Target.Length = 0 then
1359 Iterate_Source_When_Empty_Target : declare
1360 procedure Process (Src_Node : Node_Access);
1362 procedure Iterate is new HT_Ops.Generic_Iteration (Process);
1368 procedure Process (Src_Node : Node_Access) is
1369 E : Element_Type renames Src_Node.Element.all;
1370 B : Buckets_Type renames Target.HT.Buckets.all;
1371 J : constant Hash_Type := Hash (E) mod B'Length;
1372 N : Count_Type renames Target.HT.Length;
1376 X : Element_Access := new Element_Type'(E);
1378 B (J) := new Node_Type'(X, B (J));
1388 -- Start of processing for Iterate_Source_When_Empty_Target
1391 Iterate (Source.HT);
1392 end Iterate_Source_When_Empty_Target;
1395 Iterate_Source : declare
1396 procedure Process (Src_Node : Node_Access);
1398 procedure Iterate is
1399 new HT_Ops.Generic_Iteration (Process);
1405 procedure Process (Src_Node : Node_Access) is
1406 E : Element_Type renames Src_Node.Element.all;
1407 B : Buckets_Type renames Target.HT.Buckets.all;
1408 J : constant Hash_Type := Hash (E) mod B'Length;
1409 N : Count_Type renames Target.HT.Length;
1412 if B (J) = null then
1414 X : Element_Access := new Element_Type'(E);
1416 B (J) := new Node_Type'(X, null);
1425 elsif Equivalent_Elements (E, B (J).Element.all) then
1427 X : Node_Access := B (J);
1429 B (J) := B (J).Next;
1436 Prev : Node_Access := B (J);
1437 Curr : Node_Access := Prev.Next;
1440 while Curr /= null loop
1441 if Equivalent_Elements (E, Curr.Element.all) then
1442 Prev.Next := Curr.Next;
1453 X : Element_Access := new Element_Type'(E);
1455 B (J) := new Node_Type'(X, B (J));
1467 -- Start of processing for Iterate_Source
1470 Iterate (Source.HT);
1473 end Symmetric_Difference;
1475 function Symmetric_Difference (Left, Right : Set) return Set is
1476 Buckets : HT_Types.Buckets_Access;
1477 Length : Count_Type;
1480 if Left'Address = Right'Address then
1484 if Right.Length = 0 then
1488 if Left.Length = 0 then
1493 Size : constant Hash_Type :=
1494 Prime_Numbers.To_Prime (Left.Length + Right.Length);
1496 Buckets := HT_Ops.New_Buckets (Length => Size);
1501 Iterate_Left : declare
1502 procedure Process (L_Node : Node_Access);
1504 procedure Iterate is
1505 new HT_Ops.Generic_Iteration (Process);
1511 procedure Process (L_Node : Node_Access) is
1513 if not Is_In (Right.HT, L_Node) then
1515 E : Element_Type renames L_Node.Element.all;
1516 J : constant Hash_Type := Hash (E) mod Buckets'Length;
1520 X : Element_Access := new Element_Type'(E);
1522 Buckets (J) := new Node_Type'(X, Buckets (J));
1529 Length := Length + 1;
1534 -- Start of processing for Iterate_Left
1540 HT_Ops.Free_Hash_Table (Buckets);
1544 Iterate_Right : declare
1545 procedure Process (R_Node : Node_Access);
1547 procedure Iterate is
1548 new HT_Ops.Generic_Iteration (Process);
1554 procedure Process (R_Node : Node_Access) is
1556 if not Is_In (Left.HT, R_Node) then
1558 E : Element_Type renames R_Node.Element.all;
1559 J : constant Hash_Type := Hash (E) mod Buckets'Length;
1563 X : Element_Access := new Element_Type'(E);
1565 Buckets (J) := new Node_Type'(X, Buckets (J));
1572 Length := Length + 1;
1577 -- Start of processing for Iterate_Right
1583 HT_Ops.Free_Hash_Table (Buckets);
1587 return (Controlled with HT => (Buckets, Length, 0, 0));
1588 end Symmetric_Difference;
1594 function To_Set (New_Item : Element_Type) return Set is
1595 HT : Hash_Table_Type;
1598 pragma Unreferenced (Node, Inserted);
1600 Insert (HT, New_Item, Node, Inserted);
1601 return Set'(Controlled with HT);
1609 (Target : in out Set;
1612 procedure Process (Src_Node : Node_Access);
1614 procedure Iterate is
1615 new HT_Ops.Generic_Iteration (Process);
1621 procedure Process (Src_Node : Node_Access) is
1622 Src : Element_Type renames Src_Node.Element.all;
1624 function New_Node (Next : Node_Access) return Node_Access;
1625 pragma Inline (New_Node);
1628 new Element_Keys.Generic_Conditional_Insert (New_Node);
1634 function New_Node (Next : Node_Access) return Node_Access is
1635 Tgt : Element_Access := new Element_Type'(Src);
1637 return new Node_Type'(Tgt, Next);
1644 Tgt_Node : Node_Access;
1646 pragma Unreferenced (Tgt_Node, Success);
1648 -- Start of processing for Process
1651 Insert (Target.HT, Src, Tgt_Node, Success);
1654 -- Start of processing for Union
1657 if Target'Address = Source'Address then
1661 if Target.HT.Busy > 0 then
1662 raise Program_Error with
1663 "attempt to tamper with cursors (set is busy)";
1667 N : constant Count_Type := Target.Length + Source.Length;
1669 if N > HT_Ops.Capacity (Target.HT) then
1670 HT_Ops.Reserve_Capacity (Target.HT, N);
1674 Iterate (Source.HT);
1677 function Union (Left, Right : Set) return Set is
1678 Buckets : HT_Types.Buckets_Access;
1679 Length : Count_Type;
1682 if Left'Address = Right'Address then
1686 if Right.Length = 0 then
1690 if Left.Length = 0 then
1695 Size : constant Hash_Type :=
1696 Prime_Numbers.To_Prime (Left.Length + Right.Length);
1698 Buckets := HT_Ops.New_Buckets (Length => Size);
1701 Iterate_Left : declare
1702 procedure Process (L_Node : Node_Access);
1704 procedure Iterate is
1705 new HT_Ops.Generic_Iteration (Process);
1711 procedure Process (L_Node : Node_Access) is
1712 Src : Element_Type renames L_Node.Element.all;
1713 J : constant Hash_Type := Hash (Src) mod Buckets'Length;
1714 Bucket : Node_Access renames Buckets (J);
1715 Tgt : Element_Access := new Element_Type'(Src);
1717 Bucket := new Node_Type'(Tgt, Bucket);
1724 -- Start of processing for Process
1730 HT_Ops.Free_Hash_Table (Buckets);
1734 Length := Left.Length;
1736 Iterate_Right : declare
1737 procedure Process (Src_Node : Node_Access);
1739 procedure Iterate is
1740 new HT_Ops.Generic_Iteration (Process);
1746 procedure Process (Src_Node : Node_Access) is
1747 Src : Element_Type renames Src_Node.Element.all;
1748 Idx : constant Hash_Type := Hash (Src) mod Buckets'Length;
1750 Tgt_Node : Node_Access := Buckets (Idx);
1753 while Tgt_Node /= null loop
1754 if Equivalent_Elements (Src, Tgt_Node.Element.all) then
1757 Tgt_Node := Next (Tgt_Node);
1761 Tgt : Element_Access := new Element_Type'(Src);
1763 Buckets (Idx) := new Node_Type'(Tgt, Buckets (Idx));
1770 Length := Length + 1;
1773 -- Start of processing for Iterate_Right
1779 HT_Ops.Free_Hash_Table (Buckets);
1783 return (Controlled with HT => (Buckets, Length, 0, 0));
1790 function Vet (Position : Cursor) return Boolean is
1792 if Position.Node = null then
1793 return Position.Container = null;
1796 if Position.Container = null then
1800 if Position.Node.Next = Position.Node then
1804 if Position.Node.Element = null then
1809 HT : Hash_Table_Type renames Position.Container.HT;
1813 if HT.Length = 0 then
1817 if HT.Buckets = null
1818 or else HT.Buckets'Length = 0
1823 X := HT.Buckets (Element_Keys.Index (HT, Position.Node.Element.all));
1825 for J in 1 .. HT.Length loop
1826 if X = Position.Node then
1834 if X = X.Next then -- to prevent unnecessary looping
1850 (Stream : not null access Root_Stream_Type'Class;
1854 Write_Nodes (Stream, Container.HT);
1858 (Stream : not null access Root_Stream_Type'Class;
1862 raise Program_Error with "attempt to stream set cursor";
1866 (Stream : not null access Root_Stream_Type'Class;
1867 Item : Constant_Reference_Type)
1870 raise Program_Error with "attempt to stream reference";
1877 procedure Write_Node
1878 (Stream : not null access Root_Stream_Type'Class;
1882 Element_Type'Output (Stream, Node.Element.all);
1885 package body Generic_Keys is
1887 -----------------------
1888 -- Local Subprograms --
1889 -----------------------
1891 function Equivalent_Key_Node
1893 Node : Node_Access) return Boolean;
1894 pragma Inline (Equivalent_Key_Node);
1896 --------------------------
1897 -- Local Instantiations --
1898 --------------------------
1901 new Hash_Tables.Generic_Keys
1902 (HT_Types => HT_Types,
1904 Set_Next => Set_Next,
1905 Key_Type => Key_Type,
1907 Equivalent_Keys => Equivalent_Key_Node);
1909 ------------------------
1910 -- Constant_Reference --
1911 ------------------------
1913 function Constant_Reference
1914 (Container : aliased Set;
1915 Key : Key_Type) return Constant_Reference_Type
1917 Node : constant Node_Access :=
1918 Key_Keys.Find (Container.HT, Key);
1922 raise Constraint_Error with "Key not in set";
1925 if Node.Element = null then
1926 raise Program_Error with "Node has no element";
1929 return (Element => Node.Element.all'Access);
1930 end Constant_Reference;
1938 Key : Key_Type) return Boolean
1941 return Find (Container, Key) /= No_Element;
1949 (Container : in out Set;
1955 Key_Keys.Delete_Key_Sans_Free (Container.HT, Key, X);
1958 raise Constraint_Error with "key not in map"; -- ??? "set"
1970 Key : Key_Type) return Element_Type
1972 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
1976 raise Constraint_Error with "key not in map"; -- ??? "set"
1979 return Node.Element.all;
1982 -------------------------
1983 -- Equivalent_Key_Node --
1984 -------------------------
1986 function Equivalent_Key_Node
1988 Node : Node_Access) return Boolean is
1990 return Equivalent_Keys (Key, Generic_Keys.Key (Node.Element.all));
1991 end Equivalent_Key_Node;
1998 (Container : in out Set;
2003 Key_Keys.Delete_Key_Sans_Free (Container.HT, Key, X);
2013 Key : Key_Type) return Cursor
2015 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
2017 return (if Node = null then No_Element
2018 else Cursor'(Container'Unrestricted_Access, Node));
2025 function Key (Position : Cursor) return Key_Type is
2027 if Position.Node = null then
2028 raise Constraint_Error with
2029 "Position cursor equals No_Element";
2032 if Position.Node.Element = null then
2033 raise Program_Error with "Position cursor is bad";
2036 pragma Assert (Vet (Position), "bad cursor in function Key");
2038 return Key (Position.Node.Element.all);
2046 (Stream : not null access Root_Stream_Type'Class;
2047 Item : out Reference_Type)
2050 raise Program_Error with "attempt to stream reference";
2053 ------------------------------
2054 -- Reference_Preserving_Key --
2055 ------------------------------
2057 function Reference_Preserving_Key
2058 (Container : aliased in out Set;
2059 Position : Cursor) return Reference_Type
2062 if Position.Container = null then
2063 raise Constraint_Error with "Position cursor has no element";
2066 if Position.Container /= Container'Unrestricted_Access then
2067 raise Program_Error with
2068 "Position cursor designates wrong container";
2071 if Position.Node.Element = null then
2072 raise Program_Error with "Node has no element";
2077 "bad cursor in function Reference_Preserving_Key");
2079 -- Some form of finalization will be required in order to actually
2080 -- check that the key-part of the element designated by Position has
2083 return (Element => Position.Node.Element.all'Access);
2084 end Reference_Preserving_Key;
2086 function Reference_Preserving_Key
2087 (Container : aliased in out Set;
2088 Key : Key_Type) return Reference_Type
2090 Node : constant Node_Access :=
2091 Key_Keys.Find (Container.HT, Key);
2095 raise Constraint_Error with "Key not in set";
2098 if Node.Element = null then
2099 raise Program_Error with "Node has no element";
2102 -- Some form of finalization will be required in order to actually
2103 -- check that the key-part of the element designated by Key has not
2106 return (Element => Node.Element.all'Access);
2107 end Reference_Preserving_Key;
2114 (Container : in out Set;
2116 New_Item : Element_Type)
2118 Node : constant Node_Access :=
2119 Key_Keys.Find (Container.HT, Key);
2123 raise Constraint_Error with
2124 "attempt to replace key not in set";
2127 Replace_Element (Container.HT, Node, New_Item);
2130 -----------------------------------
2131 -- Update_Element_Preserving_Key --
2132 -----------------------------------
2134 procedure Update_Element_Preserving_Key
2135 (Container : in out Set;
2137 Process : not null access
2138 procedure (Element : in out Element_Type))
2140 HT : Hash_Table_Type renames Container.HT;
2144 if Position.Node = null then
2145 raise Constraint_Error with
2146 "Position cursor equals No_Element";
2149 if Position.Node.Element = null
2150 or else Position.Node.Next = Position.Node
2152 raise Program_Error with "Position cursor is bad";
2155 if Position.Container /= Container'Unrestricted_Access then
2156 raise Program_Error with
2157 "Position cursor designates wrong set";
2160 if HT.Buckets = null
2161 or else HT.Buckets'Length = 0
2162 or else HT.Length = 0
2164 raise Program_Error with "Position cursor is bad (set is empty)";
2169 "bad cursor in Update_Element_Preserving_Key");
2171 Indx := HT_Ops.Index (HT, Position.Node);
2174 E : Element_Type renames Position.Node.Element.all;
2175 K : constant Key_Type := Key (E);
2177 B : Natural renames HT.Busy;
2178 L : Natural renames HT.Lock;
2196 if Equivalent_Keys (K, Key (E)) then
2197 pragma Assert (Hash (K) = Hash (E));
2202 if HT.Buckets (Indx) = Position.Node then
2203 HT.Buckets (Indx) := Position.Node.Next;
2207 Prev : Node_Access := HT.Buckets (Indx);
2210 while Prev.Next /= Position.Node loop
2214 raise Program_Error with
2215 "Position cursor is bad (node not found)";
2219 Prev.Next := Position.Node.Next;
2223 HT.Length := HT.Length - 1;
2226 X : Node_Access := Position.Node;
2232 raise Program_Error with "key was modified";
2233 end Update_Element_Preserving_Key;
2240 (Stream : not null access Root_Stream_Type'Class;
2241 Item : Reference_Type)
2244 raise Program_Error with "attempt to stream reference";
2249 end Ada.Containers.Indefinite_Hashed_Sets;