1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- ADA.CONTAINERS.HASH_TABLES.GENERIC_BOUNDED_KEYS --
9 -- Copyright (C) 2004-2013, 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_Bounded_Keys
is
32 -----------------------------
33 -- Checked_Equivalent_Keys --
34 -----------------------------
36 function Checked_Equivalent_Keys
37 (HT
: aliased in out Hash_Table_Type
'Class;
39 Node
: Count_Type
) return Boolean
43 B
: Natural renames HT
.Busy
;
44 L
: Natural renames HT
.Lock
;
50 Result
:= Equivalent_Keys
(Key
, HT
.Nodes
(Node
));
63 end Checked_Equivalent_Keys
;
69 function Checked_Index
70 (HT
: aliased in out Hash_Table_Type
'Class;
71 Key
: Key_Type
) return Hash_Type
75 B
: Natural renames HT
.Busy
;
76 L
: Natural renames HT
.Lock
;
82 Result
:= HT
.Buckets
'First + Hash
(Key
) mod HT
.Buckets
'Length;
97 --------------------------
98 -- Delete_Key_Sans_Free --
99 --------------------------
101 procedure Delete_Key_Sans_Free
102 (HT
: in out Hash_Table_Type
'Class;
110 if HT
.Length
= 0 then
115 -- Per AI05-0022, the container implementation is required to detect
116 -- element tampering by a generic actual subprogram.
119 raise Program_Error
with
120 "attempt to tamper with cursors (container is busy)";
123 Indx
:= Checked_Index
(HT
, Key
);
124 X
:= HT
.Buckets
(Indx
);
130 if Checked_Equivalent_Keys
(HT
, Key
, X
) then
132 raise Program_Error
with
133 "attempt to tamper with cursors (container is busy)";
135 HT
.Buckets
(Indx
) := Next
(HT
.Nodes
(X
));
136 HT
.Length
:= HT
.Length
- 1;
142 X
:= Next
(HT
.Nodes
(Prev
));
148 if Checked_Equivalent_Keys
(HT
, Key
, X
) then
150 raise Program_Error
with
151 "attempt to tamper with cursors (container is busy)";
153 Set_Next
(HT
.Nodes
(Prev
), Next
=> Next
(HT
.Nodes
(X
)));
154 HT
.Length
:= HT
.Length
- 1;
158 end Delete_Key_Sans_Free
;
165 (HT
: Hash_Table_Type
'Class;
166 Key
: Key_Type
) return Count_Type
172 if HT
.Length
= 0 then
176 Indx
:= Checked_Index
(HT
'Unrestricted_Access.all, Key
);
178 Node
:= HT
.Buckets
(Indx
);
180 if Checked_Equivalent_Keys
181 (HT
'Unrestricted_Access.all, Key
, Node
)
185 Node
:= Next
(HT
.Nodes
(Node
));
191 --------------------------------
192 -- Generic_Conditional_Insert --
193 --------------------------------
195 procedure Generic_Conditional_Insert
196 (HT
: in out Hash_Table_Type
'Class;
198 Node
: out Count_Type
;
199 Inserted
: out Boolean)
204 -- Per AI05-0022, the container implementation is required to detect
205 -- element tampering by a generic actual subprogram.
208 raise Program_Error
with
209 "attempt to tamper with cursors (container is busy)";
212 Indx
:= Checked_Index
(HT
, Key
);
213 Node
:= HT
.Buckets
(Indx
);
216 if HT
.Length
= HT
.Capacity
then
217 raise Capacity_Error
with "no more capacity for insertion";
221 Set_Next
(HT
.Nodes
(Node
), Next
=> 0);
225 HT
.Buckets
(Indx
) := Node
;
226 HT
.Length
:= HT
.Length
+ 1;
232 if Checked_Equivalent_Keys
(HT
, Key
, Node
) then
237 Node
:= Next
(HT
.Nodes
(Node
));
242 if HT
.Length
= HT
.Capacity
then
243 raise Capacity_Error
with "no more capacity for insertion";
247 Set_Next
(HT
.Nodes
(Node
), Next
=> HT
.Buckets
(Indx
));
251 HT
.Buckets
(Indx
) := Node
;
252 HT
.Length
:= HT
.Length
+ 1;
253 end Generic_Conditional_Insert
;
255 -----------------------------
256 -- Generic_Replace_Element --
257 -----------------------------
259 procedure Generic_Replace_Element
260 (HT
: in out Hash_Table_Type
'Class;
264 pragma Assert
(HT
.Length
> 0);
265 pragma Assert
(Node
/= 0);
267 BB
: Buckets_Type
renames HT
.Buckets
;
268 NN
: Nodes_Type
renames HT
.Nodes
;
270 Old_Indx
: Hash_Type
;
271 New_Indx
: constant Hash_Type
:= Checked_Index
(HT
, Key
);
273 New_Bucket
: Count_Type
renames BB
(New_Indx
);
277 -- Per AI05-0022, the container implementation is required to detect
278 -- element tampering by a generic actual subprogram.
280 -- The following block appears to be vestigial -- this should be done
281 -- using Checked_Index instead. Also, we might have to move the actual
282 -- tampering checks to the top of the subprogram, in order to prevent
283 -- infinite recursion when calling Hash. (This is similar to how Insert
284 -- and Delete are implemented.) This implies that we will have to defer
285 -- the computation of New_Index until after the tampering check. ???
288 B
: Natural renames HT
.Busy
;
289 L
: Natural renames HT
.Lock
;
295 Old_Indx
:= HT
.Buckets
'First + Hash
(NN
(Node
)) mod HT
.Buckets
'Length;
308 -- Replace_Element is allowed to change a node's key to Key
309 -- (generic formal operation Assign provides the mechanism), but
310 -- only if Key is not already in the hash table. (In a unique-key
311 -- hash table as this one, a key is mapped to exactly one node.)
313 if Checked_Equivalent_Keys
(HT
, Key
, Node
) then
315 raise Program_Error
with
316 "attempt to tamper with elements (container is locked)";
319 -- The new Key value is mapped to this same Node, so Node
320 -- stays in the same bucket.
322 Assign
(NN
(Node
), Key
);
326 -- Key is not equivalent to Node, so we now have to determine if it's
327 -- equivalent to some other node in the hash table. This is the case
328 -- irrespective of whether Key is in the same or a different bucket from
333 if Checked_Equivalent_Keys
(HT
, Key
, N
) then
334 pragma Assert
(N
/= Node
);
335 raise Program_Error
with
336 "attempt to replace existing element";
342 -- We have determined that Key is not already in the hash table, so
343 -- the change is tentatively allowed. We now perform the standard
344 -- checks to determine whether the hash table is locked (because you
345 -- cannot change an element while it's in use by Query_Element or
346 -- Update_Element), or if the container is busy (because moving a
347 -- node to a different bucket would interfere with iteration).
349 if Old_Indx
= New_Indx
then
350 -- The node is already in the bucket implied by Key. In this case
351 -- we merely change its value without moving it.
354 raise Program_Error
with
355 "attempt to tamper with elements (container is locked)";
358 Assign
(NN
(Node
), Key
);
362 -- The node is a bucket different from the bucket implied by Key
365 raise Program_Error
with
366 "attempt to tamper with cursors (container is busy)";
369 -- Do the assignment first, before moving the node, so that if Assign
370 -- propagates an exception, then the hash table will not have been
371 -- modified (except for any possible side-effect Assign had on Node).
373 Assign
(NN
(Node
), Key
);
375 -- Now we can safely remove the node from its current bucket
377 N
:= BB
(Old_Indx
); -- get value of first node in old bucket
378 pragma Assert
(N
/= 0);
380 if N
= Node
then -- node is first node in its bucket
381 BB
(Old_Indx
) := Next
(NN
(Node
));
384 pragma Assert
(HT
.Length
> 1);
388 pragma Assert
(M
/= 0);
391 Set_Next
(NN
(N
), Next
=> Next
(NN
(Node
)));
399 -- Now we link the node into its new bucket (corresponding to Key)
401 Set_Next
(NN
(Node
), Next
=> New_Bucket
);
403 end Generic_Replace_Element
;
410 (HT
: Hash_Table_Type
'Class;
411 Key
: Key_Type
) return Hash_Type
is
413 return HT
.Buckets
'First + Hash
(Key
) mod HT
.Buckets
'Length;
416 end Ada
.Containers
.Hash_Tables
.Generic_Bounded_Keys
;