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 pragma Annotate
(CodePeer
, Skip_Analysis
);
46 -----------------------
47 -- Local Subprograms --
48 -----------------------
50 procedure Assign
(Node
: Node_Access
; Item
: Element_Type
);
51 pragma Inline
(Assign
);
53 function Copy_Node
(Source
: Node_Access
) return Node_Access
;
54 pragma Inline
(Copy_Node
);
56 function Equivalent_Keys
58 Node
: Node_Access
) return Boolean;
59 pragma Inline
(Equivalent_Keys
);
61 function Find_Equal_Key
62 (R_HT
: Hash_Table_Type
;
63 L_Node
: Node_Access
) return Boolean;
65 function Find_Equivalent_Key
66 (R_HT
: Hash_Table_Type
;
67 L_Node
: Node_Access
) return Boolean;
69 procedure Free
(X
: in out Node_Access
);
71 function Hash_Node
(Node
: Node_Access
) return Hash_Type
;
72 pragma Inline
(Hash_Node
);
75 (HT
: in out Hash_Table_Type
;
76 New_Item
: Element_Type
;
77 Node
: out Node_Access
;
78 Inserted
: out Boolean);
81 (HT
: aliased in out Hash_Table_Type
;
82 Key
: Node_Access
) return Boolean;
83 pragma Inline
(Is_In
);
85 function Next
(Node
: Node_Access
) return Node_Access
;
88 function Read_Node
(Stream
: not null access Root_Stream_Type
'Class)
90 pragma Inline
(Read_Node
);
92 procedure Set_Next
(Node
: Node_Access
; Next
: Node_Access
);
93 pragma Inline
(Set_Next
);
95 function Vet
(Position
: Cursor
) return Boolean;
98 (Stream
: not null access Root_Stream_Type
'Class;
100 pragma Inline
(Write_Node
);
102 --------------------------
103 -- Local Instantiations --
104 --------------------------
106 package HT_Ops
is new Hash_Tables
.Generic_Operations
107 (HT_Types
=> HT_Types
,
108 Hash_Node
=> Hash_Node
,
110 Set_Next
=> Set_Next
,
111 Copy_Node
=> Copy_Node
,
114 package Element_Keys
is new Hash_Tables
.Generic_Keys
115 (HT_Types
=> HT_Types
,
117 Set_Next
=> Set_Next
,
118 Key_Type
=> Element_Type
,
120 Equivalent_Keys
=> Equivalent_Keys
);
123 new HT_Ops
.Generic_Equal
(Find_Equal_Key
);
125 function Is_Equivalent
is
126 new HT_Ops
.Generic_Equal
(Find_Equivalent_Key
);
128 procedure Read_Nodes
is
129 new HT_Ops
.Generic_Read
(Read_Node
);
131 procedure Replace_Element
is
132 new Element_Keys
.Generic_Replace_Element
(Hash_Node
, Assign
);
134 procedure Write_Nodes
is
135 new HT_Ops
.Generic_Write
(Write_Node
);
141 function "=" (Left
, Right
: Set
) return Boolean is
143 return Is_Equal
(Left
.HT
, Right
.HT
);
150 procedure Adjust
(Container
: in out Set
) is
152 HT_Ops
.Adjust
(Container
.HT
);
155 procedure Adjust
(Control
: in out Reference_Control_Type
) is
157 if Control
.Container
/= null then
159 HT
: Hash_Table_Type
renames Control
.Container
.all.HT
;
160 B
: Natural renames HT
.Busy
;
161 L
: Natural renames HT
.Lock
;
173 procedure Assign
(Node
: Node_Access
; Item
: Element_Type
) is
175 Node
.Element
:= Item
;
178 procedure Assign
(Target
: in out Set
; Source
: Set
) is
180 if Target
'Address = Source
'Address then
185 Target
.Union
(Source
);
192 function Capacity
(Container
: Set
) return Count_Type
is
194 return HT_Ops
.Capacity
(Container
.HT
);
201 procedure Clear
(Container
: in out Set
) is
203 HT_Ops
.Clear
(Container
.HT
);
206 ------------------------
207 -- Constant_Reference --
208 ------------------------
210 function Constant_Reference
211 (Container
: aliased Set
;
212 Position
: Cursor
) return Constant_Reference_Type
215 if Position
.Container
= null then
216 raise Constraint_Error
with "Position cursor has no element";
219 if Position
.Container
/= Container
'Unrestricted_Access then
220 raise Program_Error
with
221 "Position cursor designates wrong container";
224 pragma Assert
(Vet
(Position
), "bad cursor in Constant_Reference");
227 HT
: Hash_Table_Type
renames Position
.Container
.all.HT
;
228 B
: Natural renames HT
.Busy
;
229 L
: Natural renames HT
.Lock
;
231 return R
: constant Constant_Reference_Type
:=
232 (Element
=> Position
.Node
.Element
'Access,
233 Control
=> (Controlled
with Container
'Unrestricted_Access))
239 end Constant_Reference
;
245 function Contains
(Container
: Set
; Item
: Element_Type
) return Boolean is
247 return Find
(Container
, Item
) /= No_Element
;
256 Capacity
: Count_Type
:= 0) return Set
264 elsif Capacity
>= Source
.Length
then
269 with "Requested capacity is less than Source length";
272 return Target
: Set
do
273 Target
.Reserve_Capacity
(C
);
274 Target
.Assign
(Source
);
282 function Copy_Node
(Source
: Node_Access
) return Node_Access
is
284 return new Node_Type
'(Element => Source.Element, Next => null);
292 (Container : in out Set;
298 Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
301 raise Constraint_Error with "attempt to delete element not in set";
308 (Container : in out Set;
309 Position : in out Cursor)
312 if Position.Node = null then
313 raise Constraint_Error with "Position cursor equals No_Element";
316 if Position.Container /= Container'Unrestricted_Access then
317 raise Program_Error with "Position cursor designates wrong set";
320 if Container.HT.Busy > 0 then
321 raise Program_Error with
322 "attempt to tamper with cursors (set is busy)";
325 pragma Assert (Vet (Position), "bad cursor in Delete");
327 HT_Ops.Delete_Node_Sans_Free (Container.HT, Position.Node);
329 Free (Position.Node);
330 Position.Container := null;
338 (Target : in out Set;
341 Tgt_Node : Node_Access;
342 Src_HT : Hash_Table_Type renames Source'Unrestricted_Access.HT;
345 if Target'Address = Source'Address then
350 if Src_HT.Length = 0 then
354 if Target.HT.Busy > 0 then
355 raise Program_Error with
356 "attempt to tamper with cursors (set is busy)";
359 if Src_HT.Length < Target.HT.Length then
361 Src_Node : Node_Access;
364 Src_Node := HT_Ops.First (Src_HT);
365 while Src_Node /= null loop
366 Tgt_Node := Element_Keys.Find (Target.HT, Src_Node.Element);
368 if Tgt_Node /= null then
369 HT_Ops.Delete_Node_Sans_Free (Target.HT, Tgt_Node);
373 Src_Node := HT_Ops.Next (Src_HT, Src_Node);
378 Tgt_Node := HT_Ops.First (Target.HT);
379 while Tgt_Node /= null loop
380 if Is_In (Src_HT, Tgt_Node) then
382 X : Node_Access := Tgt_Node;
384 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
385 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
390 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
396 function Difference (Left, Right : Set) return Set is
397 Left_HT : Hash_Table_Type renames Left'Unrestricted_Access.HT;
398 Right_HT : Hash_Table_Type renames Right'Unrestricted_Access.HT;
399 Buckets : HT_Types.Buckets_Access;
403 if Left'Address = Right'Address then
407 if Left_HT.Length = 0 then
411 if Right_HT.Length = 0 then
416 Size : constant Hash_Type := Prime_Numbers.To_Prime (Left.Length);
418 Buckets := HT_Ops.New_Buckets (Length => Size);
423 Iterate_Left : declare
424 procedure Process (L_Node : Node_Access);
427 new HT_Ops.Generic_Iteration (Process);
433 procedure Process (L_Node : Node_Access) is
435 if not Is_In (Right_HT, L_Node) then
437 -- Per AI05-0022, the container implementation is required
438 -- to detect element tampering by a generic actual
439 -- subprogram, hence the use of Checked_Index instead of a
440 -- simple invocation of generic formal Hash.
442 J : constant Hash_Type :=
443 HT_Ops.Checked_Index (Left_HT, Buckets.all, L_Node);
445 Bucket : Node_Access renames Buckets (J);
448 Bucket := new Node_Type'(L_Node
.Element
, Bucket
);
451 Length
:= Length
+ 1;
455 -- Start of processing for Iterate_Left
461 HT_Ops
.Free_Hash_Table
(Buckets
);
465 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
472 function Element
(Position
: Cursor
) return Element_Type
is
474 if Position
.Node
= null then
475 raise Constraint_Error
with "Position cursor equals No_Element";
478 pragma Assert
(Vet
(Position
), "bad cursor in function Element");
480 return Position
.Node
.Element
;
483 ---------------------
484 -- Equivalent_Sets --
485 ---------------------
487 function Equivalent_Sets
(Left
, Right
: Set
) return Boolean is
489 return Is_Equivalent
(Left
.HT
, Right
.HT
);
492 -------------------------
493 -- Equivalent_Elements --
494 -------------------------
496 function Equivalent_Elements
(Left
, Right
: Cursor
)
499 if Left
.Node
= null then
500 raise Constraint_Error
with
501 "Left cursor of Equivalent_Elements equals No_Element";
504 if Right
.Node
= null then
505 raise Constraint_Error
with
506 "Right cursor of Equivalent_Elements equals No_Element";
509 pragma Assert
(Vet
(Left
), "bad Left cursor in Equivalent_Elements");
510 pragma Assert
(Vet
(Right
), "bad Right cursor in Equivalent_Elements");
512 -- AI05-0022 requires that a container implementation detect element
513 -- tampering by a generic actual subprogram. However, the following case
514 -- falls outside the scope of that AI. Randy Brukardt explained on the
515 -- ARG list on 2013/02/07 that:
518 -- But for an operation like "<" [the ordered set analog of
519 -- Equivalent_Elements], there is no need to "dereference" a cursor
520 -- after the call to the generic formal parameter function, so nothing
521 -- bad could happen if tampering is undetected. And the operation can
522 -- safely return a result without a problem even if an element is
523 -- deleted from the container.
526 return Equivalent_Elements
(Left
.Node
.Element
, Right
.Node
.Element
);
527 end Equivalent_Elements
;
529 function Equivalent_Elements
(Left
: Cursor
; Right
: Element_Type
)
532 if Left
.Node
= null then
533 raise Constraint_Error
with
534 "Left cursor of Equivalent_Elements equals No_Element";
537 pragma Assert
(Vet
(Left
), "Left cursor in Equivalent_Elements is bad");
539 return Equivalent_Elements
(Left
.Node
.Element
, Right
);
540 end Equivalent_Elements
;
542 function Equivalent_Elements
(Left
: Element_Type
; Right
: Cursor
)
545 if Right
.Node
= null then
546 raise Constraint_Error
with
547 "Right cursor of Equivalent_Elements equals No_Element";
552 "Right cursor of Equivalent_Elements is bad");
554 return Equivalent_Elements
(Left
, Right
.Node
.Element
);
555 end Equivalent_Elements
;
557 ---------------------
558 -- Equivalent_Keys --
559 ---------------------
561 function Equivalent_Keys
(Key
: Element_Type
; Node
: Node_Access
)
564 return Equivalent_Elements
(Key
, Node
.Element
);
572 (Container
: in out Set
;
577 Element_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Item
, X
);
585 procedure Finalize
(Container
: in out Set
) is
587 HT_Ops
.Finalize
(Container
.HT
);
590 procedure Finalize
(Control
: in out Reference_Control_Type
) is
592 if Control
.Container
/= null then
594 HT
: Hash_Table_Type
renames Control
.Container
.all.HT
;
595 B
: Natural renames HT
.Busy
;
596 L
: Natural renames HT
.Lock
;
602 Control
.Container
:= null;
606 procedure Finalize
(Object
: in out Iterator
) is
608 if Object
.Container
/= null then
610 B
: Natural renames Object
.Container
.HT
.Busy
;
623 Item
: Element_Type
) return Cursor
625 HT
: Hash_Table_Type
renames Container
'Unrestricted_Access.HT
;
626 Node
: constant Node_Access
:= Element_Keys
.Find
(HT
, Item
);
633 return Cursor
'(Container'Unrestricted_Access, Node);
640 function Find_Equal_Key
641 (R_HT : Hash_Table_Type;
642 L_Node : Node_Access) return Boolean
644 R_Index : constant Hash_Type :=
645 Element_Keys.Index (R_HT, L_Node.Element);
647 R_Node : Node_Access := R_HT.Buckets (R_Index);
651 if R_Node = null then
655 if L_Node.Element = R_Node.Element then
659 R_Node := Next (R_Node);
663 -------------------------
664 -- Find_Equivalent_Key --
665 -------------------------
667 function Find_Equivalent_Key
668 (R_HT : Hash_Table_Type;
669 L_Node : Node_Access) return Boolean
671 R_Index : constant Hash_Type :=
672 Element_Keys.Index (R_HT, L_Node.Element);
674 R_Node : Node_Access := R_HT.Buckets (R_Index);
678 if R_Node = null then
682 if Equivalent_Elements (L_Node.Element, R_Node.Element) then
686 R_Node := Next (R_Node);
688 end Find_Equivalent_Key;
694 function First (Container : Set) return Cursor is
695 Node : constant Node_Access := HT_Ops.First (Container.HT);
702 return Cursor'(Container
'Unrestricted_Access, Node
);
705 function First
(Object
: Iterator
) return Cursor
is
707 return Object
.Container
.First
;
714 procedure Free
(X
: in out Node_Access
) is
715 procedure Deallocate
is
716 new Ada
.Unchecked_Deallocation
(Node_Type
, Node_Access
);
720 X
.Next
:= X
; -- detect mischief (in Vet)
729 function Has_Element
(Position
: Cursor
) return Boolean is
731 pragma Assert
(Vet
(Position
), "bad cursor in Has_Element");
732 return Position
.Node
/= null;
739 function Hash_Node
(Node
: Node_Access
) return Hash_Type
is
741 return Hash
(Node
.Element
);
749 (Container
: in out Set
;
750 New_Item
: Element_Type
)
756 Insert
(Container
, New_Item
, Position
, Inserted
);
759 if Container
.HT
.Lock
> 0 then
760 raise Program_Error
with
761 "attempt to tamper with elements (set is locked)";
764 Position
.Node
.Element
:= New_Item
;
773 (Container
: in out Set
;
774 New_Item
: Element_Type
;
775 Position
: out Cursor
;
776 Inserted
: out Boolean)
779 Insert
(Container
.HT
, New_Item
, Position
.Node
, Inserted
);
780 Position
.Container
:= Container
'Unchecked_Access;
784 (Container
: in out Set
;
785 New_Item
: Element_Type
)
788 pragma Unreferenced
(Position
);
793 Insert
(Container
, New_Item
, Position
, Inserted
);
796 raise Constraint_Error
with
797 "attempt to insert element already in set";
802 (HT
: in out Hash_Table_Type
;
803 New_Item
: Element_Type
;
804 Node
: out Node_Access
;
805 Inserted
: out Boolean)
807 function New_Node
(Next
: Node_Access
) return Node_Access
;
808 pragma Inline
(New_Node
);
810 procedure Local_Insert
is
811 new Element_Keys
.Generic_Conditional_Insert
(New_Node
);
817 function New_Node
(Next
: Node_Access
) return Node_Access
is
819 return new Node_Type
'(New_Item, Next);
822 -- Start of processing for Insert
825 if HT_Ops.Capacity (HT) = 0 then
826 HT_Ops.Reserve_Capacity (HT, 1);
830 raise Program_Error with
831 "attempt to tamper with cursors (set is busy)";
834 Local_Insert (HT, New_Item, Node, Inserted);
837 and then HT.Length > HT_Ops.Capacity (HT)
839 HT_Ops.Reserve_Capacity (HT, HT.Length);
847 procedure Intersection
848 (Target : in out Set;
851 Src_HT : Hash_Table_Type renames Source'Unrestricted_Access.HT;
852 Tgt_Node : Node_Access;
855 if Target'Address = Source'Address then
859 if Source.HT.Length = 0 then
864 if Target.HT.Busy > 0 then
865 raise Program_Error with
866 "attempt to tamper with cursors (set is busy)";
869 Tgt_Node := HT_Ops.First (Target.HT);
870 while Tgt_Node /= null loop
871 if Is_In (Src_HT, Tgt_Node) then
872 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
876 X : Node_Access := Tgt_Node;
878 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
879 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
886 function Intersection (Left, Right : Set) return Set is
887 Left_HT : Hash_Table_Type renames Left'Unrestricted_Access.HT;
888 Right_HT : Hash_Table_Type renames Right'Unrestricted_Access.HT;
889 Buckets : HT_Types.Buckets_Access;
893 if Left'Address = Right'Address then
897 Length := Count_Type'Min (Left.Length, Right.Length);
904 Size : constant Hash_Type := Prime_Numbers.To_Prime (Length);
906 Buckets := HT_Ops.New_Buckets (Length => Size);
911 Iterate_Left : declare
912 procedure Process (L_Node : Node_Access);
915 new HT_Ops.Generic_Iteration (Process);
921 procedure Process (L_Node : Node_Access) is
923 if Is_In (Right_HT, L_Node) then
925 -- Per AI05-0022, the container implementation is required
926 -- to detect element tampering by a generic actual
927 -- subprogram, hence the use of Checked_Index instead of a
928 -- simple invocation of generic formal Hash.
930 J : constant Hash_Type :=
931 HT_Ops.Checked_Index (Left_HT, Buckets.all, L_Node);
933 Bucket : Node_Access renames Buckets (J);
936 Bucket := new Node_Type'(L_Node
.Element
, Bucket
);
939 Length
:= Length
+ 1;
943 -- Start of processing for Iterate_Left
949 HT_Ops
.Free_Hash_Table
(Buckets
);
953 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
960 function Is_Empty
(Container
: Set
) return Boolean is
962 return Container
.HT
.Length
= 0;
970 (HT
: aliased in out Hash_Table_Type
;
971 Key
: Node_Access
) return Boolean
974 return Element_Keys
.Find
(HT
, Key
.Element
) /= null;
981 function Is_Subset
(Subset
: Set
; Of_Set
: Set
) return Boolean is
982 Subset_HT
: Hash_Table_Type
renames Subset
'Unrestricted_Access.HT
;
983 Of_Set_HT
: Hash_Table_Type
renames Of_Set
'Unrestricted_Access.HT
;
984 Subset_Node
: Node_Access
;
987 if Subset
'Address = Of_Set
'Address then
991 if Subset
.Length
> Of_Set
.Length
then
995 Subset_Node
:= HT_Ops
.First
(Subset_HT
);
996 while Subset_Node
/= null loop
997 if not Is_In
(Of_Set_HT
, Subset_Node
) then
1000 Subset_Node
:= HT_Ops
.Next
(Subset_HT
, Subset_Node
);
1012 Process
: not null access procedure (Position
: Cursor
))
1014 procedure Process_Node
(Node
: Node_Access
);
1015 pragma Inline
(Process_Node
);
1017 procedure Iterate
is
1018 new HT_Ops
.Generic_Iteration
(Process_Node
);
1024 procedure Process_Node
(Node
: Node_Access
) is
1026 Process
(Cursor
'(Container'Unrestricted_Access, Node));
1029 B : Natural renames Container'Unrestricted_Access.HT.Busy;
1031 -- Start of processing for Iterate
1037 Iterate (Container.HT);
1048 (Container : Set) return Set_Iterator_Interfaces.Forward_Iterator'Class
1050 B : Natural renames Container'Unrestricted_Access.all.HT.Busy;
1053 return It : constant Iterator :=
1054 Iterator'(Limited_Controlled
with
1055 Container
=> Container
'Unrestricted_Access);
1062 function Length
(Container
: Set
) return Count_Type
is
1064 return Container
.HT
.Length
;
1071 procedure Move
(Target
: in out Set
; Source
: in out Set
) is
1073 HT_Ops
.Move
(Target
=> Target
.HT
, Source
=> Source
.HT
);
1080 function Next
(Node
: Node_Access
) return Node_Access
is
1085 function Next
(Position
: Cursor
) return Cursor
is
1087 if Position
.Node
= null then
1091 pragma Assert
(Vet
(Position
), "bad cursor in Next");
1094 HT
: Hash_Table_Type
renames Position
.Container
.HT
;
1095 Node
: constant Node_Access
:= HT_Ops
.Next
(HT
, Position
.Node
);
1102 return Cursor
'(Position.Container, Node);
1106 procedure Next (Position : in out Cursor) is
1108 Position := Next (Position);
1113 Position : Cursor) return Cursor
1116 if Position.Container = null then
1120 if Position.Container /= Object.Container then
1121 raise Program_Error with
1122 "Position cursor of Next designates wrong set";
1125 return Next (Position);
1132 function Overlap (Left, Right : Set) return Boolean is
1133 Left_HT : Hash_Table_Type renames Left'Unrestricted_Access.HT;
1134 Right_HT : Hash_Table_Type renames Right'Unrestricted_Access.HT;
1135 Left_Node : Node_Access;
1138 if Right.Length = 0 then
1142 if Left'Address = Right'Address then
1146 Left_Node := HT_Ops.First (Left_HT);
1147 while Left_Node /= null loop
1148 if Is_In (Right_HT, Left_Node) then
1151 Left_Node := HT_Ops.Next (Left_HT, Left_Node);
1161 procedure Query_Element
1163 Process : not null access procedure (Element : Element_Type))
1166 if Position.Node = null then
1167 raise Constraint_Error with
1168 "Position cursor of Query_Element equals No_Element";
1171 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1174 HT : Hash_Table_Type renames Position.Container.HT;
1176 B : Natural renames HT.Busy;
1177 L : Natural renames HT.Lock;
1184 Process (Position.Node.Element);
1202 (Stream : not null access Root_Stream_Type'Class;
1203 Container : out Set)
1206 Read_Nodes (Stream, Container.HT);
1210 (Stream : not null access Root_Stream_Type'Class;
1214 raise Program_Error with "attempt to stream set cursor";
1218 (Stream : not null access Root_Stream_Type'Class;
1219 Item : out Constant_Reference_Type)
1222 raise Program_Error with "attempt to stream reference";
1229 function Read_Node (Stream : not null access Root_Stream_Type'Class)
1232 Node : Node_Access := new Node_Type;
1234 Element_Type'Read (Stream, Node.Element);
1247 (Container : in out Set;
1248 New_Item : Element_Type)
1250 Node : constant Node_Access :=
1251 Element_Keys.Find (Container.HT, New_Item);
1255 raise Constraint_Error with
1256 "attempt to replace element not in set";
1259 if Container.HT.Lock > 0 then
1260 raise Program_Error with
1261 "attempt to tamper with elements (set is locked)";
1264 Node.Element := New_Item;
1267 procedure Replace_Element
1268 (Container : in out Set;
1270 New_Item : Element_Type)
1273 if Position.Node = null then
1274 raise Constraint_Error with
1275 "Position cursor equals No_Element";
1278 if Position.Container /= Container'Unrestricted_Access then
1279 raise Program_Error with
1280 "Position cursor designates wrong set";
1283 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1285 Replace_Element (Container.HT, Position.Node, New_Item);
1286 end Replace_Element;
1288 ----------------------
1289 -- Reserve_Capacity --
1290 ----------------------
1292 procedure Reserve_Capacity
1293 (Container : in out Set;
1294 Capacity : Count_Type)
1297 HT_Ops.Reserve_Capacity (Container.HT, Capacity);
1298 end Reserve_Capacity;
1304 procedure Set_Next (Node : Node_Access; Next : Node_Access) is
1309 --------------------------
1310 -- Symmetric_Difference --
1311 --------------------------
1313 procedure Symmetric_Difference
1314 (Target : in out Set;
1317 Tgt_HT : Hash_Table_Type renames Target.HT;
1318 Src_HT : Hash_Table_Type renames Source.HT'Unrestricted_Access.all;
1320 -- Per AI05-0022, the container implementation is required to detect
1321 -- element tampering by a generic actual subprogram.
1323 TB : Natural renames Tgt_HT.Busy;
1324 TL : Natural renames Tgt_HT.Lock;
1326 SB : Natural renames Src_HT.Busy;
1327 SL : Natural renames Src_HT.Lock;
1330 if Target'Address = Source'Address then
1336 raise Program_Error with
1337 "attempt to tamper with cursors (set is busy)";
1341 N : constant Count_Type := Target.Length + Source.Length;
1343 if N > HT_Ops.Capacity (Tgt_HT) then
1344 HT_Ops.Reserve_Capacity (Tgt_HT, N);
1348 if Target.Length = 0 then
1349 Iterate_Source_When_Empty_Target : declare
1350 procedure Process (Src_Node : Node_Access);
1352 procedure Iterate is
1353 new HT_Ops.Generic_Iteration (Process);
1359 procedure Process (Src_Node : Node_Access) is
1360 E : Element_Type renames Src_Node.Element;
1361 B : Buckets_Type renames Tgt_HT.Buckets.all;
1362 J : constant Hash_Type := Hash (E) mod B'Length;
1363 N : Count_Type renames Tgt_HT.Length;
1366 B (J) := new Node_Type'(E
, B
(J
));
1370 -- Start of processing for Iterate_Source_When_Empty_Target
1396 end Iterate_Source_When_Empty_Target
;
1399 Iterate_Source
: declare
1400 procedure Process
(Src_Node
: Node_Access
);
1402 procedure Iterate
is
1403 new HT_Ops
.Generic_Iteration
(Process
);
1409 procedure Process
(Src_Node
: Node_Access
) is
1410 E
: Element_Type
renames Src_Node
.Element
;
1411 B
: Buckets_Type
renames Tgt_HT
.Buckets
.all;
1412 J
: constant Hash_Type
:= Hash
(E
) mod B
'Length;
1413 N
: Count_Type
renames Tgt_HT
.Length
;
1416 if B
(J
) = null then
1417 B
(J
) := new Node_Type
'(E, null);
1420 elsif Equivalent_Elements (E, B (J).Element) then
1422 X : Node_Access := B (J);
1424 B (J) := B (J).Next;
1431 Prev : Node_Access := B (J);
1432 Curr : Node_Access := Prev.Next;
1435 while Curr /= null loop
1436 if Equivalent_Elements (E, Curr.Element) then
1437 Prev.Next := Curr.Next;
1447 B (J) := new Node_Type'(E
, B
(J
));
1453 -- Start of processing for Iterate_Source
1481 end Symmetric_Difference
;
1483 function Symmetric_Difference
(Left
, Right
: Set
) return Set
is
1484 Left_HT
: Hash_Table_Type
renames Left
'Unrestricted_Access.HT
;
1485 Right_HT
: Hash_Table_Type
renames Right
'Unrestricted_Access.HT
;
1486 Buckets
: HT_Types
.Buckets_Access
;
1487 Length
: Count_Type
;
1490 if Left
'Address = Right
'Address then
1494 if Right
.Length
= 0 then
1498 if Left
.Length
= 0 then
1503 Size
: constant Hash_Type
:=
1504 Prime_Numbers
.To_Prime
(Left
.Length
+ Right
.Length
);
1506 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
1511 Iterate_Left
: declare
1512 procedure Process
(L_Node
: Node_Access
);
1514 procedure Iterate
is
1515 new HT_Ops
.Generic_Iteration
(Process
);
1521 procedure Process
(L_Node
: Node_Access
) is
1523 if not Is_In
(Right_HT
, L_Node
) then
1525 E
: Element_Type
renames L_Node
.Element
;
1527 -- Per AI05-0022, the container implementation is required
1528 -- to detect element tampering by a generic actual
1529 -- subprogram, hence the use of Checked_Index instead of a
1530 -- simple invocation of generic formal Hash.
1532 J
: constant Hash_Type
:=
1533 HT_Ops
.Checked_Index
(Left_HT
, Buckets
.all, L_Node
);
1536 Buckets
(J
) := new Node_Type
'(E, Buckets (J));
1537 Length := Length + 1;
1542 -- Start of processing for Iterate_Left
1549 HT_Ops.Free_Hash_Table (Buckets);
1553 Iterate_Right : declare
1554 procedure Process (R_Node : Node_Access);
1556 procedure Iterate is
1557 new HT_Ops.Generic_Iteration (Process);
1563 procedure Process (R_Node : Node_Access) is
1565 if not Is_In (Left_HT, R_Node) then
1567 E : Element_Type renames R_Node.Element;
1569 -- Per AI05-0022, the container implementation is required
1570 -- to detect element tampering by a generic actual
1571 -- subprogram, hence the use of Checked_Index instead of a
1572 -- simple invocation of generic formal Hash.
1574 J : constant Hash_Type :=
1575 HT_Ops.Checked_Index (Right_HT, Buckets.all, R_Node);
1578 Buckets (J) := new Node_Type'(E
, Buckets
(J
));
1579 Length
:= Length
+ 1;
1584 -- Start of processing for Iterate_Right
1591 HT_Ops
.Free_Hash_Table
(Buckets
);
1595 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
1596 end Symmetric_Difference
;
1602 function To_Set
(New_Item
: Element_Type
) return Set
is
1603 HT
: Hash_Table_Type
;
1607 pragma Unreferenced
(Node
, Inserted
);
1610 Insert
(HT
, New_Item
, Node
, Inserted
);
1611 return Set
'(Controlled with HT);
1619 (Target : in out Set;
1622 procedure Process (Src_Node : Node_Access);
1624 procedure Iterate is
1625 new HT_Ops.Generic_Iteration (Process);
1631 procedure Process (Src_Node : Node_Access) is
1632 function New_Node (Next : Node_Access) return Node_Access;
1633 pragma Inline (New_Node);
1636 new Element_Keys.Generic_Conditional_Insert (New_Node);
1642 function New_Node (Next : Node_Access) return Node_Access is
1643 Node : constant Node_Access :=
1644 new Node_Type'(Src_Node
.Element
, Next
);
1649 Tgt_Node
: Node_Access
;
1651 pragma Unreferenced
(Tgt_Node
, Success
);
1653 -- Start of processing for Process
1656 Insert
(Target
.HT
, Src_Node
.Element
, Tgt_Node
, Success
);
1659 -- Start of processing for Union
1662 if Target
'Address = Source
'Address then
1666 if Target
.HT
.Busy
> 0 then
1667 raise Program_Error
with
1668 "attempt to tamper with cursors (set is busy)";
1672 N
: constant Count_Type
:= Target
.Length
+ Source
.Length
;
1674 if N
> HT_Ops
.Capacity
(Target
.HT
) then
1675 HT_Ops
.Reserve_Capacity
(Target
.HT
, N
);
1679 Iterate
(Source
.HT
);
1682 function Union
(Left
, Right
: Set
) return Set
is
1683 Left_HT
: Hash_Table_Type
renames Left
.HT
'Unrestricted_Access.all;
1684 Right_HT
: Hash_Table_Type
renames Right
.HT
'Unrestricted_Access.all;
1685 Buckets
: HT_Types
.Buckets_Access
;
1686 Length
: Count_Type
;
1689 if Left
'Address = Right
'Address then
1693 if Right
.Length
= 0 then
1697 if Left
.Length
= 0 then
1702 Size
: constant Hash_Type
:=
1703 Prime_Numbers
.To_Prime
(Left
.Length
+ Right
.Length
);
1705 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
1708 Iterate_Left
: declare
1709 procedure Process
(L_Node
: Node_Access
);
1711 procedure Iterate
is
1712 new HT_Ops
.Generic_Iteration
(Process
);
1718 procedure Process
(L_Node
: Node_Access
) is
1719 J
: constant Hash_Type
:=
1720 Hash
(L_Node
.Element
) mod Buckets
'Length;
1723 Buckets
(J
) := new Node_Type
'(L_Node.Element, Buckets (J));
1726 -- Per AI05-0022, the container implementation is required to detect
1727 -- element tampering by a generic actual subprogram, hence the use of
1728 -- Checked_Index instead of a simple invocation of generic formal
1731 B : Integer renames Left_HT.Busy;
1732 L : Integer renames Left_HT.Lock;
1734 -- Start of processing for Iterate_Left
1750 HT_Ops.Free_Hash_Table (Buckets);
1754 Length := Left.Length;
1756 Iterate_Right : declare
1757 procedure Process (Src_Node : Node_Access);
1759 procedure Iterate is
1760 new HT_Ops.Generic_Iteration (Process);
1766 procedure Process (Src_Node : Node_Access) is
1767 J : constant Hash_Type :=
1768 Hash (Src_Node.Element) mod Buckets'Length;
1770 Tgt_Node : Node_Access := Buckets (J);
1773 while Tgt_Node /= null loop
1774 if Equivalent_Elements (Src_Node.Element, Tgt_Node.Element) then
1778 Tgt_Node := Next (Tgt_Node);
1781 Buckets (J) := new Node_Type'(Src_Node
.Element
, Buckets
(J
));
1782 Length
:= Length
+ 1;
1785 -- Per AI05-0022, the container implementation is required to detect
1786 -- element tampering by a generic actual subprogram, hence the use of
1787 -- Checked_Index instead of a simple invocation of generic formal
1790 LB
: Integer renames Left_HT
.Busy
;
1791 LL
: Integer renames Left_HT
.Lock
;
1793 RB
: Integer renames Right_HT
.Busy
;
1794 RL
: Integer renames Right_HT
.Lock
;
1796 -- Start of processing for Iterate_Right
1821 HT_Ops
.Free_Hash_Table
(Buckets
);
1825 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
1832 function Vet
(Position
: Cursor
) return Boolean is
1834 if Position
.Node
= null then
1835 return Position
.Container
= null;
1838 if Position
.Container
= null then
1842 if Position
.Node
.Next
= Position
.Node
then
1847 HT
: Hash_Table_Type
renames Position
.Container
.HT
;
1851 if HT
.Length
= 0 then
1855 if HT
.Buckets
= null
1856 or else HT
.Buckets
'Length = 0
1861 X
:= HT
.Buckets
(Element_Keys
.Checked_Index
1863 Position
.Node
.Element
));
1865 for J
in 1 .. HT
.Length
loop
1866 if X
= Position
.Node
then
1874 if X
= X
.Next
then -- to prevent unnecessary looping
1890 (Stream
: not null access Root_Stream_Type
'Class;
1894 Write_Nodes
(Stream
, Container
.HT
);
1898 (Stream
: not null access Root_Stream_Type
'Class;
1902 raise Program_Error
with "attempt to stream set cursor";
1906 (Stream
: not null access Root_Stream_Type
'Class;
1907 Item
: Constant_Reference_Type
)
1910 raise Program_Error
with "attempt to stream reference";
1917 procedure Write_Node
1918 (Stream
: not null access Root_Stream_Type
'Class;
1922 Element_Type
'Write (Stream
, Node
.Element
);
1925 package body Generic_Keys
is
1927 -----------------------
1928 -- Local Subprograms --
1929 -----------------------
1935 procedure Adjust
(Control
: in out Reference_Control_Type
) is
1937 if Control
.Container
/= null then
1939 HT
: Hash_Table_Type
renames Control
.Container
.all.HT
;
1940 B
: Natural renames HT
.Busy
;
1941 L
: Natural renames HT
.Lock
;
1949 function Equivalent_Key_Node
1951 Node
: Node_Access
) return Boolean;
1952 pragma Inline
(Equivalent_Key_Node
);
1954 --------------------------
1955 -- Local Instantiations --
1956 --------------------------
1959 new Hash_Tables
.Generic_Keys
1960 (HT_Types
=> HT_Types
,
1962 Set_Next
=> Set_Next
,
1963 Key_Type
=> Key_Type
,
1965 Equivalent_Keys
=> Equivalent_Key_Node
);
1967 ------------------------
1968 -- Constant_Reference --
1969 ------------------------
1971 function Constant_Reference
1972 (Container
: aliased Set
;
1973 Key
: Key_Type
) return Constant_Reference_Type
1975 HT
: Hash_Table_Type
renames Container
'Unrestricted_Access.HT
;
1976 Node
: constant Node_Access
:= Key_Keys
.Find
(HT
, Key
);
1980 raise Constraint_Error
with "Key not in set";
1984 B
: Natural renames HT
.Busy
;
1985 L
: Natural renames HT
.Lock
;
1987 return R
: constant Constant_Reference_Type
:=
1988 (Element
=> Node
.Element
'Access,
1989 Control
=> (Controlled
with Container
'Unrestricted_Access))
1995 end Constant_Reference
;
2003 Key
: Key_Type
) return Boolean
2006 return Find
(Container
, Key
) /= No_Element
;
2014 (Container
: in out Set
;
2020 Key_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Key
, X
);
2023 raise Constraint_Error
with "attempt to delete key not in set";
2035 Key
: Key_Type
) return Element_Type
2037 HT
: Hash_Table_Type
renames Container
'Unrestricted_Access.HT
;
2038 Node
: constant Node_Access
:= Key_Keys
.Find
(HT
, Key
);
2042 raise Constraint_Error
with "key not in set";
2045 return Node
.Element
;
2048 -------------------------
2049 -- Equivalent_Key_Node --
2050 -------------------------
2052 function Equivalent_Key_Node
2054 Node
: Node_Access
) return Boolean
2057 return Equivalent_Keys
(Key
, Generic_Keys
.Key
(Node
.Element
));
2058 end Equivalent_Key_Node
;
2065 (Container
: in out Set
;
2070 Key_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Key
, X
);
2078 procedure Finalize
(Control
: in out Reference_Control_Type
) is
2080 if Control
.Container
/= null then
2082 HT
: Hash_Table_Type
renames Control
.Container
.all.HT
;
2083 B
: Natural renames HT
.Busy
;
2084 L
: Natural renames HT
.Lock
;
2090 if Hash
(Key
(Element
(Control
.Old_Pos
))) /= Control
.Old_Hash
2092 HT_Ops
.Delete_Node_At_Index
2093 (Control
.Container
.HT
, Control
.Index
, Control
.Old_Pos
.Node
);
2094 raise Program_Error
with "key not preserved in reference";
2097 Control
.Container
:= null;
2107 Key
: Key_Type
) return Cursor
2109 HT
: Hash_Table_Type
renames Container
'Unrestricted_Access.HT
;
2110 Node
: constant Node_Access
:= Key_Keys
.Find
(HT
, Key
);
2115 return Cursor
'(Container'Unrestricted_Access, Node);
2123 function Key (Position : Cursor) return Key_Type is
2125 if Position.Node = null then
2126 raise Constraint_Error with
2127 "Position cursor equals No_Element";
2130 pragma Assert (Vet (Position), "bad cursor in function Key");
2132 return Key (Position.Node.Element);
2140 (Stream : not null access Root_Stream_Type'Class;
2141 Item : out Reference_Type)
2144 raise Program_Error with "attempt to stream reference";
2147 ------------------------------
2148 -- Reference_Preserving_Key --
2149 ------------------------------
2151 function Reference_Preserving_Key
2152 (Container : aliased in out Set;
2153 Position : Cursor) return Reference_Type
2156 if Position.Container = null then
2157 raise Constraint_Error with "Position cursor has no element";
2160 if Position.Container /= Container'Unrestricted_Access then
2161 raise Program_Error with
2162 "Position cursor designates wrong container";
2167 "bad cursor in function Reference_Preserving_Key");
2170 HT : Hash_Table_Type renames Position.Container.all.HT;
2171 B : Natural renames HT.Busy;
2172 L : Natural renames HT.Lock;
2174 return R : constant Reference_Type :=
2175 (Element => Position.Node.Element'Access,
2178 Container'Unrestricted_Access,
2179 Index => HT_Ops.Index (HT, Position.Node),
2180 Old_Pos => Position,
2181 Old_Hash => Hash (Key (Position))))
2187 end Reference_Preserving_Key;
2189 function Reference_Preserving_Key
2190 (Container : aliased in out Set;
2191 Key : Key_Type) return Reference_Type
2193 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
2197 raise Constraint_Error with "key not in set";
2201 HT : Hash_Table_Type renames Container.HT;
2202 B : Natural renames HT.Busy;
2203 L : Natural renames HT.Lock;
2204 P : constant Cursor := Find (Container, Key);
2206 return R : constant Reference_Type :=
2207 (Element => Node.Element'Access,
2210 Container'Unrestricted_Access,
2211 Index => HT_Ops.Index (HT, P.Node),
2213 Old_Hash => Hash (Key)))
2219 end Reference_Preserving_Key;
2226 (Container : in out Set;
2228 New_Item : Element_Type)
2230 Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
2234 raise Constraint_Error with
2235 "attempt to replace key not in set";
2238 Replace_Element (Container.HT, Node, New_Item);
2241 -----------------------------------
2242 -- Update_Element_Preserving_Key --
2243 -----------------------------------
2245 procedure Update_Element_Preserving_Key
2246 (Container : in out Set;
2248 Process : not null access
2249 procedure (Element : in out Element_Type))
2251 HT : Hash_Table_Type renames Container.HT;
2255 if Position.Node = null then
2256 raise Constraint_Error with
2257 "Position cursor equals No_Element";
2260 if Position.Container /= Container'Unrestricted_Access then
2261 raise Program_Error with
2262 "Position cursor designates wrong set";
2265 if HT.Buckets = null
2266 or else HT.Buckets'Length = 0
2267 or else HT.Length = 0
2268 or else Position.Node.Next = Position.Node
2270 raise Program_Error with "Position cursor is bad (set is empty)";
2275 "bad cursor in Update_Element_Preserving_Key");
2277 -- Per AI05-0022, the container implementation is required to detect
2278 -- element tampering by a generic actual subprogram.
2281 E : Element_Type renames Position.Node.Element;
2282 K : constant Key_Type := Key (E);
2284 B : Natural renames HT.Busy;
2285 L : Natural renames HT.Lock;
2294 Indx := HT_Ops.Index (HT, Position.Node);
2296 Eq := Equivalent_Keys (K, Key (E));
2312 if HT.Buckets (Indx) = Position.Node then
2313 HT.Buckets (Indx) := Position.Node.Next;
2317 Prev : Node_Access := HT.Buckets (Indx);
2320 while Prev.Next /= Position.Node loop
2324 raise Program_Error with
2325 "Position cursor is bad (node not found)";
2329 Prev.Next := Position.Node.Next;
2333 HT.Length := HT.Length - 1;
2336 X : Node_Access := Position.Node;
2342 raise Program_Error with "key was modified";
2343 end Update_Element_Preserving_Key;
2350 (Stream : not null access Root_Stream_Type'Class;
2351 Item : Reference_Type)
2354 raise Program_Error with "attempt to stream reference";
2359 end Ada.Containers.Hashed_Sets;