(*************************************************************************

           =====================================================
           Breaking the 64K barrier for BlockRead and BlockWrite
           =====================================================
                 Copyright (c) 1992,1994 by Jos‚ Campione
                   Ottawa-Orleans Personal Systems Group
                          Fidonet: 1:163/513.3

 Turbo Pascal implements two procedures for fast transfer of data from 
 files to memory blocks and viceversa: Blockread and Blockwrite. One of 
 the commonly encountered limitation in these procedures is the fact that 
 they can only handle blocks not exceeding 65535 bytes.

 This limitation bears a connection with the often asked question on how 
 to brake the 64K barrier for arrays declared in Pascal. Several answers 
 have been proposed to this effect. Perhaps one of the most elegant is 
 the one proposed by Neil Rubenking in his book on Turbo Pascal 6.0 
 Techniques and Utilities (Ziff-Davis Press, 1991). Albeit elegant, 
 Neil's approach uses OOP which may not be fully appreciated by many 
 Pascal users. 

 So, here is a less ambitious approach with several procedures and 
 functions permitting the direct handling of large memory blocks. In the 
 following unit large memory blocks are defined as arrays of blocks each
 not exceeding 64K. The only limitation for the size of the overall large 
 block is that it must not exceed the normal RAM. A longint pointer is 
 then used to access individual positions. 

 This unit uses a modified heapfunc that permits the replacement of "new" 
 with "getmem". This, together with range checking off, allows an array 
 to be declared as a single byte. During runtime it can be assigned any 
 size determined by the program. This ensures that the "tail" of the big 
 block will never be larger than strictly necessary. 

 Functions BigBlockRead and BigBlockWrite permit the reading and writing 
 of blocks from and to a file much in the same way as Pascal's BlockRead
 and BlockWrite. Only difference is that the 64K limit is not a problem 
 anymore. Note that the size of the blocks can only be defined in terms 
 of bytes and that the file being read or write must have been previously 
 assigned to variable f (an untyped file declared within the unit). Also, 
 these are not procedures but functions returning false if the reading or 
 the writing of the big block was not completed. 

 In the present implementation only one array of big blocks is permitted. 
 Variable BigBlkExist ensures that MakeBig will only work if a previous 
 big block has not been created. BigBlk is the array of blocks reserved 
 in the heap. SizBlk is an array containing the sizes in bytes of each 
 block reserved in the heap as part of the big block. NumVec contains the
 number of blocks used by the big block. 

 And last, some acknowledgements:

 Part of this unit was inspired by code contained in a file posted at 
 garbo.uwasa.fi by Prof. Timo Salmi. The code itself was based on a 
 submission by Naji Moawad. Prof. Salmi's code contained the following 
 message: 

    The code below is based on a UseNet posting in comp.lang.pascal by 
    Naji Mouawad nmouawad@watmath.waterloo.edu. Naji's idea was for a 
    vector, my adaptation is for a two-dimensional matrix. The realization
    of the idea is simpler than the one presented by Kent Porter in 
    Dr.Dobb's Journal, March 1988. 
***************************************************************************)

{$R-} { R has to be off... }
{$M 8096,0,655360}

unit bigarru;

interface

   uses crt,dos;

   const
       SizVec = $FFFF;
       MaxBlk = $FF;

   type
       Vec = array [0..0] of byte;

   var
       BigBlk  : array[0..MaxBlk] of ^Vec;
       SizBlk  : array[0..MaxBlk] of word;
       TotSizBlk : longint;
       NumVec : byte;
       HeapTop : pointer;
       BigBlkExist : boolean;

   {$F+} function HeapFunc(Size: word) : integer; {$F-}
   function MakeBig(HeapNeeded: longint): boolean;
   function Peek(p: longint; var error: boolean): byte;
   procedure Poke(b : byte; p: longint; var error: boolean);
   procedure FillRange(fromby, toby :longint; b : byte);
   procedure FillAll(b: byte);
   function BigBlockRead (var f: file): boolean;
   function BigBlockWrite(var f: file): boolean;

