1------------------------------------------------------------------------------ 2-- -- 3-- GNAT COMPILER COMPONENTS -- 4-- -- 5-- T A B L E -- 6-- -- 7-- B o d y -- 8-- -- 9-- Copyright (C) 1992-2014, 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-- GNAT was originally developed by the GNAT team at New York University. -- 28-- Extensive contributions were provided by Ada Core Technologies Inc. -- 29-- -- 30------------------------------------------------------------------------------ 31 32with Debug; use Debug; 33with Opt; use Opt; 34with Output; use Output; 35with System; use System; 36with Tree_IO; use Tree_IO; 37 38with System.Memory; use System.Memory; 39 40with Unchecked_Conversion; 41 42pragma Elaborate_All (Output); 43 44package body Table is 45 package body Table is 46 47 Min : constant Int := Int (Table_Low_Bound); 48 -- Subscript of the minimum entry in the currently allocated table 49 50 Length : Int := 0; 51 -- Number of entries in currently allocated table. The value of zero 52 -- ensures that we initially allocate the table. 53 54 ----------------------- 55 -- Local Subprograms -- 56 ----------------------- 57 58 procedure Reallocate; 59 -- Reallocate the existing table according to the current value stored 60 -- in Max. Works correctly to do an initial allocation if the table 61 -- is currently null. 62 63 function Tree_Get_Table_Address return Address; 64 -- Return Null_Address if the table length is zero, 65 -- Table (First)'Address if not. 66 67 pragma Warnings (Off); 68 -- Turn off warnings. The following unchecked conversions are only used 69 -- internally in this package, and cannot never result in any instances 70 -- of improperly aliased pointers for the client of the package. 71 72 function To_Address is new Unchecked_Conversion (Table_Ptr, Address); 73 function To_Pointer is new Unchecked_Conversion (Address, Table_Ptr); 74 75 pragma Warnings (On); 76 77 ------------ 78 -- Append -- 79 ------------ 80 81 procedure Append (New_Val : Table_Component_Type) is 82 begin 83 Set_Item (Table_Index_Type (Last_Val + 1), New_Val); 84 end Append; 85 86 ---------------- 87 -- Append_All -- 88 ---------------- 89 90 procedure Append_All (New_Vals : Table_Type) is 91 begin 92 for J in New_Vals'Range loop 93 Append (New_Vals (J)); 94 end loop; 95 end Append_All; 96 97 -------------------- 98 -- Decrement_Last -- 99 -------------------- 100 101 procedure Decrement_Last is 102 begin 103 Last_Val := Last_Val - 1; 104 end Decrement_Last; 105 106 ---------- 107 -- Free -- 108 ---------- 109 110 procedure Free is 111 begin 112 Free (To_Address (Table)); 113 Table := null; 114 Length := 0; 115 end Free; 116 117 -------------------- 118 -- Increment_Last -- 119 -------------------- 120 121 procedure Increment_Last is 122 begin 123 Last_Val := Last_Val + 1; 124 125 if Last_Val > Max then 126 Reallocate; 127 end if; 128 end Increment_Last; 129 130 ---------- 131 -- Init -- 132 ---------- 133 134 procedure Init is 135 Old_Length : constant Int := Length; 136 137 begin 138 Locked := False; 139 Last_Val := Min - 1; 140 Max := Min + (Table_Initial * Table_Factor) - 1; 141 Length := Max - Min + 1; 142 143 -- If table is same size as before (happens when table is never 144 -- expanded which is a common case), then simply reuse it. Note 145 -- that this also means that an explicit Init call right after 146 -- the implicit one in the package body is harmless. 147 148 if Old_Length = Length then 149 return; 150 151 -- Otherwise we can use Reallocate to get a table of the right size. 152 -- Note that Reallocate works fine to allocate a table of the right 153 -- initial size when it is first allocated. 154 155 else 156 Reallocate; 157 end if; 158 end Init; 159 160 ---------- 161 -- Last -- 162 ---------- 163 164 function Last return Table_Index_Type is 165 begin 166 return Table_Index_Type (Last_Val); 167 end Last; 168 169 ---------------- 170 -- Reallocate -- 171 ---------------- 172 173 procedure Reallocate is 174 New_Size : Memory.size_t; 175 New_Length : Long_Long_Integer; 176 177 begin 178 if Max < Last_Val then 179 pragma Assert (not Locked); 180 181 -- Make sure that we have at least the initial allocation. This 182 -- is needed in cases where a zero length table is written out. 183 184 Length := Int'Max (Length, Table_Initial); 185 186 -- Now increment table length until it is sufficiently large. Use 187 -- the increment value or 10, which ever is larger (the reason 188 -- for the use of 10 here is to ensure that the table does really 189 -- increase in size (which would not be the case for a table of 190 -- length 10 increased by 3% for instance). Do the intermediate 191 -- calculation in Long_Long_Integer to avoid overflow. 192 193 while Max < Last_Val loop 194 New_Length := 195 Long_Long_Integer (Length) * 196 (100 + Long_Long_Integer (Table_Increment)) / 100; 197 Length := Int'Max (Int (New_Length), Length + 10); 198 Max := Min + Length - 1; 199 end loop; 200 201 if Debug_Flag_D then 202 Write_Str ("--> Allocating new "); 203 Write_Str (Table_Name); 204 Write_Str (" table, size = "); 205 Write_Int (Max - Min + 1); 206 Write_Eol; 207 end if; 208 end if; 209 210 New_Size := 211 Memory.size_t ((Max - Min + 1) * 212 (Table_Type'Component_Size / Storage_Unit)); 213 214 if Table = null then 215 Table := To_Pointer (Alloc (New_Size)); 216 217 elsif New_Size > 0 then 218 Table := 219 To_Pointer (Realloc (Ptr => To_Address (Table), 220 Size => New_Size)); 221 end if; 222 223 if Length /= 0 and then Table = null then 224 Set_Standard_Error; 225 Write_Str ("available memory exhausted"); 226 Write_Eol; 227 Set_Standard_Output; 228 raise Unrecoverable_Error; 229 end if; 230 231 end Reallocate; 232 233 ------------- 234 -- Release -- 235 ------------- 236 237 procedure Release is 238 begin 239 Length := Last_Val - Int (Table_Low_Bound) + 1; 240 Max := Last_Val; 241 Reallocate; 242 end Release; 243 244 ------------- 245 -- Restore -- 246 ------------- 247 248 procedure Restore (T : Saved_Table) is 249 begin 250 Free (To_Address (Table)); 251 Last_Val := T.Last_Val; 252 Max := T.Max; 253 Table := T.Table; 254 Length := Max - Min + 1; 255 end Restore; 256 257 ---------- 258 -- Save -- 259 ---------- 260 261 function Save return Saved_Table is 262 Res : Saved_Table; 263 264 begin 265 Res.Last_Val := Last_Val; 266 Res.Max := Max; 267 Res.Table := Table; 268 269 Table := null; 270 Length := 0; 271 Init; 272 return Res; 273 end Save; 274 275 -------------- 276 -- Set_Item -- 277 -------------- 278 279 procedure Set_Item 280 (Index : Table_Index_Type; 281 Item : Table_Component_Type) 282 is 283 -- If Item is a value within the current allocation, and we are going 284 -- to reallocate, then we must preserve an intermediate copy here 285 -- before calling Increment_Last. Otherwise, if Table_Component_Type 286 -- is passed by reference, we are going to end up copying from 287 -- storage that might have been deallocated from Increment_Last 288 -- calling Reallocate. 289 290 subtype Allocated_Table_T is 291 Table_Type (Table'First .. Table_Index_Type (Max + 1)); 292 -- A constrained table subtype one element larger than the currently 293 -- allocated table. 294 295 Allocated_Table_Address : constant System.Address := 296 Table.all'Address; 297 -- Used for address clause below (we can't use non-static expression 298 -- Table.all'Address directly in the clause because some older 299 -- versions of the compiler do not allow it). 300 301 Allocated_Table : Allocated_Table_T; 302 pragma Import (Ada, Allocated_Table); 303 pragma Suppress (Range_Check, On => Allocated_Table); 304 for Allocated_Table'Address use Allocated_Table_Address; 305 -- Allocated_Table represents the currently allocated array, plus one 306 -- element (the supplementary element is used to have a convenient 307 -- way of computing the address just past the end of the current 308 -- allocation). Range checks are suppressed because this unit 309 -- uses direct calls to System.Memory for allocation, and this can 310 -- yield misaligned storage (and we cannot rely on the bootstrap 311 -- compiler supporting specifically disabling alignment checks, so we 312 -- need to suppress all range checks). It is safe to suppress this 313 -- check here because we know that a (possibly misaligned) object 314 -- of that type does actually exist at that address. 315 -- ??? We should really improve the allocation circuitry here to 316 -- guarantee proper alignment. 317 318 Need_Realloc : constant Boolean := Int (Index) > Max; 319 -- True if this operation requires storage reallocation (which may 320 -- involve moving table contents around). 321 322 begin 323 -- If we're going to reallocate, check whether Item references an 324 -- element of the currently allocated table. 325 326 if Need_Realloc 327 and then Allocated_Table'Address <= Item'Address 328 and then Item'Address < 329 Allocated_Table (Table_Index_Type (Max + 1))'Address 330 then 331 -- If so, save a copy on the stack because Increment_Last will 332 -- reallocate storage and might deallocate the current table. 333 334 declare 335 Item_Copy : constant Table_Component_Type := Item; 336 begin 337 Set_Last (Index); 338 Table (Index) := Item_Copy; 339 end; 340 341 else 342 -- Here we know that either we won't reallocate (case of Index < 343 -- Max) or that Item is not in the currently allocated table. 344 345 if Int (Index) > Last_Val then 346 Set_Last (Index); 347 end if; 348 349 Table (Index) := Item; 350 end if; 351 end Set_Item; 352 353 -------------- 354 -- Set_Last -- 355 -------------- 356 357 procedure Set_Last (New_Val : Table_Index_Type) is 358 begin 359 if Int (New_Val) < Last_Val then 360 Last_Val := Int (New_Val); 361 362 else 363 Last_Val := Int (New_Val); 364 365 if Last_Val > Max then 366 Reallocate; 367 end if; 368 end if; 369 end Set_Last; 370 371 ---------------------------- 372 -- Tree_Get_Table_Address -- 373 ---------------------------- 374 375 function Tree_Get_Table_Address return Address is 376 begin 377 if Length = 0 then 378 return Null_Address; 379 else 380 return Table (First)'Address; 381 end if; 382 end Tree_Get_Table_Address; 383 384 --------------- 385 -- Tree_Read -- 386 --------------- 387 388 -- Note: we allocate only the space required to accommodate the data 389 -- actually written, which means that a Tree_Write/Tree_Read sequence 390 -- does an implicit Release. 391 392 procedure Tree_Read is 393 begin 394 Tree_Read_Int (Max); 395 Last_Val := Max; 396 Length := Max - Min + 1; 397 Reallocate; 398 399 Tree_Read_Data 400 (Tree_Get_Table_Address, 401 (Last_Val - Int (First) + 1) * 402 403 -- Note the importance of parenthesizing the following division 404 -- to avoid the possibility of intermediate overflow. 405 406 (Table_Type'Component_Size / Storage_Unit)); 407 end Tree_Read; 408 409 ---------------- 410 -- Tree_Write -- 411 ---------------- 412 413 -- Note: we write out only the currently valid data, not the entire 414 -- contents of the allocated array. See note above on Tree_Read. 415 416 procedure Tree_Write is 417 begin 418 Tree_Write_Int (Int (Last)); 419 Tree_Write_Data 420 (Tree_Get_Table_Address, 421 (Last_Val - Int (First) + 1) * 422 (Table_Type'Component_Size / Storage_Unit)); 423 end Tree_Write; 424 425 begin 426 Init; 427 end Table; 428end Table; 429