1------------------------------------------------------------------------------ 2-- -- 3-- GNAT COMPILER COMPONENTS -- 4-- -- 5-- T R E E _ I O -- 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 Output; use Output; 34with Unchecked_Conversion; 35 36package body Tree_IO is 37 Debug_Flag_Tree : Boolean := False; 38 -- Debug flag for debug output from tree read/write 39 40 ------------------------------------------- 41 -- Compression Scheme Used for Tree File -- 42 ------------------------------------------- 43 44 -- We don't just write the data directly, but instead do a mild form 45 -- of compression, since we expect lots of compressible zeroes and 46 -- blanks. The compression scheme is as follows: 47 48 -- 00nnnnnn followed by nnnnnn bytes (non compressed data) 49 -- 01nnnnnn indicates nnnnnn binary zero bytes 50 -- 10nnnnnn indicates nnnnnn ASCII space bytes 51 -- 11nnnnnn bbbbbbbb indicates nnnnnnnn occurrences of byte bbbbbbbb 52 53 -- Since we expect many zeroes in trees, and many spaces in sources, 54 -- this compression should be reasonably efficient. We can put in 55 -- something better later on. 56 57 -- Note that this compression applies to the Write_Tree_Data and 58 -- Read_Tree_Data calls, not to the calls to read and write single 59 -- scalar values, which are written in memory format without any 60 -- compression. 61 62 C_Noncomp : constant := 2#00_000000#; 63 C_Zeros : constant := 2#01_000000#; 64 C_Spaces : constant := 2#10_000000#; 65 C_Repeat : constant := 2#11_000000#; 66 -- Codes for compression sequences 67 68 Max_Count : constant := 63; 69 -- Maximum data length for one compression sequence 70 71 -- The above compression scheme applies only to data written with the 72 -- Tree_Write routine and read with Tree_Read. Data written using the 73 -- Tree_Write_Char or Tree_Write_Int routines and read using the 74 -- corresponding input routines is not compressed. 75 76 type Int_Bytes is array (1 .. 4) of Byte; 77 for Int_Bytes'Size use 32; 78 79 function To_Int_Bytes is new Unchecked_Conversion (Int, Int_Bytes); 80 function To_Int is new Unchecked_Conversion (Int_Bytes, Int); 81 82 ---------------------- 83 -- Global Variables -- 84 ---------------------- 85 86 Tree_FD : File_Descriptor; 87 -- File descriptor for tree 88 89 Buflen : constant Int := 8_192; 90 -- Length of buffer for read and write file data 91 92 Buf : array (Pos range 1 .. Buflen) of Byte; 93 -- Read/write file data buffer 94 95 Bufn : Nat; 96 -- Number of bytes read/written from/to buffer 97 98 Buft : Nat; 99 -- Total number of bytes in input buffer containing valid data. Used only 100 -- for input operations. There is data left to be processed in the buffer 101 -- if Buft > Bufn. A value of zero for Buft means that the buffer is empty. 102 103 ----------------------- 104 -- Local Subprograms -- 105 ----------------------- 106 107 procedure Read_Buffer; 108 -- Reads data into buffer, setting Bufn appropriately 109 110 function Read_Byte return Byte; 111 pragma Inline (Read_Byte); 112 -- Returns next byte from input file, raises Tree_Format_Error if none left 113 114 procedure Write_Buffer; 115 -- Writes out current buffer contents 116 117 procedure Write_Byte (B : Byte); 118 pragma Inline (Write_Byte); 119 -- Write one byte to output buffer, checking for buffer-full condition 120 121 ----------------- 122 -- Read_Buffer -- 123 ----------------- 124 125 procedure Read_Buffer is 126 begin 127 Buft := Int (Read (Tree_FD, Buf (1)'Address, Integer (Buflen))); 128 129 if Buft = 0 then 130 raise Tree_Format_Error; 131 else 132 Bufn := 0; 133 end if; 134 end Read_Buffer; 135 136 --------------- 137 -- Read_Byte -- 138 --------------- 139 140 function Read_Byte return Byte is 141 begin 142 if Bufn = Buft then 143 Read_Buffer; 144 end if; 145 146 Bufn := Bufn + 1; 147 return Buf (Bufn); 148 end Read_Byte; 149 150 -------------------- 151 -- Tree_Read_Bool -- 152 -------------------- 153 154 procedure Tree_Read_Bool (B : out Boolean) is 155 begin 156 B := Boolean'Val (Read_Byte); 157 158 if Debug_Flag_Tree then 159 if B then 160 Write_Str ("True"); 161 else 162 Write_Str ("False"); 163 end if; 164 165 Write_Eol; 166 end if; 167 end Tree_Read_Bool; 168 169 -------------------- 170 -- Tree_Read_Char -- 171 -------------------- 172 173 procedure Tree_Read_Char (C : out Character) is 174 begin 175 C := Character'Val (Read_Byte); 176 177 if Debug_Flag_Tree then 178 Write_Str ("==> transmitting Character = "); 179 Write_Char (C); 180 Write_Eol; 181 end if; 182 end Tree_Read_Char; 183 184 -------------------- 185 -- Tree_Read_Data -- 186 -------------------- 187 188 procedure Tree_Read_Data (Addr : Address; Length : Int) is 189 190 type S is array (Pos) of Byte; 191 -- This is a big array, for which we have to suppress the warning 192 193 type SP is access all S; 194 195 function To_SP is new Unchecked_Conversion (Address, SP); 196 197 Data : constant SP := To_SP (Addr); 198 -- Data buffer to be read as an indexable array of bytes 199 200 OP : Pos := 1; 201 -- Pointer to next byte of data buffer to be read into 202 203 B : Byte; 204 C : Byte; 205 L : Int; 206 207 begin 208 if Debug_Flag_Tree then 209 Write_Str ("==> transmitting "); 210 Write_Int (Length); 211 Write_Str (" data bytes"); 212 Write_Eol; 213 end if; 214 215 -- Verify data length 216 217 Tree_Read_Int (L); 218 219 if L /= Length then 220 Write_Str ("==> transmitting, expected "); 221 Write_Int (Length); 222 Write_Str (" bytes, found length = "); 223 Write_Int (L); 224 Write_Eol; 225 raise Tree_Format_Error; 226 end if; 227 228 -- Loop to read data 229 230 while OP <= Length loop 231 232 -- Get compression control character 233 234 B := Read_Byte; 235 C := B and 2#00_111111#; 236 B := B and 2#11_000000#; 237 238 -- Non-repeat case 239 240 if B = C_Noncomp then 241 if Debug_Flag_Tree then 242 Write_Str ("==> uncompressed: "); 243 Write_Int (Int (C)); 244 Write_Str (", starting at "); 245 Write_Int (OP); 246 Write_Eol; 247 end if; 248 249 for J in 1 .. C loop 250 Data (OP) := Read_Byte; 251 OP := OP + 1; 252 end loop; 253 254 -- Repeated zeroes 255 256 elsif B = C_Zeros then 257 if Debug_Flag_Tree then 258 Write_Str ("==> zeroes: "); 259 Write_Int (Int (C)); 260 Write_Str (", starting at "); 261 Write_Int (OP); 262 Write_Eol; 263 end if; 264 265 for J in 1 .. C loop 266 Data (OP) := 0; 267 OP := OP + 1; 268 end loop; 269 270 -- Repeated spaces 271 272 elsif B = C_Spaces then 273 if Debug_Flag_Tree then 274 Write_Str ("==> spaces: "); 275 Write_Int (Int (C)); 276 Write_Str (", starting at "); 277 Write_Int (OP); 278 Write_Eol; 279 end if; 280 281 for J in 1 .. C loop 282 Data (OP) := Character'Pos (' '); 283 OP := OP + 1; 284 end loop; 285 286 -- Specified repeated character 287 288 else -- B = C_Repeat 289 B := Read_Byte; 290 291 if Debug_Flag_Tree then 292 Write_Str ("==> other char: "); 293 Write_Int (Int (C)); 294 Write_Str (" ("); 295 Write_Int (Int (B)); 296 Write_Char (')'); 297 Write_Str (", starting at "); 298 Write_Int (OP); 299 Write_Eol; 300 end if; 301 302 for J in 1 .. C loop 303 Data (OP) := B; 304 OP := OP + 1; 305 end loop; 306 end if; 307 end loop; 308 309 -- At end of loop, data item must be exactly filled 310 311 if OP /= Length + 1 then 312 raise Tree_Format_Error; 313 end if; 314 315 end Tree_Read_Data; 316 317 -------------------------- 318 -- Tree_Read_Initialize -- 319 -------------------------- 320 321 procedure Tree_Read_Initialize (Desc : File_Descriptor) is 322 begin 323 Buft := 0; 324 Bufn := 0; 325 Tree_FD := Desc; 326 Debug_Flag_Tree := Debug_Flag_5; 327 end Tree_Read_Initialize; 328 329 ------------------- 330 -- Tree_Read_Int -- 331 ------------------- 332 333 procedure Tree_Read_Int (N : out Int) is 334 N_Bytes : Int_Bytes; 335 336 begin 337 for J in 1 .. 4 loop 338 N_Bytes (J) := Read_Byte; 339 end loop; 340 341 N := To_Int (N_Bytes); 342 343 if Debug_Flag_Tree then 344 Write_Str ("==> transmitting Int = "); 345 Write_Int (N); 346 Write_Eol; 347 end if; 348 end Tree_Read_Int; 349 350 ------------------- 351 -- Tree_Read_Str -- 352 ------------------- 353 354 procedure Tree_Read_Str (S : out String_Ptr) is 355 N : Nat; 356 357 begin 358 Tree_Read_Int (N); 359 S := new String (1 .. Natural (N)); 360 Tree_Read_Data (S.all (1)'Address, N); 361 end Tree_Read_Str; 362 363 ------------------------- 364 -- Tree_Read_Terminate -- 365 ------------------------- 366 367 procedure Tree_Read_Terminate is 368 begin 369 -- Must be at end of input buffer, so we should get Tree_Format_Error 370 -- if we try to read one more byte, if not, we have a format error. 371 372 declare 373 B : Byte; 374 pragma Warnings (Off, B); 375 376 begin 377 B := Read_Byte; 378 379 exception 380 when Tree_Format_Error => return; 381 end; 382 383 raise Tree_Format_Error; 384 end Tree_Read_Terminate; 385 386 --------------------- 387 -- Tree_Write_Bool -- 388 --------------------- 389 390 procedure Tree_Write_Bool (B : Boolean) is 391 begin 392 if Debug_Flag_Tree then 393 Write_Str ("==> transmitting Boolean = "); 394 395 if B then 396 Write_Str ("True"); 397 else 398 Write_Str ("False"); 399 end if; 400 401 Write_Eol; 402 end if; 403 404 Write_Byte (Boolean'Pos (B)); 405 end Tree_Write_Bool; 406 407 --------------------- 408 -- Tree_Write_Char -- 409 --------------------- 410 411 procedure Tree_Write_Char (C : Character) is 412 begin 413 if Debug_Flag_Tree then 414 Write_Str ("==> transmitting Character = "); 415 Write_Char (C); 416 Write_Eol; 417 end if; 418 419 Write_Byte (Character'Pos (C)); 420 end Tree_Write_Char; 421 422 --------------------- 423 -- Tree_Write_Data -- 424 --------------------- 425 426 procedure Tree_Write_Data (Addr : Address; Length : Int) is 427 428 type S is array (Pos) of Byte; 429 -- This is a big array, for which we have to suppress the warning 430 431 type SP is access all S; 432 433 function To_SP is new Unchecked_Conversion (Address, SP); 434 435 Data : constant SP := To_SP (Addr); 436 -- Pointer to data to be written, converted to array type 437 438 IP : Pos := 1; 439 -- Input buffer pointer, next byte to be processed 440 441 NC : Nat range 0 .. Max_Count := 0; 442 -- Number of bytes of non-compressible sequence 443 444 C : Byte; 445 446 procedure Write_Non_Compressed_Sequence; 447 -- Output currently collected sequence of non-compressible data 448 449 ----------------------------------- 450 -- Write_Non_Compressed_Sequence -- 451 ----------------------------------- 452 453 procedure Write_Non_Compressed_Sequence is 454 begin 455 if NC > 0 then 456 Write_Byte (C_Noncomp + Byte (NC)); 457 458 if Debug_Flag_Tree then 459 Write_Str ("==> uncompressed: "); 460 Write_Int (NC); 461 Write_Str (", starting at "); 462 Write_Int (IP - NC); 463 Write_Eol; 464 end if; 465 466 for J in reverse 1 .. NC loop 467 Write_Byte (Data (IP - J)); 468 end loop; 469 470 NC := 0; 471 end if; 472 end Write_Non_Compressed_Sequence; 473 474 -- Start of processing for Tree_Write_Data 475 476 begin 477 if Debug_Flag_Tree then 478 Write_Str ("==> transmitting "); 479 Write_Int (Length); 480 Write_Str (" data bytes"); 481 Write_Eol; 482 end if; 483 484 -- We write the count at the start, so that we can check it on 485 -- the corresponding read to make sure that reads and writes match 486 487 Tree_Write_Int (Length); 488 489 -- Conversion loop 490 -- IP is index of next input character 491 -- NC is number of non-compressible bytes saved up 492 493 loop 494 -- If input is completely processed, then we are all done 495 496 if IP > Length then 497 Write_Non_Compressed_Sequence; 498 return; 499 end if; 500 501 -- Test for compressible sequence, must be at least three identical 502 -- bytes in a row to be worthwhile compressing. 503 504 if IP + 2 <= Length 505 and then Data (IP) = Data (IP + 1) 506 and then Data (IP) = Data (IP + 2) 507 then 508 Write_Non_Compressed_Sequence; 509 510 -- Count length of new compression sequence 511 512 C := 3; 513 IP := IP + 3; 514 515 while IP < Length 516 and then Data (IP) = Data (IP - 1) 517 and then C < Max_Count 518 loop 519 C := C + 1; 520 IP := IP + 1; 521 end loop; 522 523 -- Output compression sequence 524 525 if Data (IP - 1) = 0 then 526 if Debug_Flag_Tree then 527 Write_Str ("==> zeroes: "); 528 Write_Int (Int (C)); 529 Write_Str (", starting at "); 530 Write_Int (IP - Int (C)); 531 Write_Eol; 532 end if; 533 534 Write_Byte (C_Zeros + C); 535 536 elsif Data (IP - 1) = Character'Pos (' ') then 537 if Debug_Flag_Tree then 538 Write_Str ("==> spaces: "); 539 Write_Int (Int (C)); 540 Write_Str (", starting at "); 541 Write_Int (IP - Int (C)); 542 Write_Eol; 543 end if; 544 545 Write_Byte (C_Spaces + C); 546 547 else 548 if Debug_Flag_Tree then 549 Write_Str ("==> other char: "); 550 Write_Int (Int (C)); 551 Write_Str (" ("); 552 Write_Int (Int (Data (IP - 1))); 553 Write_Char (')'); 554 Write_Str (", starting at "); 555 Write_Int (IP - Int (C)); 556 Write_Eol; 557 end if; 558 559 Write_Byte (C_Repeat + C); 560 Write_Byte (Data (IP - 1)); 561 end if; 562 563 -- No compression possible here 564 565 else 566 -- Output non-compressed sequence if at maximum length 567 568 if NC = Max_Count then 569 Write_Non_Compressed_Sequence; 570 end if; 571 572 NC := NC + 1; 573 IP := IP + 1; 574 end if; 575 end loop; 576 577 end Tree_Write_Data; 578 579 --------------------------- 580 -- Tree_Write_Initialize -- 581 --------------------------- 582 583 procedure Tree_Write_Initialize (Desc : File_Descriptor) is 584 begin 585 Bufn := 0; 586 Tree_FD := Desc; 587 Set_Standard_Error; 588 Debug_Flag_Tree := Debug_Flag_5; 589 end Tree_Write_Initialize; 590 591 -------------------- 592 -- Tree_Write_Int -- 593 -------------------- 594 595 procedure Tree_Write_Int (N : Int) is 596 N_Bytes : constant Int_Bytes := To_Int_Bytes (N); 597 598 begin 599 if Debug_Flag_Tree then 600 Write_Str ("==> transmitting Int = "); 601 Write_Int (N); 602 Write_Eol; 603 end if; 604 605 for J in 1 .. 4 loop 606 Write_Byte (N_Bytes (J)); 607 end loop; 608 end Tree_Write_Int; 609 610 -------------------- 611 -- Tree_Write_Str -- 612 -------------------- 613 614 procedure Tree_Write_Str (S : String_Ptr) is 615 begin 616 Tree_Write_Int (S'Length); 617 Tree_Write_Data (S (1)'Address, S'Length); 618 end Tree_Write_Str; 619 620 -------------------------- 621 -- Tree_Write_Terminate -- 622 -------------------------- 623 624 procedure Tree_Write_Terminate is 625 begin 626 if Bufn > 0 then 627 Write_Buffer; 628 end if; 629 end Tree_Write_Terminate; 630 631 ------------------ 632 -- Write_Buffer -- 633 ------------------ 634 635 procedure Write_Buffer is 636 begin 637 if Integer (Bufn) = Write (Tree_FD, Buf'Address, Integer (Bufn)) then 638 Bufn := 0; 639 640 else 641 Set_Standard_Error; 642 Write_Str ("fatal error: disk full"); 643 OS_Exit (2); 644 end if; 645 end Write_Buffer; 646 647 ---------------- 648 -- Write_Byte -- 649 ---------------- 650 651 procedure Write_Byte (B : Byte) is 652 begin 653 Bufn := Bufn + 1; 654 Buf (Bufn) := B; 655 656 if Bufn = Buflen then 657 Write_Buffer; 658 end if; 659 end Write_Byte; 660 661end Tree_IO; 662