1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- ADA.CONTAINERS.INDEFINITE_HASHED_SETS --
9 -- Copyright (C) 2004-2012, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- This unit was originally developed by Matthew J Heaney. --
28 ------------------------------------------------------------------------------
30 with Ada
.Unchecked_Deallocation
;
32 with Ada
.Containers
.Hash_Tables
.Generic_Operations
;
33 pragma Elaborate_All
(Ada
.Containers
.Hash_Tables
.Generic_Operations
);
35 with Ada
.Containers
.Hash_Tables
.Generic_Keys
;
36 pragma Elaborate_All
(Ada
.Containers
.Hash_Tables
.Generic_Keys
);
38 with Ada
.Containers
.Prime_Numbers
;
40 with System
; use type System
.Address
;
42 package body Ada
.Containers
.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
);
168 procedure Adjust
(Control
: in out Reference_Control_Type
) is
170 if Control
.Container
/= null then
172 HT
: Hash_Table_Type
renames Control
.Container
.all.HT
;
173 B
: Natural renames HT
.Busy
;
174 L
: Natural renames HT
.Lock
;
186 procedure Assign
(Node
: Node_Access
; Item
: Element_Type
) is
187 X
: Element_Access
:= Node
.Element
;
189 -- The element allocator may need an accessibility check in the case the
190 -- actual type is class-wide or has access discriminants (RM 4.8(10.1)
193 pragma Unsuppress
(Accessibility_Check
);
196 Node
.Element
:= new Element_Type
'(Item);
200 procedure Assign (Target : in out Set; Source : Set) is
202 if Target'Address = Source'Address then
206 Target.Union (Source);
214 function Capacity (Container : Set) return Count_Type is
216 return HT_Ops.Capacity (Container.HT);
223 procedure Clear (Container : in out Set) is
225 HT_Ops.Clear (Container.HT);
228 ------------------------
229 -- Constant_Reference --
230 ------------------------
232 function Constant_Reference
233 (Container : aliased Set;
234 Position : Cursor) return Constant_Reference_Type
237 if Position.Container = null then
238 raise Constraint_Error with "Position cursor has no element";
241 if Position.Container /= Container'Unrestricted_Access then
242 raise Program_Error with
243 "Position cursor designates wrong container";
246 if Position.Node.Element = null then
247 raise Program_Error with "Node has no element";
250 pragma Assert (Vet (Position), "bad cursor in Constant_Reference");
253 HT : Hash_Table_Type renames Position.Container.all.HT;
254 B : Natural renames HT.Busy;
255 L : Natural renames HT.Lock;
257 return R : constant Constant_Reference_Type :=
258 (Element => Position.Node.Element.all'Access,
260 (Controlled with Container'Unrestricted_Access))
266 end Constant_Reference;
272 function Contains (Container : Set; Item : Element_Type) return Boolean is
274 return Find (Container, Item) /= No_Element;
283 Capacity : Count_Type := 0) return Set
291 elsif Capacity >= Source.Length then
296 with "Requested capacity is less than Source length";
299 return Target : Set do
300 Target.Reserve_Capacity (C);
301 Target.Assign (Source);
309 function Copy_Node (Source : Node_Access) return Node_Access is
310 E : Element_Access := new Element_Type'(Source
.Element
.all);
312 return new Node_Type
'(Element => E, Next => null);
324 (Container : in out Set;
330 Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
333 raise Constraint_Error with "attempt to delete element not in set";
340 (Container : in out Set;
341 Position : in out Cursor)
344 if Position.Node = null then
345 raise Constraint_Error with "Position cursor equals No_Element";
348 if Position.Node.Element = null then
349 raise Program_Error with "Position cursor is bad";
352 if Position.Container /= Container'Unrestricted_Access then
353 raise Program_Error with "Position cursor designates wrong set";
356 if Container.HT.Busy > 0 then
357 raise Program_Error with
358 "attempt to tamper with cursors (set is busy)";
361 pragma Assert (Vet (Position), "Position cursor is bad");
363 HT_Ops.Delete_Node_Sans_Free (Container.HT, Position.Node);
365 Free (Position.Node);
366 Position.Container := null;
374 (Target : in out Set;
377 Tgt_Node : Node_Access;
380 if Target'Address = Source'Address then
385 if Source.HT.Length = 0 then
389 if Target.HT.Busy > 0 then
390 raise Program_Error with
391 "attempt to tamper with cursors (set is busy)";
394 if Source.HT.Length < Target.HT.Length then
396 Src_Node : Node_Access;
399 Src_Node := HT_Ops.First (Source.HT);
400 while Src_Node /= null loop
401 Tgt_Node := Element_Keys.Find (Target.HT, Src_Node.Element.all);
403 if Tgt_Node /= null then
404 HT_Ops.Delete_Node_Sans_Free (Target.HT, Tgt_Node);
408 Src_Node := HT_Ops.Next (Source.HT, Src_Node);
413 Tgt_Node := HT_Ops.First (Target.HT);
414 while Tgt_Node /= null loop
415 if Is_In (Source.HT, Tgt_Node) then
417 X : Node_Access := Tgt_Node;
419 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
420 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
425 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
431 function Difference (Left, Right : Set) return Set is
432 Buckets : HT_Types.Buckets_Access;
436 if Left'Address = Right'Address then
440 if Left.Length = 0 then
444 if Right.Length = 0 then
449 Size : constant Hash_Type := Prime_Numbers.To_Prime (Left.Length);
451 Buckets := HT_Ops.New_Buckets (Length => Size);
456 Iterate_Left : declare
457 procedure Process (L_Node : Node_Access);
460 new HT_Ops.Generic_Iteration (Process);
466 procedure Process (L_Node : Node_Access) is
468 if not Is_In (Right.HT, L_Node) then
470 Src : Element_Type renames L_Node.Element.all;
471 Indx : constant Hash_Type := Hash (Src) mod Buckets'Length;
472 Bucket : Node_Access renames Buckets (Indx);
473 Tgt : Element_Access := new Element_Type'(Src
);
475 Bucket
:= new Node_Type
'(Tgt, Bucket);
482 Length := Length + 1;
486 -- Start of processing for Iterate_Left
492 HT_Ops.Free_Hash_Table (Buckets);
496 return (Controlled with HT => (Buckets, Length, 0, 0));
503 function Element (Position : Cursor) return Element_Type is
505 if Position.Node = null then
506 raise Constraint_Error with "Position cursor of equals No_Element";
509 if Position.Node.Element = null then -- handle dangling reference
510 raise Program_Error with "Position cursor is bad";
513 pragma Assert (Vet (Position), "bad cursor in function Element");
515 return Position.Node.Element.all;
518 ---------------------
519 -- Equivalent_Sets --
520 ---------------------
522 function Equivalent_Sets (Left, Right : Set) return Boolean is
524 return Is_Equivalent (Left.HT, Right.HT);
527 -------------------------
528 -- Equivalent_Elements --
529 -------------------------
531 function Equivalent_Elements (Left, Right : Cursor) return Boolean is
533 if Left.Node = null then
534 raise Constraint_Error with
535 "Left cursor of Equivalent_Elements equals No_Element";
538 if Right.Node = null then
539 raise Constraint_Error with
540 "Right cursor of Equivalent_Elements equals No_Element";
543 if Left.Node.Element = null then
544 raise Program_Error with
545 "Left cursor of Equivalent_Elements is bad";
548 if Right.Node.Element = null then
549 raise Program_Error with
550 "Right cursor of Equivalent_Elements is bad";
553 pragma Assert (Vet (Left), "bad Left cursor in Equivalent_Elements");
554 pragma Assert (Vet (Right), "bad Right cursor in Equivalent_Elements");
556 return Equivalent_Elements
557 (Left.Node.Element.all,
558 Right.Node.Element.all);
559 end Equivalent_Elements;
561 function Equivalent_Elements
563 Right : Element_Type) return Boolean
566 if Left.Node = null then
567 raise Constraint_Error with
568 "Left cursor of Equivalent_Elements equals No_Element";
571 if Left.Node.Element = null then
572 raise Program_Error with
573 "Left cursor of Equivalent_Elements is bad";
576 pragma Assert (Vet (Left), "bad Left cursor in Equivalent_Elements");
578 return Equivalent_Elements (Left.Node.Element.all, Right);
579 end Equivalent_Elements;
581 function Equivalent_Elements
582 (Left : Element_Type;
583 Right : Cursor) return Boolean
586 if Right.Node = null then
587 raise Constraint_Error with
588 "Right cursor of Equivalent_Elements equals No_Element";
591 if Right.Node.Element = null then
592 raise Program_Error with
593 "Right cursor of Equivalent_Elements is bad";
596 pragma Assert (Vet (Right), "bad Right cursor in Equivalent_Elements");
598 return Equivalent_Elements (Left, Right.Node.Element.all);
599 end Equivalent_Elements;
601 ---------------------
602 -- Equivalent_Keys --
603 ---------------------
605 function Equivalent_Keys
607 Node : Node_Access) return Boolean
610 return Equivalent_Elements (Key, Node.Element.all);
618 (Container : in out Set;
623 Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
631 procedure Finalize (Container : in out Set) is
633 HT_Ops.Finalize (Container.HT);
636 procedure Finalize (Object : in out Iterator) is
638 if Object.Container /= null then
640 B : Natural renames Object.Container.all.HT.Busy;
647 procedure Finalize (Control : in out Reference_Control_Type) is
649 if Control.Container /= null then
651 HT : Hash_Table_Type renames Control.Container.all.HT;
652 B : Natural renames HT.Busy;
653 L : Natural renames HT.Lock;
659 Control.Container := null;
669 Item : Element_Type) return Cursor
671 Node : constant Node_Access := Element_Keys.Find (Container.HT, Item);
673 return (if Node = null then No_Element
674 else Cursor'(Container
'Unrestricted_Access, Node
));
681 function Find_Equal_Key
682 (R_HT
: Hash_Table_Type
;
683 L_Node
: Node_Access
) return Boolean
685 R_Index
: constant Hash_Type
:=
686 Element_Keys
.Index
(R_HT
, L_Node
.Element
.all);
688 R_Node
: Node_Access
:= R_HT
.Buckets
(R_Index
);
692 if R_Node
= null then
696 if L_Node
.Element
.all = R_Node
.Element
.all then
700 R_Node
:= Next
(R_Node
);
704 -------------------------
705 -- Find_Equivalent_Key --
706 -------------------------
708 function Find_Equivalent_Key
709 (R_HT
: Hash_Table_Type
;
710 L_Node
: Node_Access
) return Boolean
712 R_Index
: constant Hash_Type
:=
713 Element_Keys
.Index
(R_HT
, L_Node
.Element
.all);
715 R_Node
: Node_Access
:= R_HT
.Buckets
(R_Index
);
719 if R_Node
= null then
723 if Equivalent_Elements
(L_Node
.Element
.all, R_Node
.Element
.all) then
727 R_Node
:= Next
(R_Node
);
729 end Find_Equivalent_Key
;
735 function First
(Container
: Set
) return Cursor
is
736 Node
: constant Node_Access
:= HT_Ops
.First
(Container
.HT
);
738 return (if Node
= null then No_Element
739 else Cursor
'(Container'Unrestricted_Access, Node));
742 function First (Object : Iterator) return Cursor is
744 return Object.Container.First;
751 procedure Free (X : in out Node_Access) is
752 procedure Deallocate is
753 new Ada.Unchecked_Deallocation (Node_Type, Node_Access);
760 X.Next := X; -- detect mischief (in Vet)
763 Free_Element (X.Element);
778 function Has_Element (Position : Cursor) return Boolean is
780 pragma Assert (Vet (Position), "bad cursor in Has_Element");
781 return Position.Node /= null;
788 function Hash_Node (Node : Node_Access) return Hash_Type is
790 return Hash (Node.Element.all);
798 (Container : in out Set;
799 New_Item : Element_Type)
807 Insert (Container, New_Item, Position, Inserted);
810 if Container.HT.Lock > 0 then
811 raise Program_Error with
812 "attempt to tamper with elements (set is locked)";
815 X := Position.Node.Element;
818 -- The element allocator may need an accessibility check in the
819 -- case the actual type is class-wide or has access discriminants
820 -- (see RM 4.8(10.1) and AI12-0035).
822 pragma Unsuppress (Accessibility_Check);
825 Position.Node.Element := new Element_Type'(New_Item
);
837 (Container
: in out Set
;
838 New_Item
: Element_Type
;
839 Position
: out Cursor
;
840 Inserted
: out Boolean)
843 Insert
(Container
.HT
, New_Item
, Position
.Node
, Inserted
);
844 Position
.Container
:= Container
'Unchecked_Access;
848 (Container
: in out Set
;
849 New_Item
: Element_Type
)
852 pragma Unreferenced
(Position
);
857 Insert
(Container
, New_Item
, Position
, Inserted
);
860 raise Constraint_Error
with
861 "attempt to insert element already in set";
866 (HT
: in out Hash_Table_Type
;
867 New_Item
: Element_Type
;
868 Node
: out Node_Access
;
869 Inserted
: out Boolean)
871 function New_Node
(Next
: Node_Access
) return Node_Access
;
872 pragma Inline
(New_Node
);
874 procedure Local_Insert
is
875 new Element_Keys
.Generic_Conditional_Insert
(New_Node
);
881 function New_Node
(Next
: Node_Access
) return Node_Access
is
883 -- The element allocator may need an accessibility check in the case
884 -- the actual type is class-wide or has access discriminants (see
885 -- RM 4.8(10.1) and AI12-0035).
887 pragma Unsuppress
(Accessibility_Check
);
889 Element
: Element_Access
:= new Element_Type
'(New_Item);
892 return new Node_Type'(Element
, Next
);
896 Free_Element
(Element
);
900 -- Start of processing for Insert
903 if HT_Ops
.Capacity
(HT
) = 0 then
904 HT_Ops
.Reserve_Capacity
(HT
, 1);
907 Local_Insert
(HT
, New_Item
, Node
, Inserted
);
909 if Inserted
and then HT
.Length
> HT_Ops
.Capacity
(HT
) then
910 HT_Ops
.Reserve_Capacity
(HT
, HT
.Length
);
918 procedure Intersection
919 (Target
: in out Set
;
922 Tgt_Node
: Node_Access
;
925 if Target
'Address = Source
'Address then
929 if Source
.Length
= 0 then
934 if Target
.HT
.Busy
> 0 then
935 raise Program_Error
with
936 "attempt to tamper with cursors (set is busy)";
939 Tgt_Node
:= HT_Ops
.First
(Target
.HT
);
940 while Tgt_Node
/= null loop
941 if Is_In
(Source
.HT
, Tgt_Node
) then
942 Tgt_Node
:= HT_Ops
.Next
(Target
.HT
, Tgt_Node
);
946 X
: Node_Access
:= Tgt_Node
;
948 Tgt_Node
:= HT_Ops
.Next
(Target
.HT
, Tgt_Node
);
949 HT_Ops
.Delete_Node_Sans_Free
(Target
.HT
, X
);
956 function Intersection
(Left
, Right
: Set
) return Set
is
957 Buckets
: HT_Types
.Buckets_Access
;
961 if Left
'Address = Right
'Address then
965 Length
:= Count_Type
'Min (Left
.Length
, Right
.Length
);
972 Size
: constant Hash_Type
:= Prime_Numbers
.To_Prime
(Length
);
974 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
979 Iterate_Left
: declare
980 procedure Process
(L_Node
: Node_Access
);
983 new HT_Ops
.Generic_Iteration
(Process
);
989 procedure Process
(L_Node
: Node_Access
) is
991 if Is_In
(Right
.HT
, L_Node
) then
993 Src
: Element_Type
renames L_Node
.Element
.all;
995 Indx
: constant Hash_Type
:= Hash
(Src
) mod Buckets
'Length;
997 Bucket
: Node_Access
renames Buckets
(Indx
);
999 Tgt
: Element_Access
:= new Element_Type
'(Src);
1002 Bucket := new Node_Type'(Tgt
, Bucket
);
1009 Length
:= Length
+ 1;
1013 -- Start of processing for Iterate_Left
1019 HT_Ops
.Free_Hash_Table
(Buckets
);
1023 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
1030 function Is_Empty
(Container
: Set
) return Boolean is
1032 return Container
.HT
.Length
= 0;
1039 function Is_In
(HT
: Hash_Table_Type
; Key
: Node_Access
) return Boolean is
1041 return Element_Keys
.Find
(HT
, Key
.Element
.all) /= null;
1050 Of_Set
: Set
) return Boolean
1052 Subset_Node
: Node_Access
;
1055 if Subset
'Address = Of_Set
'Address then
1059 if Subset
.Length
> Of_Set
.Length
then
1063 Subset_Node
:= HT_Ops
.First
(Subset
.HT
);
1064 while Subset_Node
/= null loop
1065 if not Is_In
(Of_Set
.HT
, Subset_Node
) then
1069 Subset_Node
:= HT_Ops
.Next
(Subset
.HT
, Subset_Node
);
1081 Process
: not null access procedure (Position
: Cursor
))
1083 procedure Process_Node
(Node
: Node_Access
);
1084 pragma Inline
(Process_Node
);
1086 procedure Iterate
is
1087 new HT_Ops
.Generic_Iteration
(Process_Node
);
1093 procedure Process_Node
(Node
: Node_Access
) is
1095 Process
(Cursor
'(Container'Unrestricted_Access, Node));
1098 B : Natural renames Container'Unrestricted_Access.all.HT.Busy;
1100 -- Start of processing for Iterate
1106 Iterate (Container.HT);
1116 function Iterate (Container : Set)
1117 return Set_Iterator_Interfaces.Forward_Iterator'Class
1119 B : Natural renames Container'Unrestricted_Access.all.HT.Busy;
1121 return It : constant Iterator :=
1122 Iterator'(Limited_Controlled
with
1123 Container
=> Container
'Unrestricted_Access)
1133 function Length
(Container
: Set
) return Count_Type
is
1135 return Container
.HT
.Length
;
1142 procedure Move
(Target
: in out Set
; Source
: in out Set
) is
1144 HT_Ops
.Move
(Target
=> Target
.HT
, Source
=> Source
.HT
);
1151 function Next
(Node
: Node_Access
) return Node_Access
is
1156 function Next
(Position
: Cursor
) return Cursor
is
1158 if Position
.Node
= null then
1162 if Position
.Node
.Element
= null then
1163 raise Program_Error
with "bad cursor in Next";
1166 pragma Assert
(Vet
(Position
), "bad cursor in Next");
1169 HT
: Hash_Table_Type
renames Position
.Container
.HT
;
1170 Node
: constant Node_Access
:= HT_Ops
.Next
(HT
, Position
.Node
);
1172 return (if Node
= null then No_Element
1173 else Cursor
'(Position.Container, Node));
1177 procedure Next (Position : in out Cursor) is
1179 Position := Next (Position);
1184 Position : Cursor) return Cursor
1187 if Position.Container = null then
1191 if Position.Container /= Object.Container then
1192 raise Program_Error with
1193 "Position cursor of Next designates wrong set";
1196 return Next (Position);
1203 function Overlap (Left, Right : Set) return Boolean is
1204 Left_Node : Node_Access;
1207 if Right.Length = 0 then
1211 if Left'Address = Right'Address then
1215 Left_Node := HT_Ops.First (Left.HT);
1216 while Left_Node /= null loop
1217 if Is_In (Right.HT, Left_Node) then
1221 Left_Node := HT_Ops.Next (Left.HT, Left_Node);
1231 procedure Query_Element
1233 Process : not null access procedure (Element : Element_Type))
1236 if Position.Node = null then
1237 raise Constraint_Error with
1238 "Position cursor of Query_Element equals No_Element";
1241 if Position.Node.Element = null then
1242 raise Program_Error with "bad cursor in Query_Element";
1245 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1248 HT : Hash_Table_Type renames
1249 Position.Container'Unrestricted_Access.all.HT;
1251 B : Natural renames HT.Busy;
1252 L : Natural renames HT.Lock;
1259 Process (Position.Node.Element.all);
1277 (Stream : not null access Root_Stream_Type'Class;
1278 Container : out Set)
1281 Read_Nodes (Stream, Container.HT);
1285 (Stream : not null access Root_Stream_Type'Class;
1289 raise Program_Error with "attempt to stream set cursor";
1293 (Stream : not null access Root_Stream_Type'Class;
1294 Item : out Constant_Reference_Type)
1297 raise Program_Error with "attempt to stream reference";
1305 (Stream : not null access Root_Stream_Type'Class) return Node_Access
1307 X : Element_Access := new Element_Type'(Element_Type
'Input (Stream
));
1309 return new Node_Type
'(X, null);
1321 (Container : in out Set;
1322 New_Item : Element_Type)
1324 Node : constant Node_Access :=
1325 Element_Keys.Find (Container.HT, New_Item);
1328 pragma Warnings (Off, X);
1332 raise Constraint_Error with
1333 "attempt to replace element not in set";
1336 if Container.HT.Lock > 0 then
1337 raise Program_Error with
1338 "attempt to tamper with elements (set is locked)";
1344 -- The element allocator may need an accessibility check in the case
1345 -- the actual type is class-wide or has access discriminants (see
1346 -- RM 4.8(10.1) and AI12-0035).
1348 pragma Unsuppress (Accessibility_Check);
1351 Node.Element := new Element_Type'(New_Item
);
1357 ---------------------
1358 -- Replace_Element --
1359 ---------------------
1361 procedure Replace_Element
1362 (Container
: in out Set
;
1364 New_Item
: Element_Type
)
1367 if Position
.Node
= null then
1368 raise Constraint_Error
with "Position cursor equals No_Element";
1371 if Position
.Node
.Element
= null then
1372 raise Program_Error
with "bad cursor in Replace_Element";
1375 if Position
.Container
/= Container
'Unrestricted_Access then
1376 raise Program_Error
with
1377 "Position cursor designates wrong set";
1380 pragma Assert
(Vet
(Position
), "bad cursor in Replace_Element");
1382 Replace_Element
(Container
.HT
, Position
.Node
, New_Item
);
1383 end Replace_Element
;
1385 ----------------------
1386 -- Reserve_Capacity --
1387 ----------------------
1389 procedure Reserve_Capacity
1390 (Container
: in out Set
;
1391 Capacity
: Count_Type
)
1394 HT_Ops
.Reserve_Capacity
(Container
.HT
, Capacity
);
1395 end Reserve_Capacity
;
1401 procedure Set_Next
(Node
: Node_Access
; Next
: Node_Access
) is
1406 --------------------------
1407 -- Symmetric_Difference --
1408 --------------------------
1410 procedure Symmetric_Difference
1411 (Target
: in out Set
;
1415 if Target
'Address = Source
'Address then
1420 if Target
.HT
.Busy
> 0 then
1421 raise Program_Error
with
1422 "attempt to tamper with cursors (set is busy)";
1426 N
: constant Count_Type
:= Target
.Length
+ Source
.Length
;
1428 if N
> HT_Ops
.Capacity
(Target
.HT
) then
1429 HT_Ops
.Reserve_Capacity
(Target
.HT
, N
);
1433 if Target
.Length
= 0 then
1434 Iterate_Source_When_Empty_Target
: declare
1435 procedure Process
(Src_Node
: Node_Access
);
1437 procedure Iterate
is new HT_Ops
.Generic_Iteration
(Process
);
1443 procedure Process
(Src_Node
: Node_Access
) is
1444 E
: Element_Type
renames Src_Node
.Element
.all;
1445 B
: Buckets_Type
renames Target
.HT
.Buckets
.all;
1446 J
: constant Hash_Type
:= Hash
(E
) mod B
'Length;
1447 N
: Count_Type
renames Target
.HT
.Length
;
1451 X
: Element_Access
:= new Element_Type
'(E);
1453 B (J) := new Node_Type'(X
, B
(J
));
1463 -- Start of processing for Iterate_Source_When_Empty_Target
1466 Iterate
(Source
.HT
);
1467 end Iterate_Source_When_Empty_Target
;
1470 Iterate_Source
: declare
1471 procedure Process
(Src_Node
: Node_Access
);
1473 procedure Iterate
is
1474 new HT_Ops
.Generic_Iteration
(Process
);
1480 procedure Process
(Src_Node
: Node_Access
) is
1481 E
: Element_Type
renames Src_Node
.Element
.all;
1482 B
: Buckets_Type
renames Target
.HT
.Buckets
.all;
1483 J
: constant Hash_Type
:= Hash
(E
) mod B
'Length;
1484 N
: Count_Type
renames Target
.HT
.Length
;
1487 if B
(J
) = null then
1489 X
: Element_Access
:= new Element_Type
'(E);
1491 B (J) := new Node_Type'(X
, null);
1500 elsif Equivalent_Elements
(E
, B
(J
).Element
.all) then
1502 X
: Node_Access
:= B
(J
);
1504 B
(J
) := B
(J
).Next
;
1511 Prev
: Node_Access
:= B
(J
);
1512 Curr
: Node_Access
:= Prev
.Next
;
1515 while Curr
/= null loop
1516 if Equivalent_Elements
(E
, Curr
.Element
.all) then
1517 Prev
.Next
:= Curr
.Next
;
1528 X
: Element_Access
:= new Element_Type
'(E);
1530 B (J) := new Node_Type'(X
, B
(J
));
1542 -- Start of processing for Iterate_Source
1545 Iterate
(Source
.HT
);
1548 end Symmetric_Difference
;
1550 function Symmetric_Difference
(Left
, Right
: Set
) return Set
is
1551 Buckets
: HT_Types
.Buckets_Access
;
1552 Length
: Count_Type
;
1555 if Left
'Address = Right
'Address then
1559 if Right
.Length
= 0 then
1563 if Left
.Length
= 0 then
1568 Size
: constant Hash_Type
:=
1569 Prime_Numbers
.To_Prime
(Left
.Length
+ Right
.Length
);
1571 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
1576 Iterate_Left
: declare
1577 procedure Process
(L_Node
: Node_Access
);
1579 procedure Iterate
is
1580 new HT_Ops
.Generic_Iteration
(Process
);
1586 procedure Process
(L_Node
: Node_Access
) is
1588 if not Is_In
(Right
.HT
, L_Node
) then
1590 E
: Element_Type
renames L_Node
.Element
.all;
1591 J
: constant Hash_Type
:= Hash
(E
) mod Buckets
'Length;
1595 X
: Element_Access
:= new Element_Type
'(E);
1597 Buckets (J) := new Node_Type'(X
, Buckets
(J
));
1604 Length
:= Length
+ 1;
1609 -- Start of processing for Iterate_Left
1615 HT_Ops
.Free_Hash_Table
(Buckets
);
1619 Iterate_Right
: declare
1620 procedure Process
(R_Node
: Node_Access
);
1622 procedure Iterate
is
1623 new HT_Ops
.Generic_Iteration
(Process
);
1629 procedure Process
(R_Node
: Node_Access
) is
1631 if not Is_In
(Left
.HT
, R_Node
) then
1633 E
: Element_Type
renames R_Node
.Element
.all;
1634 J
: constant Hash_Type
:= Hash
(E
) mod Buckets
'Length;
1638 X
: Element_Access
:= new Element_Type
'(E);
1640 Buckets (J) := new Node_Type'(X
, Buckets
(J
));
1647 Length
:= Length
+ 1;
1652 -- Start of processing for Iterate_Right
1658 HT_Ops
.Free_Hash_Table
(Buckets
);
1662 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
1663 end Symmetric_Difference
;
1669 function To_Set
(New_Item
: Element_Type
) return Set
is
1670 HT
: Hash_Table_Type
;
1673 pragma Unreferenced
(Node
, Inserted
);
1675 Insert
(HT
, New_Item
, Node
, Inserted
);
1676 return Set
'(Controlled with HT);
1684 (Target : in out Set;
1687 procedure Process (Src_Node : Node_Access);
1689 procedure Iterate is
1690 new HT_Ops.Generic_Iteration (Process);
1696 procedure Process (Src_Node : Node_Access) is
1697 Src : Element_Type renames Src_Node.Element.all;
1699 function New_Node (Next : Node_Access) return Node_Access;
1700 pragma Inline (New_Node);
1703 new Element_Keys.Generic_Conditional_Insert (New_Node);
1709 function New_Node (Next : Node_Access) return Node_Access is
1710 Tgt : Element_Access := new Element_Type'(Src
);
1712 return new Node_Type
'(Tgt, Next);
1719 Tgt_Node : Node_Access;
1721 pragma Unreferenced (Tgt_Node, Success);
1723 -- Start of processing for Process
1726 Insert (Target.HT, Src, Tgt_Node, Success);
1729 -- Start of processing for Union
1732 if Target'Address = Source'Address then
1736 if Target.HT.Busy > 0 then
1737 raise Program_Error with
1738 "attempt to tamper with cursors (set is busy)";
1742 N : constant Count_Type := Target.Length + Source.Length;
1744 if N > HT_Ops.Capacity (Target.HT) then
1745 HT_Ops.Reserve_Capacity (Target.HT, N);
1749 Iterate (Source.HT);
1752 function Union (Left, Right : Set) return Set is
1753 Buckets : HT_Types.Buckets_Access;
1754 Length : Count_Type;
1757 if Left'Address = Right'Address then
1761 if Right.Length = 0 then
1765 if Left.Length = 0 then
1770 Size : constant Hash_Type :=
1771 Prime_Numbers.To_Prime (Left.Length + Right.Length);
1773 Buckets := HT_Ops.New_Buckets (Length => Size);
1776 Iterate_Left : declare
1777 procedure Process (L_Node : Node_Access);
1779 procedure Iterate is
1780 new HT_Ops.Generic_Iteration (Process);
1786 procedure Process (L_Node : Node_Access) is
1787 Src : Element_Type renames L_Node.Element.all;
1788 J : constant Hash_Type := Hash (Src) mod Buckets'Length;
1789 Bucket : Node_Access renames Buckets (J);
1790 Tgt : Element_Access := new Element_Type'(Src
);
1792 Bucket
:= new Node_Type
'(Tgt, Bucket);
1799 -- Start of processing for Process
1805 HT_Ops.Free_Hash_Table (Buckets);
1809 Length := Left.Length;
1811 Iterate_Right : declare
1812 procedure Process (Src_Node : Node_Access);
1814 procedure Iterate is
1815 new HT_Ops.Generic_Iteration (Process);
1821 procedure Process (Src_Node : Node_Access) is
1822 Src : Element_Type renames Src_Node.Element.all;
1823 Idx : constant Hash_Type := Hash (Src) mod Buckets'Length;
1825 Tgt_Node : Node_Access := Buckets (Idx);
1828 while Tgt_Node /= null loop
1829 if Equivalent_Elements (Src, Tgt_Node.Element.all) then
1832 Tgt_Node := Next (Tgt_Node);
1836 Tgt : Element_Access := new Element_Type'(Src
);
1838 Buckets
(Idx
) := new Node_Type
'(Tgt, Buckets (Idx));
1845 Length := Length + 1;
1848 -- Start of processing for Iterate_Right
1854 HT_Ops.Free_Hash_Table (Buckets);
1858 return (Controlled with HT => (Buckets, Length, 0, 0));
1865 function Vet (Position : Cursor) return Boolean is
1867 if Position.Node = null then
1868 return Position.Container = null;
1871 if Position.Container = null then
1875 if Position.Node.Next = Position.Node then
1879 if Position.Node.Element = null then
1884 HT : Hash_Table_Type renames Position.Container.HT;
1888 if HT.Length = 0 then
1892 if HT.Buckets = null
1893 or else HT.Buckets'Length = 0
1898 X := HT.Buckets (Element_Keys.Index (HT, Position.Node.Element.all));
1900 for J in 1 .. HT.Length loop
1901 if X = Position.Node then
1909 if X = X.Next then -- to prevent unnecessary looping
1925 (Stream : not null access Root_Stream_Type'Class;
1929 Write_Nodes (Stream, Container.HT);
1933 (Stream : not null access Root_Stream_Type'Class;
1937 raise Program_Error with "attempt to stream set cursor";
1941 (Stream : not null access Root_Stream_Type'Class;
1942 Item : Constant_Reference_Type)
1945 raise Program_Error with "attempt to stream reference";
1952 procedure Write_Node
1953 (Stream : not null access Root_Stream_Type'Class;
1957 Element_Type'Output (Stream, Node.Element.all);
1960 package body Generic_Keys is
1962 -----------------------
1963 -- Local Subprograms --
1964 -----------------------
1966 function Equivalent_Key_Node
1968 Node : Node_Access) return Boolean;
1969 pragma Inline (Equivalent_Key_Node);
1971 --------------------------
1972 -- Local Instantiations --
1973 --------------------------
1976 new Hash_Tables.Generic_Keys
1977 (HT_Types => HT_Types,
1979 Set_Next => Set_Next,
1980 Key_Type => Key_Type,
1982 Equivalent_Keys => Equivalent_Key_Node);
1984 ------------------------
1985 -- Constant_Reference --
1986 ------------------------
1988 function Constant_Reference
1989 (Container : aliased Set;
1990 Key : Key_Type) return Constant_Reference_Type
1992 Node : constant Node_Access :=
1993 Key_Keys.Find (Container.HT, Key);
1997 raise Constraint_Error with "Key not in set";
2000 if Node.Element = null then
2001 raise Program_Error with "Node has no element";
2005 HT : Hash_Table_Type renames Container'Unrestricted_Access.all.HT;
2006 B : Natural renames HT.Busy;
2007 L : Natural renames HT.Lock;
2009 return R : constant Constant_Reference_Type :=
2010 (Element => Node.Element.all'Access,
2012 (Controlled with Container'Unrestricted_Access))
2018 end Constant_Reference;
2026 Key : Key_Type) return Boolean
2029 return Find (Container, Key) /= No_Element;
2037 (Container : in out Set;
2043 Key_Keys.Delete_Key_Sans_Free (Container.HT, Key, X);
2046 raise Constraint_Error with "key not in map"; -- ??? "set"
2058 Key : Key_Type) return Element_Type
2060 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
2064 raise Constraint_Error with "key not in map"; -- ??? "set"
2067 return Node.Element.all;
2070 -------------------------
2071 -- Equivalent_Key_Node --
2072 -------------------------
2074 function Equivalent_Key_Node
2076 Node : Node_Access) return Boolean is
2078 return Equivalent_Keys (Key, Generic_Keys.Key (Node.Element.all));
2079 end Equivalent_Key_Node;
2086 (Container : in out Set;
2091 Key_Keys.Delete_Key_Sans_Free (Container.HT, Key, X);
2101 Key : Key_Type) return Cursor
2103 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
2105 return (if Node = null then No_Element
2106 else Cursor'(Container
'Unrestricted_Access, Node
));
2113 function Key
(Position
: Cursor
) return Key_Type
is
2115 if Position
.Node
= null then
2116 raise Constraint_Error
with
2117 "Position cursor equals No_Element";
2120 if Position
.Node
.Element
= null then
2121 raise Program_Error
with "Position cursor is bad";
2124 pragma Assert
(Vet
(Position
), "bad cursor in function Key");
2126 return Key
(Position
.Node
.Element
.all);
2134 (Stream
: not null access Root_Stream_Type
'Class;
2135 Item
: out Reference_Type
)
2138 raise Program_Error
with "attempt to stream reference";
2141 ------------------------------
2142 -- Reference_Preserving_Key --
2143 ------------------------------
2145 function Reference_Preserving_Key
2146 (Container
: aliased in out Set
;
2147 Position
: Cursor
) return Reference_Type
2150 if Position
.Container
= null then
2151 raise Constraint_Error
with "Position cursor has no element";
2154 if Position
.Container
/= Container
'Unrestricted_Access then
2155 raise Program_Error
with
2156 "Position cursor designates wrong container";
2159 if Position
.Node
.Element
= null then
2160 raise Program_Error
with "Node has no element";
2165 "bad cursor in function Reference_Preserving_Key");
2167 -- Some form of finalization will be required in order to actually
2168 -- check that the key-part of the element designated by Position has
2171 return (Element
=> Position
.Node
.Element
.all'Access);
2172 end Reference_Preserving_Key
;
2174 function Reference_Preserving_Key
2175 (Container
: aliased in out Set
;
2176 Key
: Key_Type
) return Reference_Type
2178 Node
: constant Node_Access
:=
2179 Key_Keys
.Find
(Container
.HT
, Key
);
2183 raise Constraint_Error
with "Key not in set";
2186 if Node
.Element
= null then
2187 raise Program_Error
with "Node has no element";
2190 -- Some form of finalization will be required in order to actually
2191 -- check that the key-part of the element designated by Key has not
2194 return (Element
=> Node
.Element
.all'Access);
2195 end Reference_Preserving_Key
;
2202 (Container
: in out Set
;
2204 New_Item
: Element_Type
)
2206 Node
: constant Node_Access
:=
2207 Key_Keys
.Find
(Container
.HT
, Key
);
2211 raise Constraint_Error
with
2212 "attempt to replace key not in set";
2215 Replace_Element
(Container
.HT
, Node
, New_Item
);
2218 -----------------------------------
2219 -- Update_Element_Preserving_Key --
2220 -----------------------------------
2222 procedure Update_Element_Preserving_Key
2223 (Container
: in out Set
;
2225 Process
: not null access
2226 procedure (Element
: in out Element_Type
))
2228 HT
: Hash_Table_Type
renames Container
.HT
;
2232 if Position
.Node
= null then
2233 raise Constraint_Error
with
2234 "Position cursor equals No_Element";
2237 if Position
.Node
.Element
= null
2238 or else Position
.Node
.Next
= Position
.Node
2240 raise Program_Error
with "Position cursor is bad";
2243 if Position
.Container
/= Container
'Unrestricted_Access then
2244 raise Program_Error
with
2245 "Position cursor designates wrong set";
2248 if HT
.Buckets
= null
2249 or else HT
.Buckets
'Length = 0
2250 or else HT
.Length
= 0
2252 raise Program_Error
with "Position cursor is bad (set is empty)";
2257 "bad cursor in Update_Element_Preserving_Key");
2259 Indx
:= HT_Ops
.Index
(HT
, Position
.Node
);
2262 E
: Element_Type
renames Position
.Node
.Element
.all;
2263 K
: constant Key_Type
:= Key
(E
);
2265 B
: Natural renames HT
.Busy
;
2266 L
: Natural renames HT
.Lock
;
2284 if Equivalent_Keys
(K
, Key
(E
)) then
2285 pragma Assert
(Hash
(K
) = Hash
(E
));
2290 if HT
.Buckets
(Indx
) = Position
.Node
then
2291 HT
.Buckets
(Indx
) := Position
.Node
.Next
;
2295 Prev
: Node_Access
:= HT
.Buckets
(Indx
);
2298 while Prev
.Next
/= Position
.Node
loop
2302 raise Program_Error
with
2303 "Position cursor is bad (node not found)";
2307 Prev
.Next
:= Position
.Node
.Next
;
2311 HT
.Length
:= HT
.Length
- 1;
2314 X
: Node_Access
:= Position
.Node
;
2320 raise Program_Error
with "key was modified";
2321 end Update_Element_Preserving_Key
;
2328 (Stream
: not null access Root_Stream_Type
'Class;
2329 Item
: Reference_Type
)
2332 raise Program_Error
with "attempt to stream reference";
2337 end Ada
.Containers
.Indefinite_Hashed_Sets
;