1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- A D A . C O N T A I N E R S . F O R M A L _ H A S H E D _ M A P S --
9 -- Copyright (C) 2010-2011, 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/>. --
26 ------------------------------------------------------------------------------
28 with Ada.Containers.Hash_Tables.Generic_Bounded_Operations;
29 pragma Elaborate_All (Ada.Containers.Hash_Tables.Generic_Bounded_Operations);
31 with Ada.Containers.Hash_Tables.Generic_Bounded_Keys;
32 pragma Elaborate_All (Ada.Containers.Hash_Tables.Generic_Bounded_Keys);
34 with Ada.Containers.Prime_Numbers; use Ada.Containers.Prime_Numbers;
36 with System; use type System.Address;
38 package body Ada.Containers.Formal_Hashed_Maps is
40 -----------------------
41 -- Local Subprograms --
42 -----------------------
44 -- All local subprograms require comments ???
46 function Equivalent_Keys
48 Node : Node_Type) return Boolean;
49 pragma Inline (Equivalent_Keys);
56 with procedure Set_Element (Node : in out Node_Type);
57 procedure Generic_Allocate
59 Node : out Count_Type);
61 function Hash_Node (Node : Node_Type) return Hash_Type;
62 pragma Inline (Hash_Node);
64 function Next (Node : Node_Type) return Count_Type;
67 procedure Set_Next (Node : in out Node_Type; Next : Count_Type);
68 pragma Inline (Set_Next);
70 function Vet (Container : Map; Position : Cursor) return Boolean;
72 --------------------------
73 -- Local Instantiations --
74 --------------------------
77 new Hash_Tables.Generic_Bounded_Operations
78 (HT_Types => HT_Types,
79 Hash_Node => Hash_Node,
81 Set_Next => Set_Next);
84 new Hash_Tables.Generic_Bounded_Keys
85 (HT_Types => HT_Types,
90 Equivalent_Keys => Equivalent_Keys);
96 function "=" (Left, Right : Map) return Boolean is
98 if Length (Left) /= Length (Right) then
102 if Length (Left) = 0 then
111 Node := Left.First.Node;
113 ENode := Find (Container => Right,
114 Key => Left.Nodes (Node).Key).Node;
117 Right.Nodes (ENode).Element /= Left.Nodes (Node).Element
122 Node := HT_Ops.Next (Left, Node);
133 procedure Assign (Target : in out Map; Source : Map) is
134 procedure Insert_Element (Source_Node : Count_Type);
135 pragma Inline (Insert_Element);
137 procedure Insert_Elements is
138 new HT_Ops.Generic_Iteration (Insert_Element);
144 procedure Insert_Element (Source_Node : Count_Type) is
145 N : Node_Type renames Source.Nodes (Source_Node);
147 Target.Insert (N.Key, N.Element);
150 -- Start of processing for Assign
153 if Target'Address = Source'Address then
157 if Target.Capacity < Length (Source) then
158 raise Constraint_Error with -- correct exception ???
159 "Source length exceeds Target capacity";
166 Insert_Elements (Source);
173 function Capacity (Container : Map) return Count_Type is
175 return Container.Nodes'Length;
182 procedure Clear (Container : in out Map) is
184 HT_Ops.Clear (Container);
191 function Contains (Container : Map; Key : Key_Type) return Boolean is
193 return Find (Container, Key) /= No_Element;
202 Capacity : Count_Type := 0) return Map
204 C : constant Count_Type :=
205 Count_Type'Max (Capacity, Source.Capacity);
208 Target : Map (C, Source.Modulus);
212 Target.Length := Source.Length;
213 Target.Free := Source.Free;
216 while H <= Source.Modulus loop
217 Target.Buckets (H) := Source.Buckets (H);
222 while N <= Source.Capacity loop
223 Target.Nodes (N) := Source.Nodes (N);
229 Free (Target, Cu.Node);
236 ---------------------
237 -- Default_Modulus --
238 ---------------------
240 function Default_Modulus (Capacity : Count_Type) return Hash_Type is
242 return To_Prime (Capacity);
249 procedure Delete (Container : in out Map; Key : Key_Type) is
253 Key_Ops.Delete_Key_Sans_Free (Container, Key, X);
256 raise Constraint_Error with "attempt to delete key not in map";
262 procedure Delete (Container : in out Map; Position : in out Cursor) is
264 if not Has_Element (Container, Position) then
265 raise Constraint_Error with
266 "Position cursor of Delete has no element";
269 if Container.Busy > 0 then
270 raise Program_Error with
271 "Delete attempted to tamper with elements (map is busy)";
274 pragma Assert (Vet (Container, Position), "bad cursor in Delete");
276 HT_Ops.Delete_Node_Sans_Free (Container, Position.Node);
278 Free (Container, Position.Node);
285 function Element (Container : Map; Key : Key_Type) return Element_Type is
286 Node : constant Count_Type := Find (Container, Key).Node;
290 raise Constraint_Error with
291 "no element available because key not in map";
294 return Container.Nodes (Node).Element;
297 function Element (Container : Map; Position : Cursor) return Element_Type is
299 if not Has_Element (Container, Position) then
300 raise Constraint_Error with "Position cursor equals No_Element";
303 pragma Assert (Vet (Container, Position),
304 "bad cursor in function Element");
306 return Container.Nodes (Position.Node).Element;
309 ---------------------
310 -- Equivalent_Keys --
311 ---------------------
313 function Equivalent_Keys
315 Node : Node_Type) return Boolean
318 return Equivalent_Keys (Key, Node.Key);
321 function Equivalent_Keys
325 CRight : Cursor) return Boolean
328 if not Has_Element (Left, CLeft) then
329 raise Constraint_Error with
330 "Left cursor of Equivalent_Keys has no element";
333 if not Has_Element (Right, CRight) then
334 raise Constraint_Error with
335 "Right cursor of Equivalent_Keys has no element";
338 pragma Assert (Vet (Left, CLeft),
339 "Left cursor of Equivalent_Keys is bad");
340 pragma Assert (Vet (Right, CRight),
341 "Right cursor of Equivalent_Keys is bad");
344 LN : Node_Type renames Left.Nodes (CLeft.Node);
345 RN : Node_Type renames Right.Nodes (CRight.Node);
347 return Equivalent_Keys (LN.Key, RN.Key);
351 function Equivalent_Keys
354 Right : Key_Type) return Boolean
357 if not Has_Element (Left, CLeft) then
358 raise Constraint_Error with
359 "Left cursor of Equivalent_Keys has no element";
362 pragma Assert (Vet (Left, CLeft),
363 "Left cursor in Equivalent_Keys is bad");
366 LN : Node_Type renames Left.Nodes (CLeft.Node);
368 return Equivalent_Keys (LN.Key, Right);
372 function Equivalent_Keys
375 CRight : Cursor) return Boolean
378 if Has_Element (Right, CRight) then
379 raise Constraint_Error with
380 "Right cursor of Equivalent_Keys has no element";
383 pragma Assert (Vet (Right, CRight),
384 "Right cursor of Equivalent_Keys is bad");
387 RN : Node_Type renames Right.Nodes (CRight.Node);
390 return Equivalent_Keys (Left, RN.Key);
398 procedure Exclude (Container : in out Map; Key : Key_Type) is
401 Key_Ops.Delete_Key_Sans_Free (Container, Key, X);
409 function Find (Container : Map; Key : Key_Type) return Cursor is
410 Node : constant Count_Type :=
411 Key_Ops.Find (Container, Key);
418 return (Node => Node);
425 function First (Container : Map) return Cursor is
426 Node : constant Count_Type := HT_Ops.First (Container);
433 return (Node => Node);
440 procedure Free (HT : in out Map; X : Count_Type) is
442 HT.Nodes (X).Has_Element := False;
446 ----------------------
447 -- Generic_Allocate --
448 ----------------------
450 procedure Generic_Allocate (HT : in out Map; Node : out Count_Type) is
452 procedure Allocate is
453 new HT_Ops.Generic_Allocate (Set_Element);
457 HT.Nodes (Node).Has_Element := True;
458 end Generic_Allocate;
464 function Has_Element (Container : Map; Position : Cursor) return Boolean is
466 if Position.Node = 0 or else
467 not Container.Nodes (Position.Node).Has_Element then
478 function Hash_Node (Node : Node_Type) return Hash_Type is
480 return Hash (Node.Key);
488 (Container : in out Map;
490 New_Item : Element_Type)
496 Insert (Container, Key, New_Item, Position, Inserted);
499 if Container.Lock > 0 then
500 raise Program_Error with
501 "Include attempted to tamper with cursors (map is locked)";
505 N : Node_Type renames Container.Nodes (Position.Node);
508 N.Element := New_Item;
518 (Container : in out Map;
520 Position : out Cursor;
521 Inserted : out Boolean)
523 procedure Assign_Key (Node : in out Node_Type);
524 pragma Inline (Assign_Key);
526 function New_Node return Count_Type;
527 pragma Inline (New_Node);
529 procedure Local_Insert is
530 new Key_Ops.Generic_Conditional_Insert (New_Node);
532 procedure Allocate is
533 new Generic_Allocate (Assign_Key);
539 procedure Assign_Key (Node : in out Node_Type) is
543 -- What is following commented out line doing here ???
544 -- Node.Element := New_Item;
551 function New_Node return Count_Type is
554 Allocate (Container, Result);
558 -- Start of processing for Insert
562 Local_Insert (Container, Key, Position.Node, Inserted);
566 (Container : in out Map;
568 New_Item : Element_Type;
569 Position : out Cursor;
570 Inserted : out Boolean)
572 procedure Assign_Key (Node : in out Node_Type);
573 pragma Inline (Assign_Key);
575 function New_Node return Count_Type;
576 pragma Inline (New_Node);
578 procedure Local_Insert is
579 new Key_Ops.Generic_Conditional_Insert (New_Node);
581 procedure Allocate is
582 new Generic_Allocate (Assign_Key);
588 procedure Assign_Key (Node : in out Node_Type) is
591 Node.Element := New_Item;
598 function New_Node return Count_Type is
601 Allocate (Container, Result);
605 -- Start of processing for Insert
608 Local_Insert (Container, Key, Position.Node, Inserted);
612 (Container : in out Map;
614 New_Item : Element_Type)
617 pragma Unreferenced (Position);
622 Insert (Container, Key, New_Item, Position, Inserted);
625 raise Constraint_Error with
626 "attempt to insert key already in map";
634 function Is_Empty (Container : Map) return Boolean is
636 return Length (Container) = 0;
646 access procedure (Container : Map; Position : Cursor))
648 procedure Process_Node (Node : Count_Type);
649 pragma Inline (Process_Node);
651 procedure Local_Iterate is new HT_Ops.Generic_Iteration (Process_Node);
657 procedure Process_Node (Node : Count_Type) is
659 Process (Container, (Node => Node));
662 B : Natural renames Container'Unrestricted_Access.Busy;
664 -- Start of processing for Iterate
670 Local_Iterate (Container);
684 function Key (Container : Map; Position : Cursor) return Key_Type is
686 if not Has_Element (Container, Position) then
687 raise Constraint_Error with
688 "Position cursor of function Key has no element";
691 pragma Assert (Vet (Container, Position), "bad cursor in function Key");
693 return Container.Nodes (Position.Node).Key;
700 function Left (Container : Map; Position : Cursor) return Map is
702 C : Map (Container.Capacity, Container.Modulus) :=
703 Copy (Container, Container.Capacity);
709 if Curs = No_Element then
713 if not Has_Element (Container, Curs) then
714 raise Constraint_Error;
717 while Curs.Node /= 0 loop
720 Curs := Next (Container, (Node => Node));
730 function Length (Container : Map) return Count_Type is
732 return Container.Length;
740 (Target : in out Map;
743 NN : HT_Types.Nodes_Type renames Source.Nodes;
747 if Target'Address = Source'Address then
751 if Target.Capacity < Length (Source) then
752 raise Constraint_Error with -- ???
753 "Source length exceeds Target capacity";
756 if Source.Busy > 0 then
757 raise Program_Error with
758 "attempt to tamper with cursors of Source (list is busy)";
763 if Source.Length = 0 then
767 X := HT_Ops.First (Source);
769 Insert (Target, NN (X).Key, NN (X).Element); -- optimize???
771 Y := HT_Ops.Next (Source, X);
773 HT_Ops.Delete_Node_Sans_Free (Source, X);
784 function Next (Node : Node_Type) return Count_Type is
789 function Next (Container : Map; Position : Cursor) return Cursor is
791 if Position.Node = 0 then
795 if not Has_Element (Container, Position) then
796 raise Constraint_Error
797 with "Position has no element";
800 pragma Assert (Vet (Container, Position), "bad cursor in function Next");
803 Node : constant Count_Type := HT_Ops.Next (Container, Position.Node);
810 return (Node => Node);
814 procedure Next (Container : Map; Position : in out Cursor) is
816 Position := Next (Container, Position);
823 function Overlap (Left, Right : Map) return Boolean is
824 Left_Node : Count_Type;
825 Left_Nodes : Nodes_Type renames Left.Nodes;
828 if Length (Right) = 0 or Length (Left) = 0 then
832 if Left'Address = Right'Address then
836 Left_Node := First (Left).Node;
837 while Left_Node /= 0 loop
839 N : Node_Type renames Left_Nodes (Left_Node);
840 E : Key_Type renames N.Key;
842 if Find (Right, E).Node /= 0 then
847 Left_Node := HT_Ops.Next (Left, Left_Node);
857 procedure Query_Element
858 (Container : in out Map;
860 Process : not null access
861 procedure (Key : Key_Type; Element : Element_Type))
864 if not Has_Element (Container, Position) then
865 raise Constraint_Error with
866 "Position cursor of Query_Element has no element";
869 pragma Assert (Vet (Container, Position), "bad cursor in Query_Element");
872 N : Node_Type renames Container.Nodes (Position.Node);
873 B : Natural renames Container.Busy;
874 L : Natural renames Container.Lock;
881 K : Key_Type renames N.Key;
882 E : Element_Type renames N.Element;
902 (Stream : not null access Root_Stream_Type'Class;
905 function Read_Node (Stream : not null access Root_Stream_Type'Class)
908 procedure Read_Nodes is
909 new HT_Ops.Generic_Read (Read_Node);
916 (Stream : not null access Root_Stream_Type'Class) return Count_Type
918 procedure Read_Element (Node : in out Node_Type);
919 pragma Inline (Read_Element);
921 procedure Allocate is
922 new Generic_Allocate (Read_Element);
924 procedure Read_Element (Node : in out Node_Type) is
926 Element_Type'Read (Stream, Node.Element);
931 -- Start of processing for Read_Node
934 Allocate (Container, Node);
938 -- Start of processing for Read
941 Read_Nodes (Stream, Container);
945 (Stream : not null access Root_Stream_Type'Class;
949 raise Program_Error with "attempt to stream set cursor";
957 (Container : in out Map;
959 New_Item : Element_Type)
961 Node : constant Count_Type := Key_Ops.Find (Container, Key);
965 raise Constraint_Error with
966 "attempt to replace key not in map";
969 if Container.Lock > 0 then
970 raise Program_Error with
971 "Replace attempted to tamper with cursors (map is locked)";
975 N : Node_Type renames Container.Nodes (Node);
978 N.Element := New_Item;
982 ---------------------
983 -- Replace_Element --
984 ---------------------
986 procedure Replace_Element
987 (Container : in out Map;
989 New_Item : Element_Type)
992 if not Has_Element (Container, Position) then
993 raise Constraint_Error with
994 "Position cursor of Replace_Element has no element";
997 if Container.Lock > 0 then
998 raise Program_Error with
999 "Replace_Element attempted to tamper with cursors (map is locked)";
1002 pragma Assert (Vet (Container, Position),
1003 "bad cursor in Replace_Element");
1005 Container.Nodes (Position.Node).Element := New_Item;
1006 end Replace_Element;
1008 ----------------------
1009 -- Reserve_Capacity --
1010 ----------------------
1012 procedure Reserve_Capacity
1013 (Container : in out Map;
1014 Capacity : Count_Type)
1017 if Capacity > Container.Capacity then
1018 raise Capacity_Error with "requested capacity is too large";
1020 end Reserve_Capacity;
1026 function Right (Container : Map; Position : Cursor) return Map is
1027 Curs : Cursor := First (Container);
1028 C : Map (Container.Capacity, Container.Modulus) :=
1029 Copy (Container, Container.Capacity);
1033 if Curs = No_Element then
1038 if Position /= No_Element and not Has_Element (Container, Position) then
1039 raise Constraint_Error;
1042 while Curs.Node /= Position.Node loop
1045 Curs := Next (Container, (Node => Node));
1055 procedure Set_Next (Node : in out Node_Type; Next : Count_Type) is
1064 function Strict_Equal (Left, Right : Map) return Boolean is
1065 CuL : Cursor := First (Left);
1066 CuR : Cursor := First (Right);
1069 if Length (Left) /= Length (Right) then
1073 while CuL.Node /= 0 or CuR.Node /= 0 loop
1074 if CuL.Node /= CuR.Node or else
1075 (Left.Nodes (CuL.Node).Element /=
1076 Right.Nodes (CuR.Node).Element or
1077 Left.Nodes (CuL.Node).Key /=
1078 Right.Nodes (CuR.Node).Key) then
1082 CuL := Next (Left, CuL);
1083 CuR := Next (Right, CuR);
1089 --------------------
1090 -- Update_Element --
1091 --------------------
1093 procedure Update_Element
1094 (Container : in out Map;
1096 Process : not null access procedure (Key : Key_Type;
1097 Element : in out Element_Type))
1100 if not Has_Element (Container, Position) then
1101 raise Constraint_Error with
1102 "Position cursor of Update_Element has no element";
1105 pragma Assert (Vet (Container, Position),
1106 "bad cursor in Update_Element");
1109 B : Natural renames Container.Busy;
1110 L : Natural renames Container.Lock;
1117 N : Node_Type renames Container.Nodes (Position.Node);
1118 K : Key_Type renames N.Key;
1119 E : Element_Type renames N.Element;
1139 function Vet (Container : Map; Position : Cursor) return Boolean is
1141 if Position.Node = 0 then
1149 if Container.Length = 0 then
1153 if Container.Capacity = 0 then
1157 if Container.Buckets'Length = 0 then
1161 if Position.Node > Container.Capacity then
1165 if Container.Nodes (Position.Node).Next = Position.Node then
1169 X := Container.Buckets
1170 (Key_Ops.Index (Container, Container.Nodes (Position.Node).Key));
1172 for J in 1 .. Container.Length loop
1173 if X = Position.Node then
1181 if X = Container.Nodes (X).Next then
1183 -- Prevent unnecessary looping
1188 X := Container.Nodes (X).Next;
1200 (Stream : not null access Root_Stream_Type'Class;
1203 procedure Write_Node
1204 (Stream : not null access Root_Stream_Type'Class;
1206 pragma Inline (Write_Node);
1208 procedure Write_Nodes is new HT_Ops.Generic_Write (Write_Node);
1214 procedure Write_Node
1215 (Stream : not null access Root_Stream_Type'Class;
1219 Key_Type'Write (Stream, Node.Key);
1220 Element_Type'Write (Stream, Node.Element);
1223 -- Start of processing for Write
1226 Write_Nodes (Stream, Container);
1230 (Stream : not null access Root_Stream_Type'Class;
1234 raise Program_Error with "attempt to stream map cursor";
1237 end Ada.Containers.Formal_Hashed_Maps;