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;
39 with System; use type System.Address;
41 package body Ada.Containers.Indefinite_Hashed_Sets is
43 type Iterator is new Limited_Controlled and
44 Set_Iterator_Interfaces.Forward_Iterator with
46 Container : Set_Access;
49 overriding procedure Finalize (Object : in out Iterator);
51 overriding function First (Object : Iterator) return Cursor;
53 overriding function Next
55 Position : Cursor) return Cursor;
57 -----------------------
58 -- Local Subprograms --
59 -----------------------
61 procedure Assign (Node : Node_Access; Item : Element_Type);
62 pragma Inline (Assign);
64 function Copy_Node (Source : Node_Access) return Node_Access;
65 pragma Inline (Copy_Node);
67 function Equivalent_Keys
69 Node : Node_Access) return Boolean;
70 pragma Inline (Equivalent_Keys);
72 function Find_Equal_Key
73 (R_HT : Hash_Table_Type;
74 L_Node : Node_Access) return Boolean;
76 function Find_Equivalent_Key
77 (R_HT : Hash_Table_Type;
78 L_Node : Node_Access) return Boolean;
80 procedure Free (X : in out Node_Access);
82 function Hash_Node (Node : Node_Access) return Hash_Type;
83 pragma Inline (Hash_Node);
86 (HT : in out Hash_Table_Type;
87 New_Item : Element_Type;
88 Node : out Node_Access;
89 Inserted : out Boolean);
91 function Is_In (HT : Hash_Table_Type; 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 procedure Free_Element is
116 new Ada.Unchecked_Deallocation (Element_Type, Element_Access);
118 package HT_Ops is new Hash_Tables.Generic_Operations
119 (HT_Types => HT_Types,
120 Hash_Node => Hash_Node,
122 Set_Next => Set_Next,
123 Copy_Node => Copy_Node,
126 package Element_Keys is new Hash_Tables.Generic_Keys
127 (HT_Types => HT_Types,
129 Set_Next => Set_Next,
130 Key_Type => Element_Type,
132 Equivalent_Keys => Equivalent_Keys);
135 new HT_Ops.Generic_Equal (Find_Equal_Key);
137 function Is_Equivalent is
138 new HT_Ops.Generic_Equal (Find_Equivalent_Key);
140 procedure Read_Nodes is
141 new HT_Ops.Generic_Read (Read_Node);
143 procedure Replace_Element is
144 new Element_Keys.Generic_Replace_Element (Hash_Node, Assign);
146 procedure Write_Nodes is
147 new HT_Ops.Generic_Write (Write_Node);
153 function "=" (Left, Right : Set) return Boolean is
155 return Is_Equal (Left.HT, Right.HT);
162 procedure Adjust (Container : in out Set) is
164 HT_Ops.Adjust (Container.HT);
171 procedure Assign (Node : Node_Access; Item : Element_Type) is
172 X : Element_Access := Node.Element;
174 Node.Element := new Element_Type'(Item);
178 procedure Assign (Target : in out Set; Source : Set) is
180 if Target'Address = Source'Address then
185 Target.Union (Source);
192 function Capacity (Container : Set) return Count_Type is
194 return HT_Ops.Capacity (Container.HT);
201 procedure Clear (Container : in out Set) is
203 HT_Ops.Clear (Container.HT);
210 function Contains (Container : Set; Item : Element_Type) return Boolean is
212 return Find (Container, Item) /= No_Element;
221 Capacity : Count_Type := 0) return Set
229 elsif Capacity >= Source.Length then
234 with "Requested capacity is less than Source length";
237 return Target : Set do
238 Target.Reserve_Capacity (C);
239 Target.Assign (Source);
247 function Copy_Node (Source : Node_Access) return Node_Access is
248 E : Element_Access := new Element_Type'(Source.Element.all);
250 return new Node_Type'(Element => E, Next => null);
262 (Container : in out Set;
268 Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
271 raise Constraint_Error with "attempt to delete element not in set";
278 (Container : in out Set;
279 Position : in out Cursor)
282 if Position.Node = null then
283 raise Constraint_Error with "Position cursor equals No_Element";
286 if Position.Node.Element = null then
287 raise Program_Error with "Position cursor is bad";
290 if Position.Container /= Container'Unrestricted_Access then
291 raise Program_Error with "Position cursor designates wrong set";
294 if Container.HT.Busy > 0 then
295 raise Program_Error with
296 "attempt to tamper with cursors (set is busy)";
299 pragma Assert (Vet (Position), "Position cursor is bad");
301 HT_Ops.Delete_Node_Sans_Free (Container.HT, Position.Node);
303 Free (Position.Node);
304 Position.Container := null;
312 (Target : in out Set;
315 Tgt_Node : Node_Access;
318 if Target'Address = Source'Address then
323 if Source.HT.Length = 0 then
327 if Target.HT.Busy > 0 then
328 raise Program_Error with
329 "attempt to tamper with cursors (set is busy)";
332 if Source.HT.Length < Target.HT.Length then
334 Src_Node : Node_Access;
337 Src_Node := HT_Ops.First (Source.HT);
338 while Src_Node /= null loop
339 Tgt_Node := Element_Keys.Find (Target.HT, Src_Node.Element.all);
341 if Tgt_Node /= null then
342 HT_Ops.Delete_Node_Sans_Free (Target.HT, Tgt_Node);
346 Src_Node := HT_Ops.Next (Source.HT, Src_Node);
351 Tgt_Node := HT_Ops.First (Target.HT);
352 while Tgt_Node /= null loop
353 if Is_In (Source.HT, Tgt_Node) then
355 X : Node_Access := Tgt_Node;
357 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
358 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
363 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
369 function Difference (Left, Right : Set) return Set is
370 Buckets : HT_Types.Buckets_Access;
374 if Left'Address = Right'Address then
378 if Left.Length = 0 then
382 if Right.Length = 0 then
387 Size : constant Hash_Type := Prime_Numbers.To_Prime (Left.Length);
389 Buckets := HT_Ops.New_Buckets (Length => Size);
394 Iterate_Left : declare
395 procedure Process (L_Node : Node_Access);
398 new HT_Ops.Generic_Iteration (Process);
404 procedure Process (L_Node : Node_Access) is
406 if not Is_In (Right.HT, L_Node) then
408 Src : Element_Type renames L_Node.Element.all;
409 Indx : constant Hash_Type := Hash (Src) mod Buckets'Length;
410 Bucket : Node_Access renames Buckets (Indx);
411 Tgt : Element_Access := new Element_Type'(Src);
413 Bucket := new Node_Type'(Tgt, Bucket);
420 Length := Length + 1;
424 -- Start of processing for Iterate_Left
430 HT_Ops.Free_Hash_Table (Buckets);
434 return (Controlled with HT => (Buckets, Length, 0, 0));
441 function Element (Position : Cursor) return Element_Type is
443 if Position.Node = null then
444 raise Constraint_Error with "Position cursor of equals No_Element";
447 if Position.Node.Element = null then -- handle dangling reference
448 raise Program_Error with "Position cursor is bad";
451 pragma Assert (Vet (Position), "bad cursor in function Element");
453 return Position.Node.Element.all;
456 ---------------------
457 -- Equivalent_Sets --
458 ---------------------
460 function Equivalent_Sets (Left, Right : Set) return Boolean is
462 return Is_Equivalent (Left.HT, Right.HT);
465 -------------------------
466 -- Equivalent_Elements --
467 -------------------------
469 function Equivalent_Elements (Left, Right : Cursor) return Boolean is
471 if Left.Node = null then
472 raise Constraint_Error with
473 "Left cursor of Equivalent_Elements equals No_Element";
476 if Right.Node = null then
477 raise Constraint_Error with
478 "Right cursor of Equivalent_Elements equals No_Element";
481 if Left.Node.Element = null then
482 raise Program_Error with
483 "Left cursor of Equivalent_Elements is bad";
486 if Right.Node.Element = null then
487 raise Program_Error with
488 "Right cursor of Equivalent_Elements is bad";
491 pragma Assert (Vet (Left), "bad Left cursor in Equivalent_Elements");
492 pragma Assert (Vet (Right), "bad Right cursor in Equivalent_Elements");
494 return Equivalent_Elements
495 (Left.Node.Element.all,
496 Right.Node.Element.all);
497 end Equivalent_Elements;
499 function Equivalent_Elements
501 Right : Element_Type) return Boolean
504 if Left.Node = null then
505 raise Constraint_Error with
506 "Left 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 pragma Assert (Vet (Left), "bad Left cursor in Equivalent_Elements");
516 return Equivalent_Elements (Left.Node.Element.all, Right);
517 end Equivalent_Elements;
519 function Equivalent_Elements
520 (Left : Element_Type;
521 Right : Cursor) return Boolean
524 if Right.Node = null then
525 raise Constraint_Error with
526 "Right cursor of Equivalent_Elements equals No_Element";
529 if Right.Node.Element = null then
530 raise Program_Error with
531 "Right cursor of Equivalent_Elements is bad";
534 pragma Assert (Vet (Right), "bad Right cursor in Equivalent_Elements");
536 return Equivalent_Elements (Left, Right.Node.Element.all);
537 end Equivalent_Elements;
539 ---------------------
540 -- Equivalent_Keys --
541 ---------------------
543 function Equivalent_Keys
545 Node : Node_Access) return Boolean
548 return Equivalent_Elements (Key, Node.Element.all);
556 (Container : in out Set;
561 Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
569 procedure Finalize (Container : in out Set) is
571 HT_Ops.Finalize (Container.HT);
574 procedure Finalize (Object : in out Iterator) is
576 if Object.Container /= null then
578 B : Natural renames Object.Container.all.HT.Busy;
592 Item : Element_Type) return Cursor
594 Node : constant Node_Access := Element_Keys.Find (Container.HT, Item);
596 return (if Node = null then No_Element
597 else Cursor'(Container'Unrestricted_Access, Node));
604 function Find_Equal_Key
605 (R_HT : Hash_Table_Type;
606 L_Node : Node_Access) return Boolean
608 R_Index : constant Hash_Type :=
609 Element_Keys.Index (R_HT, L_Node.Element.all);
611 R_Node : Node_Access := R_HT.Buckets (R_Index);
615 if R_Node = null then
619 if L_Node.Element.all = R_Node.Element.all then
623 R_Node := Next (R_Node);
627 -------------------------
628 -- Find_Equivalent_Key --
629 -------------------------
631 function Find_Equivalent_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 Equivalent_Elements (L_Node.Element.all, R_Node.Element.all) then
650 R_Node := Next (R_Node);
652 end Find_Equivalent_Key;
658 function First (Container : Set) return Cursor is
659 Node : constant Node_Access := HT_Ops.First (Container.HT);
661 return (if Node = null then No_Element
662 else Cursor'(Container'Unrestricted_Access, Node));
665 function First (Object : Iterator) return Cursor is
667 return Object.Container.First;
674 procedure Free (X : in out Node_Access) is
675 procedure Deallocate is
676 new Ada.Unchecked_Deallocation (Node_Type, Node_Access);
683 X.Next := X; -- detect mischief (in Vet)
686 Free_Element (X.Element);
701 function Has_Element (Position : Cursor) return Boolean is
703 pragma Assert (Vet (Position), "bad cursor in Has_Element");
704 return Position.Node /= null;
711 function Hash_Node (Node : Node_Access) return Hash_Type is
713 return Hash (Node.Element.all);
721 (Container : in out Set;
722 New_Item : Element_Type)
730 Insert (Container, New_Item, Position, Inserted);
733 if Container.HT.Lock > 0 then
734 raise Program_Error with
735 "attempt to tamper with elements (set is locked)";
738 X := Position.Node.Element;
740 Position.Node.Element := new Element_Type'(New_Item);
751 (Container : in out Set;
752 New_Item : Element_Type;
753 Position : out Cursor;
754 Inserted : out Boolean)
757 Insert (Container.HT, New_Item, Position.Node, Inserted);
758 Position.Container := Container'Unchecked_Access;
762 (Container : in out Set;
763 New_Item : Element_Type)
766 pragma Unreferenced (Position);
771 Insert (Container, New_Item, Position, Inserted);
774 raise Constraint_Error with
775 "attempt to insert element already in set";
780 (HT : in out Hash_Table_Type;
781 New_Item : Element_Type;
782 Node : out Node_Access;
783 Inserted : out Boolean)
785 function New_Node (Next : Node_Access) return Node_Access;
786 pragma Inline (New_Node);
788 procedure Local_Insert is
789 new Element_Keys.Generic_Conditional_Insert (New_Node);
795 function New_Node (Next : Node_Access) return Node_Access is
796 Element : Element_Access := new Element_Type'(New_Item);
798 return new Node_Type'(Element, Next);
801 Free_Element (Element);
805 -- Start of processing for Insert
808 if HT_Ops.Capacity (HT) = 0 then
809 HT_Ops.Reserve_Capacity (HT, 1);
812 Local_Insert (HT, New_Item, Node, Inserted);
815 and then HT.Length > HT_Ops.Capacity (HT)
817 HT_Ops.Reserve_Capacity (HT, HT.Length);
825 procedure Intersection
826 (Target : in out Set;
829 Tgt_Node : Node_Access;
832 if Target'Address = Source'Address then
836 if Source.Length = 0 then
841 if Target.HT.Busy > 0 then
842 raise Program_Error with
843 "attempt to tamper with cursors (set is busy)";
846 Tgt_Node := HT_Ops.First (Target.HT);
847 while Tgt_Node /= null loop
848 if Is_In (Source.HT, Tgt_Node) then
849 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
853 X : Node_Access := Tgt_Node;
855 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
856 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
863 function Intersection (Left, Right : Set) return Set is
864 Buckets : HT_Types.Buckets_Access;
868 if Left'Address = Right'Address then
872 Length := Count_Type'Min (Left.Length, Right.Length);
879 Size : constant Hash_Type := Prime_Numbers.To_Prime (Length);
881 Buckets := HT_Ops.New_Buckets (Length => Size);
886 Iterate_Left : declare
887 procedure Process (L_Node : Node_Access);
890 new HT_Ops.Generic_Iteration (Process);
896 procedure Process (L_Node : Node_Access) is
898 if Is_In (Right.HT, L_Node) then
900 Src : Element_Type renames L_Node.Element.all;
902 Indx : constant Hash_Type := Hash (Src) mod Buckets'Length;
904 Bucket : Node_Access renames Buckets (Indx);
906 Tgt : Element_Access := new Element_Type'(Src);
909 Bucket := new Node_Type'(Tgt, Bucket);
916 Length := Length + 1;
920 -- Start of processing for Iterate_Left
926 HT_Ops.Free_Hash_Table (Buckets);
930 return (Controlled with HT => (Buckets, Length, 0, 0));
937 function Is_Empty (Container : Set) return Boolean is
939 return Container.HT.Length = 0;
946 function Is_In (HT : Hash_Table_Type; Key : Node_Access) return Boolean is
948 return Element_Keys.Find (HT, Key.Element.all) /= null;
957 Of_Set : Set) return Boolean
959 Subset_Node : Node_Access;
962 if Subset'Address = Of_Set'Address then
966 if Subset.Length > Of_Set.Length then
970 Subset_Node := HT_Ops.First (Subset.HT);
971 while Subset_Node /= null loop
972 if not Is_In (Of_Set.HT, Subset_Node) then
976 Subset_Node := HT_Ops.Next (Subset.HT, Subset_Node);
988 Process : not null access procedure (Position : Cursor))
990 procedure Process_Node (Node : Node_Access);
991 pragma Inline (Process_Node);
994 new HT_Ops.Generic_Iteration (Process_Node);
1000 procedure Process_Node (Node : Node_Access) is
1002 Process (Cursor'(Container'Unrestricted_Access, Node));
1005 B : Natural renames Container'Unrestricted_Access.all.HT.Busy;
1007 -- Start of processing for Iterate
1013 Iterate (Container.HT);
1023 function Iterate (Container : Set)
1024 return Set_Iterator_Interfaces.Forward_Iterator'Class
1026 B : Natural renames Container'Unrestricted_Access.all.HT.Busy;
1029 return It : constant Iterator :=
1030 Iterator'(Limited_Controlled with
1031 Container => Container'Unrestricted_Access)
1041 function Length (Container : Set) return Count_Type is
1043 return Container.HT.Length;
1050 procedure Move (Target : in out Set; Source : in out Set) is
1052 HT_Ops.Move (Target => Target.HT, Source => Source.HT);
1059 function Next (Node : Node_Access) return Node_Access is
1064 function Next (Position : Cursor) return Cursor is
1066 if Position.Node = null then
1070 if Position.Node.Element = null then
1071 raise Program_Error with "bad cursor in Next";
1074 pragma Assert (Vet (Position), "bad cursor in Next");
1077 HT : Hash_Table_Type renames Position.Container.HT;
1078 Node : constant Node_Access := HT_Ops.Next (HT, Position.Node);
1080 return (if Node = null then No_Element
1081 else Cursor'(Position.Container, Node));
1085 procedure Next (Position : in out Cursor) is
1087 Position := Next (Position);
1092 Position : Cursor) return Cursor
1095 if Position.Container = null then
1099 if Position.Container /= Object.Container then
1100 raise Program_Error with
1101 "Position cursor of Next designates wrong set";
1104 return Next (Position);
1111 function Overlap (Left, Right : Set) return Boolean is
1112 Left_Node : Node_Access;
1115 if Right.Length = 0 then
1119 if Left'Address = Right'Address then
1123 Left_Node := HT_Ops.First (Left.HT);
1124 while Left_Node /= null loop
1125 if Is_In (Right.HT, Left_Node) then
1129 Left_Node := HT_Ops.Next (Left.HT, Left_Node);
1139 procedure Query_Element
1141 Process : not null access procedure (Element : Element_Type))
1144 if Position.Node = null then
1145 raise Constraint_Error with
1146 "Position cursor of Query_Element equals No_Element";
1149 if Position.Node.Element = null then
1150 raise Program_Error with "bad cursor in Query_Element";
1153 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1156 HT : Hash_Table_Type renames
1157 Position.Container'Unrestricted_Access.all.HT;
1159 B : Natural renames HT.Busy;
1160 L : Natural renames HT.Lock;
1167 Process (Position.Node.Element.all);
1185 (Stream : not null access Root_Stream_Type'Class;
1186 Container : out Set)
1189 Read_Nodes (Stream, Container.HT);
1193 (Stream : not null access Root_Stream_Type'Class;
1197 raise Program_Error with "attempt to stream set cursor";
1201 (Stream : not null access Root_Stream_Type'Class;
1202 Item : out Constant_Reference_Type)
1205 raise Program_Error with "attempt to stream reference";
1213 (Stream : not null access Root_Stream_Type'Class) return Node_Access
1215 X : Element_Access := new Element_Type'(Element_Type'Input (Stream));
1217 return new Node_Type'(X, null);
1228 function Constant_Reference
1229 (Container : aliased Set;
1230 Position : Cursor) return Constant_Reference_Type
1232 pragma Unreferenced (Container);
1234 return (Element => Position.Node.Element.all'Access);
1235 end Constant_Reference;
1242 (Container : in out Set;
1243 New_Item : Element_Type)
1245 Node : constant Node_Access :=
1246 Element_Keys.Find (Container.HT, New_Item);
1249 pragma Warnings (Off, X);
1253 raise Constraint_Error with
1254 "attempt to replace element not in set";
1257 if Container.HT.Lock > 0 then
1258 raise Program_Error with
1259 "attempt to tamper with elements (set is locked)";
1264 Node.Element := new Element_Type'(New_Item);
1269 ---------------------
1270 -- Replace_Element --
1271 ---------------------
1273 procedure Replace_Element
1274 (Container : in out Set;
1276 New_Item : Element_Type)
1279 if Position.Node = null then
1280 raise Constraint_Error with "Position cursor equals No_Element";
1283 if Position.Node.Element = null then
1284 raise Program_Error with "bad cursor in Replace_Element";
1287 if Position.Container /= Container'Unrestricted_Access then
1288 raise Program_Error with
1289 "Position cursor designates wrong set";
1292 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1294 Replace_Element (Container.HT, Position.Node, New_Item);
1295 end Replace_Element;
1297 ----------------------
1298 -- Reserve_Capacity --
1299 ----------------------
1301 procedure Reserve_Capacity
1302 (Container : in out Set;
1303 Capacity : Count_Type)
1306 HT_Ops.Reserve_Capacity (Container.HT, Capacity);
1307 end Reserve_Capacity;
1313 procedure Set_Next (Node : Node_Access; Next : Node_Access) is
1318 --------------------------
1319 -- Symmetric_Difference --
1320 --------------------------
1322 procedure Symmetric_Difference
1323 (Target : in out Set;
1327 if Target'Address = Source'Address then
1332 if Target.HT.Busy > 0 then
1333 raise Program_Error with
1334 "attempt to tamper with cursors (set is busy)";
1338 N : constant Count_Type := Target.Length + Source.Length;
1340 if N > HT_Ops.Capacity (Target.HT) then
1341 HT_Ops.Reserve_Capacity (Target.HT, N);
1345 if Target.Length = 0 then
1346 Iterate_Source_When_Empty_Target : declare
1347 procedure Process (Src_Node : Node_Access);
1349 procedure Iterate is new HT_Ops.Generic_Iteration (Process);
1355 procedure Process (Src_Node : Node_Access) is
1356 E : Element_Type renames Src_Node.Element.all;
1357 B : Buckets_Type renames Target.HT.Buckets.all;
1358 J : constant Hash_Type := Hash (E) mod B'Length;
1359 N : Count_Type renames Target.HT.Length;
1363 X : Element_Access := new Element_Type'(E);
1365 B (J) := new Node_Type'(X, B (J));
1375 -- Start of processing for Iterate_Source_When_Empty_Target
1378 Iterate (Source.HT);
1379 end Iterate_Source_When_Empty_Target;
1382 Iterate_Source : declare
1383 procedure Process (Src_Node : Node_Access);
1385 procedure Iterate is
1386 new HT_Ops.Generic_Iteration (Process);
1392 procedure Process (Src_Node : Node_Access) is
1393 E : Element_Type renames Src_Node.Element.all;
1394 B : Buckets_Type renames Target.HT.Buckets.all;
1395 J : constant Hash_Type := Hash (E) mod B'Length;
1396 N : Count_Type renames Target.HT.Length;
1399 if B (J) = null then
1401 X : Element_Access := new Element_Type'(E);
1403 B (J) := new Node_Type'(X, null);
1412 elsif Equivalent_Elements (E, B (J).Element.all) then
1414 X : Node_Access := B (J);
1416 B (J) := B (J).Next;
1423 Prev : Node_Access := B (J);
1424 Curr : Node_Access := Prev.Next;
1427 while Curr /= null loop
1428 if Equivalent_Elements (E, Curr.Element.all) then
1429 Prev.Next := Curr.Next;
1440 X : Element_Access := new Element_Type'(E);
1442 B (J) := new Node_Type'(X, B (J));
1454 -- Start of processing for Iterate_Source
1457 Iterate (Source.HT);
1460 end Symmetric_Difference;
1462 function Symmetric_Difference (Left, Right : Set) return Set is
1463 Buckets : HT_Types.Buckets_Access;
1464 Length : Count_Type;
1467 if Left'Address = Right'Address then
1471 if Right.Length = 0 then
1475 if Left.Length = 0 then
1480 Size : constant Hash_Type :=
1481 Prime_Numbers.To_Prime (Left.Length + Right.Length);
1483 Buckets := HT_Ops.New_Buckets (Length => Size);
1488 Iterate_Left : declare
1489 procedure Process (L_Node : Node_Access);
1491 procedure Iterate is
1492 new HT_Ops.Generic_Iteration (Process);
1498 procedure Process (L_Node : Node_Access) is
1500 if not Is_In (Right.HT, L_Node) then
1502 E : Element_Type renames L_Node.Element.all;
1503 J : constant Hash_Type := Hash (E) mod Buckets'Length;
1507 X : Element_Access := new Element_Type'(E);
1509 Buckets (J) := new Node_Type'(X, Buckets (J));
1516 Length := Length + 1;
1521 -- Start of processing for Iterate_Left
1527 HT_Ops.Free_Hash_Table (Buckets);
1531 Iterate_Right : declare
1532 procedure Process (R_Node : Node_Access);
1534 procedure Iterate is
1535 new HT_Ops.Generic_Iteration (Process);
1541 procedure Process (R_Node : Node_Access) is
1543 if not Is_In (Left.HT, R_Node) then
1545 E : Element_Type renames R_Node.Element.all;
1546 J : constant Hash_Type := Hash (E) mod Buckets'Length;
1550 X : Element_Access := new Element_Type'(E);
1552 Buckets (J) := new Node_Type'(X, Buckets (J));
1559 Length := Length + 1;
1564 -- Start of processing for Iterate_Right
1570 HT_Ops.Free_Hash_Table (Buckets);
1574 return (Controlled with HT => (Buckets, Length, 0, 0));
1575 end Symmetric_Difference;
1581 function To_Set (New_Item : Element_Type) return Set is
1582 HT : Hash_Table_Type;
1585 pragma Unreferenced (Node, Inserted);
1587 Insert (HT, New_Item, Node, Inserted);
1588 return Set'(Controlled with HT);
1596 (Target : in out Set;
1599 procedure Process (Src_Node : Node_Access);
1601 procedure Iterate is
1602 new HT_Ops.Generic_Iteration (Process);
1608 procedure Process (Src_Node : Node_Access) is
1609 Src : Element_Type renames Src_Node.Element.all;
1611 function New_Node (Next : Node_Access) return Node_Access;
1612 pragma Inline (New_Node);
1615 new Element_Keys.Generic_Conditional_Insert (New_Node);
1621 function New_Node (Next : Node_Access) return Node_Access is
1622 Tgt : Element_Access := new Element_Type'(Src);
1624 return new Node_Type'(Tgt, Next);
1631 Tgt_Node : Node_Access;
1633 pragma Unreferenced (Tgt_Node, Success);
1635 -- Start of processing for Process
1638 Insert (Target.HT, Src, Tgt_Node, Success);
1641 -- Start of processing for Union
1644 if Target'Address = Source'Address then
1648 if Target.HT.Busy > 0 then
1649 raise Program_Error with
1650 "attempt to tamper with cursors (set is busy)";
1654 N : constant Count_Type := Target.Length + Source.Length;
1656 if N > HT_Ops.Capacity (Target.HT) then
1657 HT_Ops.Reserve_Capacity (Target.HT, N);
1661 Iterate (Source.HT);
1664 function Union (Left, Right : Set) return Set is
1665 Buckets : HT_Types.Buckets_Access;
1666 Length : Count_Type;
1669 if Left'Address = Right'Address then
1673 if Right.Length = 0 then
1677 if Left.Length = 0 then
1682 Size : constant Hash_Type :=
1683 Prime_Numbers.To_Prime (Left.Length + Right.Length);
1685 Buckets := HT_Ops.New_Buckets (Length => Size);
1688 Iterate_Left : declare
1689 procedure Process (L_Node : Node_Access);
1691 procedure Iterate is
1692 new HT_Ops.Generic_Iteration (Process);
1698 procedure Process (L_Node : Node_Access) is
1699 Src : Element_Type renames L_Node.Element.all;
1700 J : constant Hash_Type := Hash (Src) mod Buckets'Length;
1701 Bucket : Node_Access renames Buckets (J);
1702 Tgt : Element_Access := new Element_Type'(Src);
1704 Bucket := new Node_Type'(Tgt, Bucket);
1711 -- Start of processing for Process
1717 HT_Ops.Free_Hash_Table (Buckets);
1721 Length := Left.Length;
1723 Iterate_Right : declare
1724 procedure Process (Src_Node : Node_Access);
1726 procedure Iterate is
1727 new HT_Ops.Generic_Iteration (Process);
1733 procedure Process (Src_Node : Node_Access) is
1734 Src : Element_Type renames Src_Node.Element.all;
1735 Idx : constant Hash_Type := Hash (Src) mod Buckets'Length;
1737 Tgt_Node : Node_Access := Buckets (Idx);
1740 while Tgt_Node /= null loop
1741 if Equivalent_Elements (Src, Tgt_Node.Element.all) then
1744 Tgt_Node := Next (Tgt_Node);
1748 Tgt : Element_Access := new Element_Type'(Src);
1750 Buckets (Idx) := new Node_Type'(Tgt, Buckets (Idx));
1757 Length := Length + 1;
1760 -- Start of processing for Iterate_Right
1766 HT_Ops.Free_Hash_Table (Buckets);
1770 return (Controlled with HT => (Buckets, Length, 0, 0));
1777 function Vet (Position : Cursor) return Boolean is
1779 if Position.Node = null then
1780 return Position.Container = null;
1783 if Position.Container = null then
1787 if Position.Node.Next = Position.Node then
1791 if Position.Node.Element = null then
1796 HT : Hash_Table_Type renames Position.Container.HT;
1800 if HT.Length = 0 then
1804 if HT.Buckets = null
1805 or else HT.Buckets'Length = 0
1810 X := HT.Buckets (Element_Keys.Index (HT, Position.Node.Element.all));
1812 for J in 1 .. HT.Length loop
1813 if X = Position.Node then
1821 if X = X.Next then -- to prevent unnecessary looping
1837 (Stream : not null access Root_Stream_Type'Class;
1841 Write_Nodes (Stream, Container.HT);
1845 (Stream : not null access Root_Stream_Type'Class;
1849 raise Program_Error with "attempt to stream set cursor";
1853 (Stream : not null access Root_Stream_Type'Class;
1854 Item : Constant_Reference_Type)
1857 raise Program_Error with "attempt to stream reference";
1864 procedure Write_Node
1865 (Stream : not null access Root_Stream_Type'Class;
1869 Element_Type'Output (Stream, Node.Element.all);
1872 package body Generic_Keys is
1874 -----------------------
1875 -- Local Subprograms --
1876 -----------------------
1878 function Equivalent_Key_Node
1880 Node : Node_Access) return Boolean;
1881 pragma Inline (Equivalent_Key_Node);
1883 --------------------------
1884 -- Local Instantiations --
1885 --------------------------
1888 new Hash_Tables.Generic_Keys
1889 (HT_Types => HT_Types,
1891 Set_Next => Set_Next,
1892 Key_Type => Key_Type,
1894 Equivalent_Keys => Equivalent_Key_Node);
1902 Key : Key_Type) return Boolean
1905 return Find (Container, Key) /= No_Element;
1913 (Container : in out Set;
1919 Key_Keys.Delete_Key_Sans_Free (Container.HT, Key, X);
1922 raise Constraint_Error with "key not in map"; -- ??? "set"
1934 Key : Key_Type) return Element_Type
1936 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
1940 raise Constraint_Error with "key not in map"; -- ??? "set"
1943 return Node.Element.all;
1946 -------------------------
1947 -- Equivalent_Key_Node --
1948 -------------------------
1950 function Equivalent_Key_Node
1952 Node : Node_Access) return Boolean is
1954 return Equivalent_Keys (Key, Generic_Keys.Key (Node.Element.all));
1955 end Equivalent_Key_Node;
1962 (Container : in out Set;
1967 Key_Keys.Delete_Key_Sans_Free (Container.HT, Key, X);
1977 Key : Key_Type) return Cursor
1979 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
1981 return (if Node = null then No_Element
1982 else Cursor'(Container'Unrestricted_Access, Node));
1989 function Key (Position : Cursor) return Key_Type is
1991 if Position.Node = null then
1992 raise Constraint_Error with
1993 "Position cursor equals No_Element";
1996 if Position.Node.Element = null then
1997 raise Program_Error with "Position cursor is bad";
2000 pragma Assert (Vet (Position), "bad cursor in function Key");
2002 return Key (Position.Node.Element.all);
2010 (Container : in out Set;
2012 New_Item : Element_Type)
2014 Node : constant Node_Access :=
2015 Key_Keys.Find (Container.HT, Key);
2019 raise Constraint_Error with
2020 "attempt to replace key not in set";
2023 Replace_Element (Container.HT, Node, New_Item);
2026 procedure Update_Element_Preserving_Key
2027 (Container : in out Set;
2029 Process : not null access
2030 procedure (Element : in out Element_Type))
2032 HT : Hash_Table_Type renames Container.HT;
2036 if Position.Node = null then
2037 raise Constraint_Error with
2038 "Position cursor equals No_Element";
2041 if Position.Node.Element = null
2042 or else Position.Node.Next = Position.Node
2044 raise Program_Error with "Position cursor is bad";
2047 if Position.Container /= Container'Unrestricted_Access then
2048 raise Program_Error with
2049 "Position cursor designates wrong set";
2052 if HT.Buckets = null
2053 or else HT.Buckets'Length = 0
2054 or else HT.Length = 0
2056 raise Program_Error with "Position cursor is bad (set is empty)";
2061 "bad cursor in Update_Element_Preserving_Key");
2063 Indx := HT_Ops.Index (HT, Position.Node);
2066 E : Element_Type renames Position.Node.Element.all;
2067 K : constant Key_Type := Key (E);
2069 B : Natural renames HT.Busy;
2070 L : Natural renames HT.Lock;
2088 if Equivalent_Keys (K, Key (E)) then
2089 pragma Assert (Hash (K) = Hash (E));
2094 if HT.Buckets (Indx) = Position.Node then
2095 HT.Buckets (Indx) := Position.Node.Next;
2099 Prev : Node_Access := HT.Buckets (Indx);
2102 while Prev.Next /= Position.Node loop
2106 raise Program_Error with
2107 "Position cursor is bad (node not found)";
2111 Prev.Next := Position.Node.Next;
2115 HT.Length := HT.Length - 1;
2118 X : Node_Access := Position.Node;
2124 raise Program_Error with "key was modified";
2125 end Update_Element_Preserving_Key;
2127 ------------------------------
2128 -- Reference_Preserving_Key --
2129 ------------------------------
2131 function Reference_Preserving_Key
2132 (Container : aliased in out Set;
2133 Position : Cursor) return Reference_Type
2135 pragma Unreferenced (Container);
2137 return (Element => Position.Node.Element.all'Access);
2138 end Reference_Preserving_Key;
2140 function Reference_Preserving_Key
2141 (Container : aliased in out Set;
2142 Key : Key_Type) return Reference_Type
2144 Position : constant Cursor := Find (Container, Key);
2146 return (Element => Position.Node.Element.all'Access);
2147 end Reference_Preserving_Key;
2151 end Ada.Containers.Indefinite_Hashed_Sets;