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


(************************************************************************)
(*                                                                      *)
(*  Program ex. to      : "Tips & Tricks in Turbo Pascal", SysTime 1993 *)
(*                                                                      *)
(*  By                  : Martin Israelsen                              *)
(*                                                                      *)
(*  Title               : BUFFER.PAS                                    *)
(*                                                                      *)
(*  Chapter             : 5                                             *)
(*                                                                      *)
(*  Description         : Quicker than Turbo fileread                   *)
(*                                                                      *)
(************************************************************************)
(*$I-*)  (* Iocheck off         *)
(*$F+*)  (* Force FAR call      *)
(*$V-*)  (* Relaxed VAR check   *)
(*$R-*)  (* Range check off     *)
(*$S-*)  (* Stack check off     *)
(*$Q-*)  (* Overflow off        *)
(*$D-*)  (* Debug off           *)
(*$L-*)  (* Linenumber off      *)

Unit
  Buffer;

Interface

Type

  PByte     = ^Byte;
  PWord     = ^Word;
  PLong     = ^Longint;

  PByteArr  = ^TByteArr;
  TByteArr  = Array[1..64000] Of Byte;
  PfStr     = String[100];

  PBuffer       = ^TBuffer;
  TBuffer       = Record
                     BufFil   : File;
                     BufPtr   : PByteArr;

                     BufSize,
                     BufIndex,
                     BufUsed  : Word;

                     BufFPos,
                     BufFSize : Longint;
                  End;

Function  BufferInit(Var Br: PBuffer; MemSize: Word;
                      FilName: PfStr): Boolean;
Procedure BufferClose(Var Br: PBuffer);

Function  BufferGetByte(Br: PBuffer): Byte;
Function  BufferGetByteAsm(Br: PBuffer): Byte;

Function  BufferGetWord(Br: PBuffer): Word;
Procedure BufferGetBlock(Br: PBuffer; Var ToAdr; BlockSize: Word);
Function  BufferGetStringAsm(Br: PBuffer): String;

Function  BufferEof(Br: PBuffer): Boolean;

Implementation

(*$I-,F+*)

Function BufferInit(Var Br: PBuffer; MemSize: Word;
                    FilName: PfStr): Boolean;
