1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- A D A . C O N T A I N E R S . B O U N D E D _ H A S H E D _ M A P S --
9 -- Copyright (C) 2004-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/>. --
27 -- This unit was originally developed by Matthew J Heaney. --
28 ------------------------------------------------------------------------------
30 with Ada.Containers.Hash_Tables.Generic_Bounded_Operations;
31 pragma Elaborate_All (Ada.Containers.Hash_Tables.Generic_Bounded_Operations);
33 with Ada.Containers.Hash_Tables.Generic_Bounded_Keys;
34 pragma Elaborate_All (Ada.Containers.Hash_Tables.Generic_Bounded_Keys);
36 with Ada.Containers.Prime_Numbers; use Ada.Containers.Prime_Numbers;
37 with System; use type System.Address;
39 package body Ada.Containers.Bounded_Hashed_Maps is
42 Map_Iterator_Interfaces.Forward_Iterator with record
43 Container : Map_Access;
47 overriding function First (Object : Iterator) return Cursor;
49 overriding function Next
51 Position : Cursor) return Cursor;
53 -----------------------
54 -- Local Subprograms --
55 -----------------------
57 function Equivalent_Key_Node
59 Node : Node_Type) return Boolean;
60 pragma Inline (Equivalent_Key_Node);
62 function Hash_Node (Node : Node_Type) return Hash_Type;
63 pragma Inline (Hash_Node);
65 function Next (Node : Node_Type) return Count_Type;
68 procedure Set_Next (Node : in out Node_Type; Next : Count_Type);
69 pragma Inline (Set_Next);
71 function Vet (Position : Cursor) return Boolean;
73 --------------------------
74 -- Local Instantiations --
75 --------------------------
77 package HT_Ops is new Hash_Tables.Generic_Bounded_Operations
78 (HT_Types => HT_Types,
79 Hash_Node => Hash_Node,
81 Set_Next => Set_Next);
83 package Key_Ops is new Hash_Tables.Generic_Bounded_Keys
84 (HT_Types => HT_Types,
89 Equivalent_Keys => Equivalent_Key_Node);
95 function "=" (Left, Right : Map) return Boolean is
96 function Find_Equal_Key
97 (R_HT : Hash_Table_Type'Class;
98 L_Node : Node_Type) return Boolean;
100 function Is_Equal is new HT_Ops.Generic_Equal (Find_Equal_Key);
106 function Find_Equal_Key
107 (R_HT : Hash_Table_Type'Class;
108 L_Node : Node_Type) return Boolean
110 R_Index : constant Hash_Type := Key_Ops.Index (R_HT, L_Node.Key);
111 R_Node : Count_Type := R_HT.Buckets (R_Index);
114 while R_Node /= 0 loop
115 if Equivalent_Keys (L_Node.Key, R_HT.Nodes (R_Node).Key) then
116 return L_Node.Element = R_HT.Nodes (R_Node).Element;
119 R_Node := R_HT.Nodes (R_Node).Next;
125 -- Start of processing for "="
128 return Is_Equal (Left, Right);
135 procedure Assign (Target : in out Map; Source : Map) is
136 procedure Insert_Element (Source_Node : Count_Type);
138 procedure Insert_Elements is
139 new HT_Ops.Generic_Iteration (Insert_Element);
145 procedure Insert_Element (Source_Node : Count_Type) is
146 N : Node_Type renames Source.Nodes (Source_Node);
151 Insert (Target, N.Key, N.Element, C, B);
155 -- Start of processing for Assign
158 if Target'Address = Source'Address then
162 if Target.Capacity < Source.Length then
164 with "Target capacity is less than Source length";
167 HT_Ops.Clear (Target);
168 Insert_Elements (Source);
175 function Capacity (Container : Map) return Count_Type is
177 return Container.Capacity;
184 procedure Clear (Container : in out Map) is
186 HT_Ops.Clear (Container);
193 function Contains (Container : Map; Key : Key_Type) return Boolean is
195 return Find (Container, Key) /= No_Element;
204 Capacity : Count_Type := 0;
205 Modulus : Hash_Type := 0) return Map
214 elsif Capacity >= Source.Length then
218 raise Capacity_Error with "Capacity value too small";
222 M := Default_Modulus (C);
227 return Target : Map (Capacity => C, Modulus => M) do
228 Assign (Target => Target, Source => Source);
232 ---------------------
233 -- Default_Modulus --
234 ---------------------
236 function Default_Modulus (Capacity : Count_Type) return Hash_Type is
238 return To_Prime (Capacity);
245 procedure Delete (Container : in out Map; Key : Key_Type) is
249 Key_Ops.Delete_Key_Sans_Free (Container, Key, X);
252 raise Constraint_Error with "attempt to delete key not in map";
255 HT_Ops.Free (Container, X);
258 procedure Delete (Container : in out Map; Position : in out Cursor) is
260 if Position.Node = 0 then
261 raise Constraint_Error with
262 "Position cursor of Delete equals No_Element";
265 if Position.Container /= Container'Unrestricted_Access then
266 raise Program_Error with
267 "Position cursor of Delete designates wrong map";
270 if Container.Busy > 0 then
271 raise Program_Error with
272 "Delete attempted to tamper with cursors (map is busy)";
275 pragma Assert (Vet (Position), "bad cursor in Delete");
277 HT_Ops.Delete_Node_Sans_Free (Container, Position.Node);
278 HT_Ops.Free (Container, Position.Node);
280 Position := No_Element;
287 function Element (Container : Map; Key : Key_Type) return Element_Type is
288 Node : constant Count_Type := Key_Ops.Find (Container, Key);
292 raise Constraint_Error with
293 "no element available because key not in map";
296 return Container.Nodes (Node).Element;
299 function Element (Position : Cursor) return Element_Type is
301 if Position.Node = 0 then
302 raise Constraint_Error with
303 "Position cursor of function Element equals No_Element";
306 pragma Assert (Vet (Position), "bad cursor in function Element");
308 return Position.Container.Nodes (Position.Node).Element;
311 -------------------------
312 -- Equivalent_Key_Node --
313 -------------------------
315 function Equivalent_Key_Node
317 Node : Node_Type) return Boolean is
319 return Equivalent_Keys (Key, Node.Key);
320 end Equivalent_Key_Node;
322 ---------------------
323 -- Equivalent_Keys --
324 ---------------------
326 function Equivalent_Keys (Left, Right : Cursor)
329 if Left.Node = 0 then
330 raise Constraint_Error with
331 "Left cursor of Equivalent_Keys equals No_Element";
334 if Right.Node = 0 then
335 raise Constraint_Error with
336 "Right cursor of Equivalent_Keys equals No_Element";
339 pragma Assert (Vet (Left), "Left cursor of Equivalent_Keys is bad");
340 pragma Assert (Vet (Right), "Right cursor of Equivalent_Keys is bad");
343 LN : Node_Type renames Left.Container.Nodes (Left.Node);
344 RN : Node_Type renames Right.Container.Nodes (Right.Node);
347 return Equivalent_Keys (LN.Key, RN.Key);
351 function Equivalent_Keys (Left : Cursor; Right : Key_Type) return Boolean is
353 if Left.Node = 0 then
354 raise Constraint_Error with
355 "Left cursor of Equivalent_Keys equals No_Element";
358 pragma Assert (Vet (Left), "Left cursor in Equivalent_Keys is bad");
361 LN : Node_Type renames Left.Container.Nodes (Left.Node);
364 return Equivalent_Keys (LN.Key, Right);
368 function Equivalent_Keys (Left : Key_Type; Right : Cursor) return Boolean is
370 if Right.Node = 0 then
371 raise Constraint_Error with
372 "Right cursor of Equivalent_Keys equals No_Element";
375 pragma Assert (Vet (Right), "Right cursor of Equivalent_Keys is bad");
378 RN : Node_Type renames Right.Container.Nodes (Right.Node);
381 return Equivalent_Keys (Left, RN.Key);
389 procedure Exclude (Container : in out Map; Key : Key_Type) is
392 Key_Ops.Delete_Key_Sans_Free (Container, Key, X);
393 HT_Ops.Free (Container, X);
400 function Find (Container : Map; Key : Key_Type) return Cursor is
401 Node : constant Count_Type := Key_Ops.Find (Container, Key);
408 return Cursor'(Container'Unrestricted_Access, Node);
415 function First (Container : Map) return Cursor is
416 Node : constant Count_Type := HT_Ops.First (Container);
423 return Cursor'(Container'Unrestricted_Access, Node);
426 function First (Object : Iterator) return Cursor is
427 M : constant Map_Access := Object.Container;
428 N : constant Count_Type := HT_Ops.First (M.all);
433 return Cursor'(Object.Container.all'Unchecked_Access, N);
441 function Has_Element (Position : Cursor) return Boolean is
443 pragma Assert (Vet (Position), "bad cursor in Has_Element");
444 return Position.Node /= 0;
451 function Hash_Node (Node : Node_Type) return Hash_Type is
453 return Hash (Node.Key);
461 (Container : in out Map;
463 New_Item : Element_Type)
469 Insert (Container, Key, New_Item, Position, Inserted);
472 if Container.Lock > 0 then
473 raise Program_Error with
474 "Include attempted to tamper with elements (map is locked)";
478 N : Node_Type renames Container.Nodes (Position.Node);
482 N.Element := New_Item;
492 (Container : in out Map;
494 Position : out Cursor;
495 Inserted : out Boolean)
497 procedure Assign_Key (Node : in out Node_Type);
498 pragma Inline (Assign_Key);
500 function New_Node return Count_Type;
501 pragma Inline (New_Node);
503 procedure Local_Insert is
504 new Key_Ops.Generic_Conditional_Insert (New_Node);
506 procedure Allocate is
507 new HT_Ops.Generic_Allocate (Assign_Key);
513 procedure Assign_Key (Node : in out Node_Type) is
516 -- Node.Element := New_Item;
523 function New_Node return Count_Type is
526 Allocate (Container, Result);
530 -- Start of processing for Insert
534 -- if HT_Ops.Capacity (HT) = 0 then
535 -- HT_Ops.Reserve_Capacity (HT, 1);
538 Local_Insert (Container, Key, Position.Node, Inserted);
542 -- and then HT.Length > HT_Ops.Capacity (HT)
544 -- HT_Ops.Reserve_Capacity (HT, HT.Length);
547 Position.Container := Container'Unchecked_Access;
551 (Container : in out Map;
553 New_Item : Element_Type;
554 Position : out Cursor;
555 Inserted : out Boolean)
557 procedure Assign_Key (Node : in out Node_Type);
558 pragma Inline (Assign_Key);
560 function New_Node return Count_Type;
561 pragma Inline (New_Node);
563 procedure Local_Insert is
564 new Key_Ops.Generic_Conditional_Insert (New_Node);
566 procedure Allocate is
567 new HT_Ops.Generic_Allocate (Assign_Key);
573 procedure Assign_Key (Node : in out Node_Type) is
576 Node.Element := New_Item;
583 function New_Node return Count_Type is
586 Allocate (Container, Result);
590 -- Start of processing for Insert
594 -- if HT_Ops.Capacity (HT) = 0 then
595 -- HT_Ops.Reserve_Capacity (HT, 1);
598 Local_Insert (Container, Key, Position.Node, Inserted);
602 -- and then HT.Length > HT_Ops.Capacity (HT)
604 -- HT_Ops.Reserve_Capacity (HT, HT.Length);
607 Position.Container := Container'Unchecked_Access;
611 (Container : in out Map;
613 New_Item : Element_Type)
616 pragma Unreferenced (Position);
621 Insert (Container, Key, New_Item, Position, Inserted);
624 raise Constraint_Error with
625 "attempt to insert key already in map";
633 function Is_Empty (Container : Map) return Boolean is
635 return Container.Length = 0;
644 Process : not null access procedure (Position : Cursor))
646 procedure Process_Node (Node : Count_Type);
647 pragma Inline (Process_Node);
649 procedure Local_Iterate is new HT_Ops.Generic_Iteration (Process_Node);
655 procedure Process_Node (Node : Count_Type) is
657 Process (Cursor'(Container'Unrestricted_Access, Node));
660 B : Natural renames Container'Unrestricted_Access.Busy;
662 -- Start of processing for Iterate
668 Local_Iterate (Container);
679 (Container : Map) return Map_Iterator_Interfaces.Forward_Iterator'class
681 Node : constant Count_Type := HT_Ops.First (Container);
682 It : constant Iterator := (Container'Unrestricted_Access, Node);
691 function Key (Position : Cursor) return Key_Type is
693 if Position.Node = 0 then
694 raise Constraint_Error with
695 "Position cursor of function Key equals No_Element";
698 pragma Assert (Vet (Position), "bad cursor in function Key");
700 return Position.Container.Nodes (Position.Node).Key;
707 function Length (Container : Map) return Count_Type is
709 return Container.Length;
717 (Target : in out Map;
721 if Target'Address = Source'Address then
725 if Source.Busy > 0 then
726 raise Program_Error with
727 "attempt to tamper with cursors (container is busy)";
730 Target.Assign (Source);
738 function Next (Node : Node_Type) return Count_Type is
743 function Next (Position : Cursor) return Cursor is
745 if Position.Node = 0 then
749 pragma Assert (Vet (Position), "bad cursor in function Next");
752 M : Map renames Position.Container.all;
753 Node : constant Count_Type := HT_Ops.Next (M, Position.Node);
760 return Cursor'(Position.Container, Node);
764 procedure Next (Position : in out Cursor) is
766 Position := Next (Position);
771 Position : Cursor) return Cursor
774 if Position.Node = 0 then
777 return (Object.Container, Next (Position).Node);
785 procedure Query_Element
787 Process : not null access
788 procedure (Key : Key_Type; Element : Element_Type))
791 if Position.Node = 0 then
792 raise Constraint_Error with
793 "Position cursor of Query_Element equals No_Element";
796 pragma Assert (Vet (Position), "bad cursor in Query_Element");
799 M : Map renames Position.Container.all;
800 N : Node_Type renames M.Nodes (Position.Node);
801 B : Natural renames M.Busy;
802 L : Natural renames M.Lock;
811 Process (N.Key, N.Element);
829 (Stream : not null access Root_Stream_Type'Class;
833 (Stream : not null access Root_Stream_Type'Class) return Count_Type;
834 -- pragma Inline (Read_Node); ???
836 procedure Read_Nodes is new HT_Ops.Generic_Read (Read_Node);
843 (Stream : not null access Root_Stream_Type'Class) return Count_Type
845 procedure Read_Element (Node : in out Node_Type);
846 -- pragma Inline (Read_Element); ???
848 procedure Allocate is
849 new HT_Ops.Generic_Allocate (Read_Element);
851 procedure Read_Element (Node : in out Node_Type) is
853 Key_Type'Read (Stream, Node.Key);
854 Element_Type'Read (Stream, Node.Element);
859 -- Start of processing for Read_Node
862 Allocate (Container, Node);
866 -- Start of processing for Read
869 Read_Nodes (Stream, Container);
873 (Stream : not null access Root_Stream_Type'Class;
877 raise Program_Error with "attempt to stream map cursor";
881 (Stream : not null access Root_Stream_Type'Class;
882 Item : out Reference_Type)
885 raise Program_Error with "attempt to stream reference";
889 (Stream : not null access Root_Stream_Type'Class;
890 Item : out Constant_Reference_Type)
893 raise Program_Error with "attempt to stream reference";
900 function Constant_Reference (Container : Map; Key : Key_Type)
901 return Constant_Reference_Type is
903 return (Element => Container.Element (Key)'Unrestricted_Access);
904 end Constant_Reference;
906 function Reference (Container : Map; Key : Key_Type)
907 return Reference_Type is
909 return (Element => Container.Element (Key)'Unrestricted_Access);
917 (Container : in out Map;
919 New_Item : Element_Type)
921 Node : constant Count_Type := Key_Ops.Find (Container, Key);
925 raise Constraint_Error with
926 "attempt to replace key not in map";
929 if Container.Lock > 0 then
930 raise Program_Error with
931 "Replace attempted to tamper with elements (map is locked)";
935 N : Node_Type renames Container.Nodes (Node);
939 N.Element := New_Item;
943 ---------------------
944 -- Replace_Element --
945 ---------------------
947 procedure Replace_Element
948 (Container : in out Map;
950 New_Item : Element_Type)
953 if Position.Node = 0 then
954 raise Constraint_Error with
955 "Position cursor of Replace_Element equals No_Element";
958 if Position.Container /= Container'Unrestricted_Access then
959 raise Program_Error with
960 "Position cursor of Replace_Element designates wrong map";
963 if Position.Container.Lock > 0 then
964 raise Program_Error with
965 "Replace_Element attempted to tamper with elements (map is locked)";
968 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
970 Container.Nodes (Position.Node).Element := New_Item;
973 ----------------------
974 -- Reserve_Capacity --
975 ----------------------
977 procedure Reserve_Capacity
978 (Container : in out Map;
979 Capacity : Count_Type)
982 if Capacity > Container.Capacity then
983 raise Capacity_Error with "requested capacity is too large";
985 end Reserve_Capacity;
991 procedure Set_Next (Node : in out Node_Type; Next : Count_Type) is
1000 procedure Update_Element
1001 (Container : in out Map;
1003 Process : not null access procedure (Key : Key_Type;
1004 Element : in out Element_Type))
1007 if Position.Node = 0 then
1008 raise Constraint_Error with
1009 "Position cursor of Update_Element equals No_Element";
1012 if Position.Container /= Container'Unrestricted_Access then
1013 raise Program_Error with
1014 "Position cursor of Update_Element designates wrong map";
1017 pragma Assert (Vet (Position), "bad cursor in Update_Element");
1020 N : Node_Type renames Container.Nodes (Position.Node);
1021 B : Natural renames Container.Busy;
1022 L : Natural renames Container.Lock;
1029 Process (N.Key, N.Element);
1046 function Vet (Position : Cursor) return Boolean is
1048 if Position.Node = 0 then
1049 return Position.Container = null;
1052 if Position.Container = null then
1057 M : Map renames Position.Container.all;
1061 if M.Length = 0 then
1065 if M.Capacity = 0 then
1069 if M.Buckets'Length = 0 then
1073 if Position.Node > M.Capacity then
1077 if M.Nodes (Position.Node).Next = Position.Node then
1081 X := M.Buckets (Key_Ops.Index (M, M.Nodes (Position.Node).Key));
1083 for J in 1 .. M.Length loop
1084 if X = Position.Node then
1092 if X = M.Nodes (X).Next then -- to prevent unnecessary looping
1096 X := M.Nodes (X).Next;
1108 (Stream : not null access Root_Stream_Type'Class;
1111 procedure Write_Node
1112 (Stream : not null access Root_Stream_Type'Class;
1114 pragma Inline (Write_Node);
1116 procedure Write_Nodes is new HT_Ops.Generic_Write (Write_Node);
1122 procedure Write_Node
1123 (Stream : not null access Root_Stream_Type'Class;
1127 Key_Type'Write (Stream, Node.Key);
1128 Element_Type'Write (Stream, Node.Element);
1131 -- Start of processing for Write
1134 Write_Nodes (Stream, Container);
1138 (Stream : not null access Root_Stream_Type'Class;
1142 raise Program_Error with "attempt to stream map cursor";
1146 (Stream : not null access Root_Stream_Type'Class;
1147 Item : Reference_Type)
1150 raise Program_Error with "attempt to stream reference";
1154 (Stream : not null access Root_Stream_Type'Class;
1155 Item : Constant_Reference_Type)
1158 raise Program_Error with "attempt to stream reference";
1161 end Ada.Containers.Bounded_Hashed_Maps;