OSDN Git Service

gcc/ada/
[pf3gnuchains/gcc-fork.git] / gcc / ada / g-dyntab.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT COMPILER COMPONENTS                         --
4 --                                                                          --
5 --                   G N A T . D Y N A M I C _ T A B L E S                  --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --                     Copyright (C) 2000-2007, AdaCore                     --
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 2,  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.  See the GNU General Public License --
17 -- for  more details.  You should have  received  a copy of the GNU General --
18 -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
19 -- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
20 -- Boston, MA 02110-1301, USA.                                              --
21 --                                                                          --
22 -- As a special exception,  if other files  instantiate  generics from this --
23 -- unit, or you link  this unit with other files  to produce an executable, --
24 -- this  unit  does not  by itself cause  the resulting  executable  to  be --
25 -- covered  by the  GNU  General  Public  License.  This exception does not --
26 -- however invalidate  any other reasons why  the executable file  might be --
27 -- covered by the  GNU Public License.                                      --
28 --                                                                          --
29 -- GNAT was originally developed  by the GNAT team at  New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
31 --                                                                          --
32 ------------------------------------------------------------------------------
33
34 with GNAT.Heap_Sort_G;
35 with System;        use System;
36 with System.Memory; use System.Memory;
37
38 with Ada.Unchecked_Conversion;
39
40 package body GNAT.Dynamic_Tables is
41
42    Min : constant Integer := Integer (Table_Low_Bound);
43    --  Subscript of the minimum entry in the currently allocated table
44
45    -----------------------
46    -- Local Subprograms --
47    -----------------------
48
49    procedure Reallocate (T : in out Instance);
50    --  Reallocate the existing table according to the current value stored
51    --  in Max. Works correctly to do an initial allocation if the table
52    --  is currently null.
53
54    pragma Warnings (Off);
55    --  These unchecked conversions are in fact safe, since they never
56    --  generate improperly aliased pointer values.
57
58    function To_Address is new Ada.Unchecked_Conversion (Table_Ptr, Address);
59    function To_Pointer is new Ada.Unchecked_Conversion (Address, Table_Ptr);
60
61    pragma Warnings (On);
62
63    --------------
64    -- Allocate --
65    --------------
66
67    procedure Allocate
68      (T   : in out Instance;
69       Num : Integer := 1)
70    is
71    begin
72       T.P.Last_Val := T.P.Last_Val + Num;
73
74       if T.P.Last_Val > T.P.Max then
75          Reallocate (T);
76       end if;
77    end Allocate;
78
79    ------------
80    -- Append --
81    ------------
82
83    procedure Append (T : in out Instance; New_Val : Table_Component_Type) is
84    begin
85       Set_Item (T, Table_Index_Type (T.P.Last_Val + 1), New_Val);
86    end Append;
87
88    --------------------
89    -- Decrement_Last --
90    --------------------
91
92    procedure Decrement_Last (T : in out Instance) is
93    begin
94       T.P.Last_Val := T.P.Last_Val - 1;
95    end Decrement_Last;
96
97    --------------
98    -- For_Each --
99    --------------
100
101    procedure For_Each (Table : Instance) is
102       Quit : Boolean := False;
103    begin
104       for Index in Table_Low_Bound .. Table_Index_Type (Table.P.Last_Val) loop
105          Action (Index, Table.Table (Index), Quit);
106          exit when Quit;
107       end loop;
108    end For_Each;
109
110    ----------
111    -- Free --
112    ----------
113
114    procedure Free (T : in out Instance) is
115    begin
116       Free (To_Address (T.Table));
117       T.Table := null;
118       T.P.Length := 0;
119    end Free;
120
121    --------------------
122    -- Increment_Last --
123    --------------------
124
125    procedure Increment_Last (T : in out Instance) is
126    begin
127       T.P.Last_Val := T.P.Last_Val + 1;
128
129       if T.P.Last_Val > T.P.Max then
130          Reallocate (T);
131       end if;
132    end Increment_Last;
133
134    ----------
135    -- Init --
136    ----------
137
138    procedure Init (T : in out Instance) is
139       Old_Length : constant Integer := T.P.Length;
140
141    begin
142       T.P.Last_Val := Min - 1;
143       T.P.Max      := Min + Table_Initial - 1;
144       T.P.Length   := T.P.Max - Min + 1;
145
146       --  If table is same size as before (happens when table is never
147       --  expanded which is a common case), then simply reuse it. Note
148       --  that this also means that an explicit Init call right after
149       --  the implicit one in the package body is harmless.
150
151       if Old_Length = T.P.Length then
152          return;
153
154       --  Otherwise we can use Reallocate to get a table of the right size.
155       --  Note that Reallocate works fine to allocate a table of the right
156       --  initial size when it is first allocated.
157
158       else
159          Reallocate (T);
160       end if;
161    end Init;
162
163    ----------
164    -- Last --
165    ----------
166
167    function Last (T : Instance) return Table_Index_Type is
168    begin
169       return Table_Index_Type (T.P.Last_Val);
170    end Last;
171
172    ----------------
173    -- Reallocate --
174    ----------------
175
176    procedure Reallocate (T : in out Instance) is
177       New_Length : Integer;
178       New_Size   : size_t;
179
180    begin
181       if T.P.Max < T.P.Last_Val then
182          while T.P.Max < T.P.Last_Val loop
183             New_Length := T.P.Length * (100 + Table_Increment) / 100;
184
185             if New_Length > T.P.Length then
186                T.P.Length := New_Length;
187             else
188                T.P.Length := T.P.Length + 1;
189             end if;
190
191             T.P.Max := Min + T.P.Length - 1;
192          end loop;
193       end if;
194
195       New_Size :=
196         size_t ((T.P.Max - Min + 1) *
197                 (Table_Type'Component_Size / Storage_Unit));
198
199       if T.Table = null then
200          T.Table := To_Pointer (Alloc (New_Size));
201
202       elsif New_Size > 0 then
203          T.Table :=
204            To_Pointer (Realloc (Ptr  => To_Address (T.Table),
205                                 Size => New_Size));
206       end if;
207
208       if T.P.Length /= 0 and then T.Table = null then
209          raise Storage_Error;
210       end if;
211    end Reallocate;
212
213    -------------
214    -- Release --
215    -------------
216
217    procedure Release (T : in out Instance) is
218    begin
219       T.P.Length := T.P.Last_Val - Integer (Table_Low_Bound) + 1;
220       T.P.Max    := T.P.Last_Val;
221       Reallocate (T);
222    end Release;
223
224    --------------
225    -- Set_Item --
226    --------------
227
228    procedure Set_Item
229       (T     : in out Instance;
230        Index : Table_Index_Type;
231        Item  : Table_Component_Type)
232    is
233       --  If Item is a value within the current allocation, and we are going to
234       --  reallocate, then we must preserve an intermediate copy here before
235       --  calling Increment_Last. Otherwise, if Table_Component_Type is passed
236       --  by reference, we are going to end up copying from storage that might
237       --  have been deallocated from Increment_Last calling Reallocate.
238
239       subtype Allocated_Table_T is
240         Table_Type (T.Table'First .. Table_Index_Type (T.P.Max + 1));
241       --  A constrained table subtype one element larger than the currently
242       --  allocated table.
243
244       Allocated_Table_Address : constant System.Address :=
245                                   T.Table.all'Address;
246       --  Used for address clause below (we can't use non-static expression
247       --  Table.all'Address directly in the clause because some older versions
248       --  of the compiler do not allow it).
249
250       Allocated_Table : Allocated_Table_T;
251       pragma Import (Ada, Allocated_Table);
252       pragma Suppress (Range_Check, On => Allocated_Table);
253       for Allocated_Table'Address use Allocated_Table_Address;
254       --  Allocated_Table represents the currently allocated array, plus one
255       --  element (the supplementary element is used to have a convenient way
256       --  to the address just past the end of the current allocation). Range
257       --  checks are suppressed because this unit uses direct calls to
258       --  System.Memory for allocation, and this can yield misaligned storage
259       --  (and we cannot rely on the bootstrap compiler supporting specifically
260       --  disabling alignment cheks, so we need to suppress all range checks).
261       --  It is safe to suppress this check here because we know that a
262       --  (possibly misaligned) object of that type does actually exist at that
263       --  address.
264       --  ??? We should really improve the allocation circuitry here to
265       --  guarantee proper alignment.
266
267       Need_Realloc : constant Boolean := Integer (Index) > T.P.Max;
268       --  True if this operation requires storage reallocation (which may
269       --  involve moving table contents around).
270
271    begin
272       --  If we're going to reallocate, check wheter Item references an
273       --  element of the currently allocated table.
274
275       if Need_Realloc
276         and then Allocated_Table'Address <= Item'Address
277         and then Item'Address <
278                    Allocated_Table (Table_Index_Type (T.P.Max + 1))'Address
279       then
280          --  If so, save a copy on the stack because Increment_Last will
281          --  reallocate storage and might deallocate the current table.
282
283          declare
284             Item_Copy : constant Table_Component_Type := Item;
285          begin
286             Set_Last (T, Index);
287             T.Table (Index) := Item_Copy;
288          end;
289
290       else
291          --  Here we know that either we won't reallocate (case of Index < Max)
292          --  or that Item is not in the currently allocated table.
293
294          if Integer (Index) > T.P.Last_Val then
295             Set_Last (T, Index);
296          end if;
297
298          T.Table (Index) := Item;
299       end if;
300    end Set_Item;
301
302    --------------
303    -- Set_Last --
304    --------------
305
306    procedure Set_Last (T : in out Instance; New_Val : Table_Index_Type) is
307    begin
308       if Integer (New_Val) < T.P.Last_Val then
309          T.P.Last_Val := Integer (New_Val);
310
311       else
312          T.P.Last_Val := Integer (New_Val);
313
314          if T.P.Last_Val > T.P.Max then
315             Reallocate (T);
316          end if;
317       end if;
318    end Set_Last;
319
320    ----------------
321    -- Sort_Table --
322    ----------------
323
324    procedure Sort_Table (Table : in out Instance) is
325
326       Temp : Table_Component_Type;
327       --  A temporary position to simulate index 0
328
329       --  Local subprograms
330
331       function Index_Of (Idx : Natural) return Table_Index_Type;
332       --  Return index of Idx'th element of table
333
334       function Lower_Than (Op1, Op2 : Natural) return Boolean;
335       --  Compare two components
336
337       procedure Move (From : Natural; To : Natural);
338       --  Move one component
339
340       package Heap_Sort is new GNAT.Heap_Sort_G (Move, Lower_Than);
341
342       --------------
343       -- Index_Of --
344       --------------
345
346       function Index_Of (Idx : Natural) return Table_Index_Type is
347          J : constant Integer'Base :=
348                Table_Index_Type'Pos (First) + Idx - 1;
349       begin
350          return Table_Index_Type'Val (J);
351       end Index_Of;
352
353       ----------
354       -- Move --
355       ----------
356
357       procedure Move (From : Natural; To : Natural) is
358       begin
359          if From = 0 then
360             Table.Table (Index_Of (To)) := Temp;
361
362          elsif To = 0 then
363             Temp := Table.Table (Index_Of (From));
364
365          else
366             Table.Table (Index_Of (To)) :=
367               Table.Table (Index_Of (From));
368          end if;
369       end Move;
370
371       ----------------
372       -- Lower_Than --
373       ----------------
374
375       function Lower_Than (Op1, Op2 : Natural) return Boolean is
376       begin
377          if Op1 = 0 then
378             return Lt (Temp, Table.Table (Index_Of (Op2)));
379
380          elsif Op2 = 0 then
381             return Lt (Table.Table (Index_Of (Op1)), Temp);
382
383          else
384             return
385               Lt (Table.Table (Index_Of (Op1)),
386                    Table.Table (Index_Of (Op2)));
387          end if;
388       end Lower_Than;
389
390    --  Start of processing for Sort_Table
391
392    begin
393       Heap_Sort.Sort (Natural (Last (Table) - First) + 1);
394    end Sort_Table;
395
396 end GNAT.Dynamic_Tables;