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-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.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
41 -----------------------
42 -- Local Subprograms --
43 -----------------------
45 function Equivalent_Key_Node
47 Node : Node_Type) return Boolean;
48 pragma Inline (Equivalent_Key_Node);
50 function Hash_Node (Node : Node_Type) return Hash_Type;
51 pragma Inline (Hash_Node);
53 function Next (Node : Node_Type) return Count_Type;
56 procedure Set_Next (Node : in out Node_Type; Next : Count_Type);
57 pragma Inline (Set_Next);
59 function Vet (Position : Cursor) return Boolean;
61 --------------------------
62 -- Local Instantiations --
63 --------------------------
65 package HT_Ops is new Hash_Tables.Generic_Bounded_Operations
66 (HT_Types => HT_Types,
67 Hash_Node => Hash_Node,
69 Set_Next => Set_Next);
71 package Key_Ops is new Hash_Tables.Generic_Bounded_Keys
72 (HT_Types => HT_Types,
77 Equivalent_Keys => Equivalent_Key_Node);
83 function "=" (Left, Right : Map) return Boolean is
84 function Find_Equal_Key
85 (R_HT : Hash_Table_Type'Class;
86 L_Node : Node_Type) return Boolean;
88 function Is_Equal is new HT_Ops.Generic_Equal (Find_Equal_Key);
94 function Find_Equal_Key
95 (R_HT : Hash_Table_Type'Class;
96 L_Node : Node_Type) return Boolean
98 R_Index : constant Hash_Type := Key_Ops.Index (R_HT, L_Node.Key);
99 R_Node : Count_Type := R_HT.Buckets (R_Index);
102 while R_Node /= 0 loop
103 if Equivalent_Keys (L_Node.Key, R_HT.Nodes (R_Node).Key) then
104 return L_Node.Element = R_HT.Nodes (R_Node).Element;
107 R_Node := R_HT.Nodes (R_Node).Next;
113 -- Start of processing for "="
116 return Is_Equal (Left, Right);
123 procedure Assign (Target : in out Map; Source : Map) is
124 procedure Insert_Element (Source_Node : Count_Type);
126 procedure Insert_Elements is
127 new HT_Ops.Generic_Iteration (Insert_Element);
133 procedure Insert_Element (Source_Node : Count_Type) is
134 N : Node_Type renames Source.Nodes (Source_Node);
139 Insert (Target, N.Key, N.Element, C, B);
143 -- Start of processing for Assign
146 if Target'Address = Source'Address then
150 if Target.Capacity < Source.Length then
152 with "Target capacity is less than Source length";
155 HT_Ops.Clear (Target);
156 Insert_Elements (Source);
163 function Capacity (Container : Map) return Count_Type is
165 return Container.Capacity;
172 procedure Clear (Container : in out Map) is
174 HT_Ops.Clear (Container);
181 function Contains (Container : Map; Key : Key_Type) return Boolean is
183 return Find (Container, Key) /= No_Element;
192 Capacity : Count_Type := 0;
193 Modulus : Hash_Type := 0) return Map
202 elsif Capacity >= Source.Length then
206 raise Capacity_Error with "Capacity value too small";
210 M := Default_Modulus (C);
215 return Target : Map (Capacity => C, Modulus => M) do
216 Assign (Target => Target, Source => Source);
220 ---------------------
221 -- Default_Modulus --
222 ---------------------
224 function Default_Modulus (Capacity : Count_Type) return Hash_Type is
226 return To_Prime (Capacity);
233 procedure Delete (Container : in out Map; Key : Key_Type) is
237 Key_Ops.Delete_Key_Sans_Free (Container, Key, X);
240 raise Constraint_Error with "attempt to delete key not in map";
243 HT_Ops.Free (Container, X);
246 procedure Delete (Container : in out Map; Position : in out Cursor) is
248 if Position.Node = 0 then
249 raise Constraint_Error with
250 "Position cursor of Delete equals No_Element";
253 if Position.Container /= Container'Unrestricted_Access then
254 raise Program_Error with
255 "Position cursor of Delete designates wrong map";
258 if Container.Busy > 0 then
259 raise Program_Error with
260 "Delete attempted to tamper with cursors (map is busy)";
263 pragma Assert (Vet (Position), "bad cursor in Delete");
265 HT_Ops.Delete_Node_Sans_Free (Container, Position.Node);
266 HT_Ops.Free (Container, Position.Node);
268 Position := No_Element;
275 function Element (Container : Map; Key : Key_Type) return Element_Type is
276 Node : constant Count_Type := Key_Ops.Find (Container, Key);
280 raise Constraint_Error with
281 "no element available because key not in map";
284 return Container.Nodes (Node).Element;
287 function Element (Position : Cursor) return Element_Type is
289 if Position.Node = 0 then
290 raise Constraint_Error with
291 "Position cursor of function Element equals No_Element";
294 pragma Assert (Vet (Position), "bad cursor in function Element");
296 return Position.Container.Nodes (Position.Node).Element;
299 -------------------------
300 -- Equivalent_Key_Node --
301 -------------------------
303 function Equivalent_Key_Node
305 Node : Node_Type) return Boolean is
307 return Equivalent_Keys (Key, Node.Key);
308 end Equivalent_Key_Node;
310 ---------------------
311 -- Equivalent_Keys --
312 ---------------------
314 function Equivalent_Keys (Left, Right : Cursor)
317 if Left.Node = 0 then
318 raise Constraint_Error with
319 "Left cursor of Equivalent_Keys equals No_Element";
322 if Right.Node = 0 then
323 raise Constraint_Error with
324 "Right cursor of Equivalent_Keys equals No_Element";
327 pragma Assert (Vet (Left), "Left cursor of Equivalent_Keys is bad");
328 pragma Assert (Vet (Right), "Right cursor of Equivalent_Keys is bad");
331 LN : Node_Type renames Left.Container.Nodes (Left.Node);
332 RN : Node_Type renames Right.Container.Nodes (Right.Node);
335 return Equivalent_Keys (LN.Key, RN.Key);
339 function Equivalent_Keys (Left : Cursor; Right : Key_Type) return Boolean is
341 if Left.Node = 0 then
342 raise Constraint_Error with
343 "Left cursor of Equivalent_Keys equals No_Element";
346 pragma Assert (Vet (Left), "Left cursor in Equivalent_Keys is bad");
349 LN : Node_Type renames Left.Container.Nodes (Left.Node);
352 return Equivalent_Keys (LN.Key, Right);
356 function Equivalent_Keys (Left : Key_Type; Right : Cursor) return Boolean is
358 if Right.Node = 0 then
359 raise Constraint_Error with
360 "Right cursor of Equivalent_Keys equals No_Element";
363 pragma Assert (Vet (Right), "Right cursor of Equivalent_Keys is bad");
366 RN : Node_Type renames Right.Container.Nodes (Right.Node);
369 return Equivalent_Keys (Left, RN.Key);
377 procedure Exclude (Container : in out Map; Key : Key_Type) is
380 Key_Ops.Delete_Key_Sans_Free (Container, Key, X);
381 HT_Ops.Free (Container, X);
388 function Find (Container : Map; Key : Key_Type) return Cursor is
389 Node : constant Count_Type := Key_Ops.Find (Container, Key);
396 return Cursor'(Container'Unrestricted_Access, Node);
403 function First (Container : Map) return Cursor is
404 Node : constant Count_Type := HT_Ops.First (Container);
411 return Cursor'(Container'Unrestricted_Access, Node);
418 function Has_Element (Position : Cursor) return Boolean is
420 pragma Assert (Vet (Position), "bad cursor in Has_Element");
421 return Position.Node /= 0;
428 function Hash_Node (Node : Node_Type) return Hash_Type is
430 return Hash (Node.Key);
438 (Container : in out Map;
440 New_Item : Element_Type)
446 Insert (Container, Key, New_Item, Position, Inserted);
449 if Container.Lock > 0 then
450 raise Program_Error with
451 "Include attempted to tamper with elements (map is locked)";
455 N : Node_Type renames Container.Nodes (Position.Node);
459 N.Element := New_Item;
469 (Container : in out Map;
471 Position : out Cursor;
472 Inserted : out Boolean)
474 procedure Assign_Key (Node : in out Node_Type);
475 pragma Inline (Assign_Key);
477 function New_Node return Count_Type;
478 pragma Inline (New_Node);
480 procedure Local_Insert is
481 new Key_Ops.Generic_Conditional_Insert (New_Node);
483 procedure Allocate is
484 new HT_Ops.Generic_Allocate (Assign_Key);
490 procedure Assign_Key (Node : in out Node_Type) is
493 -- Node.Element := New_Item;
500 function New_Node return Count_Type is
503 Allocate (Container, Result);
507 -- Start of processing for Insert
511 -- if HT_Ops.Capacity (HT) = 0 then
512 -- HT_Ops.Reserve_Capacity (HT, 1);
515 Local_Insert (Container, Key, Position.Node, Inserted);
519 -- and then HT.Length > HT_Ops.Capacity (HT)
521 -- HT_Ops.Reserve_Capacity (HT, HT.Length);
524 Position.Container := Container'Unchecked_Access;
528 (Container : in out Map;
530 New_Item : Element_Type;
531 Position : out Cursor;
532 Inserted : out Boolean)
534 procedure Assign_Key (Node : in out Node_Type);
535 pragma Inline (Assign_Key);
537 function New_Node return Count_Type;
538 pragma Inline (New_Node);
540 procedure Local_Insert is
541 new Key_Ops.Generic_Conditional_Insert (New_Node);
543 procedure Allocate is
544 new HT_Ops.Generic_Allocate (Assign_Key);
550 procedure Assign_Key (Node : in out Node_Type) is
553 Node.Element := New_Item;
560 function New_Node return Count_Type is
563 Allocate (Container, Result);
567 -- Start of processing for Insert
571 -- if HT_Ops.Capacity (HT) = 0 then
572 -- HT_Ops.Reserve_Capacity (HT, 1);
575 Local_Insert (Container, Key, Position.Node, Inserted);
579 -- and then HT.Length > HT_Ops.Capacity (HT)
581 -- HT_Ops.Reserve_Capacity (HT, HT.Length);
584 Position.Container := Container'Unchecked_Access;
588 (Container : in out Map;
590 New_Item : Element_Type)
593 pragma Unreferenced (Position);
598 Insert (Container, Key, New_Item, Position, Inserted);
601 raise Constraint_Error with
602 "attempt to insert key already in map";
610 function Is_Empty (Container : Map) return Boolean is
612 return Container.Length = 0;
621 Process : not null access procedure (Position : Cursor))
623 procedure Process_Node (Node : Count_Type);
624 pragma Inline (Process_Node);
626 procedure Local_Iterate is new HT_Ops.Generic_Iteration (Process_Node);
632 procedure Process_Node (Node : Count_Type) is
634 Process (Cursor'(Container'Unrestricted_Access, Node));
637 B : Natural renames Container'Unrestricted_Access.Busy;
639 -- Start of processing for Iterate
645 Local_Iterate (Container);
659 function Key (Position : Cursor) return Key_Type is
661 if Position.Node = 0 then
662 raise Constraint_Error with
663 "Position cursor of function Key equals No_Element";
666 pragma Assert (Vet (Position), "bad cursor in function Key");
668 return Position.Container.Nodes (Position.Node).Key;
675 function Length (Container : Map) return Count_Type is
677 return Container.Length;
685 (Target : in out Map;
689 if Target'Address = Source'Address then
693 if Source.Busy > 0 then
694 raise Program_Error with
695 "attempt to tamper with cursors (container is busy)";
698 Assign (Target => Target, Source => Source);
705 function Next (Node : Node_Type) return Count_Type is
710 function Next (Position : Cursor) return Cursor is
712 if Position.Node = 0 then
716 pragma Assert (Vet (Position), "bad cursor in function Next");
719 M : Map renames Position.Container.all;
720 Node : constant Count_Type := HT_Ops.Next (M, Position.Node);
727 return Cursor'(Position.Container, Node);
731 procedure Next (Position : in out Cursor) is
733 Position := Next (Position);
740 procedure Query_Element
742 Process : not null access
743 procedure (Key : Key_Type; Element : Element_Type))
746 if Position.Node = 0 then
747 raise Constraint_Error with
748 "Position cursor of Query_Element equals No_Element";
751 pragma Assert (Vet (Position), "bad cursor in Query_Element");
754 M : Map renames Position.Container.all;
755 N : Node_Type renames M.Nodes (Position.Node);
756 B : Natural renames M.Busy;
757 L : Natural renames M.Lock;
766 Process (N.Key, N.Element);
784 (Stream : not null access Root_Stream_Type'Class;
788 (Stream : not null access Root_Stream_Type'Class) return Count_Type;
789 -- pragma Inline (Read_Node); ???
791 procedure Read_Nodes is new HT_Ops.Generic_Read (Read_Node);
798 (Stream : not null access Root_Stream_Type'Class) return Count_Type
800 procedure Read_Element (Node : in out Node_Type);
801 -- pragma Inline (Read_Element); ???
803 procedure Allocate is
804 new HT_Ops.Generic_Allocate (Read_Element);
806 procedure Read_Element (Node : in out Node_Type) is
808 Key_Type'Read (Stream, Node.Key);
809 Element_Type'Read (Stream, Node.Element);
814 -- Start of processing for Read_Node
817 Allocate (Container, Node);
821 -- Start of processing for Read
824 Read_Nodes (Stream, Container);
828 (Stream : not null access Root_Stream_Type'Class;
832 raise Program_Error with "attempt to stream map cursor";
840 (Container : in out Map;
842 New_Item : Element_Type)
844 Node : constant Count_Type := Key_Ops.Find (Container, Key);
848 raise Constraint_Error with
849 "attempt to replace key not in map";
852 if Container.Lock > 0 then
853 raise Program_Error with
854 "Replace attempted to tamper with elements (map is locked)";
858 N : Node_Type renames Container.Nodes (Node);
862 N.Element := New_Item;
866 ---------------------
867 -- Replace_Element --
868 ---------------------
870 procedure Replace_Element
871 (Container : in out Map;
873 New_Item : Element_Type)
876 if Position.Node = 0 then
877 raise Constraint_Error with
878 "Position cursor of Replace_Element equals No_Element";
881 if Position.Container /= Container'Unrestricted_Access then
882 raise Program_Error with
883 "Position cursor of Replace_Element designates wrong map";
886 if Position.Container.Lock > 0 then
887 raise Program_Error with
888 "Replace_Element attempted to tamper with elements (map is locked)";
891 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
893 Container.Nodes (Position.Node).Element := New_Item;
896 ----------------------
897 -- Reserve_Capacity --
898 ----------------------
900 procedure Reserve_Capacity
901 (Container : in out Map;
902 Capacity : Count_Type)
905 if Capacity > Container.Capacity then
906 raise Capacity_Error with "requested capacity is too large";
908 end Reserve_Capacity;
914 procedure Set_Next (Node : in out Node_Type; Next : Count_Type) is
923 procedure Update_Element
924 (Container : in out Map;
926 Process : not null access procedure (Key : Key_Type;
927 Element : in out Element_Type))
930 if Position.Node = 0 then
931 raise Constraint_Error with
932 "Position cursor of Update_Element equals No_Element";
935 if Position.Container /= Container'Unrestricted_Access then
936 raise Program_Error with
937 "Position cursor of Update_Element designates wrong map";
940 pragma Assert (Vet (Position), "bad cursor in Update_Element");
943 N : Node_Type renames Container.Nodes (Position.Node);
944 B : Natural renames Container.Busy;
945 L : Natural renames Container.Lock;
952 Process (N.Key, N.Element);
969 function Vet (Position : Cursor) return Boolean is
971 if Position.Node = 0 then
972 return Position.Container = null;
975 if Position.Container = null then
980 M : Map renames Position.Container.all;
988 if M.Capacity = 0 then
992 if M.Buckets'Length = 0 then
996 if Position.Node > M.Capacity then
1000 if M.Nodes (Position.Node).Next = Position.Node then
1004 X := M.Buckets (Key_Ops.Index (M, M.Nodes (Position.Node).Key));
1006 for J in 1 .. M.Length loop
1007 if X = Position.Node then
1015 if X = M.Nodes (X).Next then -- to prevent unnecessary looping
1019 X := M.Nodes (X).Next;
1031 (Stream : not null access Root_Stream_Type'Class;
1034 procedure Write_Node
1035 (Stream : not null access Root_Stream_Type'Class;
1037 pragma Inline (Write_Node);
1039 procedure Write_Nodes is new HT_Ops.Generic_Write (Write_Node);
1045 procedure Write_Node
1046 (Stream : not null access Root_Stream_Type'Class;
1050 Key_Type'Write (Stream, Node.Key);
1051 Element_Type'Write (Stream, Node.Element);
1054 -- Start of processing for Write
1057 Write_Nodes (Stream, Container);
1061 (Stream : not null access Root_Stream_Type'Class;
1065 raise Program_Error with "attempt to stream map cursor";
1068 end Ada.Containers.Bounded_Hashed_Maps;