Begin
   BufferInit:=False;

   (* Check if there's enough memory               *)

   If MemSize<500 Then Exit;
   If MaxAvail<Sizeof(TBuffer)+MemSize+32 Then Exit;

   New(Br);

   With BR^ Do
   Begin
      BufSize:=MemSize; BufIndex:=1; BufFPos:=0;

      (* Open the filen. Exit if there's an error *)

      Assign(BufFil,Filname); Reset(BufFil,1);

      If IoResult<>0 Then
      Begin
         Dispose(Br);
         Exit;
      End;

      (* Ok, the file is there, and there's enough *)
      (* memory. So allocate the memory and read   *)
      (* as much as possible                       *)

      GetMem(BufPtr,BufSize);
      BlockRead(BufFil,BufPtr^,BufSize,BufUsed);

      BufFSize:=FileSize(BufFil); Inc(BufFPos,BufUsed);
   End;

   BufferInit:=True;
End;

Procedure BufferClose(Var Br: PBuffer);
Begin
   With Br^ Do
   Begin
      Close(BufFil);
      Freemem(BufPtr,BufSize);
   End;

   Dispose(Br);
End;

Procedure BufferCheck(Br: PBuffer; ReqBytes: Word);
Var
   W,Rest: Word;
Begin
   With Br^ Do
   Begin
      If (BufIndex+ReqBytes>BufUsed) And (BufUsed=BufSize) Then
      Begin
         Rest:=Succ(BufSize-BufIndex);

         Move(BufPtr^[BufIndex],BufPtr^[1],Rest);
         BufIndex:=1;

         BlockRead(BufFil,BufPtr^[Succ(Rest)],BufSize-Rest,W);
         BufUsed:=Rest+W; Inc(BufFPos,W);
      End;
   End;
End;

Function BufferGetByte(Br: PBuffer): Byte;
Begin
   With Br^ Do
   Begin
      BufferCheck(Br,1);

      BufferGetByte:=BufPtr^[BufIndex];
      Inc(BufIndex);
   End;
End;

Function BufferGetByteAsm(Br: PBuffer): Byte; Assembler;
Asm
   Les   Di,Br                              (* ES:DI ->  BRecPtr         *)

   Mov   Ax,Es:[Di.TBuffer.BufIndex]        (* Check wheather the buffer should be updated *)
   Cmp   Ax,Es:[Di.TBuffer.BufUsed]
   Jle   @@NoBufCheck                       (* If not jump on            *)

   Push  Word Ptr Br[2]                     (* Push BR to BufferCheck   *)
   Push  Word Ptr Br
   Mov   Ax,0001                            (* Check for one byte           *)
   Push  Ax                                 (* Push it                      *)
   Push  CS                                 (* Push CS, and make a          *)
   Call  Near Ptr BufferCheck               (* NEAR call - it's quicker     *)

   Les   Di,Br                              (* ES:DI-> BRecPtr              *)

 @@NoBufCheck:

   Mov   Bx,Es:[Di.TBuffer.BufIndex]        (* BufferIndex in BX            *)
   Inc   Es:[Di.TBuffer.BufIndex]           (* Inc BufferIndex directly     *)
   Les   Di,Es:[Di.TBuffer.BufPtr]          (* ES:DI -> BufPtr              *)

   Xor   Ax,Ax                              (* Now get the byte             *)
   Mov   Al,Byte Ptr Es:[Di+Bx-1]
End;

Function BufferGetWord(Br: PBuffer): Word;
Begin
   With Br^ Do
   Begin
      BufferCheck(Br,2);

      BufferGetWord:=PWord(@BufPtr^[BufIndex])^;
      Inc(BufIndex,2);
   End;
End;

Procedure BufferGetBlock(Br: PBuffer; Var ToAdr; BlockSize: Word);
Begin
   With Br^ Do
   Begin
      BufferCheck(Br,BlockSize);

      Move(BufPtr^[BufIndex],ToAdr,BlockSize);
      Inc(BufIndex,BlockSize);
   End;
End;

Function BufferGetStringAsm(Br: PBuffer): String; Assembler;
Asm
   Push   Ds

   Les    Di,Br                        (* es:di -> Br *)
   Mov    Bx,Es:[Di.TBuffer.BufUsed]   (* check for buffercheck *)
   Sub    Bx,Es:[Di.TBuffer.BufIndex]
   Cmp    Bx,257
   Jae    @NoBufCheck                  (* Jump on if not        *)

   Push   Word Ptr Br[2]
   Push   Word Ptr Br

   Mov    Ax,257
   Push   Ax

   Push   Cs
   Call   Near Ptr BufferCheck

   Les    Di,Br

 @NoBufCheck:

   Mov    Bx,Es:[Di.TBuffer.BufIndex]  (* Get index in buffer     *)
   Dec    Bx                           (* Adjust for 0            *)

   Les    Di,Es:[Di.TBuffer.BufPtr]    (* Point to the buffer     *)
   Add    Di,Bx                        (* Add Index               *)
   Push   Di                           (* Save currect position   *)

   Mov    Al,$0a                       (* Search for CR = 0ah     *)
   Mov    Cx,$ff                       (* max. 255 chars          *)

   Cld                                 (* Remember                *)
   RepNz  Scasb                        (* and do the search       *)
   Jz     @Fundet                      (* Jump if we found one    *)

   Mov    Cx,0                         (* Otherwise set length to 0  *)
 @Fundet:
   Sub    Cx,$ff                       (* Which will be recalculated *)
   Neg    Cx                           (* to nomal length            *)
   Dec    Cx                           (* Dec, to avoid CR           *)

   Push   Es                           (* DS:SI->Buffer              *)
   Pop    Ds
   Pop    Si

   Les    Di,@Result                   (* ES:DI->result string        *)
   Mov    Ax,Cx

   Stosb                               (* Set length                  *)

   Shr    Cx,1                         (* Copy the string             *)
   Rep    MovSw
   Adc    Cx,Cx
   Rep    MovSb

   Pop    Ds                           (* Restore DS                  *)

   Les    Di,Br                        (* ES:DI->Br                   *)
   Inc    Ax                           (* Inc Ax, point to LF         *)

   Add    Es:[Di.TBuffer.BufIndex],Ax  (* and set BufferIndex         *)
End;


Function BufferEof(Br: PBuffer): Boolean;
Begin
   With Br^ Do
   BufferEof:=(BufIndex>BufUsed) And (BufFPos=BufFSize);
End;

End.


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