File : tree_io.adb


   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 --                                                                          --
  19 --                                                                          --
  20 --                                                                          --
  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 
  32 with Debug;  use Debug;
  33 with Output; use Output;
  34 with Unchecked_Conversion;
  35 
  36 package 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 
 661 end Tree_IO;