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-2014, 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 -----------------------
45 -- Local Subprograms --
46 -----------------------
48 procedure Assign
(Node
: Node_Access
; Item
: Element_Type
);
49 pragma Inline
(Assign
);
51 function Copy_Node
(Source
: Node_Access
) return Node_Access
;
52 pragma Inline
(Copy_Node
);
54 function Equivalent_Keys
56 Node
: Node_Access
) return Boolean;
57 pragma Inline
(Equivalent_Keys
);
59 function Find_Equal_Key
60 (R_HT
: Hash_Table_Type
;
61 L_Node
: Node_Access
) return Boolean;
63 function Find_Equivalent_Key
64 (R_HT
: Hash_Table_Type
;
65 L_Node
: Node_Access
) return Boolean;
67 procedure Free
(X
: in out Node_Access
);
69 function Hash_Node
(Node
: Node_Access
) return Hash_Type
;
70 pragma Inline
(Hash_Node
);
73 (HT
: in out Hash_Table_Type
;
74 New_Item
: Element_Type
;
75 Node
: out Node_Access
;
76 Inserted
: out Boolean);
79 (HT
: aliased in out Hash_Table_Type
;
80 Key
: Node_Access
) return Boolean;
81 pragma Inline
(Is_In
);
83 function Next
(Node
: Node_Access
) return Node_Access
;
86 function Read_Node
(Stream
: not null access Root_Stream_Type
'Class)
88 pragma Inline
(Read_Node
);
90 procedure Set_Next
(Node
: Node_Access
; Next
: Node_Access
);
91 pragma Inline
(Set_Next
);
93 function Vet
(Position
: Cursor
) return Boolean;
96 (Stream
: not null access Root_Stream_Type
'Class;
98 pragma Inline
(Write_Node
);
100 --------------------------
101 -- Local Instantiations --
102 --------------------------
104 package HT_Ops
is new Hash_Tables
.Generic_Operations
105 (HT_Types
=> HT_Types
,
106 Hash_Node
=> Hash_Node
,
108 Set_Next
=> Set_Next
,
109 Copy_Node
=> Copy_Node
,
112 package Element_Keys
is new Hash_Tables
.Generic_Keys
113 (HT_Types
=> HT_Types
,
115 Set_Next
=> Set_Next
,
116 Key_Type
=> Element_Type
,
118 Equivalent_Keys
=> Equivalent_Keys
);
121 new HT_Ops
.Generic_Equal
(Find_Equal_Key
);
123 function Is_Equivalent
is
124 new HT_Ops
.Generic_Equal
(Find_Equivalent_Key
);
126 procedure Read_Nodes
is
127 new HT_Ops
.Generic_Read
(Read_Node
);
129 procedure Replace_Element
is
130 new Element_Keys
.Generic_Replace_Element
(Hash_Node
, Assign
);
132 procedure Write_Nodes
is
133 new HT_Ops
.Generic_Write
(Write_Node
);
139 function "=" (Left
, Right
: Set
) return Boolean is
141 return Is_Equal
(Left
.HT
, Right
.HT
);
148 procedure Adjust
(Container
: in out Set
) is
150 HT_Ops
.Adjust
(Container
.HT
);
153 procedure Adjust
(Control
: in out Reference_Control_Type
) is
155 if Control
.Container
/= null then
157 HT
: Hash_Table_Type
renames Control
.Container
.all.HT
;
158 B
: Natural renames HT
.Busy
;
159 L
: Natural renames HT
.Lock
;
171 procedure Assign
(Node
: Node_Access
; Item
: Element_Type
) is
173 Node
.Element
:= Item
;
176 procedure Assign
(Target
: in out Set
; Source
: Set
) is
178 if Target
'Address = Source
'Address then
183 Target
.Union
(Source
);
190 function Capacity
(Container
: Set
) return Count_Type
is
192 return HT_Ops
.Capacity
(Container
.HT
);
199 procedure Clear
(Container
: in out Set
) is
201 HT_Ops
.Clear
(Container
.HT
);
204 ------------------------
205 -- Constant_Reference --
206 ------------------------
208 function Constant_Reference
209 (Container
: aliased Set
;
210 Position
: Cursor
) return Constant_Reference_Type
213 if Position
.Container
= null then
214 raise Constraint_Error
with "Position cursor has no element";
217 if Position
.Container
/= Container
'Unrestricted_Access then
218 raise Program_Error
with
219 "Position cursor designates wrong container";
222 pragma Assert
(Vet
(Position
), "bad cursor in Constant_Reference");
225 HT
: Hash_Table_Type
renames Position
.Container
.all.HT
;
226 B
: Natural renames HT
.Busy
;
227 L
: Natural renames HT
.Lock
;
229 return R
: constant Constant_Reference_Type
:=
230 (Element
=> Position
.Node
.Element
'Access,
231 Control
=> (Controlled
with Container
'Unrestricted_Access))
237 end Constant_Reference
;
243 function Contains
(Container
: Set
; Item
: Element_Type
) return Boolean is
245 return Find
(Container
, Item
) /= No_Element
;
254 Capacity
: Count_Type
:= 0) return Set
262 elsif Capacity
>= Source
.Length
then
267 with "Requested capacity is less than Source length";
270 return Target
: Set
do
271 Target
.Reserve_Capacity
(C
);
272 Target
.Assign
(Source
);
280 function Copy_Node
(Source
: Node_Access
) return Node_Access
is
282 return new Node_Type
'(Element => Source.Element, Next => null);
290 (Container : in out Set;
296 Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
299 raise Constraint_Error with "attempt to delete element not in set";
306 (Container : in out Set;
307 Position : in out Cursor)
310 if Position.Node = null then
311 raise Constraint_Error with "Position cursor equals No_Element";
314 if Position.Container /= Container'Unrestricted_Access then
315 raise Program_Error with "Position cursor designates wrong set";
318 if Container.HT.Busy > 0 then
319 raise Program_Error with
320 "attempt to tamper with cursors (set is busy)";
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;
336 (Target : in out Set;
339 Tgt_Node : Node_Access;
340 Src_HT : Hash_Table_Type renames Source'Unrestricted_Access.HT;
343 if Target'Address = Source'Address then
348 if Src_HT.Length = 0 then
352 if Target.HT.Busy > 0 then
353 raise Program_Error with
354 "attempt to tamper with cursors (set is busy)";
357 if Src_HT.Length < Target.HT.Length then
359 Src_Node : Node_Access;
362 Src_Node := HT_Ops.First (Src_HT);
363 while Src_Node /= null loop
364 Tgt_Node := Element_Keys.Find (Target.HT, Src_Node.Element);
366 if Tgt_Node /= null then
367 HT_Ops.Delete_Node_Sans_Free (Target.HT, Tgt_Node);
371 Src_Node := HT_Ops.Next (Src_HT, Src_Node);
376 Tgt_Node := HT_Ops.First (Target.HT);
377 while Tgt_Node /= null loop
378 if Is_In (Src_HT, Tgt_Node) then
380 X : Node_Access := Tgt_Node;
382 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
383 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
388 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
394 function Difference (Left, Right : Set) return Set is
395 Left_HT : Hash_Table_Type renames Left'Unrestricted_Access.HT;
396 Right_HT : Hash_Table_Type renames Right'Unrestricted_Access.HT;
397 Buckets : HT_Types.Buckets_Access;
401 if Left'Address = Right'Address then
405 if Left_HT.Length = 0 then
409 if Right_HT.Length = 0 then
414 Size : constant Hash_Type := Prime_Numbers.To_Prime (Left.Length);
416 Buckets := HT_Ops.New_Buckets (Length => Size);
421 Iterate_Left : declare
422 procedure Process (L_Node : Node_Access);
425 new HT_Ops.Generic_Iteration (Process);
431 procedure Process (L_Node : Node_Access) is
433 if not Is_In (Right_HT, L_Node) then
435 -- Per AI05-0022, the container implementation is required
436 -- to detect element tampering by a generic actual
437 -- subprogram, hence the use of Checked_Index instead of a
438 -- simple invocation of generic formal Hash.
440 J : constant Hash_Type :=
441 HT_Ops.Checked_Index (Left_HT, Buckets.all, L_Node);
443 Bucket : Node_Access renames Buckets (J);
446 Bucket := new Node_Type'(L_Node
.Element
, Bucket
);
449 Length
:= Length
+ 1;
453 -- Start of processing for Iterate_Left
459 HT_Ops
.Free_Hash_Table
(Buckets
);
463 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
470 function Element
(Position
: Cursor
) return Element_Type
is
472 if Position
.Node
= null then
473 raise Constraint_Error
with "Position cursor equals No_Element";
476 pragma Assert
(Vet
(Position
), "bad cursor in function Element");
478 return Position
.Node
.Element
;
481 ---------------------
482 -- Equivalent_Sets --
483 ---------------------
485 function Equivalent_Sets
(Left
, Right
: Set
) return Boolean is
487 return Is_Equivalent
(Left
.HT
, Right
.HT
);
490 -------------------------
491 -- Equivalent_Elements --
492 -------------------------
494 function Equivalent_Elements
(Left
, Right
: Cursor
)
497 if Left
.Node
= null then
498 raise Constraint_Error
with
499 "Left cursor of Equivalent_Elements equals No_Element";
502 if Right
.Node
= null then
503 raise Constraint_Error
with
504 "Right cursor of Equivalent_Elements equals No_Element";
507 pragma Assert
(Vet
(Left
), "bad Left cursor in Equivalent_Elements");
508 pragma Assert
(Vet
(Right
), "bad Right cursor in Equivalent_Elements");
510 -- AI05-0022 requires that a container implementation detect element
511 -- tampering by a generic actual subprogram. However, the following case
512 -- falls outside the scope of that AI. Randy Brukardt explained on the
513 -- ARG list on 2013/02/07 that:
516 -- But for an operation like "<" [the ordered set analog of
517 -- Equivalent_Elements], there is no need to "dereference" a cursor
518 -- after the call to the generic formal parameter function, so nothing
519 -- bad could happen if tampering is undetected. And the operation can
520 -- safely return a result without a problem even if an element is
521 -- deleted from the container.
524 return Equivalent_Elements
(Left
.Node
.Element
, Right
.Node
.Element
);
525 end Equivalent_Elements
;
527 function Equivalent_Elements
(Left
: Cursor
; Right
: Element_Type
)
530 if Left
.Node
= null then
531 raise Constraint_Error
with
532 "Left cursor of Equivalent_Elements equals No_Element";
535 pragma Assert
(Vet
(Left
), "Left cursor in Equivalent_Elements is bad");
537 return Equivalent_Elements
(Left
.Node
.Element
, Right
);
538 end Equivalent_Elements
;
540 function Equivalent_Elements
(Left
: Element_Type
; Right
: Cursor
)
543 if Right
.Node
= null then
544 raise Constraint_Error
with
545 "Right cursor of Equivalent_Elements equals No_Element";
550 "Right cursor of Equivalent_Elements is bad");
552 return Equivalent_Elements
(Left
, Right
.Node
.Element
);
553 end Equivalent_Elements
;
555 ---------------------
556 -- Equivalent_Keys --
557 ---------------------
559 function Equivalent_Keys
(Key
: Element_Type
; Node
: Node_Access
)
562 return Equivalent_Elements
(Key
, Node
.Element
);
570 (Container
: in out Set
;
575 Element_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Item
, X
);
583 procedure Finalize
(Container
: in out Set
) is
585 HT_Ops
.Finalize
(Container
.HT
);
588 procedure Finalize
(Control
: in out Reference_Control_Type
) is
590 if Control
.Container
/= null then
592 HT
: Hash_Table_Type
renames Control
.Container
.all.HT
;
593 B
: Natural renames HT
.Busy
;
594 L
: Natural renames HT
.Lock
;
600 Control
.Container
:= null;
604 procedure Finalize
(Object
: in out Iterator
) is
606 if Object
.Container
/= null then
608 B
: Natural renames Object
.Container
.HT
.Busy
;
621 Item
: Element_Type
) return Cursor
623 HT
: Hash_Table_Type
renames Container
'Unrestricted_Access.HT
;
624 Node
: constant Node_Access
:= Element_Keys
.Find
(HT
, Item
);
631 return Cursor
'(Container'Unrestricted_Access, Node);
638 function Find_Equal_Key
639 (R_HT : Hash_Table_Type;
640 L_Node : Node_Access) return Boolean
642 R_Index : constant Hash_Type :=
643 Element_Keys.Index (R_HT, L_Node.Element);
645 R_Node : Node_Access := R_HT.Buckets (R_Index);
649 if R_Node = null then
653 if L_Node.Element = R_Node.Element then
657 R_Node := Next (R_Node);
661 -------------------------
662 -- Find_Equivalent_Key --
663 -------------------------
665 function Find_Equivalent_Key
666 (R_HT : Hash_Table_Type;
667 L_Node : Node_Access) return Boolean
669 R_Index : constant Hash_Type :=
670 Element_Keys.Index (R_HT, L_Node.Element);
672 R_Node : Node_Access := R_HT.Buckets (R_Index);
676 if R_Node = null then
680 if Equivalent_Elements (L_Node.Element, R_Node.Element) then
684 R_Node := Next (R_Node);
686 end Find_Equivalent_Key;
692 function First (Container : Set) return Cursor is
693 Node : constant Node_Access := HT_Ops.First (Container.HT);
700 return Cursor'(Container
'Unrestricted_Access, Node
);
703 function First
(Object
: Iterator
) return Cursor
is
705 return Object
.Container
.First
;
712 procedure Free
(X
: in out Node_Access
) is
713 procedure Deallocate
is
714 new Ada
.Unchecked_Deallocation
(Node_Type
, Node_Access
);
718 X
.Next
:= X
; -- detect mischief (in Vet)
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 if Container
.HT
.Lock
> 0 then
758 raise Program_Error
with
759 "attempt to tamper with elements (set is locked)";
762 Position
.Node
.Element
:= New_Item
;
771 (Container
: in out Set
;
772 New_Item
: Element_Type
;
773 Position
: out Cursor
;
774 Inserted
: out Boolean)
777 Insert
(Container
.HT
, New_Item
, Position
.Node
, Inserted
);
778 Position
.Container
:= Container
'Unchecked_Access;
782 (Container
: in out Set
;
783 New_Item
: Element_Type
)
786 pragma Unreferenced
(Position
);
791 Insert
(Container
, New_Item
, Position
, Inserted
);
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);
828 raise Program_Error with
829 "attempt to tamper with cursors (set is busy)";
832 Local_Insert (HT, New_Item, Node, Inserted);
835 and then HT.Length > HT_Ops.Capacity (HT)
837 HT_Ops.Reserve_Capacity (HT, HT.Length);
845 procedure Intersection
846 (Target : in out Set;
849 Src_HT : Hash_Table_Type renames Source'Unrestricted_Access.HT;
850 Tgt_Node : Node_Access;
853 if Target'Address = Source'Address then
857 if Source.HT.Length = 0 then
862 if Target.HT.Busy > 0 then
863 raise Program_Error with
864 "attempt to tamper with cursors (set is busy)";
867 Tgt_Node := HT_Ops.First (Target.HT);
868 while Tgt_Node /= null loop
869 if Is_In (Src_HT, Tgt_Node) then
870 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
874 X : Node_Access := Tgt_Node;
876 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
877 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
884 function Intersection (Left, Right : Set) return Set is
885 Left_HT : Hash_Table_Type renames Left'Unrestricted_Access.HT;
886 Right_HT : Hash_Table_Type renames Right'Unrestricted_Access.HT;
887 Buckets : HT_Types.Buckets_Access;
891 if Left'Address = Right'Address then
895 Length := Count_Type'Min (Left.Length, Right.Length);
902 Size : constant Hash_Type := Prime_Numbers.To_Prime (Length);
904 Buckets := HT_Ops.New_Buckets (Length => Size);
909 Iterate_Left : declare
910 procedure Process (L_Node : Node_Access);
913 new HT_Ops.Generic_Iteration (Process);
919 procedure Process (L_Node : Node_Access) is
921 if Is_In (Right_HT, L_Node) then
923 -- Per AI05-0022, the container implementation is required
924 -- to detect element tampering by a generic actual
925 -- subprogram, hence the use of Checked_Index instead of a
926 -- simple invocation of generic formal Hash.
928 J : constant Hash_Type :=
929 HT_Ops.Checked_Index (Left_HT, Buckets.all, L_Node);
931 Bucket : Node_Access renames Buckets (J);
934 Bucket := new Node_Type'(L_Node
.Element
, Bucket
);
937 Length
:= Length
+ 1;
941 -- Start of processing for Iterate_Left
947 HT_Ops
.Free_Hash_Table
(Buckets
);
951 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
958 function Is_Empty
(Container
: Set
) return Boolean is
960 return Container
.HT
.Length
= 0;
968 (HT
: aliased in out Hash_Table_Type
;
969 Key
: Node_Access
) return Boolean
972 return Element_Keys
.Find
(HT
, Key
.Element
) /= null;
979 function Is_Subset
(Subset
: Set
; Of_Set
: Set
) return Boolean is
980 Subset_HT
: Hash_Table_Type
renames Subset
'Unrestricted_Access.HT
;
981 Of_Set_HT
: Hash_Table_Type
renames Of_Set
'Unrestricted_Access.HT
;
982 Subset_Node
: Node_Access
;
985 if Subset
'Address = Of_Set
'Address then
989 if Subset
.Length
> Of_Set
.Length
then
993 Subset_Node
:= HT_Ops
.First
(Subset_HT
);
994 while Subset_Node
/= null loop
995 if not Is_In
(Of_Set_HT
, Subset_Node
) then
998 Subset_Node
:= HT_Ops
.Next
(Subset_HT
, Subset_Node
);
1010 Process
: not null access procedure (Position
: Cursor
))
1012 procedure Process_Node
(Node
: Node_Access
);
1013 pragma Inline
(Process_Node
);
1015 procedure Iterate
is
1016 new HT_Ops
.Generic_Iteration
(Process_Node
);
1022 procedure Process_Node
(Node
: Node_Access
) is
1024 Process
(Cursor
'(Container'Unrestricted_Access, Node));
1027 B : Natural renames Container'Unrestricted_Access.HT.Busy;
1029 -- Start of processing for Iterate
1035 Iterate (Container.HT);
1046 (Container : Set) return Set_Iterator_Interfaces.Forward_Iterator'Class
1048 B : Natural renames Container'Unrestricted_Access.all.HT.Busy;
1051 return It : constant Iterator :=
1052 Iterator'(Limited_Controlled
with
1053 Container
=> Container
'Unrestricted_Access);
1060 function Length
(Container
: Set
) return Count_Type
is
1062 return Container
.HT
.Length
;
1069 procedure Move
(Target
: in out Set
; Source
: in out Set
) is
1071 HT_Ops
.Move
(Target
=> Target
.HT
, Source
=> Source
.HT
);
1078 function Next
(Node
: Node_Access
) return Node_Access
is
1083 function Next
(Position
: Cursor
) return Cursor
is
1085 if Position
.Node
= null then
1089 pragma Assert
(Vet
(Position
), "bad cursor in Next");
1092 HT
: Hash_Table_Type
renames Position
.Container
.HT
;
1093 Node
: constant Node_Access
:= HT_Ops
.Next
(HT
, Position
.Node
);
1100 return Cursor
'(Position.Container, Node);
1104 procedure Next (Position : in out Cursor) is
1106 Position := Next (Position);
1111 Position : Cursor) return Cursor
1114 if Position.Container = null then
1118 if Position.Container /= Object.Container then
1119 raise Program_Error with
1120 "Position cursor of Next designates wrong set";
1123 return Next (Position);
1130 function Overlap (Left, Right : Set) return Boolean is
1131 Left_HT : Hash_Table_Type renames Left'Unrestricted_Access.HT;
1132 Right_HT : Hash_Table_Type renames Right'Unrestricted_Access.HT;
1133 Left_Node : Node_Access;
1136 if Right.Length = 0 then
1140 if Left'Address = Right'Address then
1144 Left_Node := HT_Ops.First (Left_HT);
1145 while Left_Node /= null loop
1146 if Is_In (Right_HT, Left_Node) then
1149 Left_Node := HT_Ops.Next (Left_HT, Left_Node);
1159 procedure Query_Element
1161 Process : not null access procedure (Element : Element_Type))
1164 if Position.Node = null then
1165 raise Constraint_Error with
1166 "Position cursor of Query_Element equals No_Element";
1169 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1172 HT : Hash_Table_Type renames Position.Container.HT;
1174 B : Natural renames HT.Busy;
1175 L : Natural renames HT.Lock;
1182 Process (Position.Node.Element);
1200 (Stream : not null access Root_Stream_Type'Class;
1201 Container : out Set)
1204 Read_Nodes (Stream, Container.HT);
1208 (Stream : not null access Root_Stream_Type'Class;
1212 raise Program_Error with "attempt to stream set cursor";
1216 (Stream : not null access Root_Stream_Type'Class;
1217 Item : out Constant_Reference_Type)
1220 raise Program_Error with "attempt to stream reference";
1227 function Read_Node (Stream : not null access Root_Stream_Type'Class)
1230 Node : Node_Access := new Node_Type;
1232 Element_Type'Read (Stream, Node.Element);
1245 (Container : in out Set;
1246 New_Item : Element_Type)
1248 Node : constant Node_Access :=
1249 Element_Keys.Find (Container.HT, New_Item);
1253 raise Constraint_Error with
1254 "attempt to replace element not in set";
1257 if Container.HT.Lock > 0 then
1258 raise Program_Error with
1259 "attempt to tamper with elements (set is locked)";
1262 Node.Element := New_Item;
1265 procedure Replace_Element
1266 (Container : in out Set;
1268 New_Item : Element_Type)
1271 if Position.Node = null then
1272 raise Constraint_Error with
1273 "Position cursor equals No_Element";
1276 if Position.Container /= Container'Unrestricted_Access then
1277 raise Program_Error with
1278 "Position cursor designates wrong set";
1281 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1283 Replace_Element (Container.HT, Position.Node, New_Item);
1284 end Replace_Element;
1286 ----------------------
1287 -- Reserve_Capacity --
1288 ----------------------
1290 procedure Reserve_Capacity
1291 (Container : in out Set;
1292 Capacity : Count_Type)
1295 HT_Ops.Reserve_Capacity (Container.HT, Capacity);
1296 end Reserve_Capacity;
1302 procedure Set_Next (Node : Node_Access; Next : Node_Access) is
1307 --------------------------
1308 -- Symmetric_Difference --
1309 --------------------------
1311 procedure Symmetric_Difference
1312 (Target : in out Set;
1315 Tgt_HT : Hash_Table_Type renames Target.HT;
1316 Src_HT : Hash_Table_Type renames Source.HT'Unrestricted_Access.all;
1318 -- Per AI05-0022, the container implementation is required to detect
1319 -- element tampering by a generic actual subprogram.
1321 TB : Natural renames Tgt_HT.Busy;
1322 TL : Natural renames Tgt_HT.Lock;
1324 SB : Natural renames Src_HT.Busy;
1325 SL : Natural renames Src_HT.Lock;
1328 if Target'Address = Source'Address then
1334 raise Program_Error with
1335 "attempt to tamper with cursors (set is busy)";
1339 N : constant Count_Type := Target.Length + Source.Length;
1341 if N > HT_Ops.Capacity (Tgt_HT) then
1342 HT_Ops.Reserve_Capacity (Tgt_HT, N);
1346 if Target.Length = 0 then
1347 Iterate_Source_When_Empty_Target : declare
1348 procedure Process (Src_Node : Node_Access);
1350 procedure Iterate is
1351 new HT_Ops.Generic_Iteration (Process);
1357 procedure Process (Src_Node : Node_Access) is
1358 E : Element_Type renames Src_Node.Element;
1359 B : Buckets_Type renames Tgt_HT.Buckets.all;
1360 J : constant Hash_Type := Hash (E) mod B'Length;
1361 N : Count_Type renames Tgt_HT.Length;
1364 B (J) := new Node_Type'(E
, B
(J
));
1368 -- Start of processing for Iterate_Source_When_Empty_Target
1394 end Iterate_Source_When_Empty_Target
;
1397 Iterate_Source
: declare
1398 procedure Process
(Src_Node
: Node_Access
);
1400 procedure Iterate
is
1401 new HT_Ops
.Generic_Iteration
(Process
);
1407 procedure Process
(Src_Node
: Node_Access
) is
1408 E
: Element_Type
renames Src_Node
.Element
;
1409 B
: Buckets_Type
renames Tgt_HT
.Buckets
.all;
1410 J
: constant Hash_Type
:= Hash
(E
) mod B
'Length;
1411 N
: Count_Type
renames Tgt_HT
.Length
;
1414 if B
(J
) = null then
1415 B
(J
) := new Node_Type
'(E, null);
1418 elsif Equivalent_Elements (E, B (J).Element) then
1420 X : Node_Access := B (J);
1422 B (J) := B (J).Next;
1429 Prev : Node_Access := B (J);
1430 Curr : Node_Access := Prev.Next;
1433 while Curr /= null loop
1434 if Equivalent_Elements (E, Curr.Element) then
1435 Prev.Next := Curr.Next;
1445 B (J) := new Node_Type'(E
, B
(J
));
1451 -- Start of processing for Iterate_Source
1479 end Symmetric_Difference
;
1481 function Symmetric_Difference
(Left
, Right
: Set
) return Set
is
1482 Left_HT
: Hash_Table_Type
renames Left
'Unrestricted_Access.HT
;
1483 Right_HT
: Hash_Table_Type
renames Right
'Unrestricted_Access.HT
;
1484 Buckets
: HT_Types
.Buckets_Access
;
1485 Length
: Count_Type
;
1488 if Left
'Address = Right
'Address then
1492 if Right
.Length
= 0 then
1496 if Left
.Length
= 0 then
1501 Size
: constant Hash_Type
:=
1502 Prime_Numbers
.To_Prime
(Left
.Length
+ Right
.Length
);
1504 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
1509 Iterate_Left
: declare
1510 procedure Process
(L_Node
: Node_Access
);
1512 procedure Iterate
is
1513 new HT_Ops
.Generic_Iteration
(Process
);
1519 procedure Process
(L_Node
: Node_Access
) is
1521 if not Is_In
(Right_HT
, L_Node
) then
1523 E
: Element_Type
renames L_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
(Left_HT
, Buckets
.all, L_Node
);
1534 Buckets
(J
) := new Node_Type
'(E, Buckets (J));
1535 Length := Length + 1;
1540 -- Start of processing for Iterate_Left
1547 HT_Ops.Free_Hash_Table (Buckets);
1551 Iterate_Right : declare
1552 procedure Process (R_Node : Node_Access);
1554 procedure Iterate is
1555 new HT_Ops.Generic_Iteration (Process);
1561 procedure Process (R_Node : Node_Access) is
1563 if not Is_In (Left_HT, R_Node) then
1565 E : Element_Type renames R_Node.Element;
1567 -- Per AI05-0022, the container implementation is required
1568 -- to detect element tampering by a generic actual
1569 -- subprogram, hence the use of Checked_Index instead of a
1570 -- simple invocation of generic formal Hash.
1572 J : constant Hash_Type :=
1573 HT_Ops.Checked_Index (Right_HT, Buckets.all, R_Node);
1576 Buckets (J) := new Node_Type'(E
, Buckets
(J
));
1577 Length
:= Length
+ 1;
1582 -- Start of processing for Iterate_Right
1589 HT_Ops
.Free_Hash_Table
(Buckets
);
1593 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
1594 end Symmetric_Difference
;
1600 function To_Set
(New_Item
: Element_Type
) return Set
is
1601 HT
: Hash_Table_Type
;
1605 pragma Unreferenced
(Node
, Inserted
);
1608 Insert
(HT
, New_Item
, Node
, Inserted
);
1609 return Set
'(Controlled with HT);
1617 (Target : in out Set;
1620 procedure Process (Src_Node : Node_Access);
1622 procedure Iterate is
1623 new HT_Ops.Generic_Iteration (Process);
1629 procedure Process (Src_Node : Node_Access) is
1630 function New_Node (Next : Node_Access) return Node_Access;
1631 pragma Inline (New_Node);
1634 new Element_Keys.Generic_Conditional_Insert (New_Node);
1640 function New_Node (Next : Node_Access) return Node_Access is
1641 Node : constant Node_Access :=
1642 new Node_Type'(Src_Node
.Element
, Next
);
1647 Tgt_Node
: Node_Access
;
1649 pragma Unreferenced
(Tgt_Node
, Success
);
1651 -- Start of processing for Process
1654 Insert
(Target
.HT
, Src_Node
.Element
, Tgt_Node
, Success
);
1657 -- Start of processing for Union
1660 if Target
'Address = Source
'Address then
1664 if Target
.HT
.Busy
> 0 then
1665 raise Program_Error
with
1666 "attempt to tamper with cursors (set is busy)";
1670 N
: constant Count_Type
:= Target
.Length
+ Source
.Length
;
1672 if N
> HT_Ops
.Capacity
(Target
.HT
) then
1673 HT_Ops
.Reserve_Capacity
(Target
.HT
, N
);
1677 Iterate
(Source
.HT
);
1680 function Union
(Left
, Right
: Set
) return Set
is
1681 Left_HT
: Hash_Table_Type
renames Left
.HT
'Unrestricted_Access.all;
1682 Right_HT
: Hash_Table_Type
renames Right
.HT
'Unrestricted_Access.all;
1683 Buckets
: HT_Types
.Buckets_Access
;
1684 Length
: Count_Type
;
1687 if Left
'Address = Right
'Address then
1691 if Right
.Length
= 0 then
1695 if Left
.Length
= 0 then
1700 Size
: constant Hash_Type
:=
1701 Prime_Numbers
.To_Prime
(Left
.Length
+ Right
.Length
);
1703 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
1706 Iterate_Left
: declare
1707 procedure Process
(L_Node
: Node_Access
);
1709 procedure Iterate
is
1710 new HT_Ops
.Generic_Iteration
(Process
);
1716 procedure Process
(L_Node
: Node_Access
) is
1717 J
: constant Hash_Type
:=
1718 Hash
(L_Node
.Element
) mod Buckets
'Length;
1721 Buckets
(J
) := new Node_Type
'(L_Node.Element, Buckets (J));
1724 -- Per AI05-0022, the container implementation is required to detect
1725 -- element tampering by a generic actual subprogram, hence the use of
1726 -- Checked_Index instead of a simple invocation of generic formal
1729 B : Integer renames Left_HT.Busy;
1730 L : Integer renames Left_HT.Lock;
1732 -- Start of processing for Iterate_Left
1748 HT_Ops.Free_Hash_Table (Buckets);
1752 Length := Left.Length;
1754 Iterate_Right : declare
1755 procedure Process (Src_Node : Node_Access);
1757 procedure Iterate is
1758 new HT_Ops.Generic_Iteration (Process);
1764 procedure Process (Src_Node : Node_Access) is
1765 J : constant Hash_Type :=
1766 Hash (Src_Node.Element) mod Buckets'Length;
1768 Tgt_Node : Node_Access := Buckets (J);
1771 while Tgt_Node /= null loop
1772 if Equivalent_Elements (Src_Node.Element, Tgt_Node.Element) then
1776 Tgt_Node := Next (Tgt_Node);
1779 Buckets (J) := new Node_Type'(Src_Node
.Element
, Buckets
(J
));
1780 Length
:= Length
+ 1;
1783 -- Per AI05-0022, the container implementation is required to detect
1784 -- element tampering by a generic actual subprogram, hence the use of
1785 -- Checked_Index instead of a simple invocation of generic formal
1788 LB
: Integer renames Left_HT
.Busy
;
1789 LL
: Integer renames Left_HT
.Lock
;
1791 RB
: Integer renames Right_HT
.Busy
;
1792 RL
: Integer renames Right_HT
.Lock
;
1794 -- Start of processing for Iterate_Right
1819 HT_Ops
.Free_Hash_Table
(Buckets
);
1823 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
1830 function Vet
(Position
: Cursor
) return Boolean is
1832 if Position
.Node
= null then
1833 return Position
.Container
= null;
1836 if Position
.Container
= null then
1840 if Position
.Node
.Next
= Position
.Node
then
1845 HT
: Hash_Table_Type
renames Position
.Container
.HT
;
1849 if HT
.Length
= 0 then
1853 if HT
.Buckets
= null
1854 or else HT
.Buckets
'Length = 0
1859 X
:= HT
.Buckets
(Element_Keys
.Checked_Index
1861 Position
.Node
.Element
));
1863 for J
in 1 .. HT
.Length
loop
1864 if X
= Position
.Node
then
1872 if X
= X
.Next
then -- to prevent unnecessary looping
1888 (Stream
: not null access Root_Stream_Type
'Class;
1892 Write_Nodes
(Stream
, Container
.HT
);
1896 (Stream
: not null access Root_Stream_Type
'Class;
1900 raise Program_Error
with "attempt to stream set cursor";
1904 (Stream
: not null access Root_Stream_Type
'Class;
1905 Item
: Constant_Reference_Type
)
1908 raise Program_Error
with "attempt to stream reference";
1915 procedure Write_Node
1916 (Stream
: not null access Root_Stream_Type
'Class;
1920 Element_Type
'Write (Stream
, Node
.Element
);
1923 package body Generic_Keys
is
1925 -----------------------
1926 -- Local Subprograms --
1927 -----------------------
1933 procedure Adjust
(Control
: in out Reference_Control_Type
) is
1935 if Control
.Container
/= null then
1937 HT
: Hash_Table_Type
renames Control
.Container
.all.HT
;
1938 B
: Natural renames HT
.Busy
;
1939 L
: Natural renames HT
.Lock
;
1947 function Equivalent_Key_Node
1949 Node
: Node_Access
) return Boolean;
1950 pragma Inline
(Equivalent_Key_Node
);
1952 --------------------------
1953 -- Local Instantiations --
1954 --------------------------
1957 new Hash_Tables
.Generic_Keys
1958 (HT_Types
=> HT_Types
,
1960 Set_Next
=> Set_Next
,
1961 Key_Type
=> Key_Type
,
1963 Equivalent_Keys
=> Equivalent_Key_Node
);
1965 ------------------------
1966 -- Constant_Reference --
1967 ------------------------
1969 function Constant_Reference
1970 (Container
: aliased Set
;
1971 Key
: Key_Type
) return Constant_Reference_Type
1973 HT
: Hash_Table_Type
renames Container
'Unrestricted_Access.HT
;
1974 Node
: constant Node_Access
:= Key_Keys
.Find
(HT
, Key
);
1978 raise Constraint_Error
with "Key not in set";
1982 B
: Natural renames HT
.Busy
;
1983 L
: Natural renames HT
.Lock
;
1985 return R
: constant Constant_Reference_Type
:=
1986 (Element
=> Node
.Element
'Access,
1987 Control
=> (Controlled
with Container
'Unrestricted_Access))
1993 end Constant_Reference
;
2001 Key
: Key_Type
) return Boolean
2004 return Find
(Container
, Key
) /= No_Element
;
2012 (Container
: in out Set
;
2018 Key_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Key
, X
);
2021 raise Constraint_Error
with "attempt to delete key not in set";
2033 Key
: Key_Type
) return Element_Type
2035 HT
: Hash_Table_Type
renames Container
'Unrestricted_Access.HT
;
2036 Node
: constant Node_Access
:= Key_Keys
.Find
(HT
, Key
);
2040 raise Constraint_Error
with "key not in set";
2043 return Node
.Element
;
2046 -------------------------
2047 -- Equivalent_Key_Node --
2048 -------------------------
2050 function Equivalent_Key_Node
2052 Node
: Node_Access
) return Boolean
2055 return Equivalent_Keys
(Key
, Generic_Keys
.Key
(Node
.Element
));
2056 end Equivalent_Key_Node
;
2063 (Container
: in out Set
;
2068 Key_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Key
, X
);
2076 procedure Finalize
(Control
: in out Reference_Control_Type
) is
2078 if Control
.Container
/= null then
2080 HT
: Hash_Table_Type
renames Control
.Container
.all.HT
;
2081 B
: Natural renames HT
.Busy
;
2082 L
: Natural renames HT
.Lock
;
2088 if Hash
(Key
(Element
(Control
.Old_Pos
))) /= Control
.Old_Hash
2090 HT_Ops
.Delete_Node_At_Index
2091 (Control
.Container
.HT
, Control
.Index
, Control
.Old_Pos
.Node
);
2092 raise Program_Error
with "key not preserved in reference";
2095 Control
.Container
:= null;
2105 Key
: Key_Type
) return Cursor
2107 HT
: Hash_Table_Type
renames Container
'Unrestricted_Access.HT
;
2108 Node
: constant Node_Access
:= Key_Keys
.Find
(HT
, Key
);
2113 return Cursor
'(Container'Unrestricted_Access, Node);
2121 function Key (Position : Cursor) return Key_Type is
2123 if Position.Node = null then
2124 raise Constraint_Error with
2125 "Position cursor equals No_Element";
2128 pragma Assert (Vet (Position), "bad cursor in function Key");
2130 return Key (Position.Node.Element);
2138 (Stream : not null access Root_Stream_Type'Class;
2139 Item : out Reference_Type)
2142 raise Program_Error with "attempt to stream reference";
2145 ------------------------------
2146 -- Reference_Preserving_Key --
2147 ------------------------------
2149 function Reference_Preserving_Key
2150 (Container : aliased in out Set;
2151 Position : Cursor) return Reference_Type
2154 if Position.Container = null then
2155 raise Constraint_Error with "Position cursor has no element";
2158 if Position.Container /= Container'Unrestricted_Access then
2159 raise Program_Error with
2160 "Position cursor designates wrong container";
2165 "bad cursor in function Reference_Preserving_Key");
2168 HT : Hash_Table_Type renames Position.Container.all.HT;
2169 B : Natural renames HT.Busy;
2170 L : Natural renames HT.Lock;
2172 return R : constant Reference_Type :=
2173 (Element => Position.Node.Element'Access,
2176 Container'Unrestricted_Access,
2177 Index => HT_Ops.Index (HT, Position.Node),
2178 Old_Pos => Position,
2179 Old_Hash => Hash (Key (Position))))
2185 end Reference_Preserving_Key;
2187 function Reference_Preserving_Key
2188 (Container : aliased in out Set;
2189 Key : Key_Type) return Reference_Type
2191 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
2195 raise Constraint_Error with "key not in set";
2199 HT : Hash_Table_Type renames Container.HT;
2200 B : Natural renames HT.Busy;
2201 L : Natural renames HT.Lock;
2202 P : constant Cursor := Find (Container, Key);
2204 return R : constant Reference_Type :=
2205 (Element => Node.Element'Access,
2208 Container'Unrestricted_Access,
2209 Index => HT_Ops.Index (HT, P.Node),
2211 Old_Hash => Hash (Key)))
2217 end Reference_Preserving_Key;
2224 (Container : in out Set;
2226 New_Item : Element_Type)
2228 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
2232 raise Constraint_Error with
2233 "attempt to replace key not in set";
2236 Replace_Element (Container.HT, Node, New_Item);
2239 -----------------------------------
2240 -- Update_Element_Preserving_Key --
2241 -----------------------------------
2243 procedure Update_Element_Preserving_Key
2244 (Container : in out Set;
2246 Process : not null access
2247 procedure (Element : in out Element_Type))
2249 HT : Hash_Table_Type renames Container.HT;
2253 if Position.Node = null then
2254 raise Constraint_Error with
2255 "Position cursor equals No_Element";
2258 if Position.Container /= Container'Unrestricted_Access then
2259 raise Program_Error with
2260 "Position cursor designates wrong set";
2263 if HT.Buckets = null
2264 or else HT.Buckets'Length = 0
2265 or else HT.Length = 0
2266 or else Position.Node.Next = Position.Node
2268 raise Program_Error with "Position cursor is bad (set is empty)";
2273 "bad cursor in Update_Element_Preserving_Key");
2275 -- Per AI05-0022, the container implementation is required to detect
2276 -- element tampering by a generic actual subprogram.
2279 E : Element_Type renames Position.Node.Element;
2280 K : constant Key_Type := Key (E);
2282 B : Natural renames HT.Busy;
2283 L : Natural renames HT.Lock;
2292 Indx := HT_Ops.Index (HT, Position.Node);
2294 Eq := Equivalent_Keys (K, Key (E));
2310 if HT.Buckets (Indx) = Position.Node then
2311 HT.Buckets (Indx) := Position.Node.Next;
2315 Prev : Node_Access := HT.Buckets (Indx);
2318 while Prev.Next /= Position.Node loop
2322 raise Program_Error with
2323 "Position cursor is bad (node not found)";
2327 Prev.Next := Position.Node.Next;
2331 HT.Length := HT.Length - 1;
2334 X : Node_Access := Position.Node;
2340 raise Program_Error with "key was modified";
2341 end Update_Element_Preserving_Key;
2348 (Stream : not null access Root_Stream_Type'Class;
2349 Item : Reference_Type)
2352 raise Program_Error with "attempt to stream reference";
2357 end Ada.Containers.Hashed_Sets;