1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- ADA.CONTAINERS.INDEFINITE_HASHED_SETS --
9 -- Copyright (C) 2004-2013, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- This unit was originally developed by Matthew J Heaney. --
28 ------------------------------------------------------------------------------
30 with Ada
.Unchecked_Deallocation
;
32 with Ada
.Containers
.Hash_Tables
.Generic_Operations
;
33 pragma Elaborate_All
(Ada
.Containers
.Hash_Tables
.Generic_Operations
);
35 with Ada
.Containers
.Hash_Tables
.Generic_Keys
;
36 pragma Elaborate_All
(Ada
.Containers
.Hash_Tables
.Generic_Keys
);
38 with Ada
.Containers
.Prime_Numbers
;
40 with System
; use type System
.Address
;
42 package body Ada
.Containers
.Indefinite_Hashed_Sets
is
44 -----------------------
45 -- Local Subprograms --
46 -----------------------
48 procedure Assign
(Node
: Node_Access
; Item
: Element_Type
);
49 pragma Inline
(Assign
);
51 function Copy_Node
(Source
: Node_Access
) return Node_Access
;
52 pragma Inline
(Copy_Node
);
54 function Equivalent_Keys
56 Node
: Node_Access
) return Boolean;
57 pragma Inline
(Equivalent_Keys
);
59 function Find_Equal_Key
60 (R_HT
: Hash_Table_Type
;
61 L_Node
: Node_Access
) return Boolean;
63 function Find_Equivalent_Key
64 (R_HT
: Hash_Table_Type
;
65 L_Node
: Node_Access
) return Boolean;
67 procedure Free
(X
: in out Node_Access
);
69 function Hash_Node
(Node
: Node_Access
) return Hash_Type
;
70 pragma Inline
(Hash_Node
);
73 (HT
: in out Hash_Table_Type
;
74 New_Item
: Element_Type
;
75 Node
: out Node_Access
;
76 Inserted
: out Boolean);
78 function Is_In
(HT
: Hash_Table_Type
; Key
: Node_Access
) return Boolean;
79 pragma Inline
(Is_In
);
81 function Next
(Node
: Node_Access
) return Node_Access
;
84 function Read_Node
(Stream
: not null access Root_Stream_Type
'Class)
86 pragma Inline
(Read_Node
);
88 procedure Set_Next
(Node
: Node_Access
; Next
: Node_Access
);
89 pragma Inline
(Set_Next
);
91 function Vet
(Position
: Cursor
) return Boolean;
94 (Stream
: not null access Root_Stream_Type
'Class;
96 pragma Inline
(Write_Node
);
98 --------------------------
99 -- Local Instantiations --
100 --------------------------
102 procedure Free_Element
is
103 new Ada
.Unchecked_Deallocation
(Element_Type
, Element_Access
);
105 package HT_Ops
is new Hash_Tables
.Generic_Operations
106 (HT_Types
=> HT_Types
,
107 Hash_Node
=> Hash_Node
,
109 Set_Next
=> Set_Next
,
110 Copy_Node
=> Copy_Node
,
113 package Element_Keys
is new Hash_Tables
.Generic_Keys
114 (HT_Types
=> HT_Types
,
116 Set_Next
=> Set_Next
,
117 Key_Type
=> Element_Type
,
119 Equivalent_Keys
=> Equivalent_Keys
);
122 new HT_Ops
.Generic_Equal
(Find_Equal_Key
);
124 function Is_Equivalent
is
125 new HT_Ops
.Generic_Equal
(Find_Equivalent_Key
);
127 procedure Read_Nodes
is
128 new HT_Ops
.Generic_Read
(Read_Node
);
130 procedure Replace_Element
is
131 new Element_Keys
.Generic_Replace_Element
(Hash_Node
, Assign
);
133 procedure Write_Nodes
is
134 new HT_Ops
.Generic_Write
(Write_Node
);
140 function "=" (Left
, Right
: Set
) return Boolean is
142 return Is_Equal
(Left
.HT
, Right
.HT
);
149 procedure Adjust
(Container
: in out Set
) is
151 HT_Ops
.Adjust
(Container
.HT
);
154 procedure Adjust
(Control
: in out Reference_Control_Type
) is
156 if Control
.Container
/= null then
158 HT
: Hash_Table_Type
renames Control
.Container
.all.HT
;
159 B
: Natural renames HT
.Busy
;
160 L
: Natural renames HT
.Lock
;
172 procedure Assign
(Node
: Node_Access
; Item
: Element_Type
) is
173 X
: Element_Access
:= Node
.Element
;
175 -- The element allocator may need an accessibility check in the case the
176 -- actual type is class-wide or has access discriminants (RM 4.8(10.1)
179 pragma Unsuppress
(Accessibility_Check
);
182 Node
.Element
:= new Element_Type
'(Item);
186 procedure Assign (Target : in out Set; Source : Set) is
188 if Target'Address = Source'Address then
192 Target.Union (Source);
200 function Capacity (Container : Set) return Count_Type is
202 return HT_Ops.Capacity (Container.HT);
209 procedure Clear (Container : in out Set) is
211 HT_Ops.Clear (Container.HT);
214 ------------------------
215 -- Constant_Reference --
216 ------------------------
218 function Constant_Reference
219 (Container : aliased Set;
220 Position : Cursor) return Constant_Reference_Type
223 if Position.Container = null then
224 raise Constraint_Error with "Position cursor has no element";
227 if Position.Container /= Container'Unrestricted_Access then
228 raise Program_Error with
229 "Position cursor designates wrong container";
232 if Position.Node.Element = null then
233 raise Program_Error with "Node has no element";
236 pragma Assert (Vet (Position), "bad cursor in Constant_Reference");
239 HT : Hash_Table_Type renames Position.Container.all.HT;
240 B : Natural renames HT.Busy;
241 L : Natural renames HT.Lock;
243 return R : constant Constant_Reference_Type :=
244 (Element => Position.Node.Element.all'Access,
245 Control => (Controlled with Container'Unrestricted_Access))
251 end Constant_Reference;
257 function Contains (Container : Set; Item : Element_Type) return Boolean is
259 return Find (Container, Item) /= No_Element;
268 Capacity : Count_Type := 0) return Set
276 elsif Capacity >= Source.Length then
281 with "Requested capacity is less than Source length";
284 return Target : Set do
285 Target.Reserve_Capacity (C);
286 Target.Assign (Source);
294 function Copy_Node (Source : Node_Access) return Node_Access is
295 E : Element_Access := new Element_Type'(Source
.Element
.all);
297 return new Node_Type
'(Element => E, Next => null);
309 (Container : in out Set;
315 Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
318 raise Constraint_Error with "attempt to delete element not in set";
325 (Container : in out Set;
326 Position : in out Cursor)
329 if Position.Node = null then
330 raise Constraint_Error with "Position cursor equals No_Element";
333 if Position.Node.Element = null then
334 raise Program_Error with "Position cursor is bad";
337 if Position.Container /= Container'Unrestricted_Access then
338 raise Program_Error with "Position cursor designates wrong set";
341 if Container.HT.Busy > 0 then
342 raise Program_Error with
343 "attempt to tamper with cursors (set is busy)";
346 pragma Assert (Vet (Position), "Position cursor is bad");
348 HT_Ops.Delete_Node_Sans_Free (Container.HT, Position.Node);
350 Free (Position.Node);
351 Position.Container := null;
359 (Target : in out Set;
362 Tgt_Node : Node_Access;
365 if Target'Address = Source'Address then
370 if Source.HT.Length = 0 then
374 if Target.HT.Busy > 0 then
375 raise Program_Error with
376 "attempt to tamper with cursors (set is busy)";
379 if Source.HT.Length < Target.HT.Length then
381 Src_Node : Node_Access;
384 Src_Node := HT_Ops.First (Source.HT);
385 while Src_Node /= null loop
386 Tgt_Node := Element_Keys.Find (Target.HT, Src_Node.Element.all);
388 if Tgt_Node /= null then
389 HT_Ops.Delete_Node_Sans_Free (Target.HT, Tgt_Node);
393 Src_Node := HT_Ops.Next (Source.HT, Src_Node);
398 Tgt_Node := HT_Ops.First (Target.HT);
399 while Tgt_Node /= null loop
400 if Is_In (Source.HT, Tgt_Node) then
402 X : Node_Access := Tgt_Node;
404 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
405 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
410 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
416 function Difference (Left, Right : Set) return Set is
417 Buckets : HT_Types.Buckets_Access;
421 if Left'Address = Right'Address then
425 if Left.Length = 0 then
429 if Right.Length = 0 then
434 Size : constant Hash_Type := Prime_Numbers.To_Prime (Left.Length);
436 Buckets := HT_Ops.New_Buckets (Length => Size);
441 Iterate_Left : declare
442 procedure Process (L_Node : Node_Access);
445 new HT_Ops.Generic_Iteration (Process);
451 procedure Process (L_Node : Node_Access) is
453 if not Is_In (Right.HT, L_Node) then
455 Src : Element_Type renames L_Node.Element.all;
456 Indx : constant Hash_Type := Hash (Src) mod Buckets'Length;
457 Bucket : Node_Access renames Buckets (Indx);
458 Tgt : Element_Access := new Element_Type'(Src
);
460 Bucket
:= new Node_Type
'(Tgt, Bucket);
467 Length := Length + 1;
471 -- Start of processing for Iterate_Left
477 HT_Ops.Free_Hash_Table (Buckets);
481 return (Controlled with HT => (Buckets, Length, 0, 0));
488 function Element (Position : Cursor) return Element_Type is
490 if Position.Node = null then
491 raise Constraint_Error with "Position cursor of equals No_Element";
494 if Position.Node.Element = null then -- handle dangling reference
495 raise Program_Error with "Position cursor is bad";
498 pragma Assert (Vet (Position), "bad cursor in function Element");
500 return Position.Node.Element.all;
503 ---------------------
504 -- Equivalent_Sets --
505 ---------------------
507 function Equivalent_Sets (Left, Right : Set) return Boolean is
509 return Is_Equivalent (Left.HT, Right.HT);
512 -------------------------
513 -- Equivalent_Elements --
514 -------------------------
516 function Equivalent_Elements (Left, Right : Cursor) return Boolean is
518 if Left.Node = null then
519 raise Constraint_Error with
520 "Left cursor of Equivalent_Elements equals No_Element";
523 if Right.Node = null then
524 raise Constraint_Error with
525 "Right cursor of Equivalent_Elements equals No_Element";
528 if Left.Node.Element = null then
529 raise Program_Error with
530 "Left cursor of Equivalent_Elements is bad";
533 if Right.Node.Element = null then
534 raise Program_Error with
535 "Right cursor of Equivalent_Elements is bad";
538 pragma Assert (Vet (Left), "bad Left cursor in Equivalent_Elements");
539 pragma Assert (Vet (Right), "bad Right cursor in Equivalent_Elements");
541 return Equivalent_Elements
542 (Left.Node.Element.all,
543 Right.Node.Element.all);
544 end Equivalent_Elements;
546 function Equivalent_Elements
548 Right : Element_Type) return Boolean
551 if Left.Node = null then
552 raise Constraint_Error with
553 "Left cursor of Equivalent_Elements equals No_Element";
556 if Left.Node.Element = null then
557 raise Program_Error with
558 "Left cursor of Equivalent_Elements is bad";
561 pragma Assert (Vet (Left), "bad Left cursor in Equivalent_Elements");
563 return Equivalent_Elements (Left.Node.Element.all, Right);
564 end Equivalent_Elements;
566 function Equivalent_Elements
567 (Left : Element_Type;
568 Right : Cursor) return Boolean
571 if Right.Node = null then
572 raise Constraint_Error with
573 "Right cursor of Equivalent_Elements equals No_Element";
576 if Right.Node.Element = null then
577 raise Program_Error with
578 "Right cursor of Equivalent_Elements is bad";
581 pragma Assert (Vet (Right), "bad Right cursor in Equivalent_Elements");
583 return Equivalent_Elements (Left, Right.Node.Element.all);
584 end Equivalent_Elements;
586 ---------------------
587 -- Equivalent_Keys --
588 ---------------------
590 function Equivalent_Keys
592 Node : Node_Access) return Boolean
595 return Equivalent_Elements (Key, Node.Element.all);
603 (Container : in out Set;
608 Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
616 procedure Finalize (Container : in out Set) is
618 HT_Ops.Finalize (Container.HT);
621 procedure Finalize (Object : in out Iterator) is
623 if Object.Container /= null then
625 B : Natural renames Object.Container.all.HT.Busy;
632 procedure Finalize (Control : in out Reference_Control_Type) is
634 if Control.Container /= null then
636 HT : Hash_Table_Type renames Control.Container.all.HT;
637 B : Natural renames HT.Busy;
638 L : Natural renames HT.Lock;
644 Control.Container := null;
654 Item : Element_Type) return Cursor
656 Node : constant Node_Access := Element_Keys.Find (Container.HT, Item);
658 return (if Node = null then No_Element
659 else Cursor'(Container
'Unrestricted_Access, Node
));
666 function Find_Equal_Key
667 (R_HT
: Hash_Table_Type
;
668 L_Node
: Node_Access
) return Boolean
670 R_Index
: constant Hash_Type
:=
671 Element_Keys
.Index
(R_HT
, L_Node
.Element
.all);
673 R_Node
: Node_Access
:= R_HT
.Buckets
(R_Index
);
677 if R_Node
= null then
681 if L_Node
.Element
.all = R_Node
.Element
.all then
685 R_Node
:= Next
(R_Node
);
689 -------------------------
690 -- Find_Equivalent_Key --
691 -------------------------
693 function Find_Equivalent_Key
694 (R_HT
: Hash_Table_Type
;
695 L_Node
: Node_Access
) return Boolean
697 R_Index
: constant Hash_Type
:=
698 Element_Keys
.Index
(R_HT
, L_Node
.Element
.all);
700 R_Node
: Node_Access
:= R_HT
.Buckets
(R_Index
);
704 if R_Node
= null then
708 if Equivalent_Elements
(L_Node
.Element
.all, R_Node
.Element
.all) then
712 R_Node
:= Next
(R_Node
);
714 end Find_Equivalent_Key
;
720 function First
(Container
: Set
) return Cursor
is
721 Node
: constant Node_Access
:= HT_Ops
.First
(Container
.HT
);
723 return (if Node
= null then No_Element
724 else Cursor
'(Container'Unrestricted_Access, Node));
727 function First (Object : Iterator) return Cursor is
729 return Object.Container.First;
736 procedure Free (X : in out Node_Access) is
737 procedure Deallocate is
738 new Ada.Unchecked_Deallocation (Node_Type, Node_Access);
745 X.Next := X; -- detect mischief (in Vet)
748 Free_Element (X.Element);
763 function Has_Element (Position : Cursor) return Boolean is
765 pragma Assert (Vet (Position), "bad cursor in Has_Element");
766 return Position.Node /= null;
773 function Hash_Node (Node : Node_Access) return Hash_Type is
775 return Hash (Node.Element.all);
783 (Container : in out Set;
784 New_Item : Element_Type)
792 Insert (Container, New_Item, Position, Inserted);
795 if Container.HT.Lock > 0 then
796 raise Program_Error with
797 "attempt to tamper with elements (set is locked)";
800 X := Position.Node.Element;
803 -- The element allocator may need an accessibility check in the
804 -- case the actual type is class-wide or has access discriminants
805 -- (see RM 4.8(10.1) and AI12-0035).
807 pragma Unsuppress (Accessibility_Check);
810 Position.Node.Element := new Element_Type'(New_Item
);
822 (Container
: in out Set
;
823 New_Item
: Element_Type
;
824 Position
: out Cursor
;
825 Inserted
: out Boolean)
828 Insert
(Container
.HT
, New_Item
, Position
.Node
, Inserted
);
829 Position
.Container
:= Container
'Unchecked_Access;
833 (Container
: in out Set
;
834 New_Item
: Element_Type
)
837 pragma Unreferenced
(Position
);
842 Insert
(Container
, New_Item
, Position
, Inserted
);
845 raise Constraint_Error
with
846 "attempt to insert element already in set";
851 (HT
: in out Hash_Table_Type
;
852 New_Item
: Element_Type
;
853 Node
: out Node_Access
;
854 Inserted
: out Boolean)
856 function New_Node
(Next
: Node_Access
) return Node_Access
;
857 pragma Inline
(New_Node
);
859 procedure Local_Insert
is
860 new Element_Keys
.Generic_Conditional_Insert
(New_Node
);
866 function New_Node
(Next
: Node_Access
) return Node_Access
is
868 -- The element allocator may need an accessibility check in the case
869 -- the actual type is class-wide or has access discriminants (see
870 -- RM 4.8(10.1) and AI12-0035).
872 pragma Unsuppress
(Accessibility_Check
);
874 Element
: Element_Access
:= new Element_Type
'(New_Item);
877 return new Node_Type'(Element
, Next
);
881 Free_Element
(Element
);
885 -- Start of processing for Insert
888 if HT_Ops
.Capacity
(HT
) = 0 then
889 HT_Ops
.Reserve_Capacity
(HT
, 1);
892 Local_Insert
(HT
, New_Item
, Node
, Inserted
);
894 if Inserted
and then HT
.Length
> HT_Ops
.Capacity
(HT
) then
895 HT_Ops
.Reserve_Capacity
(HT
, HT
.Length
);
903 procedure Intersection
904 (Target
: in out Set
;
907 Tgt_Node
: Node_Access
;
910 if Target
'Address = Source
'Address then
914 if Source
.Length
= 0 then
919 if Target
.HT
.Busy
> 0 then
920 raise Program_Error
with
921 "attempt to tamper with cursors (set is busy)";
924 Tgt_Node
:= HT_Ops
.First
(Target
.HT
);
925 while Tgt_Node
/= null loop
926 if Is_In
(Source
.HT
, Tgt_Node
) then
927 Tgt_Node
:= HT_Ops
.Next
(Target
.HT
, Tgt_Node
);
931 X
: Node_Access
:= Tgt_Node
;
933 Tgt_Node
:= HT_Ops
.Next
(Target
.HT
, Tgt_Node
);
934 HT_Ops
.Delete_Node_Sans_Free
(Target
.HT
, X
);
941 function Intersection
(Left
, Right
: Set
) return Set
is
942 Buckets
: HT_Types
.Buckets_Access
;
946 if Left
'Address = Right
'Address then
950 Length
:= Count_Type
'Min (Left
.Length
, Right
.Length
);
957 Size
: constant Hash_Type
:= Prime_Numbers
.To_Prime
(Length
);
959 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
964 Iterate_Left
: declare
965 procedure Process
(L_Node
: Node_Access
);
968 new HT_Ops
.Generic_Iteration
(Process
);
974 procedure Process
(L_Node
: Node_Access
) is
976 if Is_In
(Right
.HT
, L_Node
) then
978 Src
: Element_Type
renames L_Node
.Element
.all;
980 Indx
: constant Hash_Type
:= Hash
(Src
) mod Buckets
'Length;
982 Bucket
: Node_Access
renames Buckets
(Indx
);
984 Tgt
: Element_Access
:= new Element_Type
'(Src);
987 Bucket := new Node_Type'(Tgt
, Bucket
);
994 Length
:= Length
+ 1;
998 -- Start of processing for Iterate_Left
1004 HT_Ops
.Free_Hash_Table
(Buckets
);
1008 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
1015 function Is_Empty
(Container
: Set
) return Boolean is
1017 return Container
.HT
.Length
= 0;
1024 function Is_In
(HT
: Hash_Table_Type
; Key
: Node_Access
) return Boolean is
1026 return Element_Keys
.Find
(HT
, Key
.Element
.all) /= null;
1035 Of_Set
: Set
) return Boolean
1037 Subset_Node
: Node_Access
;
1040 if Subset
'Address = Of_Set
'Address then
1044 if Subset
.Length
> Of_Set
.Length
then
1048 Subset_Node
:= HT_Ops
.First
(Subset
.HT
);
1049 while Subset_Node
/= null loop
1050 if not Is_In
(Of_Set
.HT
, Subset_Node
) then
1054 Subset_Node
:= HT_Ops
.Next
(Subset
.HT
, Subset_Node
);
1066 Process
: not null access procedure (Position
: Cursor
))
1068 procedure Process_Node
(Node
: Node_Access
);
1069 pragma Inline
(Process_Node
);
1071 procedure Iterate
is
1072 new HT_Ops
.Generic_Iteration
(Process_Node
);
1078 procedure Process_Node
(Node
: Node_Access
) is
1080 Process
(Cursor
'(Container'Unrestricted_Access, Node));
1083 B : Natural renames Container'Unrestricted_Access.all.HT.Busy;
1085 -- Start of processing for Iterate
1091 Iterate (Container.HT);
1101 function Iterate (Container : Set)
1102 return Set_Iterator_Interfaces.Forward_Iterator'Class
1104 B : Natural renames Container'Unrestricted_Access.all.HT.Busy;
1106 return It : constant Iterator :=
1107 Iterator'(Limited_Controlled
with
1108 Container
=> Container
'Unrestricted_Access)
1118 function Length
(Container
: Set
) return Count_Type
is
1120 return Container
.HT
.Length
;
1127 procedure Move
(Target
: in out Set
; Source
: in out Set
) is
1129 HT_Ops
.Move
(Target
=> Target
.HT
, Source
=> Source
.HT
);
1136 function Next
(Node
: Node_Access
) return Node_Access
is
1141 function Next
(Position
: Cursor
) return Cursor
is
1143 if Position
.Node
= null then
1147 if Position
.Node
.Element
= null then
1148 raise Program_Error
with "bad cursor in Next";
1151 pragma Assert
(Vet
(Position
), "bad cursor in Next");
1154 HT
: Hash_Table_Type
renames Position
.Container
.HT
;
1155 Node
: constant Node_Access
:= HT_Ops
.Next
(HT
, Position
.Node
);
1157 return (if Node
= null then No_Element
1158 else Cursor
'(Position.Container, Node));
1162 procedure Next (Position : in out Cursor) is
1164 Position := Next (Position);
1169 Position : Cursor) return Cursor
1172 if Position.Container = null then
1176 if Position.Container /= Object.Container then
1177 raise Program_Error with
1178 "Position cursor of Next designates wrong set";
1181 return Next (Position);
1188 function Overlap (Left, Right : Set) return Boolean is
1189 Left_Node : Node_Access;
1192 if Right.Length = 0 then
1196 if Left'Address = Right'Address then
1200 Left_Node := HT_Ops.First (Left.HT);
1201 while Left_Node /= null loop
1202 if Is_In (Right.HT, Left_Node) then
1206 Left_Node := HT_Ops.Next (Left.HT, Left_Node);
1216 procedure Query_Element
1218 Process : not null access procedure (Element : Element_Type))
1221 if Position.Node = null then
1222 raise Constraint_Error with
1223 "Position cursor of Query_Element equals No_Element";
1226 if Position.Node.Element = null then
1227 raise Program_Error with "bad cursor in Query_Element";
1230 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1233 HT : Hash_Table_Type renames
1234 Position.Container'Unrestricted_Access.all.HT;
1236 B : Natural renames HT.Busy;
1237 L : Natural renames HT.Lock;
1244 Process (Position.Node.Element.all);
1262 (Stream : not null access Root_Stream_Type'Class;
1263 Container : out Set)
1266 Read_Nodes (Stream, Container.HT);
1270 (Stream : not null access Root_Stream_Type'Class;
1274 raise Program_Error with "attempt to stream set cursor";
1278 (Stream : not null access Root_Stream_Type'Class;
1279 Item : out Constant_Reference_Type)
1282 raise Program_Error with "attempt to stream reference";
1290 (Stream : not null access Root_Stream_Type'Class) return Node_Access
1292 X : Element_Access := new Element_Type'(Element_Type
'Input (Stream
));
1294 return new Node_Type
'(X, null);
1306 (Container : in out Set;
1307 New_Item : Element_Type)
1309 Node : constant Node_Access :=
1310 Element_Keys.Find (Container.HT, New_Item);
1313 pragma Warnings (Off, X);
1317 raise Constraint_Error with
1318 "attempt to replace element not in set";
1321 if Container.HT.Lock > 0 then
1322 raise Program_Error with
1323 "attempt to tamper with elements (set is locked)";
1329 -- The element allocator may need an accessibility check in the case
1330 -- the actual type is class-wide or has access discriminants (see
1331 -- RM 4.8(10.1) and AI12-0035).
1333 pragma Unsuppress (Accessibility_Check);
1336 Node.Element := new Element_Type'(New_Item
);
1342 ---------------------
1343 -- Replace_Element --
1344 ---------------------
1346 procedure Replace_Element
1347 (Container
: in out Set
;
1349 New_Item
: Element_Type
)
1352 if Position
.Node
= null then
1353 raise Constraint_Error
with "Position cursor equals No_Element";
1356 if Position
.Node
.Element
= null then
1357 raise Program_Error
with "bad cursor in Replace_Element";
1360 if Position
.Container
/= Container
'Unrestricted_Access then
1361 raise Program_Error
with
1362 "Position cursor designates wrong set";
1365 pragma Assert
(Vet
(Position
), "bad cursor in Replace_Element");
1367 Replace_Element
(Container
.HT
, Position
.Node
, New_Item
);
1368 end Replace_Element
;
1370 ----------------------
1371 -- Reserve_Capacity --
1372 ----------------------
1374 procedure Reserve_Capacity
1375 (Container
: in out Set
;
1376 Capacity
: Count_Type
)
1379 HT_Ops
.Reserve_Capacity
(Container
.HT
, Capacity
);
1380 end Reserve_Capacity
;
1386 procedure Set_Next
(Node
: Node_Access
; Next
: Node_Access
) is
1391 --------------------------
1392 -- Symmetric_Difference --
1393 --------------------------
1395 procedure Symmetric_Difference
1396 (Target
: in out Set
;
1400 if Target
'Address = Source
'Address then
1405 if Target
.HT
.Busy
> 0 then
1406 raise Program_Error
with
1407 "attempt to tamper with cursors (set is busy)";
1411 N
: constant Count_Type
:= Target
.Length
+ Source
.Length
;
1413 if N
> HT_Ops
.Capacity
(Target
.HT
) then
1414 HT_Ops
.Reserve_Capacity
(Target
.HT
, N
);
1418 if Target
.Length
= 0 then
1419 Iterate_Source_When_Empty_Target
: declare
1420 procedure Process
(Src_Node
: Node_Access
);
1422 procedure Iterate
is new HT_Ops
.Generic_Iteration
(Process
);
1428 procedure Process
(Src_Node
: Node_Access
) is
1429 E
: Element_Type
renames Src_Node
.Element
.all;
1430 B
: Buckets_Type
renames Target
.HT
.Buckets
.all;
1431 J
: constant Hash_Type
:= Hash
(E
) mod B
'Length;
1432 N
: Count_Type
renames Target
.HT
.Length
;
1436 X
: Element_Access
:= new Element_Type
'(E);
1438 B (J) := new Node_Type'(X
, B
(J
));
1448 -- Start of processing for Iterate_Source_When_Empty_Target
1451 Iterate
(Source
.HT
);
1452 end Iterate_Source_When_Empty_Target
;
1455 Iterate_Source
: declare
1456 procedure Process
(Src_Node
: Node_Access
);
1458 procedure Iterate
is
1459 new HT_Ops
.Generic_Iteration
(Process
);
1465 procedure Process
(Src_Node
: Node_Access
) is
1466 E
: Element_Type
renames Src_Node
.Element
.all;
1467 B
: Buckets_Type
renames Target
.HT
.Buckets
.all;
1468 J
: constant Hash_Type
:= Hash
(E
) mod B
'Length;
1469 N
: Count_Type
renames Target
.HT
.Length
;
1472 if B
(J
) = null then
1474 X
: Element_Access
:= new Element_Type
'(E);
1476 B (J) := new Node_Type'(X
, null);
1485 elsif Equivalent_Elements
(E
, B
(J
).Element
.all) then
1487 X
: Node_Access
:= B
(J
);
1489 B
(J
) := B
(J
).Next
;
1496 Prev
: Node_Access
:= B
(J
);
1497 Curr
: Node_Access
:= Prev
.Next
;
1500 while Curr
/= null loop
1501 if Equivalent_Elements
(E
, Curr
.Element
.all) then
1502 Prev
.Next
:= Curr
.Next
;
1513 X
: Element_Access
:= new Element_Type
'(E);
1515 B (J) := new Node_Type'(X
, B
(J
));
1527 -- Start of processing for Iterate_Source
1530 Iterate
(Source
.HT
);
1533 end Symmetric_Difference
;
1535 function Symmetric_Difference
(Left
, Right
: Set
) return Set
is
1536 Buckets
: HT_Types
.Buckets_Access
;
1537 Length
: Count_Type
;
1540 if Left
'Address = Right
'Address then
1544 if Right
.Length
= 0 then
1548 if Left
.Length
= 0 then
1553 Size
: constant Hash_Type
:=
1554 Prime_Numbers
.To_Prime
(Left
.Length
+ Right
.Length
);
1556 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
1561 Iterate_Left
: declare
1562 procedure Process
(L_Node
: Node_Access
);
1564 procedure Iterate
is
1565 new HT_Ops
.Generic_Iteration
(Process
);
1571 procedure Process
(L_Node
: Node_Access
) is
1573 if not Is_In
(Right
.HT
, L_Node
) then
1575 E
: Element_Type
renames L_Node
.Element
.all;
1576 J
: constant Hash_Type
:= Hash
(E
) mod Buckets
'Length;
1580 X
: Element_Access
:= new Element_Type
'(E);
1582 Buckets (J) := new Node_Type'(X
, Buckets
(J
));
1589 Length
:= Length
+ 1;
1594 -- Start of processing for Iterate_Left
1600 HT_Ops
.Free_Hash_Table
(Buckets
);
1604 Iterate_Right
: declare
1605 procedure Process
(R_Node
: Node_Access
);
1607 procedure Iterate
is
1608 new HT_Ops
.Generic_Iteration
(Process
);
1614 procedure Process
(R_Node
: Node_Access
) is
1616 if not Is_In
(Left
.HT
, R_Node
) then
1618 E
: Element_Type
renames R_Node
.Element
.all;
1619 J
: constant Hash_Type
:= Hash
(E
) mod Buckets
'Length;
1623 X
: Element_Access
:= new Element_Type
'(E);
1625 Buckets (J) := new Node_Type'(X
, Buckets
(J
));
1632 Length
:= Length
+ 1;
1637 -- Start of processing for Iterate_Right
1643 HT_Ops
.Free_Hash_Table
(Buckets
);
1647 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
1648 end Symmetric_Difference
;
1654 function To_Set
(New_Item
: Element_Type
) return Set
is
1655 HT
: Hash_Table_Type
;
1658 pragma Unreferenced
(Node
, Inserted
);
1660 Insert
(HT
, New_Item
, Node
, Inserted
);
1661 return Set
'(Controlled with HT);
1669 (Target : in out Set;
1672 procedure Process (Src_Node : Node_Access);
1674 procedure Iterate is
1675 new HT_Ops.Generic_Iteration (Process);
1681 procedure Process (Src_Node : Node_Access) is
1682 Src : Element_Type renames Src_Node.Element.all;
1684 function New_Node (Next : Node_Access) return Node_Access;
1685 pragma Inline (New_Node);
1688 new Element_Keys.Generic_Conditional_Insert (New_Node);
1694 function New_Node (Next : Node_Access) return Node_Access is
1695 Tgt : Element_Access := new Element_Type'(Src
);
1697 return new Node_Type
'(Tgt, Next);
1704 Tgt_Node : Node_Access;
1706 pragma Unreferenced (Tgt_Node, Success);
1708 -- Start of processing for Process
1711 Insert (Target.HT, Src, Tgt_Node, Success);
1714 -- Start of processing for Union
1717 if Target'Address = Source'Address then
1721 if Target.HT.Busy > 0 then
1722 raise Program_Error with
1723 "attempt to tamper with cursors (set is busy)";
1727 N : constant Count_Type := Target.Length + Source.Length;
1729 if N > HT_Ops.Capacity (Target.HT) then
1730 HT_Ops.Reserve_Capacity (Target.HT, N);
1734 Iterate (Source.HT);
1737 function Union (Left, Right : Set) return Set is
1738 Buckets : HT_Types.Buckets_Access;
1739 Length : Count_Type;
1742 if Left'Address = Right'Address then
1746 if Right.Length = 0 then
1750 if Left.Length = 0 then
1755 Size : constant Hash_Type :=
1756 Prime_Numbers.To_Prime (Left.Length + Right.Length);
1758 Buckets := HT_Ops.New_Buckets (Length => Size);
1761 Iterate_Left : declare
1762 procedure Process (L_Node : Node_Access);
1764 procedure Iterate is
1765 new HT_Ops.Generic_Iteration (Process);
1771 procedure Process (L_Node : Node_Access) is
1772 Src : Element_Type renames L_Node.Element.all;
1773 J : constant Hash_Type := Hash (Src) mod Buckets'Length;
1774 Bucket : Node_Access renames Buckets (J);
1775 Tgt : Element_Access := new Element_Type'(Src
);
1777 Bucket
:= new Node_Type
'(Tgt, Bucket);
1784 -- Start of processing for Process
1790 HT_Ops.Free_Hash_Table (Buckets);
1794 Length := Left.Length;
1796 Iterate_Right : declare
1797 procedure Process (Src_Node : Node_Access);
1799 procedure Iterate is
1800 new HT_Ops.Generic_Iteration (Process);
1806 procedure Process (Src_Node : Node_Access) is
1807 Src : Element_Type renames Src_Node.Element.all;
1808 Idx : constant Hash_Type := Hash (Src) mod Buckets'Length;
1810 Tgt_Node : Node_Access := Buckets (Idx);
1813 while Tgt_Node /= null loop
1814 if Equivalent_Elements (Src, Tgt_Node.Element.all) then
1817 Tgt_Node := Next (Tgt_Node);
1821 Tgt : Element_Access := new Element_Type'(Src
);
1823 Buckets
(Idx
) := new Node_Type
'(Tgt, Buckets (Idx));
1830 Length := Length + 1;
1833 -- Start of processing for Iterate_Right
1839 HT_Ops.Free_Hash_Table (Buckets);
1843 return (Controlled with HT => (Buckets, Length, 0, 0));
1850 function Vet (Position : Cursor) return Boolean is
1852 if Position.Node = null then
1853 return Position.Container = null;
1856 if Position.Container = null then
1860 if Position.Node.Next = Position.Node then
1864 if Position.Node.Element = null then
1869 HT : Hash_Table_Type renames Position.Container.HT;
1873 if HT.Length = 0 then
1877 if HT.Buckets = null
1878 or else HT.Buckets'Length = 0
1883 X := HT.Buckets (Element_Keys.Index (HT, Position.Node.Element.all));
1885 for J in 1 .. HT.Length loop
1886 if X = Position.Node then
1894 if X = X.Next then -- to prevent unnecessary looping
1910 (Stream : not null access Root_Stream_Type'Class;
1914 Write_Nodes (Stream, Container.HT);
1918 (Stream : not null access Root_Stream_Type'Class;
1922 raise Program_Error with "attempt to stream set cursor";
1926 (Stream : not null access Root_Stream_Type'Class;
1927 Item : Constant_Reference_Type)
1930 raise Program_Error with "attempt to stream reference";
1937 procedure Write_Node
1938 (Stream : not null access Root_Stream_Type'Class;
1942 Element_Type'Output (Stream, Node.Element.all);
1945 package body Generic_Keys is
1947 -----------------------
1948 -- Local Subprograms --
1949 -----------------------
1951 function Equivalent_Key_Node
1953 Node : Node_Access) return Boolean;
1954 pragma Inline (Equivalent_Key_Node);
1956 --------------------------
1957 -- Local Instantiations --
1958 --------------------------
1961 new Hash_Tables.Generic_Keys
1962 (HT_Types => HT_Types,
1964 Set_Next => Set_Next,
1965 Key_Type => Key_Type,
1967 Equivalent_Keys => Equivalent_Key_Node);
1969 ------------------------
1970 -- Constant_Reference --
1971 ------------------------
1973 function Constant_Reference
1974 (Container : aliased Set;
1975 Key : Key_Type) return Constant_Reference_Type
1977 Node : constant Node_Access :=
1978 Key_Keys.Find (Container.HT, Key);
1982 raise Constraint_Error with "Key not in set";
1985 if Node.Element = null then
1986 raise Program_Error with "Node has no element";
1990 HT : Hash_Table_Type renames Container'Unrestricted_Access.all.HT;
1991 B : Natural renames HT.Busy;
1992 L : Natural renames HT.Lock;
1994 return R : constant Constant_Reference_Type :=
1995 (Element => Node.Element.all'Access,
1996 Control => (Controlled with Container'Unrestricted_Access))
2002 end Constant_Reference;
2010 Key : Key_Type) return Boolean
2013 return Find (Container, Key) /= No_Element;
2021 (Container : in out Set;
2027 Key_Keys.Delete_Key_Sans_Free (Container.HT, Key, X);
2030 raise Constraint_Error with "key not in map"; -- ??? "set"
2042 Key : Key_Type) return Element_Type
2044 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
2048 raise Constraint_Error with "key not in map"; -- ??? "set"
2051 return Node.Element.all;
2054 -------------------------
2055 -- Equivalent_Key_Node --
2056 -------------------------
2058 function Equivalent_Key_Node
2060 Node : Node_Access) return Boolean is
2062 return Equivalent_Keys (Key, Generic_Keys.Key (Node.Element.all));
2063 end Equivalent_Key_Node;
2070 (Container : in out Set;
2075 Key_Keys.Delete_Key_Sans_Free (Container.HT, Key, X);
2085 Key : Key_Type) return Cursor
2087 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
2089 return (if Node = null then No_Element
2090 else Cursor'(Container
'Unrestricted_Access, Node
));
2097 function Key
(Position
: Cursor
) return Key_Type
is
2099 if Position
.Node
= null then
2100 raise Constraint_Error
with
2101 "Position cursor equals No_Element";
2104 if Position
.Node
.Element
= null then
2105 raise Program_Error
with "Position cursor is bad";
2108 pragma Assert
(Vet
(Position
), "bad cursor in function Key");
2110 return Key
(Position
.Node
.Element
.all);
2118 (Stream
: not null access Root_Stream_Type
'Class;
2119 Item
: out Reference_Type
)
2122 raise Program_Error
with "attempt to stream reference";
2125 ------------------------------
2126 -- Reference_Preserving_Key --
2127 ------------------------------
2129 function Reference_Preserving_Key
2130 (Container
: aliased in out Set
;
2131 Position
: Cursor
) return Reference_Type
2134 if Position
.Container
= null then
2135 raise Constraint_Error
with "Position cursor has no element";
2138 if Position
.Container
/= Container
'Unrestricted_Access then
2139 raise Program_Error
with
2140 "Position cursor designates wrong container";
2143 if Position
.Node
.Element
= null then
2144 raise Program_Error
with "Node has no element";
2149 "bad cursor in function Reference_Preserving_Key");
2151 -- Some form of finalization will be required in order to actually
2152 -- check that the key-part of the element designated by Position has
2155 return (Element
=> Position
.Node
.Element
.all'Access);
2156 end Reference_Preserving_Key
;
2158 function Reference_Preserving_Key
2159 (Container
: aliased in out Set
;
2160 Key
: Key_Type
) return Reference_Type
2162 Node
: constant Node_Access
:=
2163 Key_Keys
.Find
(Container
.HT
, Key
);
2167 raise Constraint_Error
with "Key not in set";
2170 if Node
.Element
= null then
2171 raise Program_Error
with "Node has no element";
2174 -- Some form of finalization will be required in order to actually
2175 -- check that the key-part of the element designated by Key has not
2178 return (Element
=> Node
.Element
.all'Access);
2179 end Reference_Preserving_Key
;
2186 (Container
: in out Set
;
2188 New_Item
: Element_Type
)
2190 Node
: constant Node_Access
:=
2191 Key_Keys
.Find
(Container
.HT
, Key
);
2195 raise Constraint_Error
with
2196 "attempt to replace key not in set";
2199 Replace_Element
(Container
.HT
, Node
, New_Item
);
2202 -----------------------------------
2203 -- Update_Element_Preserving_Key --
2204 -----------------------------------
2206 procedure Update_Element_Preserving_Key
2207 (Container
: in out Set
;
2209 Process
: not null access
2210 procedure (Element
: in out Element_Type
))
2212 HT
: Hash_Table_Type
renames Container
.HT
;
2216 if Position
.Node
= null then
2217 raise Constraint_Error
with
2218 "Position cursor equals No_Element";
2221 if Position
.Node
.Element
= null
2222 or else Position
.Node
.Next
= Position
.Node
2224 raise Program_Error
with "Position cursor is bad";
2227 if Position
.Container
/= Container
'Unrestricted_Access then
2228 raise Program_Error
with
2229 "Position cursor designates wrong set";
2232 if HT
.Buckets
= null
2233 or else HT
.Buckets
'Length = 0
2234 or else HT
.Length
= 0
2236 raise Program_Error
with "Position cursor is bad (set is empty)";
2241 "bad cursor in Update_Element_Preserving_Key");
2243 Indx
:= HT_Ops
.Index
(HT
, Position
.Node
);
2246 E
: Element_Type
renames Position
.Node
.Element
.all;
2247 K
: constant Key_Type
:= Key
(E
);
2249 B
: Natural renames HT
.Busy
;
2250 L
: Natural renames HT
.Lock
;
2268 if Equivalent_Keys
(K
, Key
(E
)) then
2269 pragma Assert
(Hash
(K
) = Hash
(E
));
2274 if HT
.Buckets
(Indx
) = Position
.Node
then
2275 HT
.Buckets
(Indx
) := Position
.Node
.Next
;
2279 Prev
: Node_Access
:= HT
.Buckets
(Indx
);
2282 while Prev
.Next
/= Position
.Node
loop
2286 raise Program_Error
with
2287 "Position cursor is bad (node not found)";
2291 Prev
.Next
:= Position
.Node
.Next
;
2295 HT
.Length
:= HT
.Length
- 1;
2298 X
: Node_Access
:= Position
.Node
;
2304 raise Program_Error
with "key was modified";
2305 end Update_Element_Preserving_Key
;
2312 (Stream
: not null access Root_Stream_Type
'Class;
2313 Item
: Reference_Type
)
2316 raise Program_Error
with "attempt to stream reference";
2321 end Ada
.Containers
.Indefinite_Hashed_Sets
;