1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- A D A . C O N T A I N E R S . H A S H E D _ S E T S --
9 -- Copyright (C) 2004-2015, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- This unit was originally developed by Matthew J Heaney. --
28 ------------------------------------------------------------------------------
30 with Ada
.Unchecked_Deallocation
;
32 with Ada
.Containers
.Hash_Tables
.Generic_Operations
;
33 pragma Elaborate_All
(Ada
.Containers
.Hash_Tables
.Generic_Operations
);
35 with Ada
.Containers
.Hash_Tables
.Generic_Keys
;
36 pragma Elaborate_All
(Ada
.Containers
.Hash_Tables
.Generic_Keys
);
38 with Ada
.Containers
.Prime_Numbers
;
40 with System
; use type System
.Address
;
42 package body Ada
.Containers
.Hashed_Sets
is
44 pragma Annotate
(CodePeer
, Skip_Analysis
);
46 -----------------------
47 -- Local Subprograms --
48 -----------------------
50 procedure Assign
(Node
: Node_Access
; Item
: Element_Type
);
51 pragma Inline
(Assign
);
53 function Copy_Node
(Source
: Node_Access
) return Node_Access
;
54 pragma Inline
(Copy_Node
);
56 function Equivalent_Keys
58 Node
: Node_Access
) return Boolean;
59 pragma Inline
(Equivalent_Keys
);
61 function Find_Equal_Key
62 (R_HT
: Hash_Table_Type
;
63 L_Node
: Node_Access
) return Boolean;
65 function Find_Equivalent_Key
66 (R_HT
: Hash_Table_Type
;
67 L_Node
: Node_Access
) return Boolean;
69 procedure Free
(X
: in out Node_Access
);
71 function Hash_Node
(Node
: Node_Access
) return Hash_Type
;
72 pragma Inline
(Hash_Node
);
75 (HT
: in out Hash_Table_Type
;
76 New_Item
: Element_Type
;
77 Node
: out Node_Access
;
78 Inserted
: out Boolean);
81 (HT
: aliased in out Hash_Table_Type
;
82 Key
: Node_Access
) return Boolean;
83 pragma Inline
(Is_In
);
85 function Next
(Node
: Node_Access
) return Node_Access
;
88 function Read_Node
(Stream
: not null access Root_Stream_Type
'Class)
90 pragma Inline
(Read_Node
);
92 procedure Set_Next
(Node
: Node_Access
; Next
: Node_Access
);
93 pragma Inline
(Set_Next
);
95 function Vet
(Position
: Cursor
) return Boolean;
98 (Stream
: not null access Root_Stream_Type
'Class;
100 pragma Inline
(Write_Node
);
102 --------------------------
103 -- Local Instantiations --
104 --------------------------
106 package HT_Ops
is new Hash_Tables
.Generic_Operations
107 (HT_Types
=> HT_Types
,
108 Hash_Node
=> Hash_Node
,
110 Set_Next
=> Set_Next
,
111 Copy_Node
=> Copy_Node
,
114 package Element_Keys
is new Hash_Tables
.Generic_Keys
115 (HT_Types
=> HT_Types
,
117 Set_Next
=> Set_Next
,
118 Key_Type
=> Element_Type
,
120 Equivalent_Keys
=> Equivalent_Keys
);
123 new HT_Ops
.Generic_Equal
(Find_Equal_Key
);
125 function Is_Equivalent
is
126 new HT_Ops
.Generic_Equal
(Find_Equivalent_Key
);
128 procedure Read_Nodes
is
129 new HT_Ops
.Generic_Read
(Read_Node
);
131 procedure Replace_Element
is
132 new Element_Keys
.Generic_Replace_Element
(Hash_Node
, Assign
);
134 procedure Write_Nodes
is
135 new HT_Ops
.Generic_Write
(Write_Node
);
141 function "=" (Left
, Right
: Set
) return Boolean is
143 return Is_Equal
(Left
.HT
, Right
.HT
);
150 procedure Adjust
(Container
: in out Set
) is
152 HT_Ops
.Adjust
(Container
.HT
);
155 procedure Adjust
(Control
: in out Reference_Control_Type
) is
157 if Control
.Container
/= null then
159 HT
: Hash_Table_Type
renames Control
.Container
.all.HT
;
160 B
: Natural renames HT
.Busy
;
161 L
: Natural renames HT
.Lock
;
173 procedure Assign
(Node
: Node_Access
; Item
: Element_Type
) is
175 Node
.Element
:= 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
);
206 ------------------------
207 -- Constant_Reference --
208 ------------------------
210 function Constant_Reference
211 (Container
: aliased Set
;
212 Position
: Cursor
) return Constant_Reference_Type
215 if Position
.Container
= null then
216 raise Constraint_Error
with "Position cursor has no element";
219 if Position
.Container
/= Container
'Unrestricted_Access then
220 raise Program_Error
with
221 "Position cursor designates wrong container";
224 pragma Assert
(Vet
(Position
), "bad cursor in Constant_Reference");
227 HT
: Hash_Table_Type
renames Position
.Container
.all.HT
;
228 B
: Natural renames HT
.Busy
;
229 L
: Natural renames HT
.Lock
;
231 return R
: constant Constant_Reference_Type
:=
232 (Element
=> Position
.Node
.Element
'Access,
233 Control
=> (Controlled
with Container
'Unrestricted_Access))
239 end Constant_Reference
;
245 function Contains
(Container
: Set
; Item
: Element_Type
) return Boolean is
247 return Find
(Container
, Item
) /= No_Element
;
256 Capacity
: Count_Type
:= 0) return Set
264 elsif Capacity
>= Source
.Length
then
269 with "Requested capacity is less than Source length";
272 return Target
: Set
do
273 Target
.Reserve_Capacity
(C
);
274 Target
.Assign
(Source
);
282 function Copy_Node
(Source
: Node_Access
) return Node_Access
is
284 return new Node_Type
'(Element => Source.Element, Next => null);
292 (Container : in out Set;
298 Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
301 raise Constraint_Error with "attempt to delete element not in set";
308 (Container : in out Set;
309 Position : in out Cursor)
312 if Position.Node = null then
313 raise Constraint_Error with "Position cursor equals No_Element";
316 if Position.Container /= Container'Unrestricted_Access then
317 raise Program_Error with "Position cursor designates wrong set";
320 if Container.HT.Busy > 0 then
321 raise Program_Error with
322 "attempt to tamper with cursors (set is busy)";
325 pragma Assert (Vet (Position), "bad cursor in Delete");
327 HT_Ops.Delete_Node_Sans_Free (Container.HT, Position.Node);
329 Free (Position.Node);
330 Position.Container := null;
338 (Target : in out Set;
341 Tgt_Node : Node_Access;
342 Src_HT : Hash_Table_Type renames Source'Unrestricted_Access.HT;
345 if Target'Address = Source'Address then
350 if Src_HT.Length = 0 then
354 if Target.HT.Busy > 0 then
355 raise Program_Error with
356 "attempt to tamper with cursors (set is busy)";
359 if Src_HT.Length < Target.HT.Length then
361 Src_Node : Node_Access;
364 Src_Node := HT_Ops.First (Src_HT);
365 while Src_Node /= null loop
366 Tgt_Node := Element_Keys.Find (Target.HT, Src_Node.Element);
368 if Tgt_Node /= null then
369 HT_Ops.Delete_Node_Sans_Free (Target.HT, Tgt_Node);
373 Src_Node := HT_Ops.Next (Src_HT, Src_Node);
378 Tgt_Node := HT_Ops.First (Target.HT);
379 while Tgt_Node /= null loop
380 if Is_In (Src_HT, Tgt_Node) then
382 X : Node_Access := Tgt_Node;
384 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
385 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
390 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
396 function Difference (Left, Right : Set) return Set is
397 Left_HT : Hash_Table_Type renames Left'Unrestricted_Access.HT;
398 Right_HT : Hash_Table_Type renames Right'Unrestricted_Access.HT;
399 Buckets : HT_Types.Buckets_Access;
403 if Left'Address = Right'Address then
407 if Left_HT.Length = 0 then
411 if Right_HT.Length = 0 then
416 Size : constant Hash_Type := Prime_Numbers.To_Prime (Left.Length);
418 Buckets := HT_Ops.New_Buckets (Length => Size);
423 Iterate_Left : declare
424 procedure Process (L_Node : Node_Access);
427 new HT_Ops.Generic_Iteration (Process);
433 procedure Process (L_Node : Node_Access) is
435 if not Is_In (Right_HT, L_Node) then
437 -- Per AI05-0022, the container implementation is required
438 -- to detect element tampering by a generic actual
439 -- subprogram, hence the use of Checked_Index instead of a
440 -- simple invocation of generic formal Hash.
442 J : constant Hash_Type :=
443 HT_Ops.Checked_Index (Left_HT, Buckets.all, L_Node);
445 Bucket : Node_Access renames Buckets (J);
448 Bucket := new Node_Type'(L_Node
.Element
, Bucket
);
451 Length
:= Length
+ 1;
455 -- Start of processing for Iterate_Left
461 HT_Ops
.Free_Hash_Table
(Buckets
);
465 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
472 function Element
(Position
: Cursor
) return Element_Type
is
474 if Position
.Node
= null then
475 raise Constraint_Error
with "Position cursor equals No_Element";
478 pragma Assert
(Vet
(Position
), "bad cursor in function Element");
480 return Position
.Node
.Element
;
483 ---------------------
484 -- Equivalent_Sets --
485 ---------------------
487 function Equivalent_Sets
(Left
, Right
: Set
) return Boolean is
489 return Is_Equivalent
(Left
.HT
, Right
.HT
);
492 -------------------------
493 -- Equivalent_Elements --
494 -------------------------
496 function Equivalent_Elements
(Left
, Right
: Cursor
)
499 if Left
.Node
= null then
500 raise Constraint_Error
with
501 "Left cursor of Equivalent_Elements equals No_Element";
504 if Right
.Node
= null then
505 raise Constraint_Error
with
506 "Right cursor of Equivalent_Elements equals No_Element";
509 pragma Assert
(Vet
(Left
), "bad Left cursor in Equivalent_Elements");
510 pragma Assert
(Vet
(Right
), "bad Right cursor in Equivalent_Elements");
512 -- AI05-0022 requires that a container implementation detect element
513 -- tampering by a generic actual subprogram. However, the following case
514 -- falls outside the scope of that AI. Randy Brukardt explained on the
515 -- ARG list on 2013/02/07 that:
518 -- But for an operation like "<" [the ordered set analog of
519 -- Equivalent_Elements], there is no need to "dereference" a cursor
520 -- after the call to the generic formal parameter function, so nothing
521 -- bad could happen if tampering is undetected. And the operation can
522 -- safely return a result without a problem even if an element is
523 -- deleted from the container.
526 return Equivalent_Elements
(Left
.Node
.Element
, Right
.Node
.Element
);
527 end Equivalent_Elements
;
529 function Equivalent_Elements
(Left
: Cursor
; Right
: Element_Type
)
532 if Left
.Node
= null then
533 raise Constraint_Error
with
534 "Left cursor of Equivalent_Elements equals No_Element";
537 pragma Assert
(Vet
(Left
), "Left cursor in Equivalent_Elements is bad");
539 return Equivalent_Elements
(Left
.Node
.Element
, Right
);
540 end Equivalent_Elements
;
542 function Equivalent_Elements
(Left
: Element_Type
; Right
: Cursor
)
545 if Right
.Node
= null then
546 raise Constraint_Error
with
547 "Right cursor of Equivalent_Elements equals No_Element";
552 "Right cursor of Equivalent_Elements is bad");
554 return Equivalent_Elements
(Left
, Right
.Node
.Element
);
555 end Equivalent_Elements
;
557 ---------------------
558 -- Equivalent_Keys --
559 ---------------------
561 function Equivalent_Keys
(Key
: Element_Type
; Node
: Node_Access
)
564 return Equivalent_Elements
(Key
, Node
.Element
);
572 (Container
: in out Set
;
577 Element_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Item
, X
);
585 procedure Finalize
(Container
: in out Set
) is
587 HT_Ops
.Finalize
(Container
.HT
);
590 procedure Finalize
(Control
: in out Reference_Control_Type
) is
592 if Control
.Container
/= null then
594 HT
: Hash_Table_Type
renames Control
.Container
.all.HT
;
595 B
: Natural renames HT
.Busy
;
596 L
: Natural renames HT
.Lock
;
602 Control
.Container
:= null;
606 procedure Finalize
(Object
: in out Iterator
) is
608 if Object
.Container
/= null then
610 B
: Natural renames Object
.Container
.HT
.Busy
;
623 Item
: Element_Type
) return Cursor
625 HT
: Hash_Table_Type
renames Container
'Unrestricted_Access.HT
;
626 Node
: constant Node_Access
:= Element_Keys
.Find
(HT
, Item
);
633 return Cursor
'(Container'Unrestricted_Access, Node);
640 function Find_Equal_Key
641 (R_HT : Hash_Table_Type;
642 L_Node : Node_Access) return Boolean
644 R_Index : constant Hash_Type :=
645 Element_Keys.Index (R_HT, L_Node.Element);
647 R_Node : Node_Access := R_HT.Buckets (R_Index);
651 if R_Node = null then
655 if L_Node.Element = R_Node.Element then
659 R_Node := Next (R_Node);
663 -------------------------
664 -- Find_Equivalent_Key --
665 -------------------------
667 function Find_Equivalent_Key
668 (R_HT : Hash_Table_Type;
669 L_Node : Node_Access) return Boolean
671 R_Index : constant Hash_Type :=
672 Element_Keys.Index (R_HT, L_Node.Element);
674 R_Node : Node_Access := R_HT.Buckets (R_Index);
678 if R_Node = null then
682 if Equivalent_Elements (L_Node.Element, R_Node.Element) then
686 R_Node := Next (R_Node);
688 end Find_Equivalent_Key;
694 function First (Container : Set) return Cursor is
695 Node : constant Node_Access := HT_Ops.First (Container.HT);
702 return Cursor'(Container
'Unrestricted_Access, Node
);
705 function First
(Object
: Iterator
) return Cursor
is
707 return Object
.Container
.First
;
714 procedure Free
(X
: in out Node_Access
) is
715 procedure Deallocate
is
716 new Ada
.Unchecked_Deallocation
(Node_Type
, Node_Access
);
720 X
.Next
:= X
; -- detect mischief (in Vet)
725 ------------------------
726 -- Get_Element_Access --
727 ------------------------
729 function Get_Element_Access
730 (Position
: Cursor
) return not null Element_Access
is
732 return Position
.Node
.Element
'Access;
733 end Get_Element_Access
;
739 function Has_Element
(Position
: Cursor
) return Boolean is
741 pragma Assert
(Vet
(Position
), "bad cursor in Has_Element");
742 return Position
.Node
/= null;
749 function Hash_Node
(Node
: Node_Access
) return Hash_Type
is
751 return Hash
(Node
.Element
);
759 (Container
: in out Set
;
760 New_Item
: Element_Type
)
766 Insert
(Container
, New_Item
, Position
, Inserted
);
769 if Container
.HT
.Lock
> 0 then
770 raise Program_Error
with
771 "attempt to tamper with elements (set is locked)";
774 Position
.Node
.Element
:= New_Item
;
783 (Container
: in out Set
;
784 New_Item
: Element_Type
;
785 Position
: out Cursor
;
786 Inserted
: out Boolean)
789 Insert
(Container
.HT
, New_Item
, Position
.Node
, Inserted
);
790 Position
.Container
:= Container
'Unchecked_Access;
794 (Container
: in out Set
;
795 New_Item
: Element_Type
)
798 pragma Unreferenced
(Position
);
803 Insert
(Container
, New_Item
, Position
, Inserted
);
806 raise Constraint_Error
with
807 "attempt to insert element already in set";
812 (HT
: in out Hash_Table_Type
;
813 New_Item
: Element_Type
;
814 Node
: out Node_Access
;
815 Inserted
: out Boolean)
817 function New_Node
(Next
: Node_Access
) return Node_Access
;
818 pragma Inline
(New_Node
);
820 procedure Local_Insert
is
821 new Element_Keys
.Generic_Conditional_Insert
(New_Node
);
827 function New_Node
(Next
: Node_Access
) return Node_Access
is
829 return new Node_Type
'(New_Item, Next);
832 -- Start of processing for Insert
835 if HT_Ops.Capacity (HT) = 0 then
836 HT_Ops.Reserve_Capacity (HT, 1);
840 raise Program_Error with
841 "attempt to tamper with cursors (set is busy)";
844 Local_Insert (HT, New_Item, Node, Inserted);
847 and then HT.Length > HT_Ops.Capacity (HT)
849 HT_Ops.Reserve_Capacity (HT, HT.Length);
857 procedure Intersection
858 (Target : in out Set;
861 Src_HT : Hash_Table_Type renames Source'Unrestricted_Access.HT;
862 Tgt_Node : Node_Access;
865 if Target'Address = Source'Address then
869 if Source.HT.Length = 0 then
874 if Target.HT.Busy > 0 then
875 raise Program_Error with
876 "attempt to tamper with cursors (set is busy)";
879 Tgt_Node := HT_Ops.First (Target.HT);
880 while Tgt_Node /= null loop
881 if Is_In (Src_HT, Tgt_Node) then
882 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
886 X : Node_Access := Tgt_Node;
888 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
889 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
896 function Intersection (Left, Right : Set) return Set is
897 Left_HT : Hash_Table_Type renames Left'Unrestricted_Access.HT;
898 Right_HT : Hash_Table_Type renames Right'Unrestricted_Access.HT;
899 Buckets : HT_Types.Buckets_Access;
903 if Left'Address = Right'Address then
907 Length := Count_Type'Min (Left.Length, Right.Length);
914 Size : constant Hash_Type := Prime_Numbers.To_Prime (Length);
916 Buckets := HT_Ops.New_Buckets (Length => Size);
921 Iterate_Left : declare
922 procedure Process (L_Node : Node_Access);
925 new HT_Ops.Generic_Iteration (Process);
931 procedure Process (L_Node : Node_Access) is
933 if Is_In (Right_HT, L_Node) then
935 -- Per AI05-0022, the container implementation is required
936 -- to detect element tampering by a generic actual
937 -- subprogram, hence the use of Checked_Index instead of a
938 -- simple invocation of generic formal Hash.
940 J : constant Hash_Type :=
941 HT_Ops.Checked_Index (Left_HT, Buckets.all, L_Node);
943 Bucket : Node_Access renames Buckets (J);
946 Bucket := new Node_Type'(L_Node
.Element
, Bucket
);
949 Length
:= Length
+ 1;
953 -- Start of processing for Iterate_Left
959 HT_Ops
.Free_Hash_Table
(Buckets
);
963 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
970 function Is_Empty
(Container
: Set
) return Boolean is
972 return Container
.HT
.Length
= 0;
980 (HT
: aliased in out Hash_Table_Type
;
981 Key
: Node_Access
) return Boolean
984 return Element_Keys
.Find
(HT
, Key
.Element
) /= null;
991 function Is_Subset
(Subset
: Set
; Of_Set
: Set
) return Boolean is
992 Subset_HT
: Hash_Table_Type
renames Subset
'Unrestricted_Access.HT
;
993 Of_Set_HT
: Hash_Table_Type
renames Of_Set
'Unrestricted_Access.HT
;
994 Subset_Node
: Node_Access
;
997 if Subset
'Address = Of_Set
'Address then
1001 if Subset
.Length
> Of_Set
.Length
then
1005 Subset_Node
:= HT_Ops
.First
(Subset_HT
);
1006 while Subset_Node
/= null loop
1007 if not Is_In
(Of_Set_HT
, Subset_Node
) then
1010 Subset_Node
:= HT_Ops
.Next
(Subset_HT
, Subset_Node
);
1022 Process
: not null access procedure (Position
: Cursor
))
1024 procedure Process_Node
(Node
: Node_Access
);
1025 pragma Inline
(Process_Node
);
1027 procedure Iterate
is
1028 new HT_Ops
.Generic_Iteration
(Process_Node
);
1034 procedure Process_Node
(Node
: Node_Access
) is
1036 Process
(Cursor
'(Container'Unrestricted_Access, Node));
1039 B : Natural renames Container'Unrestricted_Access.HT.Busy;
1041 -- Start of processing for Iterate
1047 Iterate (Container.HT);
1058 (Container : Set) return Set_Iterator_Interfaces.Forward_Iterator'Class
1060 B : Natural renames Container'Unrestricted_Access.all.HT.Busy;
1063 return It : constant Iterator :=
1064 Iterator'(Limited_Controlled
with
1065 Container
=> Container
'Unrestricted_Access);
1072 function Length
(Container
: Set
) return Count_Type
is
1074 return Container
.HT
.Length
;
1081 procedure Move
(Target
: in out Set
; Source
: in out Set
) is
1083 HT_Ops
.Move
(Target
=> Target
.HT
, Source
=> Source
.HT
);
1090 function Next
(Node
: Node_Access
) return Node_Access
is
1095 function Next
(Position
: Cursor
) return Cursor
is
1097 if Position
.Node
= null then
1101 pragma Assert
(Vet
(Position
), "bad cursor in Next");
1104 HT
: Hash_Table_Type
renames Position
.Container
.HT
;
1105 Node
: constant Node_Access
:= HT_Ops
.Next
(HT
, Position
.Node
);
1112 return Cursor
'(Position.Container, Node);
1116 procedure Next (Position : in out Cursor) is
1118 Position := Next (Position);
1123 Position : Cursor) return Cursor
1126 if Position.Container = null then
1130 if Position.Container /= Object.Container then
1131 raise Program_Error with
1132 "Position cursor of Next designates wrong set";
1135 return Next (Position);
1142 function Overlap (Left, Right : Set) return Boolean is
1143 Left_HT : Hash_Table_Type renames Left'Unrestricted_Access.HT;
1144 Right_HT : Hash_Table_Type renames Right'Unrestricted_Access.HT;
1145 Left_Node : Node_Access;
1148 if Right.Length = 0 then
1152 if Left'Address = Right'Address then
1156 Left_Node := HT_Ops.First (Left_HT);
1157 while Left_Node /= null loop
1158 if Is_In (Right_HT, Left_Node) then
1161 Left_Node := HT_Ops.Next (Left_HT, Left_Node);
1167 ----------------------
1168 -- Pseudo_Reference --
1169 ----------------------
1171 function Pseudo_Reference
1172 (Container : aliased Set'Class) return Reference_Control_Type
1174 C : constant Set_Access := Container'Unrestricted_Access;
1175 B : Natural renames C.HT.Busy;
1176 L : Natural renames C.HT.Lock;
1178 return R : constant Reference_Control_Type :=
1184 end Pseudo_Reference;
1190 procedure Query_Element
1192 Process : not null access procedure (Element : Element_Type))
1195 if Position.Node = null then
1196 raise Constraint_Error with
1197 "Position cursor of Query_Element equals No_Element";
1200 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1203 HT : Hash_Table_Type renames Position.Container.HT;
1205 B : Natural renames HT.Busy;
1206 L : Natural renames HT.Lock;
1213 Process (Position.Node.Element);
1231 (Stream : not null access Root_Stream_Type'Class;
1232 Container : out Set)
1235 Read_Nodes (Stream, Container.HT);
1239 (Stream : not null access Root_Stream_Type'Class;
1243 raise Program_Error with "attempt to stream set cursor";
1247 (Stream : not null access Root_Stream_Type'Class;
1248 Item : out Constant_Reference_Type)
1251 raise Program_Error with "attempt to stream reference";
1258 function Read_Node (Stream : not null access Root_Stream_Type'Class)
1261 Node : Node_Access := new Node_Type;
1263 Element_Type'Read (Stream, Node.Element);
1276 (Container : in out Set;
1277 New_Item : Element_Type)
1279 Node : constant Node_Access :=
1280 Element_Keys.Find (Container.HT, New_Item);
1284 raise Constraint_Error with
1285 "attempt to replace element not in set";
1288 if Container.HT.Lock > 0 then
1289 raise Program_Error with
1290 "attempt to tamper with elements (set is locked)";
1293 Node.Element := New_Item;
1296 procedure Replace_Element
1297 (Container : in out Set;
1299 New_Item : Element_Type)
1302 if Position.Node = null then
1303 raise Constraint_Error with
1304 "Position cursor equals No_Element";
1307 if Position.Container /= Container'Unrestricted_Access then
1308 raise Program_Error with
1309 "Position cursor designates wrong set";
1312 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1314 Replace_Element (Container.HT, Position.Node, New_Item);
1315 end Replace_Element;
1317 ----------------------
1318 -- Reserve_Capacity --
1319 ----------------------
1321 procedure Reserve_Capacity
1322 (Container : in out Set;
1323 Capacity : Count_Type)
1326 HT_Ops.Reserve_Capacity (Container.HT, Capacity);
1327 end Reserve_Capacity;
1333 procedure Set_Next (Node : Node_Access; Next : Node_Access) is
1338 --------------------------
1339 -- Symmetric_Difference --
1340 --------------------------
1342 procedure Symmetric_Difference
1343 (Target : in out Set;
1346 Tgt_HT : Hash_Table_Type renames Target.HT;
1347 Src_HT : Hash_Table_Type renames Source.HT'Unrestricted_Access.all;
1349 -- Per AI05-0022, the container implementation is required to detect
1350 -- element tampering by a generic actual subprogram.
1352 TB : Natural renames Tgt_HT.Busy;
1353 TL : Natural renames Tgt_HT.Lock;
1355 SB : Natural renames Src_HT.Busy;
1356 SL : Natural renames Src_HT.Lock;
1359 if Target'Address = Source'Address then
1365 raise Program_Error with
1366 "attempt to tamper with cursors (set is busy)";
1370 N : constant Count_Type := Target.Length + Source.Length;
1372 if N > HT_Ops.Capacity (Tgt_HT) then
1373 HT_Ops.Reserve_Capacity (Tgt_HT, N);
1377 if Target.Length = 0 then
1378 Iterate_Source_When_Empty_Target : declare
1379 procedure Process (Src_Node : Node_Access);
1381 procedure Iterate is
1382 new HT_Ops.Generic_Iteration (Process);
1388 procedure Process (Src_Node : Node_Access) is
1389 E : Element_Type renames Src_Node.Element;
1390 B : Buckets_Type renames Tgt_HT.Buckets.all;
1391 J : constant Hash_Type := Hash (E) mod B'Length;
1392 N : Count_Type renames Tgt_HT.Length;
1395 B (J) := new Node_Type'(E
, B
(J
));
1399 -- Start of processing for Iterate_Source_When_Empty_Target
1425 end Iterate_Source_When_Empty_Target
;
1428 Iterate_Source
: declare
1429 procedure Process
(Src_Node
: Node_Access
);
1431 procedure Iterate
is
1432 new HT_Ops
.Generic_Iteration
(Process
);
1438 procedure Process
(Src_Node
: Node_Access
) is
1439 E
: Element_Type
renames Src_Node
.Element
;
1440 B
: Buckets_Type
renames Tgt_HT
.Buckets
.all;
1441 J
: constant Hash_Type
:= Hash
(E
) mod B
'Length;
1442 N
: Count_Type
renames Tgt_HT
.Length
;
1445 if B
(J
) = null then
1446 B
(J
) := new Node_Type
'(E, null);
1449 elsif Equivalent_Elements (E, B (J).Element) then
1451 X : Node_Access := B (J);
1453 B (J) := B (J).Next;
1460 Prev : Node_Access := B (J);
1461 Curr : Node_Access := Prev.Next;
1464 while Curr /= null loop
1465 if Equivalent_Elements (E, Curr.Element) then
1466 Prev.Next := Curr.Next;
1476 B (J) := new Node_Type'(E
, B
(J
));
1482 -- Start of processing for Iterate_Source
1510 end Symmetric_Difference
;
1512 function Symmetric_Difference
(Left
, Right
: Set
) return Set
is
1513 Left_HT
: Hash_Table_Type
renames Left
'Unrestricted_Access.HT
;
1514 Right_HT
: Hash_Table_Type
renames Right
'Unrestricted_Access.HT
;
1515 Buckets
: HT_Types
.Buckets_Access
;
1516 Length
: Count_Type
;
1519 if Left
'Address = Right
'Address then
1523 if Right
.Length
= 0 then
1527 if Left
.Length
= 0 then
1532 Size
: constant Hash_Type
:=
1533 Prime_Numbers
.To_Prime
(Left
.Length
+ Right
.Length
);
1535 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
1540 Iterate_Left
: declare
1541 procedure Process
(L_Node
: Node_Access
);
1543 procedure Iterate
is
1544 new HT_Ops
.Generic_Iteration
(Process
);
1550 procedure Process
(L_Node
: Node_Access
) is
1552 if not Is_In
(Right_HT
, L_Node
) then
1554 E
: Element_Type
renames L_Node
.Element
;
1556 -- Per AI05-0022, the container implementation is required
1557 -- to detect element tampering by a generic actual
1558 -- subprogram, hence the use of Checked_Index instead of a
1559 -- simple invocation of generic formal Hash.
1561 J
: constant Hash_Type
:=
1562 HT_Ops
.Checked_Index
(Left_HT
, Buckets
.all, L_Node
);
1565 Buckets
(J
) := new Node_Type
'(E, Buckets (J));
1566 Length := Length + 1;
1571 -- Start of processing for Iterate_Left
1578 HT_Ops.Free_Hash_Table (Buckets);
1582 Iterate_Right : declare
1583 procedure Process (R_Node : Node_Access);
1585 procedure Iterate is
1586 new HT_Ops.Generic_Iteration (Process);
1592 procedure Process (R_Node : Node_Access) is
1594 if not Is_In (Left_HT, R_Node) then
1596 E : Element_Type renames R_Node.Element;
1598 -- Per AI05-0022, the container implementation is required
1599 -- to detect element tampering by a generic actual
1600 -- subprogram, hence the use of Checked_Index instead of a
1601 -- simple invocation of generic formal Hash.
1603 J : constant Hash_Type :=
1604 HT_Ops.Checked_Index (Right_HT, Buckets.all, R_Node);
1607 Buckets (J) := new Node_Type'(E
, Buckets
(J
));
1608 Length
:= Length
+ 1;
1613 -- Start of processing for Iterate_Right
1620 HT_Ops
.Free_Hash_Table
(Buckets
);
1624 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
1625 end Symmetric_Difference
;
1631 function To_Set
(New_Item
: Element_Type
) return Set
is
1632 HT
: Hash_Table_Type
;
1636 pragma Unreferenced
(Node
, Inserted
);
1639 Insert
(HT
, New_Item
, Node
, Inserted
);
1640 return Set
'(Controlled with HT);
1648 (Target : in out Set;
1651 procedure Process (Src_Node : Node_Access);
1653 procedure Iterate is
1654 new HT_Ops.Generic_Iteration (Process);
1660 procedure Process (Src_Node : Node_Access) is
1661 function New_Node (Next : Node_Access) return Node_Access;
1662 pragma Inline (New_Node);
1665 new Element_Keys.Generic_Conditional_Insert (New_Node);
1671 function New_Node (Next : Node_Access) return Node_Access is
1672 Node : constant Node_Access :=
1673 new Node_Type'(Src_Node
.Element
, Next
);
1678 Tgt_Node
: Node_Access
;
1680 pragma Unreferenced
(Tgt_Node
, Success
);
1682 -- Start of processing for Process
1685 Insert
(Target
.HT
, Src_Node
.Element
, Tgt_Node
, Success
);
1688 -- Start of processing for Union
1691 if Target
'Address = Source
'Address then
1695 if Target
.HT
.Busy
> 0 then
1696 raise Program_Error
with
1697 "attempt to tamper with cursors (set is busy)";
1701 N
: constant Count_Type
:= Target
.Length
+ Source
.Length
;
1703 if N
> HT_Ops
.Capacity
(Target
.HT
) then
1704 HT_Ops
.Reserve_Capacity
(Target
.HT
, N
);
1708 Iterate
(Source
.HT
);
1711 function Union
(Left
, Right
: Set
) return Set
is
1712 Left_HT
: Hash_Table_Type
renames Left
.HT
'Unrestricted_Access.all;
1713 Right_HT
: Hash_Table_Type
renames Right
.HT
'Unrestricted_Access.all;
1714 Buckets
: HT_Types
.Buckets_Access
;
1715 Length
: Count_Type
;
1718 if Left
'Address = Right
'Address then
1722 if Right
.Length
= 0 then
1726 if Left
.Length
= 0 then
1731 Size
: constant Hash_Type
:=
1732 Prime_Numbers
.To_Prime
(Left
.Length
+ Right
.Length
);
1734 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
1737 Iterate_Left
: declare
1738 procedure Process
(L_Node
: Node_Access
);
1740 procedure Iterate
is
1741 new HT_Ops
.Generic_Iteration
(Process
);
1747 procedure Process
(L_Node
: Node_Access
) is
1748 J
: constant Hash_Type
:=
1749 Hash
(L_Node
.Element
) mod Buckets
'Length;
1752 Buckets
(J
) := new Node_Type
'(L_Node.Element, Buckets (J));
1755 -- Per AI05-0022, the container implementation is required to detect
1756 -- element tampering by a generic actual subprogram, hence the use of
1757 -- Checked_Index instead of a simple invocation of generic formal
1760 B : Integer renames Left_HT.Busy;
1761 L : Integer renames Left_HT.Lock;
1763 -- Start of processing for Iterate_Left
1779 HT_Ops.Free_Hash_Table (Buckets);
1783 Length := Left.Length;
1785 Iterate_Right : declare
1786 procedure Process (Src_Node : Node_Access);
1788 procedure Iterate is
1789 new HT_Ops.Generic_Iteration (Process);
1795 procedure Process (Src_Node : Node_Access) is
1796 J : constant Hash_Type :=
1797 Hash (Src_Node.Element) mod Buckets'Length;
1799 Tgt_Node : Node_Access := Buckets (J);
1802 while Tgt_Node /= null loop
1803 if Equivalent_Elements (Src_Node.Element, Tgt_Node.Element) then
1807 Tgt_Node := Next (Tgt_Node);
1810 Buckets (J) := new Node_Type'(Src_Node
.Element
, Buckets
(J
));
1811 Length
:= Length
+ 1;
1814 -- Per AI05-0022, the container implementation is required to detect
1815 -- element tampering by a generic actual subprogram, hence the use of
1816 -- Checked_Index instead of a simple invocation of generic formal
1819 LB
: Integer renames Left_HT
.Busy
;
1820 LL
: Integer renames Left_HT
.Lock
;
1822 RB
: Integer renames Right_HT
.Busy
;
1823 RL
: Integer renames Right_HT
.Lock
;
1825 -- Start of processing for Iterate_Right
1850 HT_Ops
.Free_Hash_Table
(Buckets
);
1854 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
1861 function Vet
(Position
: Cursor
) return Boolean is
1863 if Position
.Node
= null then
1864 return Position
.Container
= null;
1867 if Position
.Container
= null then
1871 if Position
.Node
.Next
= Position
.Node
then
1876 HT
: Hash_Table_Type
renames Position
.Container
.HT
;
1880 if HT
.Length
= 0 then
1884 if HT
.Buckets
= null
1885 or else HT
.Buckets
'Length = 0
1890 X
:= HT
.Buckets
(Element_Keys
.Checked_Index
1892 Position
.Node
.Element
));
1894 for J
in 1 .. HT
.Length
loop
1895 if X
= Position
.Node
then
1903 if X
= X
.Next
then -- to prevent unnecessary looping
1919 (Stream
: not null access Root_Stream_Type
'Class;
1923 Write_Nodes
(Stream
, Container
.HT
);
1927 (Stream
: not null access Root_Stream_Type
'Class;
1931 raise Program_Error
with "attempt to stream set cursor";
1935 (Stream
: not null access Root_Stream_Type
'Class;
1936 Item
: Constant_Reference_Type
)
1939 raise Program_Error
with "attempt to stream reference";
1946 procedure Write_Node
1947 (Stream
: not null access Root_Stream_Type
'Class;
1951 Element_Type
'Write (Stream
, Node
.Element
);
1954 package body Generic_Keys
is
1956 -----------------------
1957 -- Local Subprograms --
1958 -----------------------
1964 procedure Adjust
(Control
: in out Reference_Control_Type
) is
1966 if Control
.Container
/= null then
1968 HT
: Hash_Table_Type
renames Control
.Container
.all.HT
;
1969 B
: Natural renames HT
.Busy
;
1970 L
: Natural renames HT
.Lock
;
1978 function Equivalent_Key_Node
1980 Node
: Node_Access
) return Boolean;
1981 pragma Inline
(Equivalent_Key_Node
);
1983 --------------------------
1984 -- Local Instantiations --
1985 --------------------------
1988 new Hash_Tables
.Generic_Keys
1989 (HT_Types
=> HT_Types
,
1991 Set_Next
=> Set_Next
,
1992 Key_Type
=> Key_Type
,
1994 Equivalent_Keys
=> Equivalent_Key_Node
);
1996 ------------------------
1997 -- Constant_Reference --
1998 ------------------------
2000 function Constant_Reference
2001 (Container
: aliased Set
;
2002 Key
: Key_Type
) return Constant_Reference_Type
2004 HT
: Hash_Table_Type
renames Container
'Unrestricted_Access.HT
;
2005 Node
: constant Node_Access
:= Key_Keys
.Find
(HT
, Key
);
2009 raise Constraint_Error
with "Key not in set";
2013 B
: Natural renames HT
.Busy
;
2014 L
: Natural renames HT
.Lock
;
2016 return R
: constant Constant_Reference_Type
:=
2017 (Element
=> Node
.Element
'Access,
2018 Control
=> (Controlled
with Container
'Unrestricted_Access))
2024 end Constant_Reference
;
2032 Key
: Key_Type
) return Boolean
2035 return Find
(Container
, Key
) /= No_Element
;
2043 (Container
: in out Set
;
2049 Key_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Key
, X
);
2052 raise Constraint_Error
with "attempt to delete key not in set";
2064 Key
: Key_Type
) return Element_Type
2066 HT
: Hash_Table_Type
renames Container
'Unrestricted_Access.HT
;
2067 Node
: constant Node_Access
:= Key_Keys
.Find
(HT
, Key
);
2071 raise Constraint_Error
with "key not in set";
2074 return Node
.Element
;
2077 -------------------------
2078 -- Equivalent_Key_Node --
2079 -------------------------
2081 function Equivalent_Key_Node
2083 Node
: Node_Access
) return Boolean
2086 return Equivalent_Keys
(Key
, Generic_Keys
.Key
(Node
.Element
));
2087 end Equivalent_Key_Node
;
2094 (Container
: in out Set
;
2099 Key_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Key
, X
);
2107 procedure Finalize
(Control
: in out Reference_Control_Type
) is
2109 if Control
.Container
/= null then
2111 HT
: Hash_Table_Type
renames Control
.Container
.all.HT
;
2112 B
: Natural renames HT
.Busy
;
2113 L
: Natural renames HT
.Lock
;
2119 if Hash
(Key
(Element
(Control
.Old_Pos
))) /= Control
.Old_Hash
2121 HT_Ops
.Delete_Node_At_Index
2122 (Control
.Container
.HT
, Control
.Index
, Control
.Old_Pos
.Node
);
2123 raise Program_Error
with "key not preserved in reference";
2126 Control
.Container
:= null;
2136 Key
: Key_Type
) return Cursor
2138 HT
: Hash_Table_Type
renames Container
'Unrestricted_Access.HT
;
2139 Node
: constant Node_Access
:= Key_Keys
.Find
(HT
, Key
);
2144 return Cursor
'(Container'Unrestricted_Access, Node);
2152 function Key (Position : Cursor) return Key_Type is
2154 if Position.Node = null then
2155 raise Constraint_Error with
2156 "Position cursor equals No_Element";
2159 pragma Assert (Vet (Position), "bad cursor in function Key");
2161 return Key (Position.Node.Element);
2169 (Stream : not null access Root_Stream_Type'Class;
2170 Item : out Reference_Type)
2173 raise Program_Error with "attempt to stream reference";
2176 ------------------------------
2177 -- Reference_Preserving_Key --
2178 ------------------------------
2180 function Reference_Preserving_Key
2181 (Container : aliased in out Set;
2182 Position : Cursor) return Reference_Type
2185 if Position.Container = null then
2186 raise Constraint_Error with "Position cursor has no element";
2189 if Position.Container /= Container'Unrestricted_Access then
2190 raise Program_Error with
2191 "Position cursor designates wrong container";
2196 "bad cursor in function Reference_Preserving_Key");
2199 HT : Hash_Table_Type renames Position.Container.all.HT;
2200 B : Natural renames HT.Busy;
2201 L : Natural renames HT.Lock;
2203 return R : constant Reference_Type :=
2204 (Element => Position.Node.Element'Access,
2207 Container'Unrestricted_Access,
2208 Index => HT_Ops.Index (HT, Position.Node),
2209 Old_Pos => Position,
2210 Old_Hash => Hash (Key (Position))))
2216 end Reference_Preserving_Key;
2218 function Reference_Preserving_Key
2219 (Container : aliased in out Set;
2220 Key : Key_Type) return Reference_Type
2222 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
2226 raise Constraint_Error with "key not in set";
2230 HT : Hash_Table_Type renames Container.HT;
2231 B : Natural renames HT.Busy;
2232 L : Natural renames HT.Lock;
2233 P : constant Cursor := Find (Container, Key);
2235 return R : constant Reference_Type :=
2236 (Element => Node.Element'Access,
2239 Container'Unrestricted_Access,
2240 Index => HT_Ops.Index (HT, P.Node),
2242 Old_Hash => Hash (Key)))
2248 end Reference_Preserving_Key;
2255 (Container : in out Set;
2257 New_Item : Element_Type)
2259 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
2263 raise Constraint_Error with
2264 "attempt to replace key not in set";
2267 Replace_Element (Container.HT, Node, New_Item);
2270 -----------------------------------
2271 -- Update_Element_Preserving_Key --
2272 -----------------------------------
2274 procedure Update_Element_Preserving_Key
2275 (Container : in out Set;
2277 Process : not null access
2278 procedure (Element : in out Element_Type))
2280 HT : Hash_Table_Type renames Container.HT;
2284 if Position.Node = null then
2285 raise Constraint_Error with
2286 "Position cursor equals No_Element";
2289 if Position.Container /= Container'Unrestricted_Access then
2290 raise Program_Error with
2291 "Position cursor designates wrong set";
2294 if HT.Buckets = null
2295 or else HT.Buckets'Length = 0
2296 or else HT.Length = 0
2297 or else Position.Node.Next = Position.Node
2299 raise Program_Error with "Position cursor is bad (set is empty)";
2304 "bad cursor in Update_Element_Preserving_Key");
2306 -- Per AI05-0022, the container implementation is required to detect
2307 -- element tampering by a generic actual subprogram.
2310 E : Element_Type renames Position.Node.Element;
2311 K : constant Key_Type := Key (E);
2313 B : Natural renames HT.Busy;
2314 L : Natural renames HT.Lock;
2323 Indx := HT_Ops.Index (HT, Position.Node);
2325 Eq := Equivalent_Keys (K, Key (E));
2341 if HT.Buckets (Indx) = Position.Node then
2342 HT.Buckets (Indx) := Position.Node.Next;
2346 Prev : Node_Access := HT.Buckets (Indx);
2349 while Prev.Next /= Position.Node loop
2353 raise Program_Error with
2354 "Position cursor is bad (node not found)";
2358 Prev.Next := Position.Node.Next;
2362 HT.Length := HT.Length - 1;
2365 X : Node_Access := Position.Node;
2371 raise Program_Error with "key was modified";
2372 end Update_Element_Preserving_Key;
2379 (Stream : not null access Root_Stream_Type'Class;
2380 Item : Reference_Type)
2383 raise Program_Error with "attempt to stream reference";
2388 end Ada.Containers.Hashed_Sets;