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 Ada.Finalization; use Ada.Finalization;
39 with System; use type System.Address;
41 package body Ada.Containers.Bounded_Hashed_Maps is
43 type Iterator is new Limited_Controlled and
44 Map_Iterator_Interfaces.Forward_Iterator with
46 Container : Map_Access;
49 overriding procedure Finalize (Object : in out Iterator);
51 overriding function First (Object : Iterator) return Cursor;
53 overriding function Next
55 Position : Cursor) return Cursor;
57 -----------------------
58 -- Local Subprograms --
59 -----------------------
61 function Equivalent_Key_Node
63 Node : Node_Type) return Boolean;
64 pragma Inline (Equivalent_Key_Node);
66 function Hash_Node (Node : Node_Type) return Hash_Type;
67 pragma Inline (Hash_Node);
69 function Next (Node : Node_Type) return Count_Type;
72 procedure Set_Next (Node : in out Node_Type; Next : Count_Type);
73 pragma Inline (Set_Next);
75 function Vet (Position : Cursor) return Boolean;
77 --------------------------
78 -- Local Instantiations --
79 --------------------------
81 package HT_Ops is new Hash_Tables.Generic_Bounded_Operations
82 (HT_Types => HT_Types,
83 Hash_Node => Hash_Node,
85 Set_Next => Set_Next);
87 package Key_Ops is new Hash_Tables.Generic_Bounded_Keys
88 (HT_Types => HT_Types,
93 Equivalent_Keys => Equivalent_Key_Node);
99 function "=" (Left, Right : Map) return Boolean is
100 function Find_Equal_Key
101 (R_HT : Hash_Table_Type'Class;
102 L_Node : Node_Type) return Boolean;
104 function Is_Equal is new HT_Ops.Generic_Equal (Find_Equal_Key);
110 function Find_Equal_Key
111 (R_HT : Hash_Table_Type'Class;
112 L_Node : Node_Type) return Boolean
114 R_Index : constant Hash_Type := Key_Ops.Index (R_HT, L_Node.Key);
115 R_Node : Count_Type := R_HT.Buckets (R_Index);
118 while R_Node /= 0 loop
119 if Equivalent_Keys (L_Node.Key, R_HT.Nodes (R_Node).Key) then
120 return L_Node.Element = R_HT.Nodes (R_Node).Element;
123 R_Node := R_HT.Nodes (R_Node).Next;
129 -- Start of processing for "="
132 return Is_Equal (Left, Right);
139 procedure Assign (Target : in out Map; Source : Map) is
140 procedure Insert_Element (Source_Node : Count_Type);
142 procedure Insert_Elements is
143 new HT_Ops.Generic_Iteration (Insert_Element);
149 procedure Insert_Element (Source_Node : Count_Type) is
150 N : Node_Type renames Source.Nodes (Source_Node);
155 Insert (Target, N.Key, N.Element, C, B);
159 -- Start of processing for Assign
162 if Target'Address = Source'Address then
166 if Target.Capacity < Source.Length then
168 with "Target capacity is less than Source length";
171 HT_Ops.Clear (Target);
172 Insert_Elements (Source);
179 function Capacity (Container : Map) return Count_Type is
181 return Container.Capacity;
188 procedure Clear (Container : in out Map) is
190 HT_Ops.Clear (Container);
197 function Contains (Container : Map; Key : Key_Type) return Boolean is
199 return Find (Container, Key) /= No_Element;
208 Capacity : Count_Type := 0;
209 Modulus : Hash_Type := 0) return Map
218 elsif Capacity >= Source.Length then
222 raise Capacity_Error with "Capacity value too small";
226 M := Default_Modulus (C);
231 return Target : Map (Capacity => C, Modulus => M) do
232 Assign (Target => Target, Source => Source);
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";
259 HT_Ops.Free (Container, X);
262 procedure Delete (Container : in out Map; Position : in out Cursor) is
264 if Position.Node = 0 then
265 raise Constraint_Error with
266 "Position cursor of Delete equals No_Element";
269 if Position.Container /= Container'Unrestricted_Access then
270 raise Program_Error with
271 "Position cursor of Delete designates wrong map";
274 if Container.Busy > 0 then
275 raise Program_Error with
276 "Delete attempted to tamper with cursors (map is busy)";
279 pragma Assert (Vet (Position), "bad cursor in Delete");
281 HT_Ops.Delete_Node_Sans_Free (Container, Position.Node);
282 HT_Ops.Free (Container, Position.Node);
284 Position := No_Element;
291 function Element (Container : Map; Key : Key_Type) return Element_Type is
292 Node : constant Count_Type := Key_Ops.Find (Container, Key);
296 raise Constraint_Error with
297 "no element available because key not in map";
300 return Container.Nodes (Node).Element;
303 function Element (Position : Cursor) return Element_Type is
305 if Position.Node = 0 then
306 raise Constraint_Error with
307 "Position cursor of function Element equals No_Element";
310 pragma Assert (Vet (Position), "bad cursor in function Element");
312 return Position.Container.Nodes (Position.Node).Element;
315 -------------------------
316 -- Equivalent_Key_Node --
317 -------------------------
319 function Equivalent_Key_Node
321 Node : Node_Type) return Boolean is
323 return Equivalent_Keys (Key, Node.Key);
324 end Equivalent_Key_Node;
326 ---------------------
327 -- Equivalent_Keys --
328 ---------------------
330 function Equivalent_Keys (Left, Right : Cursor)
333 if Left.Node = 0 then
334 raise Constraint_Error with
335 "Left cursor of Equivalent_Keys equals No_Element";
338 if Right.Node = 0 then
339 raise Constraint_Error with
340 "Right cursor of Equivalent_Keys equals No_Element";
343 pragma Assert (Vet (Left), "Left cursor of Equivalent_Keys is bad");
344 pragma Assert (Vet (Right), "Right cursor of Equivalent_Keys is bad");
347 LN : Node_Type renames Left.Container.Nodes (Left.Node);
348 RN : Node_Type renames Right.Container.Nodes (Right.Node);
351 return Equivalent_Keys (LN.Key, RN.Key);
355 function Equivalent_Keys (Left : Cursor; Right : Key_Type) return Boolean is
357 if Left.Node = 0 then
358 raise Constraint_Error with
359 "Left cursor of Equivalent_Keys equals No_Element";
362 pragma Assert (Vet (Left), "Left cursor in Equivalent_Keys is bad");
365 LN : Node_Type renames Left.Container.Nodes (Left.Node);
368 return Equivalent_Keys (LN.Key, Right);
372 function Equivalent_Keys (Left : Key_Type; Right : Cursor) return Boolean is
374 if Right.Node = 0 then
375 raise Constraint_Error with
376 "Right cursor of Equivalent_Keys equals No_Element";
379 pragma Assert (Vet (Right), "Right cursor of Equivalent_Keys is bad");
382 RN : Node_Type renames Right.Container.Nodes (Right.Node);
385 return Equivalent_Keys (Left, RN.Key);
393 procedure Exclude (Container : in out Map; Key : Key_Type) is
396 Key_Ops.Delete_Key_Sans_Free (Container, Key, X);
397 HT_Ops.Free (Container, X);
404 procedure Finalize (Object : in out Iterator) is
406 if Object.Container /= null then
408 B : Natural renames Object.Container.all.Busy;
419 function Find (Container : Map; Key : Key_Type) return Cursor is
420 Node : constant Count_Type := Key_Ops.Find (Container, Key);
425 return Cursor'(Container'Unrestricted_Access, Node);
433 function First (Container : Map) return Cursor is
434 Node : constant Count_Type := HT_Ops.First (Container);
439 return Cursor'(Container'Unrestricted_Access, Node);
443 function First (Object : Iterator) return Cursor is
445 return Object.Container.First;
452 function Has_Element (Position : Cursor) return Boolean is
454 pragma Assert (Vet (Position), "bad cursor in Has_Element");
455 return Position.Node /= 0;
462 function Hash_Node (Node : Node_Type) return Hash_Type is
464 return Hash (Node.Key);
472 (Container : in out Map;
474 New_Item : Element_Type)
480 Insert (Container, Key, New_Item, Position, Inserted);
483 if Container.Lock > 0 then
484 raise Program_Error with
485 "Include attempted to tamper with elements (map is locked)";
489 N : Node_Type renames Container.Nodes (Position.Node);
492 N.Element := New_Item;
502 (Container : in out Map;
504 Position : out Cursor;
505 Inserted : out Boolean)
507 procedure Assign_Key (Node : in out Node_Type);
508 pragma Inline (Assign_Key);
510 function New_Node return Count_Type;
511 pragma Inline (New_Node);
513 procedure Local_Insert is
514 new Key_Ops.Generic_Conditional_Insert (New_Node);
516 procedure Allocate is
517 new HT_Ops.Generic_Allocate (Assign_Key);
523 procedure Assign_Key (Node : in out Node_Type) is
527 -- Note that we do not also assign the element component of the node
528 -- here, because this version of Insert does not accept an element
531 -- Node.Element := New_Item;
532 -- What is this deleted code about???
539 function New_Node return Count_Type is
542 Allocate (Container, Result);
546 -- Start of processing for Insert
549 -- The buckets array length is specified by the user as a discriminant
550 -- of the container type, so it is possible for the buckets array to
551 -- have a length of zero. We must check for this case specifically, in
552 -- order to prevent divide-by-zero errors later, when we compute the
553 -- buckets array index value for a key, given its hash value.
555 if Container.Buckets'Length = 0 then
556 raise Capacity_Error with "No capacity for insertion";
559 Local_Insert (Container, Key, Position.Node, Inserted);
560 Position.Container := Container'Unchecked_Access;
564 (Container : in out Map;
566 New_Item : Element_Type;
567 Position : out Cursor;
568 Inserted : out Boolean)
570 procedure Assign_Key (Node : in out Node_Type);
571 pragma Inline (Assign_Key);
573 function New_Node return Count_Type;
574 pragma Inline (New_Node);
576 procedure Local_Insert is
577 new Key_Ops.Generic_Conditional_Insert (New_Node);
579 procedure Allocate is
580 new HT_Ops.Generic_Allocate (Assign_Key);
586 procedure Assign_Key (Node : in out Node_Type) is
589 Node.Element := New_Item;
596 function New_Node return Count_Type is
599 Allocate (Container, Result);
603 -- Start of processing for Insert
606 -- The buckets array length is specified by the user as a discriminant
607 -- of the container type, so it is possible for the buckets array to
608 -- have a length of zero. We must check for this case specifically, in
609 -- order to prevent divide-by-zero errors later, when we compute the
610 -- buckets array index value for a key, given its hash value.
612 if Container.Buckets'Length = 0 then
613 raise Capacity_Error with "No capacity for insertion";
616 Local_Insert (Container, Key, Position.Node, Inserted);
617 Position.Container := Container'Unchecked_Access;
621 (Container : in out Map;
623 New_Item : Element_Type)
626 pragma Unreferenced (Position);
631 Insert (Container, Key, New_Item, Position, Inserted);
634 raise Constraint_Error with
635 "attempt to insert key already in map";
643 function Is_Empty (Container : Map) return Boolean is
645 return Container.Length = 0;
654 Process : not null access procedure (Position : Cursor))
656 procedure Process_Node (Node : Count_Type);
657 pragma Inline (Process_Node);
659 procedure Local_Iterate is new HT_Ops.Generic_Iteration (Process_Node);
665 procedure Process_Node (Node : Count_Type) is
667 Process (Cursor'(Container'Unrestricted_Access, Node));
670 B : Natural renames Container'Unrestricted_Access.all.Busy;
672 -- Start of processing for Iterate
678 Local_Iterate (Container);
689 (Container : Map) return Map_Iterator_Interfaces.Forward_Iterator'Class
691 B : Natural renames Container'Unrestricted_Access.all.Busy;
694 return It : constant Iterator :=
695 (Limited_Controlled with
696 Container => Container'Unrestricted_Access)
706 function Key (Position : Cursor) return Key_Type is
708 if Position.Node = 0 then
709 raise Constraint_Error with
710 "Position cursor of function Key equals No_Element";
713 pragma Assert (Vet (Position), "bad cursor in function Key");
715 return Position.Container.Nodes (Position.Node).Key;
722 function Length (Container : Map) return Count_Type is
724 return Container.Length;
732 (Target : in out Map;
736 if Target'Address = Source'Address then
740 if Source.Busy > 0 then
741 raise Program_Error with
742 "attempt to tamper with cursors (container is busy)";
745 Target.Assign (Source);
753 function Next (Node : Node_Type) return Count_Type is
758 function Next (Position : Cursor) return Cursor is
760 if Position.Node = 0 then
764 pragma Assert (Vet (Position), "bad cursor in function Next");
767 M : Map renames Position.Container.all;
768 Node : constant Count_Type := HT_Ops.Next (M, Position.Node);
773 return Cursor'(Position.Container, Node);
778 procedure Next (Position : in out Cursor) is
780 Position := Next (Position);
785 Position : Cursor) return Cursor
788 if Position.Container = null then
792 if Position.Container /= Object.Container then
793 raise Program_Error with
794 "Position cursor of Next designates wrong map";
797 return Next (Position);
804 procedure Query_Element
806 Process : not null access
807 procedure (Key : Key_Type; Element : Element_Type))
810 if Position.Node = 0 then
811 raise Constraint_Error with
812 "Position cursor of Query_Element equals No_Element";
815 pragma Assert (Vet (Position), "bad cursor in Query_Element");
818 M : Map renames Position.Container.all;
819 N : Node_Type renames M.Nodes (Position.Node);
820 B : Natural renames M.Busy;
821 L : Natural renames M.Lock;
830 Process (N.Key, N.Element);
848 (Stream : not null access Root_Stream_Type'Class;
852 (Stream : not null access Root_Stream_Type'Class) return Count_Type;
853 -- pragma Inline (Read_Node); ???
855 procedure Read_Nodes is new HT_Ops.Generic_Read (Read_Node);
862 (Stream : not null access Root_Stream_Type'Class) return Count_Type
864 procedure Read_Element (Node : in out Node_Type);
865 -- pragma Inline (Read_Element); ???
867 procedure Allocate is
868 new HT_Ops.Generic_Allocate (Read_Element);
870 procedure Read_Element (Node : in out Node_Type) is
872 Key_Type'Read (Stream, Node.Key);
873 Element_Type'Read (Stream, Node.Element);
878 -- Start of processing for Read_Node
881 Allocate (Container, Node);
885 -- Start of processing for Read
888 Read_Nodes (Stream, Container);
892 (Stream : not null access Root_Stream_Type'Class;
896 raise Program_Error with "attempt to stream map cursor";
900 (Stream : not null access Root_Stream_Type'Class;
901 Item : out Reference_Type)
904 raise Program_Error with "attempt to stream reference";
908 (Stream : not null access Root_Stream_Type'Class;
909 Item : out Constant_Reference_Type)
912 raise Program_Error with "attempt to stream reference";
919 function Constant_Reference (Container : Map; Key : Key_Type)
920 return Constant_Reference_Type is
922 return (Element => Container.Element (Key)'Unrestricted_Access);
923 end Constant_Reference;
925 function Reference (Container : Map; Key : Key_Type)
926 return Reference_Type is
928 return (Element => Container.Element (Key)'Unrestricted_Access);
936 (Container : in out Map;
938 New_Item : Element_Type)
940 Node : constant Count_Type := Key_Ops.Find (Container, Key);
944 raise Constraint_Error with
945 "attempt to replace key not in map";
948 if Container.Lock > 0 then
949 raise Program_Error with
950 "Replace attempted to tamper with elements (map is locked)";
954 N : Node_Type renames Container.Nodes (Node);
958 N.Element := New_Item;
962 ---------------------
963 -- Replace_Element --
964 ---------------------
966 procedure Replace_Element
967 (Container : in out Map;
969 New_Item : Element_Type)
972 if Position.Node = 0 then
973 raise Constraint_Error with
974 "Position cursor of Replace_Element equals No_Element";
977 if Position.Container /= Container'Unrestricted_Access then
978 raise Program_Error with
979 "Position cursor of Replace_Element designates wrong map";
982 if Position.Container.Lock > 0 then
983 raise Program_Error with
984 "Replace_Element attempted to tamper with elements (map is locked)";
987 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
989 Container.Nodes (Position.Node).Element := New_Item;
992 ----------------------
993 -- Reserve_Capacity --
994 ----------------------
996 procedure Reserve_Capacity
997 (Container : in out Map;
998 Capacity : Count_Type)
1001 if Capacity > Container.Capacity then
1002 raise Capacity_Error with "requested capacity is too large";
1004 end Reserve_Capacity;
1010 procedure Set_Next (Node : in out Node_Type; Next : Count_Type) is
1015 --------------------
1016 -- Update_Element --
1017 --------------------
1019 procedure Update_Element
1020 (Container : in out Map;
1022 Process : not null access procedure (Key : Key_Type;
1023 Element : in out Element_Type))
1026 if Position.Node = 0 then
1027 raise Constraint_Error with
1028 "Position cursor of Update_Element equals No_Element";
1031 if Position.Container /= Container'Unrestricted_Access then
1032 raise Program_Error with
1033 "Position cursor of Update_Element designates wrong map";
1036 pragma Assert (Vet (Position), "bad cursor in Update_Element");
1039 N : Node_Type renames Container.Nodes (Position.Node);
1040 B : Natural renames Container.Busy;
1041 L : Natural renames Container.Lock;
1048 Process (N.Key, N.Element);
1065 function Vet (Position : Cursor) return Boolean is
1067 if Position.Node = 0 then
1068 return Position.Container = null;
1071 if Position.Container = null then
1076 M : Map renames Position.Container.all;
1080 if M.Length = 0 then
1084 if M.Capacity = 0 then
1088 if M.Buckets'Length = 0 then
1092 if Position.Node > M.Capacity then
1096 if M.Nodes (Position.Node).Next = Position.Node then
1100 X := M.Buckets (Key_Ops.Index (M, M.Nodes (Position.Node).Key));
1102 for J in 1 .. M.Length loop
1103 if X = Position.Node then
1111 if X = M.Nodes (X).Next then -- to prevent unnecessary looping
1115 X := M.Nodes (X).Next;
1127 (Stream : not null access Root_Stream_Type'Class;
1130 procedure Write_Node
1131 (Stream : not null access Root_Stream_Type'Class;
1133 pragma Inline (Write_Node);
1135 procedure Write_Nodes is new HT_Ops.Generic_Write (Write_Node);
1141 procedure Write_Node
1142 (Stream : not null access Root_Stream_Type'Class;
1146 Key_Type'Write (Stream, Node.Key);
1147 Element_Type'Write (Stream, Node.Element);
1150 -- Start of processing for Write
1153 Write_Nodes (Stream, Container);
1157 (Stream : not null access Root_Stream_Type'Class;
1161 raise Program_Error with "attempt to stream map cursor";
1165 (Stream : not null access Root_Stream_Type'Class;
1166 Item : Reference_Type)
1169 raise Program_Error with "attempt to stream reference";
1173 (Stream : not null access Root_Stream_Type'Class;
1174 Item : Constant_Reference_Type)
1177 raise Program_Error with "attempt to stream reference";
1180 end Ada.Containers.Bounded_Hashed_Maps;