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 _ M A P S --
9 -- Copyright (C) 2004-2010, 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 package body Ada.Containers.Hashed_Maps is
41 Map_Iterator_Interfaces.Forward_Iterator with record
42 Container : Map_Access;
46 overriding function First (Object : Iterator) return Cursor;
47 overriding function Next (Object : Iterator; Position : Cursor)
50 -----------------------
51 -- Local Subprograms --
52 -----------------------
55 (Source : Node_Access) return Node_Access;
56 pragma Inline (Copy_Node);
58 function Equivalent_Key_Node
60 Node : Node_Access) return Boolean;
61 pragma Inline (Equivalent_Key_Node);
63 procedure Free (X : in out Node_Access);
65 function Find_Equal_Key
66 (R_HT : Hash_Table_Type;
67 L_Node : Node_Access) return Boolean;
69 function Hash_Node (Node : Node_Access) return Hash_Type;
70 pragma Inline (Hash_Node);
72 function Next (Node : Node_Access) return Node_Access;
76 (Stream : not null access Root_Stream_Type'Class) return Node_Access;
77 pragma Inline (Read_Node);
79 procedure Set_Next (Node : Node_Access; Next : Node_Access);
80 pragma Inline (Set_Next);
82 function Vet (Position : Cursor) return Boolean;
85 (Stream : not null access Root_Stream_Type'Class;
87 pragma Inline (Write_Node);
89 --------------------------
90 -- Local Instantiations --
91 --------------------------
93 package HT_Ops is new Hash_Tables.Generic_Operations
94 (HT_Types => HT_Types,
95 Hash_Node => Hash_Node,
98 Copy_Node => Copy_Node,
101 package Key_Ops is new Hash_Tables.Generic_Keys
102 (HT_Types => HT_Types,
104 Set_Next => Set_Next,
105 Key_Type => Key_Type,
107 Equivalent_Keys => Equivalent_Key_Node);
109 function Is_Equal is new HT_Ops.Generic_Equal (Find_Equal_Key);
111 procedure Read_Nodes is new HT_Ops.Generic_Read (Read_Node);
112 procedure Write_Nodes is new HT_Ops.Generic_Write (Write_Node);
118 function "=" (Left, Right : Map) return Boolean is
120 return Is_Equal (Left.HT, Right.HT);
127 procedure Adjust (Container : in out Map) is
129 HT_Ops.Adjust (Container.HT);
136 function Capacity (Container : Map) return Count_Type is
138 return HT_Ops.Capacity (Container.HT);
145 procedure Clear (Container : in out Map) is
147 HT_Ops.Clear (Container.HT);
154 function Contains (Container : Map; Key : Key_Type) return Boolean is
156 return Find (Container, Key) /= No_Element;
164 (Source : Node_Access) return Node_Access
166 Target : constant Node_Access :=
167 new Node_Type'(Key => Source.Key,
168 Element => Source.Element,
178 procedure Delete (Container : in out Map; Key : Key_Type) is
182 Key_Ops.Delete_Key_Sans_Free (Container.HT, Key, X);
185 raise Constraint_Error with "attempt to delete key not in map";
191 procedure Delete (Container : in out Map; Position : in out Cursor) is
193 if Position.Node = null then
194 raise Constraint_Error with
195 "Position cursor of Delete equals No_Element";
198 if Position.Container /= Container'Unrestricted_Access then
199 raise Program_Error with
200 "Position cursor of Delete designates wrong map";
203 if Container.HT.Busy > 0 then
204 raise Program_Error with
205 "Delete attempted to tamper with cursors (map is busy)";
208 pragma Assert (Vet (Position), "bad cursor in Delete");
210 HT_Ops.Delete_Node_Sans_Free (Container.HT, Position.Node);
212 Free (Position.Node);
213 Position.Container := null;
220 function Element (Container : Map; Key : Key_Type) return Element_Type is
221 Node : constant Node_Access := Key_Ops.Find (Container.HT, Key);
225 raise Constraint_Error with
226 "no element available because key not in map";
232 function Element (Position : Cursor) return Element_Type is
234 if Position.Node = null then
235 raise Constraint_Error with
236 "Position cursor of function Element equals No_Element";
239 pragma Assert (Vet (Position), "bad cursor in function Element");
241 return Position.Node.Element;
244 -------------------------
245 -- Equivalent_Key_Node --
246 -------------------------
248 function Equivalent_Key_Node
250 Node : Node_Access) return Boolean is
252 return Equivalent_Keys (Key, Node.Key);
253 end Equivalent_Key_Node;
255 ---------------------
256 -- Equivalent_Keys --
257 ---------------------
259 function Equivalent_Keys (Left, Right : Cursor)
262 if Left.Node = null then
263 raise Constraint_Error with
264 "Left cursor of Equivalent_Keys equals No_Element";
267 if Right.Node = null then
268 raise Constraint_Error with
269 "Right cursor of Equivalent_Keys equals No_Element";
272 pragma Assert (Vet (Left), "Left cursor of Equivalent_Keys is bad");
273 pragma Assert (Vet (Right), "Right cursor of Equivalent_Keys is bad");
275 return Equivalent_Keys (Left.Node.Key, Right.Node.Key);
278 function Equivalent_Keys (Left : Cursor; Right : Key_Type) return Boolean is
280 if Left.Node = null then
281 raise Constraint_Error with
282 "Left cursor of Equivalent_Keys equals No_Element";
285 pragma Assert (Vet (Left), "Left cursor in Equivalent_Keys is bad");
287 return Equivalent_Keys (Left.Node.Key, Right);
290 function Equivalent_Keys (Left : Key_Type; Right : Cursor) return Boolean is
292 if Right.Node = null then
293 raise Constraint_Error with
294 "Right cursor of Equivalent_Keys equals No_Element";
297 pragma Assert (Vet (Right), "Right cursor of Equivalent_Keys is bad");
299 return Equivalent_Keys (Left, Right.Node.Key);
306 procedure Exclude (Container : in out Map; Key : Key_Type) is
309 Key_Ops.Delete_Key_Sans_Free (Container.HT, Key, X);
317 procedure Finalize (Container : in out Map) is
319 HT_Ops.Finalize (Container.HT);
326 function Find (Container : Map; Key : Key_Type) return Cursor is
327 Node : constant Node_Access := Key_Ops.Find (Container.HT, Key);
334 return Cursor'(Container'Unchecked_Access, Node);
341 function Find_Equal_Key
342 (R_HT : Hash_Table_Type;
343 L_Node : Node_Access) return Boolean
345 R_Index : constant Hash_Type := Key_Ops.Index (R_HT, L_Node.Key);
346 R_Node : Node_Access := R_HT.Buckets (R_Index);
349 while R_Node /= null loop
350 if Equivalent_Keys (L_Node.Key, R_Node.Key) then
351 return L_Node.Element = R_Node.Element;
354 R_Node := R_Node.Next;
364 function First (Container : Map) return Cursor is
365 Node : constant Node_Access := HT_Ops.First (Container.HT);
372 return Cursor'(Container'Unchecked_Access, Node);
375 function First (Object : Iterator) return Cursor is
376 M : constant Map_Access := Object.Container;
377 N : constant Node_Access := HT_Ops.First (M.HT);
383 return Cursor'(Object.Container.all'Unchecked_Access, N);
390 procedure Free (X : in out Node_Access) is
391 procedure Deallocate is
392 new Ada.Unchecked_Deallocation (Node_Type, Node_Access);
395 X.Next := X; -- detect mischief (in Vet)
404 function Has_Element (Position : Cursor) return Boolean is
406 pragma Assert (Vet (Position), "bad cursor in Has_Element");
407 return Position.Node /= null;
414 function Hash_Node (Node : Node_Access) return Hash_Type is
416 return Hash (Node.Key);
424 (Container : in out Map;
426 New_Item : Element_Type)
432 Insert (Container, Key, New_Item, Position, Inserted);
435 if Container.HT.Lock > 0 then
436 raise Program_Error with
437 "Include attempted to tamper with elements (map is locked)";
440 Position.Node.Key := Key;
441 Position.Node.Element := New_Item;
450 (Container : in out Map;
452 Position : out Cursor;
453 Inserted : out Boolean)
455 function New_Node (Next : Node_Access) return Node_Access;
456 pragma Inline (New_Node);
458 procedure Local_Insert is
459 new Key_Ops.Generic_Conditional_Insert (New_Node);
465 function New_Node (Next : Node_Access) return Node_Access is
467 return new Node_Type'(Key => Key,
472 HT : Hash_Table_Type renames Container.HT;
474 -- Start of processing for Insert
477 if HT_Ops.Capacity (HT) = 0 then
478 HT_Ops.Reserve_Capacity (HT, 1);
481 Local_Insert (HT, Key, Position.Node, Inserted);
484 and then HT.Length > HT_Ops.Capacity (HT)
486 HT_Ops.Reserve_Capacity (HT, HT.Length);
489 Position.Container := Container'Unchecked_Access;
493 (Container : in out Map;
495 New_Item : Element_Type;
496 Position : out Cursor;
497 Inserted : out Boolean)
499 function New_Node (Next : Node_Access) return Node_Access;
500 pragma Inline (New_Node);
502 procedure Local_Insert is
503 new Key_Ops.Generic_Conditional_Insert (New_Node);
509 function New_Node (Next : Node_Access) return Node_Access is
511 return new Node_Type'(Key, New_Item, Next);
514 HT : Hash_Table_Type renames Container.HT;
516 -- Start of processing for Insert
519 if HT_Ops.Capacity (HT) = 0 then
520 HT_Ops.Reserve_Capacity (HT, 1);
523 Local_Insert (HT, Key, Position.Node, Inserted);
526 and then HT.Length > HT_Ops.Capacity (HT)
528 HT_Ops.Reserve_Capacity (HT, HT.Length);
531 Position.Container := Container'Unchecked_Access;
535 (Container : in out Map;
537 New_Item : Element_Type)
540 pragma Unreferenced (Position);
545 Insert (Container, Key, New_Item, Position, Inserted);
548 raise Constraint_Error with
549 "attempt to insert key already in map";
557 function Is_Empty (Container : Map) return Boolean is
559 return Container.HT.Length = 0;
568 Process : not null access procedure (Position : Cursor))
570 procedure Process_Node (Node : Node_Access);
571 pragma Inline (Process_Node);
573 procedure Local_Iterate is new HT_Ops.Generic_Iteration (Process_Node);
579 procedure Process_Node (Node : Node_Access) is
581 Process (Cursor'(Container'Unchecked_Access, Node));
584 B : Natural renames Container'Unrestricted_Access.HT.Busy;
586 -- Start of processing for Iterate
592 Local_Iterate (Container.HT);
602 function Iterate (Container : Map)
603 return Map_Iterator_Interfaces.Forward_Iterator'class
605 Node : constant Node_Access := HT_Ops.First (Container.HT);
606 It : constant Iterator := (Container'Unrestricted_Access, Node);
615 function Key (Position : Cursor) return Key_Type is
617 if Position.Node = null then
618 raise Constraint_Error with
619 "Position cursor of function Key equals No_Element";
622 pragma Assert (Vet (Position), "bad cursor in function Key");
624 return Position.Node.Key;
631 function Length (Container : Map) return Count_Type is
633 return Container.HT.Length;
641 (Target : in out Map;
645 HT_Ops.Move (Target => Target.HT, Source => Source.HT);
652 function Next (Node : Node_Access) return Node_Access is
657 function Next (Position : Cursor) return Cursor is
659 if Position.Node = null then
663 pragma Assert (Vet (Position), "bad cursor in function Next");
666 HT : Hash_Table_Type renames Position.Container.HT;
667 Node : constant Node_Access := HT_Ops.Next (HT, Position.Node);
674 return Cursor'(Position.Container, Node);
678 procedure Next (Position : in out Cursor) is
680 Position := Next (Position);
683 function Next (Object : Iterator; Position : Cursor) return Cursor is
685 if Position.Node = null then
689 return (Object.Container, Next (Position).Node);
697 procedure Query_Element
699 Process : not null access
700 procedure (Key : Key_Type; Element : Element_Type))
703 if Position.Node = null then
704 raise Constraint_Error with
705 "Position cursor of Query_Element equals No_Element";
708 pragma Assert (Vet (Position), "bad cursor in Query_Element");
711 M : Map renames Position.Container.all;
712 HT : Hash_Table_Type renames M.HT'Unrestricted_Access.all;
714 B : Natural renames HT.Busy;
715 L : Natural renames HT.Lock;
722 K : Key_Type renames Position.Node.Key;
723 E : Element_Type renames Position.Node.Element;
744 (Stream : not null access Root_Stream_Type'Class;
748 Read_Nodes (Stream, Container.HT);
752 (Stream : not null access Root_Stream_Type'Class;
756 raise Program_Error with "attempt to stream map cursor";
760 (Stream : not null access Root_Stream_Type'Class;
761 Item : out Reference_Type)
764 raise Program_Error with "attempt to stream reference";
768 (Stream : not null access Root_Stream_Type'Class;
769 Item : out Constant_Reference_Type)
772 raise Program_Error with "attempt to stream reference";
779 function Constant_Reference (Container : Map; Key : Key_Type)
780 return Constant_Reference_Type is
782 return (Element => Container.Element (Key)'Unrestricted_Access);
783 end Constant_Reference;
785 function Reference (Container : Map; Key : Key_Type)
786 return Reference_Type is
788 return (Element => Container.Element (Key)'Unrestricted_Access);
796 (Stream : not null access Root_Stream_Type'Class) return Node_Access
798 Node : Node_Access := new Node_Type;
801 Key_Type'Read (Stream, Node.Key);
802 Element_Type'Read (Stream, Node.Element);
816 (Container : in out Map;
818 New_Item : Element_Type)
820 Node : constant Node_Access := Key_Ops.Find (Container.HT, Key);
824 raise Constraint_Error with
825 "attempt to replace key not in map";
828 if Container.HT.Lock > 0 then
829 raise Program_Error with
830 "Replace attempted to tamper with elements (map is locked)";
834 Node.Element := New_Item;
837 ---------------------
838 -- Replace_Element --
839 ---------------------
841 procedure Replace_Element
842 (Container : in out Map;
844 New_Item : Element_Type)
847 if Position.Node = null then
848 raise Constraint_Error with
849 "Position cursor of Replace_Element equals No_Element";
852 if Position.Container /= Container'Unrestricted_Access then
853 raise Program_Error with
854 "Position cursor of Replace_Element designates wrong map";
857 if Position.Container.HT.Lock > 0 then
858 raise Program_Error with
859 "Replace_Element attempted to tamper with elements (map is locked)";
862 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
864 Position.Node.Element := New_Item;
867 ----------------------
868 -- Reserve_Capacity --
869 ----------------------
871 procedure Reserve_Capacity
872 (Container : in out Map;
873 Capacity : Count_Type)
876 HT_Ops.Reserve_Capacity (Container.HT, Capacity);
877 end Reserve_Capacity;
883 procedure Set_Next (Node : Node_Access; Next : Node_Access) is
892 procedure Update_Element
893 (Container : in out Map;
895 Process : not null access procedure (Key : Key_Type;
896 Element : in out Element_Type))
899 if Position.Node = null then
900 raise Constraint_Error with
901 "Position cursor of Update_Element equals No_Element";
904 if Position.Container /= Container'Unrestricted_Access then
905 raise Program_Error with
906 "Position cursor of Update_Element designates wrong map";
909 pragma Assert (Vet (Position), "bad cursor in Update_Element");
912 HT : Hash_Table_Type renames Container.HT;
913 B : Natural renames HT.Busy;
914 L : Natural renames HT.Lock;
921 K : Key_Type renames Position.Node.Key;
922 E : Element_Type renames Position.Node.Element;
943 function Vet (Position : Cursor) return Boolean is
945 if Position.Node = null then
946 return Position.Container = null;
949 if Position.Container = null then
953 if Position.Node.Next = Position.Node then
958 HT : Hash_Table_Type renames Position.Container.HT;
962 if HT.Length = 0 then
967 or else HT.Buckets'Length = 0
972 X := HT.Buckets (Key_Ops.Index (HT, Position.Node.Key));
974 for J in 1 .. HT.Length loop
975 if X = Position.Node then
983 if X = X.Next then -- to prevent unnecessary looping
999 (Stream : not null access Root_Stream_Type'Class;
1003 Write_Nodes (Stream, Container.HT);
1007 (Stream : not null access Root_Stream_Type'Class;
1011 raise Program_Error with "attempt to stream map cursor";
1015 (Stream : not null access Root_Stream_Type'Class;
1016 Item : Reference_Type)
1019 raise Program_Error with "attempt to stream reference";
1023 (Stream : not null access Root_Stream_Type'Class;
1024 Item : Constant_Reference_Type)
1027 raise Program_Error with "attempt to stream reference";
1034 procedure Write_Node
1035 (Stream : not null access Root_Stream_Type'Class;
1039 Key_Type'Write (Stream, Node.Key);
1040 Element_Type'Write (Stream, Node.Element);
1043 end Ada.Containers.Hashed_Maps;