1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- A D A . C O N T A I N E R S . B O U N D E D _ H A S H E D _ S E T S --
9 -- Copyright (C) 2004-2023, 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
.Containers
.Hash_Tables
.Generic_Bounded_Operations
;
31 pragma Elaborate_All
(Ada
.Containers
.Hash_Tables
.Generic_Bounded_Operations
);
33 with Ada
.Containers
.Hash_Tables
.Generic_Bounded_Keys
;
34 pragma Elaborate_All
(Ada
.Containers
.Hash_Tables
.Generic_Bounded_Keys
);
36 with Ada
.Containers
.Helpers
; use Ada
.Containers
.Helpers
;
38 with Ada
.Containers
.Prime_Numbers
; use Ada
.Containers
.Prime_Numbers
;
40 with System
; use type System
.Address
;
41 with System
.Put_Images
;
43 package body Ada
.Containers
.Bounded_Hashed_Sets
with
48 pragma Warnings
(Off
, "variable ""Busy*"" is not referenced");
49 pragma Warnings
(Off
, "variable ""Lock*"" is not referenced");
50 -- See comment in Ada.Containers.Helpers
52 -----------------------
53 -- Local Subprograms --
54 -----------------------
56 function Equivalent_Keys
58 Node
: Node_Type
) return Boolean;
59 pragma Inline
(Equivalent_Keys
);
61 function Hash_Node
(Node
: Node_Type
) return Hash_Type
;
62 pragma Inline
(Hash_Node
);
65 (Container
: in out Set
;
66 New_Item
: Element_Type
;
67 Node
: out Count_Type
;
68 Inserted
: out Boolean);
70 function Is_In
(HT
: Set
; Key
: Node_Type
) return Boolean;
71 pragma Inline
(Is_In
);
73 procedure Set_Element
(Node
: in out Node_Type
; Item
: Element_Type
);
74 pragma Inline
(Set_Element
);
76 function Next
(Node
: Node_Type
) return Count_Type
;
79 procedure Set_Next
(Node
: in out Node_Type
; Next
: Count_Type
);
80 pragma Inline
(Set_Next
);
82 function Vet
(Position
: Cursor
) return Boolean with Inline
;
84 --------------------------
85 -- Local Instantiations --
86 --------------------------
88 package HT_Ops
is new Hash_Tables
.Generic_Bounded_Operations
89 (HT_Types
=> HT_Types
,
90 Hash_Node
=> Hash_Node
,
92 Set_Next
=> Set_Next
);
94 package Element_Keys
is new Hash_Tables
.Generic_Bounded_Keys
95 (HT_Types
=> HT_Types
,
98 Key_Type
=> Element_Type
,
100 Equivalent_Keys
=> Equivalent_Keys
);
102 procedure Replace_Element
is
103 new Element_Keys
.Generic_Replace_Element
(Hash_Node
, Set_Element
);
109 function "=" (Left
, Right
: Set
) return Boolean is
110 function Find_Equal_Key
111 (R_HT
: Hash_Table_Type
'Class;
112 L_Node
: Node_Type
) return Boolean;
113 pragma Inline
(Find_Equal_Key
);
116 new HT_Ops
.Generic_Equal
(Find_Equal_Key
);
122 function Find_Equal_Key
123 (R_HT
: Hash_Table_Type
'Class;
124 L_Node
: Node_Type
) return Boolean
126 R_Index
: constant Hash_Type
:=
127 Element_Keys
.Index
(R_HT
, L_Node
.Element
);
129 R_Node
: Count_Type
:= R_HT
.Buckets
(R_Index
);
137 if L_Node
.Element
= R_HT
.Nodes
(R_Node
).Element
then
141 R_Node
:= Next
(R_HT
.Nodes
(R_Node
));
145 -- Start of processing for "="
148 return Is_Equal
(Left
, Right
);
155 procedure Assign
(Target
: in out Set
; Source
: Set
) is
156 procedure Insert_Element
(Source_Node
: Count_Type
);
158 procedure Insert_Elements
is
159 new HT_Ops
.Generic_Iteration
(Insert_Element
);
165 procedure Insert_Element
(Source_Node
: Count_Type
) is
166 N
: Node_Type
renames Source
.Nodes
(Source_Node
);
170 Insert
(Target
, N
.Element
, X
, B
);
174 -- Start of processing for Assign
177 if Target
'Address = Source
'Address then
181 if Checks
and then Target
.Capacity
< Source
.Length
then
183 with "Target capacity is less than Source length";
186 HT_Ops
.Clear
(Target
);
187 Insert_Elements
(Source
);
194 function Capacity
(Container
: Set
) return Count_Type
is
196 return Container
.Capacity
;
203 procedure Clear
(Container
: in out Set
) is
205 HT_Ops
.Clear
(Container
);
208 ------------------------
209 -- Constant_Reference --
210 ------------------------
212 function Constant_Reference
213 (Container
: aliased Set
;
214 Position
: Cursor
) return Constant_Reference_Type
217 if Checks
and then Position
.Container
= null then
218 raise Constraint_Error
with "Position cursor has no element";
221 if Checks
and then Position
.Container
/= Container
'Unrestricted_Access
223 raise Program_Error
with
224 "Position cursor designates wrong container";
227 pragma Assert
(Vet
(Position
), "bad cursor in Constant_Reference");
230 N
: Node_Type
renames Container
.Nodes
(Position
.Node
);
231 TC
: constant Tamper_Counts_Access
:=
232 Container
.TC
'Unrestricted_Access;
234 return R
: constant Constant_Reference_Type
:=
235 (Element
=> N
.Element
'Unchecked_Access,
236 Control
=> (Controlled
with TC
))
241 end Constant_Reference
;
247 function Contains
(Container
: Set
; Item
: Element_Type
) return Boolean is
249 return Find
(Container
, Item
) /= No_Element
;
258 Capacity
: Count_Type
:= 0;
259 Modulus
: Hash_Type
:= 0) return Set
261 C
: constant Count_Type
:=
262 (if Capacity
= 0 then Source
.Length
267 if Checks
and then C
< Source
.Length
then
268 raise Capacity_Error
with "Capacity too small";
272 M
:= Default_Modulus
(C
);
277 return Target
: Set
(Capacity
=> C
, Modulus
=> M
) do
278 Assign
(Target
=> Target
, Source
=> Source
);
282 ---------------------
283 -- Default_Modulus --
284 ---------------------
286 function Default_Modulus
(Capacity
: Count_Type
) return Hash_Type
is
288 return To_Prime
(Capacity
);
296 (Container
: in out Set
;
302 Element_Keys
.Delete_Key_Sans_Free
(Container
, Item
, X
);
304 if Checks
and then X
= 0 then
305 raise Constraint_Error
with "attempt to delete element not in set";
308 HT_Ops
.Free
(Container
, X
);
312 (Container
: in out Set
;
313 Position
: in out Cursor
)
316 TC_Check
(Container
.TC
);
318 if Checks
and then Position
.Node
= 0 then
319 raise Constraint_Error
with "Position cursor equals No_Element";
322 if Checks
and then Position
.Container
/= Container
'Unrestricted_Access
324 raise Program_Error
with "Position cursor designates wrong set";
327 pragma Assert
(Vet
(Position
), "bad cursor in Delete");
329 HT_Ops
.Delete_Node_Sans_Free
(Container
, Position
.Node
);
330 HT_Ops
.Free
(Container
, Position
.Node
);
332 Position
:= No_Element
;
340 (Target
: in out Set
;
343 Tgt_Node
, Src_Node
: Count_Type
;
345 Src
: Set
renames Source
'Unrestricted_Access.all;
347 TN
: Nodes_Type
renames Target
.Nodes
;
348 SN
: Nodes_Type
renames Source
.Nodes
;
351 if Target
'Address = Source
'Address then
352 HT_Ops
.Clear
(Target
);
356 if Source
.Length
= 0 then
360 TC_Check
(Target
.TC
);
362 if Source
.Length
< Target
.Length
then
363 Src_Node
:= HT_Ops
.First
(Source
);
364 while Src_Node
/= 0 loop
365 Tgt_Node
:= Element_Keys
.Find
(Target
, SN
(Src_Node
).Element
);
367 if Tgt_Node
/= 0 then
368 HT_Ops
.Delete_Node_Sans_Free
(Target
, Tgt_Node
);
369 HT_Ops
.Free
(Target
, Tgt_Node
);
372 Src_Node
:= HT_Ops
.Next
(Src
, Src_Node
);
376 Tgt_Node
:= HT_Ops
.First
(Target
);
377 while Tgt_Node
/= 0 loop
378 if Is_In
(Source
, TN
(Tgt_Node
)) then
380 X
: constant Count_Type
:= Tgt_Node
;
382 Tgt_Node
:= HT_Ops
.Next
(Target
, Tgt_Node
);
383 HT_Ops
.Delete_Node_Sans_Free
(Target
, X
);
384 HT_Ops
.Free
(Target
, X
);
388 Tgt_Node
:= HT_Ops
.Next
(Target
, Tgt_Node
);
394 function Difference
(Left
, Right
: Set
) return Set
is
396 if Left
'Address = Right
'Address then
400 if Left
.Length
= 0 then
404 if Right
.Length
= 0 then
408 return Result
: Set
(Left
.Length
, To_Prime
(Left
.Length
)) do
409 Iterate_Left
: declare
410 procedure Process
(L_Node
: Count_Type
);
413 new HT_Ops
.Generic_Iteration
(Process
);
419 procedure Process
(L_Node
: Count_Type
) is
420 N
: Node_Type
renames Left
.Nodes
(L_Node
);
424 if not Is_In
(Right
, N
) then
425 Insert
(Result
, N
.Element
, X
, B
); -- optimize this ???
427 pragma Assert
(X
> 0);
431 -- Start of processing for Iterate_Left
443 function Element
(Position
: Cursor
) return Element_Type
is
445 if Checks
and then Position
.Node
= 0 then
446 raise Constraint_Error
with "Position cursor equals No_Element";
449 pragma Assert
(Vet
(Position
), "bad cursor in function Element");
452 S
: Set
renames Position
.Container
.all;
453 N
: Node_Type
renames S
.Nodes
(Position
.Node
);
463 function Empty
(Capacity
: Count_Type
:= 10) return Set
is
465 return Result
: Set
(Capacity
, 0) do
466 Reserve_Capacity
(Result
, Capacity
);
470 ---------------------
471 -- Equivalent_Sets --
472 ---------------------
474 function Equivalent_Sets
(Left
, Right
: Set
) return Boolean is
475 function Find_Equivalent_Key
476 (R_HT
: Hash_Table_Type
'Class;
477 L_Node
: Node_Type
) return Boolean;
478 pragma Inline
(Find_Equivalent_Key
);
480 function Is_Equivalent
is
481 new HT_Ops
.Generic_Equal
(Find_Equivalent_Key
);
483 -------------------------
484 -- Find_Equivalent_Key --
485 -------------------------
487 function Find_Equivalent_Key
488 (R_HT
: Hash_Table_Type
'Class;
489 L_Node
: Node_Type
) return Boolean
491 R_Index
: constant Hash_Type
:=
492 Element_Keys
.Index
(R_HT
, L_Node
.Element
);
494 R_Node
: Count_Type
:= R_HT
.Buckets
(R_Index
);
496 RN
: Nodes_Type
renames R_HT
.Nodes
;
504 if Equivalent_Elements
(L_Node
.Element
, RN
(R_Node
).Element
) then
508 R_Node
:= Next
(R_HT
.Nodes
(R_Node
));
510 end Find_Equivalent_Key
;
512 -- Start of processing for Equivalent_Sets
515 return Is_Equivalent
(Left
, Right
);
518 -------------------------
519 -- Equivalent_Elements --
520 -------------------------
522 function Equivalent_Elements
(Left
, Right
: Cursor
)
526 if Checks
and then Left
.Node
= 0 then
527 raise Constraint_Error
with
528 "Left cursor of Equivalent_Elements equals No_Element";
531 if Checks
and then Right
.Node
= 0 then
532 raise Constraint_Error
with
533 "Right cursor of Equivalent_Elements equals No_Element";
536 pragma Assert
(Vet
(Left
), "bad Left cursor in Equivalent_Elements");
537 pragma Assert
(Vet
(Right
), "bad Right cursor in Equivalent_Elements");
539 -- AI05-0022 requires that a container implementation detect element
540 -- tampering by a generic actual subprogram. However, the following case
541 -- falls outside the scope of that AI. Randy Brukardt explained on the
542 -- ARG list on 2013/02/07 that:
545 -- But for an operation like "<" [the ordered set analog of
546 -- Equivalent_Elements], there is no need to "dereference" a cursor
547 -- after the call to the generic formal parameter function, so nothing
548 -- bad could happen if tampering is undetected. And the operation can
549 -- safely return a result without a problem even if an element is
550 -- deleted from the container.
554 LN
: Node_Type
renames Left
.Container
.Nodes
(Left
.Node
);
555 RN
: Node_Type
renames Right
.Container
.Nodes
(Right
.Node
);
557 return Equivalent_Elements
(LN
.Element
, RN
.Element
);
559 end Equivalent_Elements
;
561 function Equivalent_Elements
563 Right
: Element_Type
) return Boolean
566 if Checks
and then Left
.Node
= 0 then
567 raise Constraint_Error
with
568 "Left cursor of Equivalent_Elements equals No_Element";
571 pragma Assert
(Vet
(Left
), "Left cursor in Equivalent_Elements is bad");
574 LN
: Node_Type
renames Left
.Container
.Nodes
(Left
.Node
);
576 return Equivalent_Elements
(LN
.Element
, Right
);
578 end Equivalent_Elements
;
580 function Equivalent_Elements
581 (Left
: Element_Type
;
582 Right
: Cursor
) return Boolean
585 if Checks
and then Right
.Node
= 0 then
586 raise Constraint_Error
with
587 "Right cursor of Equivalent_Elements equals No_Element";
592 "Right cursor of Equivalent_Elements is bad");
595 RN
: Node_Type
renames Right
.Container
.Nodes
(Right
.Node
);
597 return Equivalent_Elements
(Left
, RN
.Element
);
599 end Equivalent_Elements
;
601 ---------------------
602 -- Equivalent_Keys --
603 ---------------------
605 function Equivalent_Keys
607 Node
: Node_Type
) return Boolean
610 return Equivalent_Elements
(Key
, Node
.Element
);
618 (Container
: in out Set
;
623 Element_Keys
.Delete_Key_Sans_Free
(Container
, Item
, X
);
624 HT_Ops
.Free
(Container
, X
);
631 procedure Finalize
(Object
: in out Iterator
) is
633 if Object
.Container
/= null then
634 Unbusy
(Object
.Container
.TC
);
644 Item
: Element_Type
) return Cursor
646 Node
: constant Count_Type
:=
647 Element_Keys
.Find
(Container
'Unrestricted_Access.all, Item
);
649 return (if Node
= 0 then No_Element
650 else Cursor
'(Container'Unrestricted_Access, Node));
657 function First (Container : Set) return Cursor is
658 Node : constant Count_Type := HT_Ops.First (Container);
660 return (if Node = 0 then No_Element
661 else Cursor'(Container
'Unrestricted_Access, Node
));
664 overriding
function First
(Object
: Iterator
) return Cursor
is
666 return Object
.Container
.First
;
669 ------------------------
670 -- Get_Element_Access --
671 ------------------------
673 function Get_Element_Access
674 (Position
: Cursor
) return not null Element_Access
is
676 return Position
.Container
.Nodes
(Position
.Node
).Element
'Access;
677 end Get_Element_Access
;
683 function Has_Element
(Position
: Cursor
) return Boolean is
685 pragma Assert
(Vet
(Position
), "bad cursor in Has_Element");
686 return Position
.Node
/= 0;
693 function Hash_Node
(Node
: Node_Type
) return Hash_Type
is
695 return Hash
(Node
.Element
);
703 (Container
: in out Set
;
704 New_Item
: Element_Type
)
710 Insert
(Container
, New_Item
, Position
, Inserted
);
713 TE_Check
(Container
.TC
);
715 Container
.Nodes
(Position
.Node
).Element
:= New_Item
;
724 (Container
: in out Set
;
725 New_Item
: Element_Type
;
726 Position
: out Cursor
;
727 Inserted
: out Boolean)
730 Insert
(Container
, New_Item
, Position
.Node
, Inserted
);
731 Position
.Container
:= Container
'Unchecked_Access;
735 (Container
: in out Set
;
736 New_Item
: Element_Type
)
742 Insert
(Container
, New_Item
, Position
, Inserted
);
744 if Checks
and then not Inserted
then
745 raise Constraint_Error
with
746 "attempt to insert element already in set";
751 (Container
: in out Set
;
752 New_Item
: Element_Type
;
753 Node
: out Count_Type
;
754 Inserted
: out Boolean)
756 procedure Allocate_Set_Element
(Node
: in out Node_Type
);
757 pragma Inline
(Allocate_Set_Element
);
759 function New_Node
return Count_Type
;
760 pragma Inline
(New_Node
);
762 procedure Local_Insert
is
763 new Element_Keys
.Generic_Conditional_Insert
(New_Node
);
765 procedure Allocate
is
766 new HT_Ops
.Generic_Allocate
(Allocate_Set_Element
);
768 ---------------------------
769 -- Allocate_Set_Element --
770 ---------------------------
772 procedure Allocate_Set_Element
(Node
: in out Node_Type
) is
774 Node
.Element
:= New_Item
;
775 end Allocate_Set_Element
;
781 function New_Node
return Count_Type
is
784 Allocate
(Container
, Result
);
788 -- Start of processing for Insert
791 -- The buckets array length is specified by the user as a discriminant
792 -- of the container type, so it is possible for the buckets array to
793 -- have a length of zero. We must check for this case specifically, in
794 -- order to prevent divide-by-zero errors later, when we compute the
795 -- buckets array index value for an element, given its hash value.
797 if Checks
and then Container
.Buckets
'Length = 0 then
798 raise Capacity_Error
with "No capacity for insertion";
801 Local_Insert
(Container
, New_Item
, Node
, Inserted
);
808 procedure Intersection
809 (Target
: in out Set
;
812 Tgt_Node
: Count_Type
;
813 TN
: Nodes_Type
renames Target
.Nodes
;
816 if Target
'Address = Source
'Address then
820 if Source
.Length
= 0 then
821 HT_Ops
.Clear
(Target
);
825 TC_Check
(Target
.TC
);
827 Tgt_Node
:= HT_Ops
.First
(Target
);
828 while Tgt_Node
/= 0 loop
829 if Is_In
(Source
, TN
(Tgt_Node
)) then
830 Tgt_Node
:= HT_Ops
.Next
(Target
, Tgt_Node
);
834 X
: constant Count_Type
:= Tgt_Node
;
836 Tgt_Node
:= HT_Ops
.Next
(Target
, Tgt_Node
);
837 HT_Ops
.Delete_Node_Sans_Free
(Target
, X
);
838 HT_Ops
.Free
(Target
, X
);
844 function Intersection
(Left
, Right
: Set
) return Set
is
848 if Left
'Address = Right
'Address then
852 C
:= Count_Type
'Min (Left
.Length
, Right
.Length
);
858 return Result
: Set
(C
, To_Prime
(C
)) do
859 Iterate_Left
: declare
860 procedure Process
(L_Node
: Count_Type
);
863 new HT_Ops
.Generic_Iteration
(Process
);
869 procedure Process
(L_Node
: Count_Type
) is
870 N
: Node_Type
renames Left
.Nodes
(L_Node
);
875 if Is_In
(Right
, N
) then
876 Insert
(Result
, N
.Element
, X
, B
); -- optimize ???
878 pragma Assert
(X
> 0);
882 -- Start of processing for Iterate_Left
894 function Is_Empty
(Container
: Set
) return Boolean is
896 return Container
.Length
= 0;
903 function Is_In
(HT
: Set
; Key
: Node_Type
) return Boolean is
905 return Element_Keys
.Find
(HT
'Unrestricted_Access.all, Key
.Element
) /= 0;
912 function Is_Subset
(Subset
: Set
; Of_Set
: Set
) return Boolean is
913 Subset_Node
: Count_Type
;
914 SN
: Nodes_Type
renames Subset
.Nodes
;
917 if Subset
'Address = Of_Set
'Address then
921 if Subset
.Length
> Of_Set
.Length
then
925 Subset_Node
:= HT_Ops
.First
(Subset
);
926 while Subset_Node
/= 0 loop
927 if not Is_In
(Of_Set
, SN
(Subset_Node
)) then
930 Subset_Node
:= HT_Ops
.Next
931 (Subset
'Unrestricted_Access.all, Subset_Node
);
943 Process
: not null access procedure (Position
: Cursor
))
945 procedure Process_Node
(Node
: Count_Type
);
946 pragma Inline
(Process_Node
);
949 new HT_Ops
.Generic_Iteration
(Process_Node
);
955 procedure Process_Node
(Node
: Count_Type
) is
957 Process
(Cursor
'(Container'Unrestricted_Access, Node));
960 Busy : With_Busy (Container.TC'Unrestricted_Access);
962 -- Start of processing for Iterate
968 function Iterate (Container : Set)
969 return Set_Iterator_Interfaces.Forward_Iterator'Class
972 Busy (Container.TC'Unrestricted_Access.all);
973 return It : constant Iterator :=
974 Iterator'(Limited_Controlled
with
975 Container
=> Container
'Unrestricted_Access);
982 function Length
(Container
: Set
) return Count_Type
is
984 return Container
.Length
;
991 procedure Move
(Target
: in out Set
; Source
: in out Set
) is
993 if Target
'Address = Source
'Address then
997 TC_Check
(Source
.TC
);
999 Target
.Assign
(Source
);
1007 function Next
(Node
: Node_Type
) return Count_Type
is
1012 function Next
(Position
: Cursor
) return Cursor
is
1014 if Position
.Node
= 0 then
1018 pragma Assert
(Vet
(Position
), "bad cursor in Next");
1021 HT
: Set
renames Position
.Container
.all;
1022 Node
: constant Count_Type
:= HT_Ops
.Next
(HT
, Position
.Node
);
1029 return Cursor
'(Position.Container, Node);
1033 procedure Next (Position : in out Cursor) is
1035 Position := Next (Position);
1040 Position : Cursor) return Cursor
1043 if Position.Container = null then
1047 if Checks and then Position.Container /= Object.Container then
1048 raise Program_Error with
1049 "Position cursor of Next designates wrong set";
1052 return Next (Position);
1059 function Overlap (Left, Right : Set) return Boolean is
1060 Left_Node : Count_Type;
1063 if Right.Length = 0 then
1067 if Left'Address = Right'Address then
1071 Left_Node := HT_Ops.First (Left);
1072 while Left_Node /= 0 loop
1073 if Is_In (Right, Left.Nodes (Left_Node)) then
1076 Left_Node := HT_Ops.Next (Left'Unrestricted_Access.all, Left_Node);
1082 ----------------------
1083 -- Pseudo_Reference --
1084 ----------------------
1086 function Pseudo_Reference
1087 (Container : aliased Set'Class) return Reference_Control_Type
1089 TC : constant Tamper_Counts_Access :=
1090 Container.TC'Unrestricted_Access;
1092 return R : constant Reference_Control_Type := (Controlled with TC) do
1095 end Pseudo_Reference;
1101 procedure Query_Element
1103 Process : not null access procedure (Element : Element_Type))
1106 if Checks and then Position.Node = 0 then
1107 raise Constraint_Error with
1108 "Position cursor of Query_Element equals No_Element";
1111 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1114 S : Set renames Position.Container.all;
1115 Lock : With_Lock (S.TC'Unrestricted_Access);
1117 Process (S.Nodes (Position.Node).Element);
1126 (S : in out Ada.Strings.Text_Buffers.Root_Buffer_Type'Class; V : Set)
1128 First_Time : Boolean := True;
1129 use System.Put_Images;
1135 First_Time := False;
1137 Simple_Array_Between (S);
1140 Element_Type'Put_Image (S, X);
1151 (Stream : not null access Root_Stream_Type'Class;
1152 Container : out Set)
1154 function Read_Node (Stream : not null access Root_Stream_Type'Class)
1157 procedure Read_Nodes is
1158 new HT_Ops.Generic_Read (Read_Node);
1164 function Read_Node (Stream : not null access Root_Stream_Type'Class)
1167 procedure Read_Element (Node : in out Node_Type);
1168 pragma Inline (Read_Element);
1170 procedure Allocate is
1171 new HT_Ops.Generic_Allocate (Read_Element);
1173 procedure Read_Element (Node : in out Node_Type) is
1175 Element_Type'Read (Stream, Node.Element);
1180 -- Start of processing for Read_Node
1183 Allocate (Container, Node);
1187 -- Start of processing for Read
1190 Read_Nodes (Stream, Container);
1194 (Stream : not null access Root_Stream_Type'Class;
1198 raise Program_Error with "attempt to stream set cursor";
1202 (Stream : not null access Root_Stream_Type'Class;
1203 Item : out Constant_Reference_Type)
1206 raise Program_Error with "attempt to stream reference";
1214 (Container : in out Set;
1215 New_Item : Element_Type)
1217 Node : constant Count_Type := Element_Keys.Find (Container, New_Item);
1220 TE_Check (Container.TC);
1222 if Checks and then Node = 0 then
1223 raise Constraint_Error with
1224 "attempt to replace element not in set";
1227 Container.Nodes (Node).Element := New_Item;
1230 procedure Replace_Element
1231 (Container : in out Set;
1233 New_Item : Element_Type)
1236 if Checks and then Position.Node = 0 then
1237 raise Constraint_Error with
1238 "Position cursor equals No_Element";
1241 if Checks and then Position.Container /= Container'Unrestricted_Access
1243 raise Program_Error with
1244 "Position cursor designates wrong set";
1247 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1249 Replace_Element (Container, Position.Node, New_Item);
1250 end Replace_Element;
1252 ----------------------
1253 -- Reserve_Capacity --
1254 ----------------------
1256 procedure Reserve_Capacity
1257 (Container : in out Set;
1258 Capacity : Count_Type)
1261 if Checks and then Capacity > Container.Capacity then
1262 raise Capacity_Error with "requested capacity is too large";
1264 end Reserve_Capacity;
1270 procedure Set_Element (Node : in out Node_Type; Item : Element_Type) is
1272 Node.Element := Item;
1279 procedure Set_Next (Node : in out Node_Type; Next : Count_Type) is
1284 --------------------------
1285 -- Symmetric_Difference --
1286 --------------------------
1288 procedure Symmetric_Difference
1289 (Target : in out Set;
1292 procedure Process (Source_Node : Count_Type);
1293 pragma Inline (Process);
1295 procedure Iterate is
1296 new HT_Ops.Generic_Iteration (Process);
1302 procedure Process (Source_Node : Count_Type) is
1303 N : Node_Type renames Source.Nodes (Source_Node);
1308 if Is_In (Target, N) then
1309 Delete (Target, N.Element);
1311 Insert (Target, N.Element, X, B);
1316 -- Start of processing for Symmetric_Difference
1319 if Target'Address = Source'Address then
1320 HT_Ops.Clear (Target);
1324 if Target.Length = 0 then
1325 Assign (Target => Target, Source => Source);
1329 TC_Check (Target.TC);
1332 end Symmetric_Difference;
1334 function Symmetric_Difference (Left, Right : Set) return Set is
1338 if Left'Address = Right'Address then
1342 if Right.Length = 0 then
1346 if Left.Length = 0 then
1350 C := Left.Length + Right.Length;
1352 return Result : Set (C, To_Prime (C)) do
1353 Iterate_Left : declare
1354 procedure Process (L_Node : Count_Type);
1356 procedure Iterate is
1357 new HT_Ops.Generic_Iteration (Process);
1363 procedure Process (L_Node : Count_Type) is
1364 N : Node_Type renames Left.Nodes (L_Node);
1368 if not Is_In (Right, N) then
1369 Insert (Result, N.Element, X, B);
1374 -- Start of processing for Iterate_Left
1380 Iterate_Right : declare
1381 procedure Process (R_Node : Count_Type);
1383 procedure Iterate is
1384 new HT_Ops.Generic_Iteration (Process);
1390 procedure Process (R_Node : Count_Type) is
1391 N : Node_Type renames Right.Nodes (R_Node);
1395 if not Is_In (Left, N) then
1396 Insert (Result, N.Element, X, B);
1401 -- Start of processing for Iterate_Right
1407 end Symmetric_Difference;
1413 function To_Set (New_Item : Element_Type) return Set is
1417 return Result : Set (1, 1) do
1418 Insert (Result, New_Item, X, B);
1428 (Target : in out Set;
1431 procedure Process (Src_Node : Count_Type);
1433 procedure Iterate is
1434 new HT_Ops.Generic_Iteration (Process);
1440 procedure Process (Src_Node : Count_Type) is
1441 N : Node_Type renames Source.Nodes (Src_Node);
1445 Insert (Target, N.Element, X, B);
1448 -- Start of processing for Union
1451 if Target'Address = Source'Address then
1455 TC_Check (Target.TC);
1457 -- ??? why is this code commented out ???
1459 -- N : constant Count_Type := Target.Length + Source.Length;
1461 -- if N > HT_Ops.Capacity (Target.HT) then
1462 -- HT_Ops.Reserve_Capacity (Target.HT, N);
1469 function Union (Left, Right : Set) return Set is
1473 if Left'Address = Right'Address then
1477 if Right.Length = 0 then
1481 if Left.Length = 0 then
1485 C := Left.Length + Right.Length;
1487 return Result : Set (C, To_Prime (C)) do
1488 Assign (Target => Result, Source => Left);
1489 Union (Target => Result, Source => Right);
1497 function Vet (Position : Cursor) return Boolean is
1499 if not Container_Checks'Enabled then
1503 if Position.Node = 0 then
1504 return Position.Container = null;
1507 if Position.Container = null then
1512 S : Set renames Position.Container.all;
1513 N : Nodes_Type renames S.Nodes;
1517 if S.Length = 0 then
1521 if Position.Node > N'Last then
1525 if N (Position.Node).Next = Position.Node then
1529 X := S.Buckets (Element_Keys.Checked_Index
1530 (S, N (Position.Node).Element));
1532 for J in 1 .. S.Length loop
1533 if X = Position.Node then
1541 if X = N (X).Next then -- to prevent unnecessary looping
1557 (Stream : not null access Root_Stream_Type'Class;
1560 procedure Write_Node
1561 (Stream : not null access Root_Stream_Type'Class;
1563 pragma Inline (Write_Node);
1565 procedure Write_Nodes is
1566 new HT_Ops.Generic_Write (Write_Node);
1572 procedure Write_Node
1573 (Stream : not null access Root_Stream_Type'Class;
1577 Element_Type'Write (Stream, Node.Element);
1580 -- Start of processing for Write
1583 Write_Nodes (Stream, Container);
1587 (Stream : not null access Root_Stream_Type'Class;
1591 raise Program_Error with "attempt to stream set cursor";
1595 (Stream : not null access Root_Stream_Type'Class;
1596 Item : Constant_Reference_Type)
1599 raise Program_Error with "attempt to stream reference";
1602 -- Ada 2022 features:
1604 function Has_Element (Container : Set; Position : Cursor) return Boolean is
1606 pragma Assert (Vet (Position), "bad cursor in Has_Element");
1607 pragma Assert ((Position.Container = null) = (Position.Node = 0),
1608 "bad nullity in Has_Element");
1609 return Position.Container = Container'Unrestricted_Access;
1612 function Tampering_With_Cursors_Prohibited
1613 (Container : Set) return Boolean
1616 return Is_Busy (Container.TC);
1617 end Tampering_With_Cursors_Prohibited;
1619 function Element (Container : Set; Position : Cursor) return Element_Type is
1621 if Checks and then not Has_Element (Container, Position) then
1622 raise Program_Error with "Position for wrong Container";
1625 return Element (Position);
1628 procedure Query_Element
1631 Process : not null access procedure (Element : Element_Type)) is
1633 if Checks and then not Has_Element (Container, Position) then
1634 raise Program_Error with "Position for wrong Container";
1637 Query_Element (Position, Process);
1640 function Next (Container : Set; Position : Cursor) return Cursor is
1643 not (Position = No_Element or else Has_Element (Container, Position))
1645 raise Program_Error with "Position for wrong Container";
1648 return Next (Position);
1651 procedure Next (Container : Set; Position : in out Cursor) is
1653 Position := Next (Container, Position);
1660 package body Generic_Keys is
1662 -----------------------
1663 -- Local Subprograms --
1664 -----------------------
1666 function Equivalent_Key_Node
1668 Node : Node_Type) return Boolean;
1669 pragma Inline (Equivalent_Key_Node);
1671 --------------------------
1672 -- Local Instantiations --
1673 --------------------------
1676 new Hash_Tables.Generic_Bounded_Keys
1677 (HT_Types => HT_Types,
1679 Set_Next => Set_Next,
1680 Key_Type => Key_Type,
1682 Equivalent_Keys => Equivalent_Key_Node);
1684 ------------------------
1685 -- Constant_Reference --
1686 ------------------------
1688 function Constant_Reference
1689 (Container : aliased Set;
1690 Key : Key_Type) return Constant_Reference_Type
1692 Position : constant Cursor := Find (Container, Key);
1695 if Checks and then Position = No_Element then
1696 raise Constraint_Error with "key not in set";
1699 return Constant_Reference (Container, Position);
1700 end Constant_Reference;
1708 Key : Key_Type) return Boolean
1711 return Find (Container, Key) /= No_Element;
1719 (Container : in out Set;
1725 Key_Keys.Delete_Key_Sans_Free (Container, Key, X);
1727 if Checks and then X = 0 then
1728 raise Constraint_Error with "attempt to delete key not in set";
1731 HT_Ops.Free (Container, X);
1740 Key : Key_Type) return Element_Type
1742 Node : constant Count_Type :=
1743 Key_Keys.Find (Container'Unrestricted_Access.all, Key);
1746 if Checks and then Node = 0 then
1747 raise Constraint_Error with "key not in set";
1750 return Container.Nodes (Node).Element;
1753 -------------------------
1754 -- Equivalent_Key_Node --
1755 -------------------------
1757 function Equivalent_Key_Node
1759 Node : Node_Type) return Boolean
1762 return Equivalent_Keys (Key, Generic_Keys.Key (Node.Element));
1763 end Equivalent_Key_Node;
1770 (Container : in out Set;
1775 Key_Keys.Delete_Key_Sans_Free (Container, Key, X);
1776 HT_Ops.Free (Container, X);
1783 procedure Finalize (Control : in out Reference_Control_Type) is
1785 if Control.Container /= null then
1786 Impl.Reference_Control_Type (Control).Finalize;
1789 Hash (Key (Element (Control.Old_Pos))) /= Control.Old_Hash
1791 HT_Ops.Delete_Node_At_Index
1792 (Control.Container.all, Control.Index, Control.Old_Pos.Node);
1793 raise Program_Error with "key not preserved in reference";
1796 Control.Container := null;
1806 Key : Key_Type) return Cursor
1808 Node : constant Count_Type :=
1809 Key_Keys.Find (Container'Unrestricted_Access.all, Key);
1811 return (if Node = 0 then No_Element
1812 else Cursor'(Container
'Unrestricted_Access, Node
));
1819 function Key
(Position
: Cursor
) return Key_Type
is
1821 if Checks
and then Position
.Node
= 0 then
1822 raise Constraint_Error
with
1823 "Position cursor equals No_Element";
1826 pragma Assert
(Vet
(Position
), "bad cursor in function Key");
1827 return Key
(Position
.Container
.Nodes
(Position
.Node
).Element
);
1835 (Stream
: not null access Root_Stream_Type
'Class;
1836 Item
: out Reference_Type
)
1839 raise Program_Error
with "attempt to stream reference";
1842 ------------------------------
1843 -- Reference_Preserving_Key --
1844 ------------------------------
1846 function Reference_Preserving_Key
1847 (Container
: aliased in out Set
;
1848 Position
: Cursor
) return Reference_Type
1851 if Checks
and then Position
.Container
= null then
1852 raise Constraint_Error
with "Position cursor has no element";
1855 if Checks
and then Position
.Container
/= Container
'Unrestricted_Access
1857 raise Program_Error
with
1858 "Position cursor designates wrong container";
1863 "bad cursor in function Reference_Preserving_Key");
1866 N
: Node_Type
renames Container
.Nodes
(Position
.Node
);
1868 return R
: constant Reference_Type
:=
1869 (Element
=> N
.Element
'Unrestricted_Access,
1872 Container
.TC
'Unrestricted_Access,
1873 Container
'Unrestricted_Access,
1874 Index
=> Key_Keys
.Index
(Container
, Key
(Position
)),
1875 Old_Pos
=> Position
,
1876 Old_Hash
=> Hash
(Key
(Position
))))
1878 Busy
(Container
.TC
);
1881 end Reference_Preserving_Key
;
1883 function Reference_Preserving_Key
1884 (Container
: aliased in out Set
;
1885 Key
: Key_Type
) return Reference_Type
1887 Position
: constant Cursor
:= Find
(Container
, Key
);
1890 if Checks
and then Position
= No_Element
then
1891 raise Constraint_Error
with "key not in set";
1894 return Reference_Preserving_Key
(Container
, Position
);
1895 end Reference_Preserving_Key
;
1902 (Container
: in out Set
;
1904 New_Item
: Element_Type
)
1906 Node
: constant Count_Type
:= Key_Keys
.Find
(Container
, Key
);
1909 if Checks
and then Node
= 0 then
1910 raise Constraint_Error
with
1911 "attempt to replace key not in set";
1914 Replace_Element
(Container
, Node
, New_Item
);
1917 -----------------------------------
1918 -- Update_Element_Preserving_Key --
1919 -----------------------------------
1921 procedure Update_Element_Preserving_Key
1922 (Container
: in out Set
;
1924 Process
: not null access
1925 procedure (Element
: in out Element_Type
))
1928 N
: Nodes_Type
renames Container
.Nodes
;
1931 if Checks
and then Position
.Node
= 0 then
1932 raise Constraint_Error
with
1933 "Position cursor equals No_Element";
1936 if Checks
and then Position
.Container
/= Container
'Unrestricted_Access
1938 raise Program_Error
with
1939 "Position cursor designates wrong set";
1942 -- ??? why is this code commented out ???
1943 -- if HT.Buckets = null
1944 -- or else HT.Buckets'Length = 0
1945 -- or else HT.Length = 0
1946 -- or else Position.Node.Next = Position.Node
1948 -- raise Program_Error with
1949 -- "Position cursor is bad (set is empty)";
1954 "bad cursor in Update_Element_Preserving_Key");
1956 -- Per AI05-0022, the container implementation is required to detect
1957 -- element tampering by a generic actual subprogram.
1960 E
: Element_Type
renames N
(Position
.Node
).Element
;
1961 K
: constant Key_Type
:= Key
(E
);
1962 Lock
: With_Lock
(Container
.TC
'Unrestricted_Access);
1964 -- Record bucket now, in case key is changed
1965 Indx
:= HT_Ops
.Index
(Container
.Buckets
, N
(Position
.Node
));
1969 if Equivalent_Keys
(K
, Key
(E
)) then
1974 -- Key was modified, so remove this node from set.
1976 if Container
.Buckets
(Indx
) = Position
.Node
then
1977 Container
.Buckets
(Indx
) := N
(Position
.Node
).Next
;
1981 Prev
: Count_Type
:= Container
.Buckets
(Indx
);
1984 while N
(Prev
).Next
/= Position
.Node
loop
1985 Prev
:= N
(Prev
).Next
;
1987 if Checks
and then Prev
= 0 then
1988 raise Program_Error
with
1989 "Position cursor is bad (node not found)";
1993 N
(Prev
).Next
:= N
(Position
.Node
).Next
;
1997 Container
.Length
:= Container
.Length
- 1;
1998 HT_Ops
.Free
(Container
, Position
.Node
);
2000 raise Program_Error
with "key was modified";
2001 end Update_Element_Preserving_Key
;
2008 (Stream
: not null access Root_Stream_Type
'Class;
2009 Item
: Reference_Type
)
2012 raise Program_Error
with "attempt to stream reference";
2017 end Ada
.Containers
.Bounded_Hashed_Sets
;