1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- ADA.CONTAINERS.HASH_TABLES.GENERIC_FORMAL_KEYS --
9 -- Copyright (C) 2004-2023, 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 package body Ada
.Containers
.Hash_Tables
.Generic_Formal_Keys
is
32 Checks
: constant Boolean := Container_Checks
'Enabled;
34 --------------------------
35 -- Delete_Key_Sans_Free --
36 --------------------------
38 procedure Delete_Key_Sans_Free
39 (HT
: in out Hash_Table_Type
;
52 Indx
:= Index
(HT
, Key
);
53 X
:= HT
.Buckets
(Indx
);
59 if Equivalent_Keys
(Key
, HT
.Nodes
(X
)) then
60 HT
.Buckets
(Indx
) := Next
(HT
.Nodes
(X
));
61 HT
.Length
:= HT
.Length
- 1;
67 X
:= Next
(HT
.Nodes
(Prev
));
73 if Equivalent_Keys
(Key
, HT
.Nodes
(X
)) then
74 Set_Next
(HT
.Nodes
(Prev
), Next
=> Next
(HT
.Nodes
(X
)));
75 HT
.Length
:= HT
.Length
- 1;
79 end Delete_Key_Sans_Free
;
86 (HT
: Hash_Table_Type
;
87 Key
: Key_Type
) return Count_Type
97 Indx
:= Index
(HT
, Key
);
99 Node
:= HT
.Buckets
(Indx
);
101 if Equivalent_Keys
(Key
, HT
.Nodes
(Node
)) then
104 Node
:= Next
(HT
.Nodes
(Node
));
110 --------------------------------
111 -- Generic_Conditional_Insert --
112 --------------------------------
114 procedure Generic_Conditional_Insert
115 (HT
: in out Hash_Table_Type
;
117 Node
: out Count_Type
;
118 Inserted
: out Boolean)
123 Indx
:= Index
(HT
, Key
);
124 Node
:= HT
.Buckets
(Indx
);
127 if Checks
and then HT
.Length
= HT
.Capacity
then
128 raise Capacity_Error
with "no more capacity for insertion";
132 Set_Next
(HT
.Nodes
(Node
), Next
=> 0);
136 HT
.Buckets
(Indx
) := Node
;
137 HT
.Length
:= HT
.Length
+ 1;
143 if Equivalent_Keys
(Key
, HT
.Nodes
(Node
)) then
148 Node
:= Next
(HT
.Nodes
(Node
));
153 if Checks
and then HT
.Length
= HT
.Capacity
then
154 raise Capacity_Error
with "no more capacity for insertion";
158 Set_Next
(HT
.Nodes
(Node
), Next
=> HT
.Buckets
(Indx
));
162 HT
.Buckets
(Indx
) := Node
;
163 HT
.Length
:= HT
.Length
+ 1;
164 end Generic_Conditional_Insert
;
166 -----------------------------
167 -- Generic_Replace_Element --
168 -----------------------------
170 procedure Generic_Replace_Element
171 (HT
: in out Hash_Table_Type
;
175 pragma Assert
(HT
.Length
> 0);
176 pragma Assert
(Node
/= 0);
178 BB
: Buckets_Type
renames HT
.Buckets
;
179 NN
: Nodes_Type
renames HT
.Nodes
;
181 Old_Indx
: Hash_Type
;
182 New_Indx
: constant Hash_Type
:= Index
(HT
, Key
);
184 New_Bucket
: Count_Type
renames BB
(New_Indx
);
188 Old_Indx
:= HT
.Buckets
'First + Hash
(NN
(Node
)) mod HT
.Buckets
'Length;
190 -- Replace_Element is allowed to change a node's key to Key
191 -- (generic formal operation Assign provides the mechanism), but
192 -- only if Key is not already in the hash table. (In a unique-key
193 -- hash table as this one, a key is mapped to exactly one node.)
195 if Equivalent_Keys
(Key
, NN
(Node
)) then
196 -- The new Key value is mapped to this same Node, so Node
197 -- stays in the same bucket.
199 Assign
(NN
(Node
), Key
);
203 -- Key is not equivalent to Node, so we now have to determine if it's
204 -- equivalent to some other node in the hash table. This is the case
205 -- irrespective of whether Key is in the same or a different bucket from
210 if Checks
and then Equivalent_Keys
(Key
, NN
(N
)) then
211 pragma Assert
(N
/= Node
);
212 raise Program_Error
with
213 "attempt to replace existing element";
219 -- We have determined that Key is not already in the hash table, so
220 -- the change is allowed.
222 if Old_Indx
= New_Indx
then
223 -- The node is already in the bucket implied by Key. In this case
224 -- we merely change its value without moving it.
226 Assign
(NN
(Node
), Key
);
230 -- The node is in a bucket different from the bucket implied by Key.
231 -- Do the assignment first, before moving the node, so that if Assign
232 -- propagates an exception, then the hash table will not have been
233 -- modified (except for any possible side-effect Assign had on Node).
235 Assign
(NN
(Node
), Key
);
237 -- Now we can safely remove the node from its current bucket
239 N
:= BB
(Old_Indx
); -- get value of first node in old bucket
240 pragma Assert
(N
/= 0);
242 if N
= Node
then -- node is first node in its bucket
243 BB
(Old_Indx
) := Next
(NN
(Node
));
246 pragma Assert
(HT
.Length
> 1);
250 pragma Assert
(M
/= 0);
253 Set_Next
(NN
(N
), Next
=> Next
(NN
(Node
)));
261 -- Now we link the node into its new bucket (corresponding to Key)
263 Set_Next
(NN
(Node
), Next
=> New_Bucket
);
265 end Generic_Replace_Element
;
272 (HT
: Hash_Table_Type
;
273 Key
: Key_Type
) return Hash_Type
is
275 return HT
.Buckets
'First + Hash
(Key
) mod HT
.Buckets
'Length;
278 end Ada
.Containers
.Hash_Tables
.Generic_Formal_Keys
;