1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- A D A . C O N T A I N E R S . F O R M A L _ H A S H E D _ M A P S --
9 -- Copyright (C) 2010-2012, 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/>. --
26 ------------------------------------------------------------------------------
28 with Ada
.Containers
.Hash_Tables
.Generic_Bounded_Operations
;
29 pragma Elaborate_All
(Ada
.Containers
.Hash_Tables
.Generic_Bounded_Operations
);
31 with Ada
.Containers
.Hash_Tables
.Generic_Bounded_Keys
;
32 pragma Elaborate_All
(Ada
.Containers
.Hash_Tables
.Generic_Bounded_Keys
);
34 with Ada
.Containers
.Prime_Numbers
; use Ada
.Containers
.Prime_Numbers
;
36 with System
; use type System
.Address
;
38 package body Ada
.Containers
.Formal_Hashed_Maps
is
40 -----------------------
41 -- Local Subprograms --
42 -----------------------
44 -- All local subprograms require comments ???
46 function Equivalent_Keys
48 Node
: Node_Type
) return Boolean;
49 pragma Inline
(Equivalent_Keys
);
56 with procedure Set_Element
(Node
: in out Node_Type
);
57 procedure Generic_Allocate
59 Node
: out Count_Type
);
61 function Hash_Node
(Node
: Node_Type
) return Hash_Type
;
62 pragma Inline
(Hash_Node
);
64 function Next
(Node
: Node_Type
) return Count_Type
;
67 procedure Set_Next
(Node
: in out Node_Type
; Next
: Count_Type
);
68 pragma Inline
(Set_Next
);
70 function Vet
(Container
: Map
; Position
: Cursor
) return Boolean;
72 --------------------------
73 -- Local Instantiations --
74 --------------------------
77 new Hash_Tables
.Generic_Bounded_Operations
78 (HT_Types
=> HT_Types
,
79 Hash_Node
=> Hash_Node
,
81 Set_Next
=> Set_Next
);
84 new Hash_Tables
.Generic_Bounded_Keys
85 (HT_Types
=> HT_Types
,
90 Equivalent_Keys
=> Equivalent_Keys
);
96 function "=" (Left
, Right
: Map
) return Boolean is
98 if Length
(Left
) /= Length
(Right
) then
102 if Length
(Left
) = 0 then
111 Node
:= Left
.First
.Node
;
113 ENode
:= Find
(Container
=> Right
,
114 Key
=> Left
.Nodes
(Node
).Key
).Node
;
117 Right
.Nodes
(ENode
).Element
/= Left
.Nodes
(Node
).Element
122 Node
:= HT_Ops
.Next
(Left
, Node
);
133 procedure Assign
(Target
: in out Map
; Source
: Map
) is
134 procedure Insert_Element
(Source_Node
: Count_Type
);
135 pragma Inline
(Insert_Element
);
137 procedure Insert_Elements
is
138 new HT_Ops
.Generic_Iteration
(Insert_Element
);
144 procedure Insert_Element
(Source_Node
: Count_Type
) is
145 N
: Node_Type
renames Source
.Nodes
(Source_Node
);
147 Target
.Insert
(N
.Key
, N
.Element
);
150 -- Start of processing for Assign
153 if Target
'Address = Source
'Address then
157 if Target
.Capacity
< Length
(Source
) then
158 raise Constraint_Error
with -- correct exception ???
159 "Source length exceeds Target capacity";
166 Insert_Elements
(Source
);
173 function Capacity
(Container
: Map
) return Count_Type
is
175 return Container
.Nodes
'Length;
182 procedure Clear
(Container
: in out Map
) is
184 HT_Ops
.Clear
(Container
);
191 function Contains
(Container
: Map
; Key
: Key_Type
) return Boolean is
193 return Find
(Container
, Key
) /= No_Element
;
202 Capacity
: Count_Type
:= 0) return Map
204 C
: constant Count_Type
:=
205 Count_Type
'Max (Capacity
, Source
.Capacity
);
208 Target
: Map
(C
, Source
.Modulus
);
212 Target
.Length
:= Source
.Length
;
213 Target
.Free
:= Source
.Free
;
216 while H
<= Source
.Modulus
loop
217 Target
.Buckets
(H
) := Source
.Buckets
(H
);
222 while N
<= Source
.Capacity
loop
223 Target
.Nodes
(N
) := Source
.Nodes
(N
);
229 Free
(Target
, Cu
.Node
);
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";
262 procedure Delete
(Container
: in out Map
; Position
: in out Cursor
) is
264 if not Has_Element
(Container
, Position
) then
265 raise Constraint_Error
with
266 "Position cursor of Delete has no element";
269 if Container
.Busy
> 0 then
270 raise Program_Error
with
271 "Delete attempted to tamper with elements (map is busy)";
274 pragma Assert
(Vet
(Container
, Position
), "bad cursor in Delete");
276 HT_Ops
.Delete_Node_Sans_Free
(Container
, Position
.Node
);
278 Free
(Container
, Position
.Node
);
285 function Element
(Container
: Map
; Key
: Key_Type
) return Element_Type
is
286 Node
: constant Count_Type
:= Find
(Container
, Key
).Node
;
290 raise Constraint_Error
with
291 "no element available because key not in map";
294 return Container
.Nodes
(Node
).Element
;
297 function Element
(Container
: Map
; Position
: Cursor
) return Element_Type
is
299 if not Has_Element
(Container
, Position
) then
300 raise Constraint_Error
with "Position cursor equals No_Element";
303 pragma Assert
(Vet
(Container
, Position
),
304 "bad cursor in function Element");
306 return Container
.Nodes
(Position
.Node
).Element
;
309 ---------------------
310 -- Equivalent_Keys --
311 ---------------------
313 function Equivalent_Keys
315 Node
: Node_Type
) return Boolean
318 return Equivalent_Keys
(Key
, Node
.Key
);
321 function Equivalent_Keys
325 CRight
: Cursor
) return Boolean
328 if not Has_Element
(Left
, CLeft
) then
329 raise Constraint_Error
with
330 "Left cursor of Equivalent_Keys has no element";
333 if not Has_Element
(Right
, CRight
) then
334 raise Constraint_Error
with
335 "Right cursor of Equivalent_Keys has no element";
338 pragma Assert
(Vet
(Left
, CLeft
),
339 "Left cursor of Equivalent_Keys is bad");
340 pragma Assert
(Vet
(Right
, CRight
),
341 "Right cursor of Equivalent_Keys is bad");
344 LN
: Node_Type
renames Left
.Nodes
(CLeft
.Node
);
345 RN
: Node_Type
renames Right
.Nodes
(CRight
.Node
);
347 return Equivalent_Keys
(LN
.Key
, RN
.Key
);
351 function Equivalent_Keys
354 Right
: Key_Type
) return Boolean
357 if not Has_Element
(Left
, CLeft
) then
358 raise Constraint_Error
with
359 "Left cursor of Equivalent_Keys has no element";
362 pragma Assert
(Vet
(Left
, CLeft
),
363 "Left cursor in Equivalent_Keys is bad");
366 LN
: Node_Type
renames Left
.Nodes
(CLeft
.Node
);
368 return Equivalent_Keys
(LN
.Key
, Right
);
372 function Equivalent_Keys
375 CRight
: Cursor
) return Boolean
378 if Has_Element
(Right
, CRight
) then
379 raise Constraint_Error
with
380 "Right cursor of Equivalent_Keys has no element";
383 pragma Assert
(Vet
(Right
, CRight
),
384 "Right cursor of Equivalent_Keys is bad");
387 RN
: Node_Type
renames Right
.Nodes
(CRight
.Node
);
390 return Equivalent_Keys
(Left
, RN
.Key
);
398 procedure Exclude
(Container
: in out Map
; Key
: Key_Type
) is
401 Key_Ops
.Delete_Key_Sans_Free
(Container
, Key
, X
);
409 function Find
(Container
: Map
; Key
: Key_Type
) return Cursor
is
410 Node
: constant Count_Type
:= Key_Ops
.Find
(Container
, Key
);
417 return (Node
=> Node
);
424 function First
(Container
: Map
) return Cursor
is
425 Node
: constant Count_Type
:= HT_Ops
.First
(Container
);
432 return (Node
=> Node
);
439 procedure Free
(HT
: in out Map
; X
: Count_Type
) is
441 HT
.Nodes
(X
).Has_Element
:= False;
445 ----------------------
446 -- Generic_Allocate --
447 ----------------------
449 procedure Generic_Allocate
(HT
: in out Map
; Node
: out Count_Type
) is
451 procedure Allocate
is
452 new HT_Ops
.Generic_Allocate
(Set_Element
);
456 HT
.Nodes
(Node
).Has_Element
:= True;
457 end Generic_Allocate
;
463 function Has_Element
(Container
: Map
; Position
: Cursor
) return Boolean is
465 if Position
.Node
= 0 or else
466 not Container
.Nodes
(Position
.Node
).Has_Element
then
477 function Hash_Node
(Node
: Node_Type
) return Hash_Type
is
479 return Hash
(Node
.Key
);
487 (Container
: in out Map
;
489 New_Item
: Element_Type
)
495 Insert
(Container
, Key
, New_Item
, Position
, Inserted
);
498 if Container
.Lock
> 0 then
499 raise Program_Error
with
500 "Include attempted to tamper with cursors (map is locked)";
504 N
: Node_Type
renames Container
.Nodes
(Position
.Node
);
507 N
.Element
:= New_Item
;
517 (Container
: in out Map
;
519 Position
: out Cursor
;
520 Inserted
: out Boolean)
522 procedure Assign_Key
(Node
: in out Node_Type
);
523 pragma Inline
(Assign_Key
);
525 function New_Node
return Count_Type
;
526 pragma Inline
(New_Node
);
528 procedure Local_Insert
is
529 new Key_Ops
.Generic_Conditional_Insert
(New_Node
);
531 procedure Allocate
is
532 new Generic_Allocate
(Assign_Key
);
538 procedure Assign_Key
(Node
: in out Node_Type
) is
542 -- What is following commented out line doing here ???
543 -- Node.Element := New_Item;
550 function New_Node
return Count_Type
is
553 Allocate
(Container
, Result
);
557 -- Start of processing for Insert
561 Local_Insert
(Container
, Key
, Position
.Node
, Inserted
);
565 (Container
: in out Map
;
567 New_Item
: Element_Type
;
568 Position
: out Cursor
;
569 Inserted
: out Boolean)
571 procedure Assign_Key
(Node
: in out Node_Type
);
572 pragma Inline
(Assign_Key
);
574 function New_Node
return Count_Type
;
575 pragma Inline
(New_Node
);
577 procedure Local_Insert
is
578 new Key_Ops
.Generic_Conditional_Insert
(New_Node
);
580 procedure Allocate
is
581 new Generic_Allocate
(Assign_Key
);
587 procedure Assign_Key
(Node
: in out Node_Type
) is
590 Node
.Element
:= New_Item
;
597 function New_Node
return Count_Type
is
600 Allocate
(Container
, Result
);
604 -- Start of processing for Insert
607 Local_Insert
(Container
, Key
, Position
.Node
, Inserted
);
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 Length
(Container
) = 0;
645 access procedure (Container
: Map
; Position
: Cursor
))
647 procedure Process_Node
(Node
: Count_Type
);
648 pragma Inline
(Process_Node
);
650 procedure Local_Iterate
is new HT_Ops
.Generic_Iteration
(Process_Node
);
656 procedure Process_Node
(Node
: Count_Type
) is
658 Process
(Container
, (Node
=> Node
));
661 B
: Natural renames Container
'Unrestricted_Access.Busy
;
663 -- Start of processing for Iterate
669 Local_Iterate
(Container
);
683 function Key
(Container
: Map
; Position
: Cursor
) return Key_Type
is
685 if not Has_Element
(Container
, Position
) then
686 raise Constraint_Error
with
687 "Position cursor of function Key has no element";
690 pragma Assert
(Vet
(Container
, Position
), "bad cursor in function Key");
692 return Container
.Nodes
(Position
.Node
).Key
;
699 function Left
(Container
: Map
; Position
: Cursor
) return Map
is
701 C
: Map
(Container
.Capacity
, Container
.Modulus
) :=
702 Copy
(Container
, Container
.Capacity
);
708 if Curs
= No_Element
then
712 if not Has_Element
(Container
, Curs
) then
713 raise Constraint_Error
;
716 while Curs
.Node
/= 0 loop
719 Curs
:= Next
(Container
, (Node
=> Node
));
729 function Length
(Container
: Map
) return Count_Type
is
731 return Container
.Length
;
739 (Target
: in out Map
;
742 NN
: HT_Types
.Nodes_Type
renames Source
.Nodes
;
746 if Target
'Address = Source
'Address then
750 if Target
.Capacity
< Length
(Source
) then
751 raise Constraint_Error
with -- ???
752 "Source length exceeds Target capacity";
755 if Source
.Busy
> 0 then
756 raise Program_Error
with
757 "attempt to tamper with cursors of Source (list is busy)";
762 if Source
.Length
= 0 then
766 X
:= HT_Ops
.First
(Source
);
768 Insert
(Target
, NN
(X
).Key
, NN
(X
).Element
); -- optimize???
770 Y
:= HT_Ops
.Next
(Source
, X
);
772 HT_Ops
.Delete_Node_Sans_Free
(Source
, X
);
783 function Next
(Node
: Node_Type
) return Count_Type
is
788 function Next
(Container
: Map
; Position
: Cursor
) return Cursor
is
790 if Position
.Node
= 0 then
794 if not Has_Element
(Container
, Position
) then
795 raise Constraint_Error
796 with "Position has no element";
799 pragma Assert
(Vet
(Container
, Position
), "bad cursor in function Next");
802 Node
: constant Count_Type
:= HT_Ops
.Next
(Container
, Position
.Node
);
809 return (Node
=> Node
);
813 procedure Next
(Container
: Map
; Position
: in out Cursor
) is
815 Position
:= Next
(Container
, Position
);
822 function Overlap
(Left
, Right
: Map
) return Boolean is
823 Left_Node
: Count_Type
;
824 Left_Nodes
: Nodes_Type
renames Left
.Nodes
;
827 if Length
(Right
) = 0 or Length
(Left
) = 0 then
831 if Left
'Address = Right
'Address then
835 Left_Node
:= First
(Left
).Node
;
836 while Left_Node
/= 0 loop
838 N
: Node_Type
renames Left_Nodes
(Left_Node
);
839 E
: Key_Type
renames N
.Key
;
841 if Find
(Right
, E
).Node
/= 0 then
846 Left_Node
:= HT_Ops
.Next
(Left
, Left_Node
);
856 procedure Query_Element
857 (Container
: in out Map
;
859 Process
: not null access
860 procedure (Key
: Key_Type
; Element
: Element_Type
))
863 if not Has_Element
(Container
, Position
) then
864 raise Constraint_Error
with
865 "Position cursor of Query_Element has no element";
868 pragma Assert
(Vet
(Container
, Position
), "bad cursor in Query_Element");
871 N
: Node_Type
renames Container
.Nodes
(Position
.Node
);
872 B
: Natural renames Container
.Busy
;
873 L
: Natural renames Container
.Lock
;
880 K
: Key_Type
renames N
.Key
;
881 E
: Element_Type
renames N
.Element
;
901 (Stream
: not null access Root_Stream_Type
'Class;
904 function Read_Node
(Stream
: not null access Root_Stream_Type
'Class)
907 procedure Read_Nodes
is
908 new HT_Ops
.Generic_Read
(Read_Node
);
915 (Stream
: not null access Root_Stream_Type
'Class) return Count_Type
917 procedure Read_Element
(Node
: in out Node_Type
);
918 pragma Inline
(Read_Element
);
920 procedure Allocate
is
921 new Generic_Allocate
(Read_Element
);
923 procedure Read_Element
(Node
: in out Node_Type
) is
925 Element_Type
'Read (Stream
, Node
.Element
);
930 -- Start of processing for Read_Node
933 Allocate
(Container
, Node
);
937 -- Start of processing for Read
940 Read_Nodes
(Stream
, Container
);
944 (Stream
: not null access Root_Stream_Type
'Class;
948 raise Program_Error
with "attempt to stream set cursor";
956 (Container
: in out Map
;
958 New_Item
: Element_Type
)
960 Node
: constant Count_Type
:= Key_Ops
.Find
(Container
, Key
);
964 raise Constraint_Error
with
965 "attempt to replace key not in map";
968 if Container
.Lock
> 0 then
969 raise Program_Error
with
970 "Replace attempted to tamper with cursors (map is locked)";
974 N
: Node_Type
renames Container
.Nodes
(Node
);
977 N
.Element
:= New_Item
;
981 ---------------------
982 -- Replace_Element --
983 ---------------------
985 procedure Replace_Element
986 (Container
: in out Map
;
988 New_Item
: Element_Type
)
991 if not Has_Element
(Container
, Position
) then
992 raise Constraint_Error
with
993 "Position cursor of Replace_Element has no element";
996 if Container
.Lock
> 0 then
997 raise Program_Error
with
998 "Replace_Element attempted to tamper with cursors (map is locked)";
1001 pragma Assert
(Vet
(Container
, Position
),
1002 "bad cursor in Replace_Element");
1004 Container
.Nodes
(Position
.Node
).Element
:= New_Item
;
1005 end Replace_Element
;
1007 ----------------------
1008 -- Reserve_Capacity --
1009 ----------------------
1011 procedure Reserve_Capacity
1012 (Container
: in out Map
;
1013 Capacity
: Count_Type
)
1016 if Capacity
> Container
.Capacity
then
1017 raise Capacity_Error
with "requested capacity is too large";
1019 end Reserve_Capacity
;
1025 function Right
(Container
: Map
; Position
: Cursor
) return Map
is
1026 Curs
: Cursor
:= First
(Container
);
1027 C
: Map
(Container
.Capacity
, Container
.Modulus
) :=
1028 Copy
(Container
, Container
.Capacity
);
1032 if Curs
= No_Element
then
1037 if Position
/= No_Element
and not Has_Element
(Container
, Position
) then
1038 raise Constraint_Error
;
1041 while Curs
.Node
/= Position
.Node
loop
1044 Curs
:= Next
(Container
, (Node
=> Node
));
1054 procedure Set_Next
(Node
: in out Node_Type
; Next
: Count_Type
) is
1063 function Strict_Equal
(Left
, Right
: Map
) return Boolean is
1064 CuL
: Cursor
:= First
(Left
);
1065 CuR
: Cursor
:= First
(Right
);
1068 if Length
(Left
) /= Length
(Right
) then
1072 while CuL
.Node
/= 0 or CuR
.Node
/= 0 loop
1073 if CuL
.Node
/= CuR
.Node
or else
1074 (Left
.Nodes
(CuL
.Node
).Element
/=
1075 Right
.Nodes
(CuR
.Node
).Element
or
1076 Left
.Nodes
(CuL
.Node
).Key
/=
1077 Right
.Nodes
(CuR
.Node
).Key
) then
1081 CuL
:= Next
(Left
, CuL
);
1082 CuR
:= Next
(Right
, CuR
);
1088 --------------------
1089 -- Update_Element --
1090 --------------------
1092 procedure Update_Element
1093 (Container
: in out Map
;
1095 Process
: not null access procedure (Key
: Key_Type
;
1096 Element
: in out Element_Type
))
1099 if not Has_Element
(Container
, Position
) then
1100 raise Constraint_Error
with
1101 "Position cursor of Update_Element has no element";
1104 pragma Assert
(Vet
(Container
, Position
),
1105 "bad cursor in Update_Element");
1108 B
: Natural renames Container
.Busy
;
1109 L
: Natural renames Container
.Lock
;
1116 N
: Node_Type
renames Container
.Nodes
(Position
.Node
);
1117 K
: Key_Type
renames N
.Key
;
1118 E
: Element_Type
renames N
.Element
;
1138 function Vet
(Container
: Map
; Position
: Cursor
) return Boolean is
1140 if Position
.Node
= 0 then
1148 if Container
.Length
= 0 then
1152 if Container
.Capacity
= 0 then
1156 if Container
.Buckets
'Length = 0 then
1160 if Position
.Node
> Container
.Capacity
then
1164 if Container
.Nodes
(Position
.Node
).Next
= Position
.Node
then
1168 X
:= Container
.Buckets
1169 (Key_Ops
.Index
(Container
, Container
.Nodes
(Position
.Node
).Key
));
1171 for J
in 1 .. Container
.Length
loop
1172 if X
= Position
.Node
then
1180 if X
= Container
.Nodes
(X
).Next
then
1182 -- Prevent unnecessary looping
1187 X
:= Container
.Nodes
(X
).Next
;
1199 (Stream
: not null access Root_Stream_Type
'Class;
1202 procedure Write_Node
1203 (Stream
: not null access Root_Stream_Type
'Class;
1205 pragma Inline
(Write_Node
);
1207 procedure Write_Nodes
is new HT_Ops
.Generic_Write
(Write_Node
);
1213 procedure Write_Node
1214 (Stream
: not null access Root_Stream_Type
'Class;
1218 Key_Type
'Write (Stream
, Node
.Key
);
1219 Element_Type
'Write (Stream
, Node
.Element
);
1222 -- Start of processing for Write
1225 Write_Nodes
(Stream
, Container
);
1229 (Stream
: not null access Root_Stream_Type
'Class;
1233 raise Program_Error
with "attempt to stream map cursor";
1236 end Ada
.Containers
.Formal_Hashed_Maps
;