1-- Copyright 1994 Grady Booch 2-- Copyright 2003-2014 Simon Wright <simon@pushface.org> 3-- Copyright 2005 Martin Krischik 4 5-- This package is free software; you can redistribute it and/or 6-- modify it under terms of the GNU General Public License as 7-- published by the Free Software Foundation; either version 2, or 8-- (at your option) any later version. This package is distributed in 9-- the hope that it will be useful, but WITHOUT ANY WARRANTY; without 10-- even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11-- PARTICULAR PURPOSE. See the GNU General Public License for more 12-- details. You should have received a copy of the GNU General Public 13-- License distributed with this package; see file COPYING. If not, 14-- write to the Free Software Foundation, 59 Temple Place - Suite 15-- 330, Boston, MA 02111-1307, USA. 16 17-- As a special exception, if other files instantiate generics from 18-- this unit, or you link this unit with other files to produce an 19-- executable, this unit does not by itself cause the resulting 20-- executable to be covered by the GNU General Public License. This 21-- exception does not however invalidate any other reasons why the 22-- executable file might be covered by the GNU Public License. 23 24with Ada.Unchecked_Deallocation; 25with System.Address_To_Access_Conversions; 26 27package body BC.Support.Indefinite_Unmanaged is 28 use type IR.Pointer; 29 30 -- We can't take 'Access of components of constant (in parameter) 31 -- objects; but we need to be able to do this so that we can 32 -- update the cache (which doesn't violate the abstraction, just 33 -- the Ada restriction). This technique is due to Matthew Heaney. 34 package Allow_Access 35 is new System.Address_To_Access_Conversions (Unm_Node); 36 37 function Create (I : Item; Previous, Next : Node_Ref) return Node_Ref; 38 pragma Inline (Create); 39 40 function Create (I : Item; Previous, Next : Node_Ref) return Node_Ref is 41 Result : Node_Ref; 42 begin 43 Result := new Node'(Element => IR.Create (Value => I), 44 Previous => Previous, 45 Next => Next); 46 if Previous /= null then 47 Previous.Next := Result; 48 end if; 49 if Next /= null then 50 Next.Previous := Result; 51 end if; 52 return Result; 53 end Create; 54 55 procedure Delete_Node is new 56 Ada.Unchecked_Deallocation (Node, Node_Ref); 57 58 procedure Update_Cache (Obj : in out Unm_Node; Index : Positive); 59 60 procedure Update_Cache (Obj : in out Unm_Node; Index : Positive) is 61 begin 62 if Index > Obj.Size then 63 raise BC.Range_Error; 64 end if; 65 if Obj.Cache /= null then 66 if Index = Obj.Cache_Index then 67 return; 68 elsif Index = Obj.Cache_Index + 1 then 69 Obj.Cache := Obj.Cache.Next; 70 Obj.Cache_Index := Index; 71 return; 72 elsif Index = Obj.Cache_Index - 1 then 73 Obj.Cache := Obj.Cache.Previous; 74 Obj.Cache_Index := Index; 75 return; 76 end if; 77 end if; 78 declare 79 Ptr : Node_Ref := Obj.Rep; 80 begin 81 for I in 1 .. Index - 1 loop 82 Ptr := Ptr.Next; 83 end loop; 84 Obj.Cache := Ptr; 85 Obj.Cache_Index := Index; 86 end; 87 end Update_Cache; 88 89 function "=" (Left, Right : in Unm_Node) return Boolean is 90 begin 91 if Left.Size = Right.Size then 92 declare 93 Temp_L : Node_Ref := Left.Rep; 94 Temp_R : Node_Ref := Right.Rep; 95 begin 96 while Temp_L /= null loop 97 if IR.Value (Temp_L.Element) 98 /= IR.Value (Temp_R.Element) 99 then 100 return False; 101 end if; 102 Temp_L := Temp_L.Next; 103 Temp_R := Temp_R.Next; 104 end loop; 105 return True; 106 end; 107 else 108 return False; 109 end if; 110 end "="; 111 112 procedure Clear (Obj : in out Unm_Node) is 113 Ptr : Node_Ref; 114 begin 115 while Obj.Rep /= null loop 116 Ptr := Obj.Rep; 117 Obj.Rep := Obj.Rep.Next; 118 Delete_Node (Ptr); 119 end loop; 120 Obj.Last := null; 121 Obj.Size := 0; 122 Obj.Cache := null; 123 Obj.Cache_Index := 0; 124 end Clear; 125 126 procedure Insert (Obj : in out Unm_Node; Elem : Item) is 127 begin 128 Obj.Rep := Create (Elem, Previous => null, Next => Obj.Rep); 129 if Obj.Last = null then 130 Obj.Last := Obj.Rep; 131 end if; 132 Obj.Size := Obj.Size + 1; 133 Obj.Cache := Obj.Rep; 134 Obj.Cache_Index := 1; 135 end Insert; 136 137 procedure Insert (Obj : in out Unm_Node; Elem : Item; Before : Positive) is 138 begin 139 if Before > Obj.Size then 140 raise BC.Range_Error; 141 end if; 142 if Obj.Size = 0 or else Before = 1 then 143 Insert (Obj, Elem); 144 else 145 declare 146 Temp_Node : Node_Ref; 147 begin 148 Update_Cache (Obj, Before); 149 Temp_Node := Create (Elem, 150 Previous => Obj.Cache.Previous, 151 Next => Obj.Cache); 152 if Temp_Node.Previous = null then 153 Obj.Rep := Temp_Node; 154 end if; 155 Obj.Size := Obj.Size + 1; 156 Obj.Cache := Temp_Node; 157 end; 158 end if; 159 end Insert; 160 161 procedure Append (Obj : in out Unm_Node; Elem : Item) is 162 begin 163 Obj.Last := Create (Elem, Previous => Obj.Last, Next => null); 164 if Obj.Last.Previous /= null then 165 Obj.Last.Previous.Next := Obj.Last; 166 end if; 167 if Obj.Rep = null then 168 Obj.Rep := Obj.Last; 169 end if; 170 Obj.Size := Obj.Size + 1; 171 Obj.Cache := Obj.Last; 172 Obj.Cache_Index := Obj.Size; 173 end Append; 174 175 procedure Append (Obj : in out Unm_Node; Elem : Item; After : Positive) is 176 begin 177 if After > Obj.Size then 178 raise BC.Range_Error; 179 end if; 180 if Obj.Size = 0 then 181 Append (Obj, Elem); 182 else 183 declare 184 Temp_Node : Node_Ref; 185 begin 186 Update_Cache (Obj, After); 187 Temp_Node := Create (Elem, 188 Previous => Obj.Cache, 189 Next => Obj.Cache.Next); 190 if Temp_Node.Previous /= null then 191 Temp_Node.Previous.Next := Temp_Node; 192 end if; 193 if Temp_Node.Next = null then 194 Obj.Last := Temp_Node; 195 end if; 196 Obj.Size := Obj.Size + 1; 197 Obj.Cache := Temp_Node; 198 Obj.Cache_Index := Obj.Cache_Index + 1; 199 end; 200 end if; 201 end Append; 202 203 procedure Remove (Obj : in out Unm_Node; From : Positive) is 204 begin 205 if From > Obj.Size then 206 raise BC.Range_Error; 207 end if; 208 if Obj.Size = 0 then 209 raise BC.Underflow; 210 end if; 211 if Obj.Size = 1 then 212 Clear (Obj); 213 else 214 declare 215 Ptr : Node_Ref; 216 begin 217 Update_Cache (Obj, From); 218 Ptr := Obj.Cache; 219 if Ptr.Previous = null then 220 Obj.Rep := Ptr.Next; 221 else 222 Ptr.Previous.Next := Ptr.Next; 223 end if; 224 if Ptr.Next = null then 225 Obj.Last := Ptr.Previous; 226 else 227 Ptr.Next.Previous := Ptr.Previous; 228 end if; 229 Obj.Size := Obj.Size - 1; 230 if Ptr.Next /= null then 231 Obj.Cache := Ptr.Next; 232 elsif Ptr.Previous /= null then 233 Obj.Cache := Ptr.Previous; 234 Obj.Cache_Index := Obj.Cache_Index - 1; 235 else 236 Obj.Cache := null; 237 Obj.Cache_Index := 0; 238 end if; 239 Delete_Node (Ptr); 240 end; 241 end if; 242 end Remove; 243 244 procedure Replace (Obj : in out Unm_Node; Index : Positive; Elem : Item) is 245 begin 246 if Index > Obj.Size then 247 raise BC.Range_Error; 248 end if; 249 if not ((Obj.Cache /= null) and then (Index = Obj.Cache_Index)) then 250 declare 251 Ptr : Node_Ref := Obj.Rep; 252 begin 253 for I in 1 .. Obj.Size loop 254 if I = Index then 255 Obj.Cache := Ptr; 256 Obj.Cache_Index := I; 257 exit; 258 else 259 Ptr := Ptr.Next; 260 end if; 261 end loop; 262 end; 263 end if; 264 Obj.Cache.Element := IR.Create (Value => Elem); 265 end Replace; 266 267 function Length (Obj : Unm_Node) return Natural is 268 begin 269 return Obj.Size; 270 end Length; 271 272 function First (Obj : Unm_Node) return Item is 273 begin 274 if Obj.Size = 0 then 275 raise BC.Underflow; 276 end if; 277 return IR.Value (Obj.Rep.Element); 278 end First; 279 280 function Last (Obj : Unm_Node) return Item is 281 begin 282 if Obj.Size = 0 then 283 raise BC.Underflow; 284 end if; 285 return IR.Value (Obj.Last.Element); 286 end Last; 287 288 function Item_At (Obj : Unm_Node; Index : Positive) return Item is 289 Tmp : Item_Ptr; 290 begin 291 if Index > Obj.Size then 292 raise BC.Range_Error; 293 end if; 294 Tmp := Item_At (Obj, Index); 295 return Tmp.all; 296 end Item_At; 297 298 function Item_At (Obj : Unm_Node; Index : Positive) return Item_Ptr is 299 U : constant Allow_Access.Object_Pointer 300 := Allow_Access.To_Pointer (Obj'Address); 301 -- Note, although (GNAT 3.11p) the value in Obj is successfully 302 -- updated via U, the optimiser can get fooled; when we return 303 -- next/previous cache hits, we must return via U. I don't 304 -- think this is a bug; the pointer aliasing is a nasty trick, 305 -- after all. 306 begin 307 if Index > Obj.Size then 308 raise BC.Range_Error; 309 end if; 310 Update_Cache (U.all, Index); 311 return IR.Value_Access (U.Cache.Element); 312 end Item_At; 313 314 function Location (Obj : Unm_Node; Elem : Item; Start : Positive := 1) 315 return Natural is 316 Ptr : Node_Ref := Obj.Rep; 317 U : constant Allow_Access.Object_Pointer 318 := Allow_Access.To_Pointer (Obj'Address); 319 begin 320 -- XXX the C++ (which indexes from 0) nevertheless checks 321 -- "start <= count". We have to special-case the empty Node; 322 -- the C++ indexes from 0, so it can legally start with index 0 323 -- when the Node is empty. 324 if Obj.Size = 0 then 325 return 0; 326 end if; 327 if Start > Obj.Size then 328 raise BC.Range_Error; 329 end if; 330 if Start = Obj.Cache_Index 331 and then Elem = IR.Value (Obj.Cache.Element) 332 then 333 return Obj.Cache_Index; 334 end if; 335 for I in 1 .. Start - 1 loop 336 Ptr := Ptr.Next; -- advance to Start point 337 end loop; 338 for I in Start .. Obj.Size loop 339 if Ptr.Element = Elem then 340 U.Cache := Ptr; 341 U.Cache_Index := I; 342 return I; 343 else 344 Ptr := Ptr.Next; 345 end if; 346 end loop; 347 return 0; 348 end Location; 349 350 procedure Adjust (U : in out Unm_Node) is 351 Tmp : Node_Ref := U.Last; 352 begin 353 if Tmp /= null then 354 U.Last := Create (IR.Value (Tmp.Element), 355 Previous => null, 356 Next => null); 357 U.Rep := U.Last; 358 Tmp := Tmp.Previous; -- move to previous node from orig list 359 while Tmp /= null loop 360 U.Rep := Create (IR.Value (Tmp.Element), 361 Previous => null, 362 Next => U.Rep); 363 Tmp := Tmp.Previous; 364 end loop; 365 end if; 366 U.Cache := null; 367 U.Cache_Index := 0; 368 end Adjust; 369 370 procedure Finalize (U : in out Unm_Node) is 371 Ptr : Node_Ref; 372 begin 373 -- code to delete Rep copied from Clear() 374 while U.Rep /= null loop 375 Ptr := U.Rep; 376 U.Rep := U.Rep.Next; 377 Delete_Node (Ptr); 378 end loop; 379 end Finalize; 380 381 procedure Write_Unm_Node 382 (Stream : access Ada.Streams.Root_Stream_Type'Class; 383 Obj : Unm_Node) is 384 N : Node_Ref := Obj.Rep; 385 begin 386 Integer'Write (Stream, Obj.Size); 387 while N /= null loop 388 Item'Output (Stream, IR.Value (N.Element)); 389 N := N.Next; 390 end loop; 391 end Write_Unm_Node; 392 393 procedure Read_Unm_Node 394 (Stream : access Ada.Streams.Root_Stream_Type'Class; 395 Obj : out Unm_Node) is 396 Count : Integer; 397 begin 398 Clear (Obj); 399 Integer'Read (Stream, Count); 400 for I in 1 .. Count loop 401 declare 402 Elem : constant Item := Item'Input (Stream); 403 begin 404 Append (Obj, Elem); 405 end; 406 end loop; 407 end Read_Unm_Node; 408 409end BC.Support.Indefinite_Unmanaged; 410