implementation

   {$F+} function HeapFunc(Size: word) : integer; {$F-}
   begin
     HeapFunc:= 1;
   end;

   { Create the dynamic variables }
   { HeapNeeded is the needed number of BYTES }
   function MakeBig(HeapNeeded: longint): boolean;
   var
     i          : integer;
     error      : boolean;
   begin
     error:= false;
     if BigBlkExist then begin
       Makebig:= false;
       exit;
     end;
     fillchar(sizblk,sizeof(sizblk),0);
     NumVec:= (HeapNeeded div SizVec);
     if (HeapNeeded < SizVec) then begin
       SizBlk[NumVec]:= HeapNeeded;
       BigBlk[NumVec]:= nil;
       GetMem(BigBlk[NumVec], SizBlk[NumVec]);
       if BigBlk[NumVec] = nil then error:= true;
     end else begin
       i:= -1;
       while not error and (i < NumVec - 1) do begin
         inc(i,1);
         SizBlk[i]:= SizVec;
         BigBlk[i]:= nil;
         GetMem(BigBlk[i],SizBlk[i]);
         if BigBlk[i] = nil then error:= true;
       end;
       if not error then begin
         SizBlk[NumVec]:= HeapNeeded - ((i + 1) * SizVec);
         BigBlk[NumVec]:= nil;
         GetMem(BigBlk[NumVec], SizBlk[NumVec]);
         if BigBlk[NumVec] = nil then error:= true;
       end;
     end;
     if not error then begin
       TotSizBlk:= HeapNeeded;
       BigBlkExist:= true;
       MakeBig:= true;
     end else begin
       MakeBig:= false;
       release(heaptop);
     end;
   end;  { makebig }

   function Peek(p: longint; var error: boolean): byte;
   var
     VecNum: byte;
     BytNum: word;
   begin
     if BigBlkExist and not (p > totsizblk) then begin
       error:= false;
       VecNum:= p div SizVec;
       BytNum:= p - (VecNum * SizVec);
       peek:= BigBlk[VecNum]^[BytNum];
     end else begin
       error:= true;
       peek:= 0;
     end;
   end;

   procedure Poke(b: byte; p: longint; var error: boolean);
   var
     VecNum: byte;
     BytNum: word;
   begin
      if BigBlkExist and not (p > totsizblk) then begin
        error:= false;
        VecNum:= p div SizVec;
        BytNum:= p - (VecNum * SizVec);
        BigBlk[VecNum]^[BytNum]:= b;
      end else error:= true;
   end;

   procedure FillRange(fromby, toby :longint; b : byte);
   var
     p: longint;
     VecNum: byte;
     BytNum: word;
   begin
     If BigBlkExist then begin
       for p:= fromby to toby do begin
         VecNum:= p div SizVec;
         BytNum:= p - (VecNum * SizVec);
         BigBlk[VecNum]^[BytNum]:= b;
       end;
     end;
   end;

   procedure FillAll(b: byte);
   var
     i : byte;
   begin
     if BigBlkExist then
       for i:= 0 to NumVec do
         fillchar(BigBlk[i]^,SizBlk[i],b);
   end;

   function BigBlockRead (var f: file): boolean;
   var
     i : integer;
     error : boolean;
   begin
     error:= false;
     BigBlockRead:= true;
     {$I-} reset(f,1); {$I+}
     if (ioresult = 0) and bigblkexist then begin
       i:= -1;
       while not error and (i < NumVec) do begin
         inc(i,1);
         {$I-} BlockRead(f,BigBlk[i]^,SizBlk[i]); {$I+}
         if ioresult <> 0 then error:= true;
       end;
       if not error then {$I-}close(f){$I+} else BigBlockRead:= false;
     end else BigBlockRead:= false;
   end;

   function BigBlockWrite(var f: file): boolean;
   var
     i : integer;
     error : boolean;
   begin
     error:= false;
     BigBlockWrite:= true;
     {$I-} rewrite(f,1); {$I+}
     if (ioresult = 0) and bigblkexist then begin
       i:= -1;
       while not error and (i < NumVec) do begin
         inc(i,1);
         {$I-} BlockWrite(f,BigBlk[i]^,SizBlk[i]); {$I+}
         if ioresult <> 0 then error:= true;
       end;
       if not error then {$I-}close(f){$I+} else BigBlockWrite:= false;
     end else BigBlockWrite:= false;
   end;

begin
  heaperror:= @heapfunc;
  BigBlkExist:= false;
  mark(heaptop);
end.

