[Back to OOP SWAG index] [Back to Main SWAG index] [Original]

    {
      This is a relatively simple, fairly small, marginally tested, but extremely
    useful object I cooked up last week to provide a familiar interface while
    allowing almost a full 64K data buffer on all sorts of typed and untyped
    files (except for textfiles--come on, what do you think SetTextBuf is for!).

      Enjoy, and please let me know of any bugs, fixes, enhancements, ridicule,
    pity, thankfullness, or admiration you might have for me, thanks!

    {- FastFile unit: Buffered file I/O object -}

    Unit FastFile;

    {
         Updated 12/1/94

         Copyright 1994 by Ryan Thompson, Osiris Development.

         You are free to use this code in your programs, however,
         it may not be included for-profit software, or source/unit
         libraries, without my permission.

           Voice: (707)443-4803
             FAX: (707)445-0599
             BBS: (707)445-0500 v.32terbo/HST
         FidoNet: 1:125/37.0
           Email: sysop@f37.n125.z1.fidonet.org

    }

    INTERFACE

    Type
      FileBuffer = Array[0..$fff0] of byte;

      BufFileObj = Object
        BufFile : File;
        Buf : ^FileBuffer;
        IOResult : Integer;
        Filename : String;
        Open,
        Dirty : Boolean;
        BufFilePos : Longint;
        RecordSize,
        BufSize,
        BufPosz,
        BufTop : Word;
        Constructor Init(BufferSize : Word);
        Destructor Done;

        Procedure Assign(Fname : String);
        Procedure Reset(RecSize : Word);
        Procedure Rewrite(RecSize : Word);
        Procedure Close;
        Procedure Rename(Fname : String);
        Procedure Erase;

        Function EOF : Boolean;
        Function FilePos : Longint;
        Function FileSize : Longint;
        Procedure Seek(Pos : Longint);

        Procedure Read(Var V);
        Procedure Write(Var V);
        Procedure BlockRead(Var V; Count : Word; var Result : Word);
        Procedure BlockWrite(Var V; Count : Word; var Result : Word);

        Procedure FillBuffer;
        Procedure FlushBuffer;
      End;
      BufferedFile = ^BufFileObj;

    IMPLEMENTATION

    Constructor BufFileObj.Init(BufferSize : Word);
      Begin
        Filename:= '';
        Open:= false;
        Dirty:= false;
        BufFilePos:= 0;
        RecordSize:= 0;
        BufSize:= BufferSize;
        BufPos:= 0;
        BufTop:= 0;
        Getmem(Buf, BufSize);
        if Buf = nil then Fail;
      End;

    Destructor BufFileObj.Done;
      Begin
        If Open then Close;
        if Buf <> nil then Freemem(Buf, BufSize);
      End;

    Procedure BufFileObj.Assign(Fname : String);
      Begin
        if Open then Close;
        Filename:= FName;
        system.Assign(BufFile, Fname);
        IOResult:= system.IOResult;
      End;

    Procedure BufFileObj.Reset(RecSize : Word);
      Begin
        if Open then Close;
        if RecSize = 0
        then RecordSize:= 128
        else RecordSize:= RecSize;
        system.Reset(BufFile, 1);
        IOResult:= system.IOResult;
        BufFilePos:= 0;
        BufPos:= 0;
        FillBuffer;
        if IOResult = 0
        then Open:= true
        else Open:= false;
      End;

    Procedure BufFileObj.Rewrite(RecSize : Word);
      Begin
        if Open then Close;
        if RecSize = 0
        then RecordSize:= 128
        else RecordSize:= RecSize;
        BufTop:= 0;
        system.Rewrite(BufFile, 1);
        IOResult:= system.IOResult;
        if IOResult = 0
        then Open:= true
        else Open:= false;
      End;

    Procedure BufFileObj.Close;
      Begin
        If Dirty then FlushBuffer;
        system.Close(BufFile);
        IOResult:= system.IoResult;
        Open:= false;
      End;

    Procedure BufFileObj.Rename(Fname : String);
      Begin
        system.Rename(BufFile, Fname);
        IOResult:= system.IOResult;
        if IOResult = 0 then Filename:= FName;
      End;

    Procedure BufFileObj.Erase;
      Begin
        if Open then Close;
        system.Erase(BufFile);
        IOResult:= system.IOResult;
      End;

    Function BufFileObj.EOF : Boolean;
      Begin
        EOF:= BufFileObj.FilePos >= BufFileObj.FileSize;
      End;

    Function BufFileObj.FileSize : Longint;
      Begin
        FileSize:= system.FileSize(BufFile) div RecordSize;
      End;

    Function BufFileObj.FilePos : Longint;
      Begin
        FilePos:= (BufFilePos + BufPos) div RecordSize;
      End;

    Procedure BufFileObj.Seek(Pos : Longint);
      Begin
        Pos:= Pos * RecordSize;
        if (Pos >= BufFilePos + BufSize) or
           (Pos < BufFilePos)
        then begin
          if Dirty then FlushBuffer;
          system.seek(BufFile, Pos);
          BufFilePos:= Pos;
          BufPos:= 0;
          FillBuffer;
        end
        else BufPos:= Pos - BufFilePos;
        IoResult:= system.IoResult;
      End;

    Procedure BufFileObj.Read(Var V);
      Var
        Overflow : Word;
      Begin
        If BufPos + RecordSize > BufTop
        then begin
          Overflow:= BufSize - BufPos;
          Move(Buf^[BufPos], V, Overflow);
          inc(BufFilePos, BufSize);
          BufPos:= 0;
          FillBuffer;
          Move(Buf^[BufPos], FileBuffer(V)[Overflow], RecordSize - Overflow);
          BufPos:= BufPos + RecordSize - Overflow;
        end
        else begin
          Move(Buf^[BufPos], V, RecordSize);
          inc(BufPos, RecordSize);
        end;
        IoResult:= system.IoResult;
      End;

    Procedure BufFileObj.BlockRead(Var V; Count : Word; var Result : Word);
      Begin
        Result:= 0;
        While (Result < Count) and
              (IoResult = 0) and
              not EOF
        do begin
          Read(FileBuffer(V)[Result * RecordSize]);
          Inc(Result);
        end;
      End;

    Procedure BufFileObj.Write(Var V);
      Var
        Overflow : Word;
      Begin
        Dirty:= true;
        If BufPos + RecordSize > BufSize
        then begin
          Overflow:= BufSize - BufPos;
          if Overflow > 0
          then Move(V, Buf^[BufPos], Overflow);
          BufTop:= BufSize;
          FlushBuffer;
          inc(BufFilePos, BufSize);
          BufPos:= 0;
          FillBuffer;
          Move(FileBuffer(V)[Overflow], Buf^[BufPos], RecordSize - Overflow);
          BufPos:= BufPos + RecordSize - Overflow;
        end
        else begin
          Move(V, Buf^[BufPos], RecordSize);
          inc(BufPos, RecordSize);
        end;
        if BufTop < BufPos
        then BufTop:= BufPos;
        IoResult:= system.IoResult;
      End;

    Procedure BufFileObj.BlockWrite(Var V; Count : Word; var Result : Word);
      Begin
        Result:= 0;
        While (Result < Count) and
              (IoResult = 0)
        do begin
          Write(FileBuffer(V)[Result * RecordSize]);
          Inc(Result);
        end;
      End;

    Procedure BufFileObj.FillBuffer;
      Begin
        system.Seek(BufFile, BufFilePos);
        system.BlockRead(BufFile, Buf^, BufSize, BufTop);
        IoResult:= system.IoResult;
        if IoResult = 0 then Dirty:= false;
      End;

    Procedure BufFileObj.FlushBuffer;
      Begin
        system.Seek(BufFile, BufFilePos);
        system.BlockWrite(BufFile, Buf^, BufTop, BufTop);
        IoResult:= system.IoResult;
      End;

    Begin
    End.

    {
      There is is....  now here's a brief explanation of how to use it:

      As you can see, all of the most often-used functions are available in the
    object definition.  Instead of passing a file handle to the procedures as the
    first parameter, the object itself defines the file.  The syntax of the
    commands is as similar as is possible.  The only things to really watch for
    are that Reset and Rewrite both require a parameter, and EOF works on all file
    types.

        Procedure Assign(FName : String);
        Procedure Reset(RecSize : Word);
        Procedure Rewrite(RecSize : Word);
        Procedure Close;
        Procedure Rename(Fname : String);
        Procedure Erase;
        Function EOF : Boolean;
        Function FilePos : Longint;
        Function FileSize : Longint;
        Procedure Seek(Pos : Longint);
        Procedure Read(Var V);
        Procedure Write(Var V);
        Procedure BlockRead(Var V; Count : Word; var Result : Word);
        Procedure BlockWrite(Var V; Count : Word; var Result : Word);

      A simple program to use these functions would look like the following
    untested, off-the-cuff code:
    }

    Program TestFile;

    Uses
      FastFile;

    Var
      InFile,
      OutFile : BufferedFile;
      Data : Array[1..120] of byte;

    Begin
      New(InFile, Init($8000));            { Allocate a 32K buffer };
      New(OutFile, Init($4000));           { 16K Buffer }
      InFile^.Assign(Parameter(1));        { Assign input to a filename }
      InFile^.Reset(sizeof(data));
      OutFile^.Assign(Parameter(2));
      OutFile^.Rewrite(sizeof(data));
      While not InFile^.EOF do begin
        InFile^.Read(Data);
        if Data[1] <> 0 then OutFile^.Write(Data);
      end;
      InFile^.Close;
      OutFile^.Close;
      InFile^.Erase;
      OutFile^.Rename(Parameter(1));
      Dispose(InFile);                     { Close the file and free the memory }
      Dispose(OutFile);
    End.

    {
      As you see, this implements a very silly program which removes the original
    after stripping unwanted records from it.  Without the large RAM buffers,
    this would be an extremely slow, but since all READs and WRITEs and SEEKs and
    everything else are done to RAM, few actual disk accesses result.  I used
    this in a USEnet mail processing program and the speed went from 15K/sec to
    130K/sec.

      The easiest way to use it is to simply change structures like:

    Assign(Filehandle, name);   --->   Filehandle^.Assign(name);

      ...in many cases a couple of Search..Replace passes are all that you need
    to modify your software to use this.  I've done it will all of my own and
    am very pleased.

      Again, if you run into any problems, please let me know!  I can guarantee
    you only that it works fine for me.  :-)
    }

[Back to OOP SWAG index] [Back to Main SWAG index] [Original]