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
;