PR c++/37276
[official-gcc.git] / gcc / ada / a-chtgke.adb
blob89649f33a5d1158388688d34f87fa3fbd51ee252
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT LIBRARY COMPONENTS --
4 -- --
5 -- ADA.CONTAINERS.HASH_TABLES.GENERIC_KEYS --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2004-2010, Free Software Foundation, Inc. --
10 -- --
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. --
17 -- --
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. --
21 -- --
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 -- --
27 -- This unit was originally developed by Matthew J Heaney. --
28 ------------------------------------------------------------------------------
30 package body Ada.Containers.Hash_Tables.Generic_Keys is
32 --------------------------
33 -- Delete_Key_Sans_Free --
34 --------------------------
36 procedure Delete_Key_Sans_Free
37 (HT : in out Hash_Table_Type;
38 Key : Key_Type;
39 X : out Node_Access)
41 Indx : Hash_Type;
42 Prev : Node_Access;
44 begin
45 if HT.Length = 0 then
46 X := null;
47 return;
48 end if;
50 Indx := Index (HT, Key);
51 X := HT.Buckets (Indx);
53 if X = null then
54 return;
55 end if;
57 if Equivalent_Keys (Key, X) then
58 if HT.Busy > 0 then
59 raise Program_Error with
60 "attempt to tamper with cursors (container is busy)";
61 end if;
62 HT.Buckets (Indx) := Next (X);
63 HT.Length := HT.Length - 1;
64 return;
65 end if;
67 loop
68 Prev := X;
69 X := Next (Prev);
71 if X = null then
72 return;
73 end if;
75 if Equivalent_Keys (Key, X) then
76 if HT.Busy > 0 then
77 raise Program_Error with
78 "attempt to tamper with cursors (container is busy)";
79 end if;
80 Set_Next (Node => Prev, Next => Next (X));
81 HT.Length := HT.Length - 1;
82 return;
83 end if;
84 end loop;
85 end Delete_Key_Sans_Free;
87 ----------
88 -- Find --
89 ----------
91 function Find
92 (HT : Hash_Table_Type;
93 Key : Key_Type) return Node_Access is
95 Indx : Hash_Type;
96 Node : Node_Access;
98 begin
99 if HT.Length = 0 then
100 return null;
101 end if;
103 Indx := Index (HT, Key);
105 Node := HT.Buckets (Indx);
106 while Node /= null loop
107 if Equivalent_Keys (Key, Node) then
108 return Node;
109 end if;
110 Node := Next (Node);
111 end loop;
113 return null;
114 end Find;
116 --------------------------------
117 -- Generic_Conditional_Insert --
118 --------------------------------
120 procedure Generic_Conditional_Insert
121 (HT : in out Hash_Table_Type;
122 Key : Key_Type;
123 Node : out Node_Access;
124 Inserted : out Boolean)
126 Indx : constant Hash_Type := Index (HT, Key);
127 B : Node_Access renames HT.Buckets (Indx);
129 begin
130 if B = null then
131 if HT.Busy > 0 then
132 raise Program_Error with
133 "attempt to tamper with cursors (container is busy)";
134 end if;
136 if HT.Length = Count_Type'Last then
137 raise Constraint_Error;
138 end if;
140 Node := New_Node (Next => null);
141 Inserted := True;
143 B := Node;
144 HT.Length := HT.Length + 1;
146 return;
147 end if;
149 Node := B;
150 loop
151 if Equivalent_Keys (Key, Node) then
152 Inserted := False;
153 return;
154 end if;
156 Node := Next (Node);
158 exit when Node = null;
159 end loop;
161 if HT.Busy > 0 then
162 raise Program_Error with
163 "attempt to tamper with cursors (container is busy)";
164 end if;
166 if HT.Length = Count_Type'Last then
167 raise Constraint_Error;
168 end if;
170 Node := New_Node (Next => B);
171 Inserted := True;
173 B := Node;
174 HT.Length := HT.Length + 1;
175 end Generic_Conditional_Insert;
177 -----------
178 -- Index --
179 -----------
181 function Index
182 (HT : Hash_Table_Type;
183 Key : Key_Type) return Hash_Type is
184 begin
185 return Hash (Key) mod HT.Buckets'Length;
186 end Index;
188 -----------------------------
189 -- Generic_Replace_Element --
190 -----------------------------
192 procedure Generic_Replace_Element
193 (HT : in out Hash_Table_Type;
194 Node : Node_Access;
195 Key : Key_Type)
197 pragma Assert (HT.Length > 0);
198 pragma Assert (Node /= null);
200 Old_Hash : constant Hash_Type := Hash (Node);
201 Old_Indx : constant Hash_Type := Old_Hash mod HT.Buckets'Length;
203 New_Hash : constant Hash_Type := Hash (Key);
204 New_Indx : constant Hash_Type := New_Hash mod HT.Buckets'Length;
206 New_Bucket : Node_Access renames HT.Buckets (New_Indx);
207 N, M : Node_Access;
209 begin
210 if Equivalent_Keys (Key, Node) then
211 pragma Assert (New_Hash = Old_Hash);
213 if HT.Lock > 0 then
214 raise Program_Error with
215 "attempt to tamper with elements (container is locked)";
216 end if;
218 -- We can change a node's key to Key (that's what Assign is for), but
219 -- only if Key is not already in the hash table. (In a unique-key
220 -- hash table as this one a key is mapped to exactly one node only.)
221 -- The exception is when Key is mapped to Node, in which case the
222 -- change is allowed.
224 Assign (Node, Key);
225 pragma Assert (Hash (Node) = New_Hash);
226 pragma Assert (Equivalent_Keys (Key, Node));
227 return;
228 end if;
230 -- Key is not equivalent to Node, so we now have to determine if it's
231 -- equivalent to some other node in the hash table. This is the case
232 -- irrespective of whether Key is in the same or a different bucket from
233 -- Node.
235 N := New_Bucket;
236 while N /= null loop
237 if Equivalent_Keys (Key, N) then
238 pragma Assert (N /= Node);
239 raise Program_Error with
240 "attempt to replace existing element";
241 end if;
243 N := Next (N);
244 end loop;
246 -- We have determined that Key is not already in the hash table, so
247 -- the change is tentatively allowed. We now perform the standard
248 -- checks to determine whether the hash table is locked (because you
249 -- cannot change an element while it's in use by Query_Element or
250 -- Update_Element), or if the container is busy (because moving a
251 -- node to a different bucket would interfere with iteration).
253 if Old_Indx = New_Indx then
254 -- The node is already in the bucket implied by Key. In this case
255 -- we merely change its value without moving it.
257 if HT.Lock > 0 then
258 raise Program_Error with
259 "attempt to tamper with elements (container is locked)";
260 end if;
262 Assign (Node, Key);
263 pragma Assert (Hash (Node) = New_Hash);
264 pragma Assert (Equivalent_Keys (Key, Node));
265 return;
266 end if;
268 -- The node is a bucket different from the bucket implied by Key
270 if HT.Busy > 0 then
271 raise Program_Error with
272 "attempt to tamper with cursors (container is busy)";
273 end if;
275 -- Do the assignment first, before moving the node, so that if Assign
276 -- propagates an exception, then the hash table will not have been
277 -- modified (except for any possible side-effect Assign had on Node).
279 Assign (Node, Key);
280 pragma Assert (Hash (Node) = New_Hash);
281 pragma Assert (Equivalent_Keys (Key, Node));
283 -- Now we can safely remove the node from its current bucket
285 N := HT.Buckets (Old_Indx);
286 pragma Assert (N /= null);
288 if N = Node then
289 HT.Buckets (Old_Indx) := Next (Node);
291 else
292 pragma Assert (HT.Length > 1);
294 loop
295 M := Next (N);
296 pragma Assert (M /= null);
298 if M = Node then
299 Set_Next (Node => N, Next => Next (Node));
300 exit;
301 end if;
303 N := M;
304 end loop;
305 end if;
307 -- Now we link the node into its new bucket (corresponding to Key)
309 Set_Next (Node => Node, Next => New_Bucket);
310 New_Bucket := Node;
311 end Generic_Replace_Element;
313 end Ada.Containers.Hash_Tables.Generic_Keys;