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-2024, 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
.Helpers
; use Ada
.Containers
.Helpers
;
40 with Ada
.Containers
.Prime_Numbers
;
42 with System
; use type System
.Address
;
43 with System
.Put_Images
;
45 package body Ada
.Containers
.Hashed_Sets
with
49 pragma Warnings
(Off
, "variable ""Busy*"" is not referenced");
50 pragma Warnings
(Off
, "variable ""Lock*"" is not referenced");
51 -- See comment in Ada.Containers.Helpers
53 -----------------------
54 -- Local Subprograms --
55 -----------------------
57 procedure Assign
(Node
: Node_Access
; Item
: Element_Type
);
58 pragma Inline
(Assign
);
60 function Copy_Node
(Source
: Node_Access
) return Node_Access
;
61 pragma Inline
(Copy_Node
);
63 function Equivalent_Keys
65 Node
: Node_Access
) return Boolean;
66 pragma Inline
(Equivalent_Keys
);
68 function Find_Equal_Key
69 (R_HT
: Hash_Table_Type
;
70 L_Node
: Node_Access
) return Boolean;
72 function Find_Equivalent_Key
73 (R_HT
: Hash_Table_Type
;
74 L_Node
: Node_Access
) return Boolean;
76 procedure Free
(X
: in out Node_Access
);
78 function Hash_Node
(Node
: Node_Access
) return Hash_Type
;
79 pragma Inline
(Hash_Node
);
82 (HT
: in out Hash_Table_Type
;
83 New_Item
: Element_Type
;
84 Node
: out Node_Access
;
85 Inserted
: out Boolean);
88 (HT
: aliased in out Hash_Table_Type
;
89 Key
: Node_Access
) return Boolean;
90 pragma Inline
(Is_In
);
92 function Next
(Node
: Node_Access
) return Node_Access
;
95 function Read_Node
(Stream
: not null access Root_Stream_Type
'Class)
97 pragma Inline
(Read_Node
);
99 procedure Set_Next
(Node
: Node_Access
; Next
: Node_Access
);
100 pragma Inline
(Set_Next
);
102 function Vet
(Position
: Cursor
) return Boolean with Inline
;
105 (Stream
: not null access Root_Stream_Type
'Class;
107 pragma Inline
(Write_Node
);
109 --------------------------
110 -- Local Instantiations --
111 --------------------------
113 package HT_Ops
is new Hash_Tables
.Generic_Operations
114 (HT_Types
=> HT_Types
,
115 Hash_Node
=> Hash_Node
,
117 Set_Next
=> Set_Next
,
118 Copy_Node
=> Copy_Node
,
121 package Element_Keys
is new Hash_Tables
.Generic_Keys
122 (HT_Types
=> HT_Types
,
124 Set_Next
=> Set_Next
,
125 Key_Type
=> Element_Type
,
127 Equivalent_Keys
=> Equivalent_Keys
);
130 new HT_Ops
.Generic_Equal
(Find_Equal_Key
);
132 function Is_Equivalent
is
133 new HT_Ops
.Generic_Equal
(Find_Equivalent_Key
);
135 procedure Read_Nodes
is
136 new HT_Ops
.Generic_Read
(Read_Node
);
138 procedure Replace_Element
is
139 new Element_Keys
.Generic_Replace_Element
(Hash_Node
, Assign
);
141 procedure Write_Nodes
is
142 new HT_Ops
.Generic_Write
(Write_Node
);
148 function "=" (Left
, Right
: Cursor
) return Boolean is
151 Left
.Container
= Right
.Container
152 and then Left
.Node
= Right
.Node
;
155 function "=" (Left
, Right
: Set
) return Boolean is
157 return Is_Equal
(Left
.HT
, Right
.HT
);
164 procedure Adjust
(Container
: in out Set
) is
166 HT_Ops
.Adjust
(Container
.HT
);
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 Checks
and then Position
.Container
= null then
216 raise Constraint_Error
with "Position cursor has no element";
219 if Checks
and then Position
.Container
/= Container
'Unrestricted_Access
221 raise Program_Error
with
222 "Position cursor designates wrong container";
225 pragma Assert
(Vet
(Position
), "bad cursor in Constant_Reference");
228 HT
: Hash_Table_Type
renames Position
.Container
.all.HT
;
229 TC
: constant Tamper_Counts_Access
:=
230 HT
.TC
'Unrestricted_Access;
232 return R
: constant Constant_Reference_Type
:=
233 (Element
=> Position
.Node
.Element
'Access,
234 Control
=> (Controlled
with TC
))
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
261 if Capacity
< Source
.Length
then
262 if Checks
and then Capacity
/= 0 then
264 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);
300 if Checks and then X = null then
301 raise Constraint_Error with "attempt to delete element not in set";
308 (Container : in out Set;
309 Position : in out Cursor)
312 TC_Check (Container.HT.TC);
314 if Checks and then Position.Node = null then
315 raise Constraint_Error with "Position cursor equals No_Element";
318 if Checks and then Position.Container /= Container'Unrestricted_Access
320 raise Program_Error with "Position cursor designates wrong set";
323 pragma Assert (Vet (Position), "bad cursor in Delete");
325 HT_Ops.Delete_Node_Sans_Free (Container.HT, Position.Node);
327 Free (Position.Node);
328 Position.Container := null;
329 Position.Position := No_Element.Position;
330 pragma Assert (Position = No_Element);
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 TC_Check (Target.HT.TC);
356 if Src_HT.Length < Target.HT.Length then
358 Src_Node : Node_Access;
361 Src_Node := HT_Ops.First (Src_HT);
362 while Src_Node /= null loop
363 Tgt_Node := Element_Keys.Find (Target.HT, Src_Node.Element);
365 if Tgt_Node /= null then
366 HT_Ops.Delete_Node_Sans_Free (Target.HT, Tgt_Node);
370 Src_Node := HT_Ops.Next (Src_HT, Src_Node);
375 Tgt_Node := HT_Ops.First (Target.HT);
376 while Tgt_Node /= null loop
377 if Is_In (Src_HT, Tgt_Node) then
379 X : Node_Access := Tgt_Node;
381 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
382 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
387 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
393 function Difference (Left, Right : Set) return Set is
394 Left_HT : Hash_Table_Type renames Left'Unrestricted_Access.HT;
395 Right_HT : Hash_Table_Type renames Right'Unrestricted_Access.HT;
396 Buckets : HT_Types.Buckets_Access;
400 if Left'Address = Right'Address then
404 if Left_HT.Length = 0 then
408 if Right_HT.Length = 0 then
413 Size : constant Hash_Type := Prime_Numbers.To_Prime (Left.Length);
415 Buckets := HT_Ops.New_Buckets (Length => Size);
420 Iterate_Left : declare
421 procedure Process (L_Node : Node_Access);
424 new HT_Ops.Generic_Iteration (Process);
430 procedure Process (L_Node : Node_Access) is
432 if not Is_In (Right_HT, L_Node) then
434 -- Per AI05-0022, the container implementation is required
435 -- to detect element tampering by a generic actual
436 -- subprogram, hence the use of Checked_Index instead of a
437 -- simple invocation of generic formal Hash.
439 J : constant Hash_Type :=
440 HT_Ops.Checked_Index (Left_HT, Buckets.all, L_Node);
442 Bucket : Node_Access renames Buckets (J);
445 Bucket := new Node_Type'(L_Node
.Element
, Bucket
);
448 Length
:= Length
+ 1;
452 -- Start of processing for Iterate_Left
458 HT_Ops
.Free_Hash_Table
(Buckets
);
462 return (Controlled
with HT
=> (Buckets
, Length
, (Busy
=> 0, Lock
=> 0)));
469 function Element
(Position
: Cursor
) return Element_Type
is
471 if Checks
and then Position
.Node
= null then
472 raise Constraint_Error
with "Position cursor equals No_Element";
475 pragma Assert
(Vet
(Position
), "bad cursor in function Element");
477 return Position
.Node
.Element
;
484 function Empty
(Capacity
: Count_Type
:= 1000) return Set
is
486 return Result
: Set
do
487 Reserve_Capacity
(Result
, Capacity
);
491 ---------------------
492 -- Equivalent_Sets --
493 ---------------------
495 function Equivalent_Sets
(Left
, Right
: Set
) return Boolean is
497 return Is_Equivalent
(Left
.HT
, Right
.HT
);
500 -------------------------
501 -- Equivalent_Elements --
502 -------------------------
504 function Equivalent_Elements
(Left
, Right
: Cursor
)
507 if Checks
and then Left
.Node
= null then
508 raise Constraint_Error
with
509 "Left cursor of Equivalent_Elements equals No_Element";
512 if Checks
and then Right
.Node
= null then
513 raise Constraint_Error
with
514 "Right cursor of Equivalent_Elements equals No_Element";
517 pragma Assert
(Vet
(Left
), "bad Left cursor in Equivalent_Elements");
518 pragma Assert
(Vet
(Right
), "bad Right cursor in Equivalent_Elements");
520 -- AI05-0022 requires that a container implementation detect element
521 -- tampering by a generic actual subprogram. However, the following case
522 -- falls outside the scope of that AI. Randy Brukardt explained on the
523 -- ARG list on 2013/02/07 that:
526 -- But for an operation like "<" [the ordered set analog of
527 -- Equivalent_Elements], there is no need to "dereference" a cursor
528 -- after the call to the generic formal parameter function, so nothing
529 -- bad could happen if tampering is undetected. And the operation can
530 -- safely return a result without a problem even if an element is
531 -- deleted from the container.
534 return Equivalent_Elements
(Left
.Node
.Element
, Right
.Node
.Element
);
535 end Equivalent_Elements
;
537 function Equivalent_Elements
(Left
: Cursor
; Right
: Element_Type
)
540 if Checks
and then Left
.Node
= null then
541 raise Constraint_Error
with
542 "Left cursor of Equivalent_Elements equals No_Element";
545 pragma Assert
(Vet
(Left
), "Left cursor in Equivalent_Elements is bad");
547 return Equivalent_Elements
(Left
.Node
.Element
, Right
);
548 end Equivalent_Elements
;
550 function Equivalent_Elements
(Left
: Element_Type
; Right
: Cursor
)
553 if Checks
and then Right
.Node
= null then
554 raise Constraint_Error
with
555 "Right cursor of Equivalent_Elements equals No_Element";
560 "Right cursor of Equivalent_Elements is bad");
562 return Equivalent_Elements
(Left
, Right
.Node
.Element
);
563 end Equivalent_Elements
;
565 ---------------------
566 -- Equivalent_Keys --
567 ---------------------
569 function Equivalent_Keys
(Key
: Element_Type
; Node
: Node_Access
)
572 return Equivalent_Elements
(Key
, Node
.Element
);
580 (Container
: in out Set
;
585 Element_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Item
, X
);
593 procedure Finalize
(Container
: in out Set
) is
595 HT_Ops
.Finalize
(Container
.HT
);
598 procedure Finalize
(Object
: in out Iterator
) is
600 if Object
.Container
/= null then
601 Unbusy
(Object
.Container
.HT
.TC
);
611 Item
: Element_Type
) return Cursor
613 HT
: Hash_Table_Type
renames Container
'Unrestricted_Access.HT
;
614 Node
: constant Node_Access
:= Element_Keys
.Find
(HT
, Item
);
621 (Container'Unrestricted_Access, Node, HT_Ops.Index (HT, Node));
628 function Find_Equal_Key
629 (R_HT : Hash_Table_Type;
630 L_Node : Node_Access) return Boolean
632 R_Index : constant Hash_Type :=
633 Element_Keys.Index (R_HT, L_Node.Element);
635 R_Node : Node_Access := R_HT.Buckets (R_Index);
639 if R_Node = null then
643 if L_Node.Element = R_Node.Element then
647 R_Node := Next (R_Node);
651 -------------------------
652 -- Find_Equivalent_Key --
653 -------------------------
655 function Find_Equivalent_Key
656 (R_HT : Hash_Table_Type;
657 L_Node : Node_Access) return Boolean
659 R_Index : constant Hash_Type :=
660 Element_Keys.Index (R_HT, L_Node.Element);
662 R_Node : Node_Access := R_HT.Buckets (R_Index);
666 if R_Node = null then
670 if Equivalent_Elements (L_Node.Element, R_Node.Element) then
674 R_Node := Next (R_Node);
676 end Find_Equivalent_Key;
682 function First (Container : Set) return Cursor is
684 Node : constant Node_Access := HT_Ops.First (Container.HT, Pos);
690 return Cursor'(Container
'Unrestricted_Access, Node
, Pos
);
693 function First
(Object
: Iterator
) return Cursor
is
695 return Object
.Container
.First
;
702 procedure Free
(X
: in out Node_Access
) is
703 procedure Deallocate
is
704 new Ada
.Unchecked_Deallocation
(Node_Type
, Node_Access
);
708 X
.Next
:= X
; -- detect mischief (in Vet)
713 ------------------------
714 -- Get_Element_Access --
715 ------------------------
717 function Get_Element_Access
718 (Position
: Cursor
) return not null Element_Access
is
720 return Position
.Node
.Element
'Access;
721 end Get_Element_Access
;
727 function Has_Element
(Position
: Cursor
) return Boolean is
729 pragma Assert
(Vet
(Position
), "bad cursor in Has_Element");
730 return Position
.Node
/= null;
737 function Hash_Node
(Node
: Node_Access
) return Hash_Type
is
739 return Hash
(Node
.Element
);
747 (Container
: in out Set
;
748 New_Item
: Element_Type
)
754 Insert
(Container
, New_Item
, Position
, Inserted
);
757 TE_Check
(Container
.HT
.TC
);
759 Position
.Node
.Element
:= New_Item
;
768 (Container
: in out Set
;
769 New_Item
: Element_Type
;
770 Position
: out Cursor
;
771 Inserted
: out Boolean)
774 Insert
(Container
.HT
, New_Item
, Position
.Node
, Inserted
);
775 Position
.Container
:= Container
'Unchecked_Access;
777 -- Note that we do not set the Position component of the cursor,
778 -- because it may become incorrect on subsequent insertions/deletions
779 -- from the container. This will lose some optimizations but prevents
780 -- anomalies when the underlying hash-table is expanded or shrunk.
784 (Container
: in out Set
;
785 New_Item
: Element_Type
)
791 Insert
(Container
, New_Item
, Position
, Inserted
);
793 if Checks
and then not Inserted
then
794 raise Constraint_Error
with
795 "attempt to insert element already in set";
800 (HT
: in out Hash_Table_Type
;
801 New_Item
: Element_Type
;
802 Node
: out Node_Access
;
803 Inserted
: out Boolean)
805 function New_Node
(Next
: Node_Access
) return Node_Access
;
806 pragma Inline
(New_Node
);
808 procedure Local_Insert
is
809 new Element_Keys
.Generic_Conditional_Insert
(New_Node
);
815 function New_Node
(Next
: Node_Access
) return Node_Access
is
817 return new Node_Type
'(New_Item, Next);
820 -- Start of processing for Insert
823 if HT_Ops.Capacity (HT) = 0 then
824 HT_Ops.Reserve_Capacity (HT, 1);
829 Local_Insert (HT, New_Item, Node, Inserted);
832 and then HT.Length > HT_Ops.Capacity (HT)
834 HT_Ops.Reserve_Capacity (HT, HT.Length);
842 procedure Intersection
843 (Target : in out Set;
846 Src_HT : Hash_Table_Type renames Source'Unrestricted_Access.HT;
847 Tgt_Node : Node_Access;
850 if Target'Address = Source'Address then
854 if Source.HT.Length = 0 then
859 TC_Check (Target.HT.TC);
861 Tgt_Node := HT_Ops.First (Target.HT);
862 while Tgt_Node /= null loop
863 if Is_In (Src_HT, Tgt_Node) then
864 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
868 X : Node_Access := Tgt_Node;
870 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
871 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
878 function Intersection (Left, Right : Set) return Set is
879 Left_HT : Hash_Table_Type renames Left'Unrestricted_Access.HT;
880 Right_HT : Hash_Table_Type renames Right'Unrestricted_Access.HT;
881 Buckets : HT_Types.Buckets_Access;
885 if Left'Address = Right'Address then
889 Length := Count_Type'Min (Left.Length, Right.Length);
896 Size : constant Hash_Type := Prime_Numbers.To_Prime (Length);
898 Buckets := HT_Ops.New_Buckets (Length => Size);
903 Iterate_Left : declare
904 procedure Process (L_Node : Node_Access);
907 new HT_Ops.Generic_Iteration (Process);
913 procedure Process (L_Node : Node_Access) is
915 if Is_In (Right_HT, L_Node) then
917 -- Per AI05-0022, the container implementation is required
918 -- to detect element tampering by a generic actual
919 -- subprogram, hence the use of Checked_Index instead of a
920 -- simple invocation of generic formal Hash.
922 J : constant Hash_Type :=
923 HT_Ops.Checked_Index (Left_HT, Buckets.all, L_Node);
925 Bucket : Node_Access renames Buckets (J);
928 Bucket := new Node_Type'(L_Node
.Element
, Bucket
);
931 Length
:= Length
+ 1;
935 -- Start of processing for Iterate_Left
941 HT_Ops
.Free_Hash_Table
(Buckets
);
945 return (Controlled
with HT
=> (Buckets
, Length
, (Busy
=> 0, Lock
=> 0)));
952 function Is_Empty
(Container
: Set
) return Boolean is
954 return Container
.HT
.Length
= 0;
962 (HT
: aliased in out Hash_Table_Type
;
963 Key
: Node_Access
) return Boolean
966 return Element_Keys
.Find
(HT
, Key
.Element
) /= null;
973 function Is_Subset
(Subset
: Set
; Of_Set
: Set
) return Boolean is
974 Subset_HT
: Hash_Table_Type
renames Subset
'Unrestricted_Access.HT
;
975 Of_Set_HT
: Hash_Table_Type
renames Of_Set
'Unrestricted_Access.HT
;
976 Subset_Node
: Node_Access
;
979 if Subset
'Address = Of_Set
'Address then
983 if Subset
.Length
> Of_Set
.Length
then
987 Subset_Node
:= HT_Ops
.First
(Subset_HT
);
988 while Subset_Node
/= null loop
989 if not Is_In
(Of_Set_HT
, Subset_Node
) then
992 Subset_Node
:= HT_Ops
.Next
(Subset_HT
, Subset_Node
);
1004 Process
: not null access procedure (Position
: Cursor
))
1006 procedure Process_Node
(Node
: Node_Access
; Position
: Hash_Type
);
1007 pragma Inline
(Process_Node
);
1009 procedure Iterate
is
1010 new HT_Ops
.Generic_Iteration_With_Position
(Process_Node
);
1016 procedure Process_Node
(Node
: Node_Access
; Position
: Hash_Type
) is
1018 Process
(Cursor
'(Container'Unrestricted_Access, Node, Position));
1021 Busy : With_Busy (Container.HT.TC'Unrestricted_Access);
1023 -- Start of processing for Iterate
1026 Iterate (Container.HT);
1030 (Container : Set) return Set_Iterator_Interfaces.Forward_Iterator'Class
1033 Busy (Container.HT.TC'Unrestricted_Access.all);
1034 return It : constant Iterator :=
1035 Iterator'(Limited_Controlled
with
1036 Container
=> Container
'Unrestricted_Access);
1043 function Length
(Container
: Set
) return Count_Type
is
1045 return Container
.HT
.Length
;
1052 procedure Move
(Target
: in out Set
; Source
: in out Set
) is
1054 HT_Ops
.Move
(Target
=> Target
.HT
, Source
=> Source
.HT
);
1061 function Next
(Node
: Node_Access
) return Node_Access
is
1066 function Next
(Position
: Cursor
) return Cursor
is
1070 if Position
.Node
= null then
1074 pragma Assert
(Vet
(Position
), "bad cursor in Next");
1076 Pos
:= Position
.Position
;
1077 Node
:= HT_Ops
.Next
(Position
.Container
.HT
, Position
.Node
, Pos
);
1083 return Cursor
'(Position.Container, Node, Pos);
1086 procedure Next (Position : in out Cursor) is
1088 Position := Next (Position);
1093 Position : Cursor) return Cursor
1096 if Position.Container = null then
1100 if Checks and then Position.Container /= Object.Container then
1101 raise Program_Error with
1102 "Position cursor of Next designates wrong set";
1105 return Next (Position);
1112 function Overlap (Left, Right : Set) return Boolean is
1113 Left_HT : Hash_Table_Type renames Left'Unrestricted_Access.HT;
1114 Right_HT : Hash_Table_Type renames Right'Unrestricted_Access.HT;
1115 Left_Node : Node_Access;
1118 if Right.Length = 0 then
1122 if Left'Address = Right'Address then
1126 Left_Node := HT_Ops.First (Left_HT);
1127 while Left_Node /= null loop
1128 if Is_In (Right_HT, Left_Node) then
1131 Left_Node := HT_Ops.Next (Left_HT, Left_Node);
1137 ----------------------
1138 -- Pseudo_Reference --
1139 ----------------------
1141 function Pseudo_Reference
1142 (Container : aliased Set'Class) return Reference_Control_Type
1144 TC : constant Tamper_Counts_Access :=
1145 Container.HT.TC'Unrestricted_Access;
1147 return R : constant Reference_Control_Type := (Controlled with TC) do
1150 end Pseudo_Reference;
1156 procedure Query_Element
1158 Process : not null access procedure (Element : Element_Type))
1161 if Checks and then Position.Node = null then
1162 raise Constraint_Error with
1163 "Position cursor of Query_Element equals No_Element";
1166 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1169 HT : Hash_Table_Type renames Position.Container.HT;
1170 Lock : With_Lock (HT.TC'Unrestricted_Access);
1172 Process (Position.Node.Element);
1181 (S : in out Ada.Strings.Text_Buffers.Root_Buffer_Type'Class; V : Set)
1183 First_Time : Boolean := True;
1184 use System.Put_Images;
1190 First_Time := False;
1192 Simple_Array_Between (S);
1195 Element_Type'Put_Image (S, X);
1206 (Stream : not null access Root_Stream_Type'Class;
1207 Container : out Set)
1210 Read_Nodes (Stream, Container.HT);
1214 (Stream : not null access Root_Stream_Type'Class;
1218 raise Program_Error with "attempt to stream set cursor";
1222 (Stream : not null access Root_Stream_Type'Class;
1223 Item : out Constant_Reference_Type)
1226 raise Program_Error with "attempt to stream reference";
1233 function Read_Node (Stream : not null access Root_Stream_Type'Class)
1236 Node : Node_Access := new Node_Type;
1238 Element_Type'Read (Stream, Node.Element);
1251 (Container : in out Set;
1252 New_Item : Element_Type)
1254 Node : constant Node_Access :=
1255 Element_Keys.Find (Container.HT, New_Item);
1258 TE_Check (Container.HT.TC);
1260 if Checks and then Node = null then
1261 raise Constraint_Error with
1262 "attempt to replace element not in set";
1265 Node.Element := New_Item;
1268 procedure Replace_Element
1269 (Container : in out Set;
1271 New_Item : Element_Type)
1274 if Checks and then Position.Node = null then
1275 raise Constraint_Error with
1276 "Position cursor equals No_Element";
1279 if Checks and then Position.Container /= Container'Unrestricted_Access
1281 raise Program_Error with
1282 "Position cursor designates wrong set";
1285 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1287 Replace_Element (Container.HT, Position.Node, New_Item);
1288 end Replace_Element;
1290 ----------------------
1291 -- Reserve_Capacity --
1292 ----------------------
1294 procedure Reserve_Capacity
1295 (Container : in out Set;
1296 Capacity : Count_Type)
1299 HT_Ops.Reserve_Capacity (Container.HT, Capacity);
1300 end Reserve_Capacity;
1306 procedure Set_Next (Node : Node_Access; Next : Node_Access) is
1311 --------------------------
1312 -- Symmetric_Difference --
1313 --------------------------
1315 procedure Symmetric_Difference
1316 (Target : in out Set;
1319 Tgt_HT : Hash_Table_Type renames Target.HT;
1320 Src_HT : Hash_Table_Type renames Source.HT'Unrestricted_Access.all;
1322 if Target'Address = Source'Address then
1327 TC_Check (Tgt_HT.TC);
1330 N : constant Count_Type := Target.Length + Source.Length;
1332 if N > HT_Ops.Capacity (Tgt_HT) then
1333 HT_Ops.Reserve_Capacity (Tgt_HT, N);
1337 if Target.Length = 0 then
1338 Iterate_Source_When_Empty_Target : declare
1339 procedure Process (Src_Node : Node_Access);
1341 procedure Iterate is new HT_Ops.Generic_Iteration (Process);
1347 procedure Process (Src_Node : Node_Access) is
1348 E : Element_Type renames Src_Node.Element;
1349 B : Buckets_Type renames Tgt_HT.Buckets.all;
1350 J : constant Hash_Type := Hash (E) mod B'Length;
1351 N : Count_Type renames Tgt_HT.Length;
1354 B (J) := new Node_Type'(E
, B
(J
));
1358 -- Per AI05-0022, the container implementation is required to
1359 -- detect element tampering by a generic actual subprogram.
1361 Lock_Tgt
: With_Lock
(Tgt_HT
.TC
'Unrestricted_Access);
1362 Lock_Src
: With_Lock
(Src_HT
.TC
'Unrestricted_Access);
1364 -- Start of processing for Iterate_Source_When_Empty_Target
1368 end Iterate_Source_When_Empty_Target
;
1371 Iterate_Source
: declare
1372 procedure Process
(Src_Node
: Node_Access
);
1374 procedure Iterate
is
1375 new HT_Ops
.Generic_Iteration
(Process
);
1381 procedure Process
(Src_Node
: Node_Access
) is
1382 E
: Element_Type
renames Src_Node
.Element
;
1383 B
: Buckets_Type
renames Tgt_HT
.Buckets
.all;
1384 J
: constant Hash_Type
:= Hash
(E
) mod B
'Length;
1385 N
: Count_Type
renames Tgt_HT
.Length
;
1388 if B
(J
) = null then
1389 B
(J
) := new Node_Type
'(E, null);
1392 elsif Equivalent_Elements (E, B (J).Element) then
1394 X : Node_Access := B (J);
1396 B (J) := B (J).Next;
1403 Prev : Node_Access := B (J);
1404 Curr : Node_Access := Prev.Next;
1407 while Curr /= null loop
1408 if Equivalent_Elements (E, Curr.Element) then
1409 Prev.Next := Curr.Next;
1419 B (J) := new Node_Type'(E
, B
(J
));
1425 -- Per AI05-0022, the container implementation is required to
1426 -- detect element tampering by a generic actual subprogram.
1428 Lock_Tgt
: With_Lock
(Tgt_HT
.TC
'Unrestricted_Access);
1429 Lock_Src
: With_Lock
(Src_HT
.TC
'Unrestricted_Access);
1431 -- Start of processing for Iterate_Source
1437 end Symmetric_Difference
;
1439 function Symmetric_Difference
(Left
, Right
: Set
) return Set
is
1440 Left_HT
: Hash_Table_Type
renames Left
'Unrestricted_Access.HT
;
1441 Right_HT
: Hash_Table_Type
renames Right
'Unrestricted_Access.HT
;
1442 Buckets
: HT_Types
.Buckets_Access
;
1443 Length
: Count_Type
;
1446 if Left
'Address = Right
'Address then
1450 if Right
.Length
= 0 then
1454 if Left
.Length
= 0 then
1459 Size
: constant Hash_Type
:=
1460 Prime_Numbers
.To_Prime
(Left
.Length
+ Right
.Length
);
1462 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
1467 Iterate_Left
: declare
1468 procedure Process
(L_Node
: Node_Access
);
1470 procedure Iterate
is
1471 new HT_Ops
.Generic_Iteration
(Process
);
1477 procedure Process
(L_Node
: Node_Access
) is
1479 if not Is_In
(Right_HT
, L_Node
) then
1481 E
: Element_Type
renames L_Node
.Element
;
1483 -- Per AI05-0022, the container implementation is required
1484 -- to detect element tampering by a generic actual
1485 -- subprogram, hence the use of Checked_Index instead of a
1486 -- simple invocation of generic formal Hash.
1488 J
: constant Hash_Type
:=
1489 HT_Ops
.Checked_Index
(Left_HT
, Buckets
.all, L_Node
);
1492 Buckets
(J
) := new Node_Type
'(E, Buckets (J));
1493 Length := Length + 1;
1498 -- Start of processing for Iterate_Left
1505 HT_Ops.Free_Hash_Table (Buckets);
1509 Iterate_Right : declare
1510 procedure Process (R_Node : Node_Access);
1512 procedure Iterate is
1513 new HT_Ops.Generic_Iteration (Process);
1519 procedure Process (R_Node : Node_Access) is
1521 if not Is_In (Left_HT, R_Node) then
1523 E : Element_Type renames R_Node.Element;
1525 -- Per AI05-0022, the container implementation is required
1526 -- to detect element tampering by a generic actual
1527 -- subprogram, hence the use of Checked_Index instead of a
1528 -- simple invocation of generic formal Hash.
1530 J : constant Hash_Type :=
1531 HT_Ops.Checked_Index (Right_HT, Buckets.all, R_Node);
1534 Buckets (J) := new Node_Type'(E
, Buckets
(J
));
1535 Length
:= Length
+ 1;
1540 -- Start of processing for Iterate_Right
1547 HT_Ops
.Free_Hash_Table
(Buckets
);
1551 return (Controlled
with HT
=> (Buckets
, Length
, (Busy
=> 0, Lock
=> 0)));
1552 end Symmetric_Difference
;
1558 function To_Set
(New_Item
: Element_Type
) return Set
is
1559 HT
: Hash_Table_Type
;
1565 Insert
(HT
, New_Item
, Node
, Inserted
);
1566 return Set
'(Controlled with HT);
1574 (Target : in out Set;
1577 procedure Process (Src_Node : Node_Access);
1579 procedure Iterate is
1580 new HT_Ops.Generic_Iteration (Process);
1586 procedure Process (Src_Node : Node_Access) is
1587 function New_Node (Next : Node_Access) return Node_Access;
1588 pragma Inline (New_Node);
1591 new Element_Keys.Generic_Conditional_Insert (New_Node);
1597 function New_Node (Next : Node_Access) return Node_Access is
1598 Node : constant Node_Access :=
1599 new Node_Type'(Src_Node
.Element
, Next
);
1604 Tgt_Node
: Node_Access
;
1607 -- Start of processing for Process
1610 Insert
(Target
.HT
, Src_Node
.Element
, Tgt_Node
, Success
);
1613 -- Start of processing for Union
1616 if Target
'Address = Source
'Address then
1620 TC_Check
(Target
.HT
.TC
);
1623 N
: constant Count_Type
:= Target
.Length
+ Source
.Length
;
1625 if N
> HT_Ops
.Capacity
(Target
.HT
) then
1626 HT_Ops
.Reserve_Capacity
(Target
.HT
, N
);
1630 Iterate
(Source
.HT
);
1633 function Union
(Left
, Right
: Set
) return Set
is
1634 Left_HT
: Hash_Table_Type
renames Left
.HT
'Unrestricted_Access.all;
1635 Right_HT
: Hash_Table_Type
renames Right
.HT
'Unrestricted_Access.all;
1636 Buckets
: HT_Types
.Buckets_Access
;
1637 Length
: Count_Type
;
1640 if Left
'Address = Right
'Address then
1644 if Right
.Length
= 0 then
1648 if Left
.Length
= 0 then
1653 Size
: constant Hash_Type
:=
1654 Prime_Numbers
.To_Prime
(Left
.Length
+ Right
.Length
);
1656 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
1659 Iterate_Left
: declare
1660 procedure Process
(L_Node
: Node_Access
);
1662 procedure Iterate
is
1663 new HT_Ops
.Generic_Iteration
(Process
);
1669 procedure Process
(L_Node
: Node_Access
) is
1670 J
: constant Hash_Type
:=
1671 Hash
(L_Node
.Element
) mod Buckets
'Length;
1674 Buckets
(J
) := new Node_Type
'(L_Node.Element, Buckets (J));
1677 -- Per AI05-0022, the container implementation is required to detect
1678 -- element tampering by a generic actual subprogram, hence the use of
1679 -- Checked_Index instead of a simple invocation of generic formal
1682 Lock_Left : With_Lock (Left_HT.TC'Unrestricted_Access);
1684 -- Start of processing for Iterate_Left
1690 HT_Ops.Free_Hash_Table (Buckets);
1694 Length := Left.Length;
1696 Iterate_Right : declare
1697 procedure Process (Src_Node : Node_Access);
1699 procedure Iterate is
1700 new HT_Ops.Generic_Iteration (Process);
1706 procedure Process (Src_Node : Node_Access) is
1707 J : constant Hash_Type :=
1708 Hash (Src_Node.Element) mod Buckets'Length;
1710 Tgt_Node : Node_Access := Buckets (J);
1713 while Tgt_Node /= null loop
1714 if Equivalent_Elements (Src_Node.Element, Tgt_Node.Element) then
1718 Tgt_Node := Next (Tgt_Node);
1721 Buckets (J) := new Node_Type'(Src_Node
.Element
, Buckets
(J
));
1722 Length
:= Length
+ 1;
1725 -- Per AI05-0022, the container implementation is required to detect
1726 -- element tampering by a generic actual subprogram, hence the use of
1727 -- Checked_Index instead of a simple invocation of generic formal
1730 Lock_Left
: With_Lock
(Left_HT
.TC
'Unrestricted_Access);
1731 Lock_Right
: With_Lock
(Right_HT
.TC
'Unrestricted_Access);
1733 -- Start of processing for Iterate_Right
1739 HT_Ops
.Free_Hash_Table
(Buckets
);
1743 return (Controlled
with HT
=> (Buckets
, Length
, (Busy
=> 0, Lock
=> 0)));
1750 function Vet
(Position
: Cursor
) return Boolean is
1752 if not Container_Checks
'Enabled then
1756 if Position
.Node
= null then
1757 return Position
.Container
= null;
1760 if Position
.Container
= null then
1764 if Position
.Node
.Next
= Position
.Node
then
1769 HT
: Hash_Table_Type
renames Position
.Container
.HT
;
1773 if HT
.Length
= 0 then
1777 if HT
.Buckets
= null
1778 or else HT
.Buckets
'Length = 0
1783 X
:= HT
.Buckets
(Element_Keys
.Checked_Index
1785 Position
.Node
.Element
));
1787 for J
in 1 .. HT
.Length
loop
1788 if X
= Position
.Node
then
1796 if X
= X
.Next
then -- to prevent unnecessary looping
1812 (Stream
: not null access Root_Stream_Type
'Class;
1816 Write_Nodes
(Stream
, Container
.HT
);
1820 (Stream
: not null access Root_Stream_Type
'Class;
1824 raise Program_Error
with "attempt to stream set cursor";
1828 (Stream
: not null access Root_Stream_Type
'Class;
1829 Item
: Constant_Reference_Type
)
1832 raise Program_Error
with "attempt to stream reference";
1839 procedure Write_Node
1840 (Stream
: not null access Root_Stream_Type
'Class;
1844 Element_Type
'Write (Stream
, Node
.Element
);
1847 -- Ada 2022 features:
1849 function Has_Element
(Container
: Set
; Position
: Cursor
) return Boolean is
1851 pragma Assert
(Vet
(Position
), "bad cursor in Has_Element");
1852 pragma Assert
((Position
.Container
= null) = (Position
.Node
= null),
1853 "bad nullity in Has_Element");
1854 return Position
.Container
= Container
'Unrestricted_Access;
1857 function Tampering_With_Cursors_Prohibited
1858 (Container
: Set
) return Boolean
1861 return Is_Busy
(Container
.HT
.TC
);
1862 end Tampering_With_Cursors_Prohibited
;
1864 function Element
(Container
: Set
; Position
: Cursor
) return Element_Type
is
1866 if Checks
and then not Has_Element
(Container
, Position
) then
1867 raise Program_Error
with "Position for wrong Container";
1870 return Element
(Position
);
1873 procedure Query_Element
1876 Process
: not null access procedure (Element
: Element_Type
)) is
1878 if Checks
and then not Has_Element
(Container
, Position
) then
1879 raise Program_Error
with "Position for wrong Container";
1882 Query_Element
(Position
, Process
);
1885 function Next
(Container
: Set
; Position
: Cursor
) return Cursor
is
1888 not (Position
= No_Element
or else Has_Element
(Container
, Position
))
1890 raise Program_Error
with "Position for wrong Container";
1893 return Next
(Position
);
1896 procedure Next
(Container
: Set
; Position
: in out Cursor
) is
1898 Position
:= Next
(Container
, Position
);
1905 package body Generic_Keys
is
1907 -----------------------
1908 -- Local Subprograms --
1909 -----------------------
1911 function Equivalent_Key_Node
1913 Node
: Node_Access
) return Boolean;
1914 pragma Inline
(Equivalent_Key_Node
);
1916 --------------------------
1917 -- Local Instantiations --
1918 --------------------------
1921 new Hash_Tables
.Generic_Keys
1922 (HT_Types
=> HT_Types
,
1924 Set_Next
=> Set_Next
,
1925 Key_Type
=> Key_Type
,
1927 Equivalent_Keys
=> Equivalent_Key_Node
);
1929 ------------------------
1930 -- Constant_Reference --
1931 ------------------------
1933 function Constant_Reference
1934 (Container
: aliased Set
;
1935 Key
: Key_Type
) return Constant_Reference_Type
1937 Position
: constant Cursor
:= Find
(Container
, Key
);
1940 if Checks
and then Position
= No_Element
then
1941 raise Constraint_Error
with "Key not in set";
1944 return Constant_Reference
(Container
, Position
);
1945 end Constant_Reference
;
1953 Key
: Key_Type
) return Boolean
1956 return Find
(Container
, Key
) /= No_Element
;
1964 (Container
: in out Set
;
1970 Key_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Key
, X
);
1972 if Checks
and then X
= null then
1973 raise Constraint_Error
with "attempt to delete key not in set";
1985 Key
: Key_Type
) return Element_Type
1987 HT
: Hash_Table_Type
renames Container
'Unrestricted_Access.HT
;
1988 Node
: constant Node_Access
:= Key_Keys
.Find
(HT
, Key
);
1991 if Checks
and then Node
= null then
1992 raise Constraint_Error
with "key not in set";
1995 return Node
.Element
;
1998 -------------------------
1999 -- Equivalent_Key_Node --
2000 -------------------------
2002 function Equivalent_Key_Node
2004 Node
: Node_Access
) return Boolean
2007 return Equivalent_Keys
(Key
, Generic_Keys
.Key
(Node
.Element
));
2008 end Equivalent_Key_Node
;
2015 (Container
: in out Set
;
2020 Key_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Key
, X
);
2028 procedure Finalize
(Control
: in out Reference_Control_Type
) is
2030 if Control
.Container
/= null then
2031 Impl
.Reference_Control_Type
(Control
).Finalize
;
2034 Hash
(Key
(Element
(Control
.Old_Pos
))) /= Control
.Old_Hash
2036 HT_Ops
.Delete_Node_At_Index
2037 (Control
.Container
.HT
, Control
.Index
, Control
.Old_Pos
.Node
);
2038 raise Program_Error
with "key not preserved in reference";
2041 Control
.Container
:= null;
2051 Key
: Key_Type
) return Cursor
2053 HT
: Hash_Table_Type
renames Container
'Unrestricted_Access.HT
;
2054 Node
: constant Node_Access
:= Key_Keys
.Find
(HT
, Key
);
2060 (Container'Unrestricted_Access, Node, HT_Ops.Index (HT, Node));
2068 function Key (Position : Cursor) return Key_Type is
2070 if Checks and then Position.Node = null then
2071 raise Constraint_Error with
2072 "Position cursor equals No_Element";
2075 pragma Assert (Vet (Position), "bad cursor in function Key");
2077 return Key (Position.Node.Element);
2085 (Stream : not null access Root_Stream_Type'Class;
2086 Item : out Reference_Type)
2089 raise Program_Error with "attempt to stream reference";
2092 ------------------------------
2093 -- Reference_Preserving_Key --
2094 ------------------------------
2096 function Reference_Preserving_Key
2097 (Container : aliased in out Set;
2098 Position : Cursor) return Reference_Type
2101 if Checks and then Position.Container = null then
2102 raise Constraint_Error with "Position cursor has no element";
2105 if Checks and then Position.Container /= Container'Unrestricted_Access
2107 raise Program_Error with
2108 "Position cursor designates wrong container";
2113 "bad cursor in function Reference_Preserving_Key");
2116 HT : Hash_Table_Type renames Position.Container.all.HT;
2118 return R : constant Reference_Type :=
2119 (Element => Position.Node.Element'Access,
2122 HT.TC'Unrestricted_Access,
2123 Container'Unrestricted_Access,
2124 Index => HT_Ops.Index (HT, Position.Node),
2125 Old_Pos => Position,
2126 Old_Hash => Hash (Key (Position))))
2131 end Reference_Preserving_Key;
2133 function Reference_Preserving_Key
2134 (Container : aliased in out Set;
2135 Key : Key_Type) return Reference_Type
2137 Position : constant Cursor := Find (Container, Key);
2140 if Checks and then Position = No_Element then
2141 raise Constraint_Error with "key not in set";
2144 return Reference_Preserving_Key (Container, Position);
2145 end Reference_Preserving_Key;
2152 (Container : in out Set;
2154 New_Item : Element_Type)
2156 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
2159 if Checks and then Node = null then
2160 raise Constraint_Error with
2161 "attempt to replace key not in set";
2164 Replace_Element (Container.HT, Node, New_Item);
2167 -----------------------------------
2168 -- Update_Element_Preserving_Key --
2169 -----------------------------------
2171 procedure Update_Element_Preserving_Key
2172 (Container : in out Set;
2174 Process : not null access
2175 procedure (Element : in out Element_Type))
2177 HT : Hash_Table_Type renames Container.HT;
2181 if Checks and then Position.Node = null then
2182 raise Constraint_Error with
2183 "Position cursor equals No_Element";
2186 if Checks and then Position.Container /= Container'Unrestricted_Access
2188 raise Program_Error with
2189 "Position cursor designates wrong set";
2194 or else HT.Buckets'Length = 0
2195 or else HT.Length = 0
2196 or else Position.Node.Next = Position.Node)
2198 raise Program_Error with "Position cursor is bad (set is empty)";
2203 "bad cursor in Update_Element_Preserving_Key");
2205 -- Per AI05-0022, the container implementation is required to detect
2206 -- element tampering by a generic actual subprogram.
2209 E : Element_Type renames Position.Node.Element;
2210 K : constant Key_Type := Key (E);
2211 Lock : With_Lock (HT.TC'Unrestricted_Access);
2213 Indx := HT_Ops.Index (HT, Position.Node);
2216 if Equivalent_Keys (K, Key (E)) then
2221 if HT.Buckets (Indx) = Position.Node then
2222 HT.Buckets (Indx) := Position.Node.Next;
2226 Prev : Node_Access := HT.Buckets (Indx);
2229 while Prev.Next /= Position.Node loop
2232 if Checks and then Prev = null then
2233 raise Program_Error with
2234 "Position cursor is bad (node not found)";
2238 Prev.Next := Position.Node.Next;
2242 HT.Length := HT.Length - 1;
2245 X : Node_Access := Position.Node;
2251 raise Program_Error with "key was modified";
2252 end Update_Element_Preserving_Key;
2259 (Stream : not null access Root_Stream_Type'Class;
2260 Item : Reference_Type)
2263 raise Program_Error with "attempt to stream reference";
2268 end Ada.Containers.Hashed_Sets;