 | 
|
|
|
{ }
{ Readers v3.08 }
{ }
{ This unit is copyright © 1999-2004 by David J Butler }
{ }
{ This unit is part of Delphi Fundamentals. }
{ Its original file name is cReaders.pas }
{ The latest version is available from the Fundamentals home page }
{ http://fundementals.sourceforge.net/ }
{ }
{ I invite you to use this unit, free of charge. }
{ I invite you to distibute this unit, but it must be for free. }
{ I also invite you to contribute to its development, }
{ but do not distribute a modified copy of this file. }
{ }
{ A forum is available on SourceForge for general discussion }
{ http://sourceforge.net/forum/forum.php?forum_id=2117 }
{ }
{ }
{ Revision history: }
{ 01/03/1999 0.01 Initial version. }
{ 26/06/1999 1.02 Created cStreams unit. }
{ 03/05/2002 3.03 Created cReaders unit. }
{ 13/05/2002 3.04 Added TBufferedReader and TSplitBufferReader. }
{ 13/07/2002 3.05 Moved text reader functionality to AReaderEx. }
{ 23/08/2002 3.06 Added SelfTest procedure. }
{ 09/04/2003 3.07 Memory reader support for blocks with unknown size. }
{ 06/03/2004 3.08 Improvements to AReaderEx. }
{ }
{$INCLUDE ..\cDefines.inc}
unit cReaders;
interface
uses
{ Delphi }
Windows,
SysUtils,
{ Fundamentals }
cUtils;
{ }
{ AReader }
{ Abstract base class for a Reader. }
{ }
{ Inherited classes must implement the abstract methods from AReader. }
{ }
{ Read returns the actual number of bytes copied to the buffer. }
{ Size returns -1 for reader's with unknown data size. }
{ }
type
AReader = class
protected
function GetPosition: Int64; virtual; abstract;
procedure SetPosition(const Position: Int64); virtual; abstract;
function GetSize: Int64; virtual; abstract;
public
function Read(var Buffer; const Size: Integer): Integer; virtual; abstract;
property Position: Int64 read GetPosition write SetPosition;
property Size: Int64 read GetSize;
function EOF: Boolean; virtual; abstract;
end;
EReader = class(Exception);
{ }
{ AReaderEx }
{ Base class for Reader implementations. AReaderEx extends AReader with }
{ commonly used functions. }
{ }
{ All methods in AReaderEx is implemented using calls to the abstract }
{ methods in AReader. Reader implementations can override the virtual }
{ methods in AReaderEx with more efficient versions. }
{ }
{ Match functions return True when a match is found. Match leaves the }
{ reader's position unchanged except if a match is made and SkipOnMatch }
{ is True. }
{ }
{ Locate returns the offset (relative to the current position) of the }
{ first match in the stream. Locate preserves the reader's position. }
{ Locate returns -1 if a match was not made. }
{ }
type
TReaderEOLType = (
eolEOF, // EOF Files, Internet
eolEOFAtEOF, // #26 at EOF Files
eolCR, // #13 Unix, Internet
eolLF, // #10 Internet
eolCRLF, // #13#10 MS-DOS, Windows, Internet
eolLFCR); // #10#13 Mac
TReaderEOLTypes = Set of TReaderEOLType;
const
DefaultReaderEOLTypes = [eolEOF, eolEOFAtEOF, eolCR, eolLF, eolCRLF, eolLFCR];
type
AReaderEx = class(AReader)
private
function SkipLineTerminator(const EOLTypes: TReaderEOLTypes): Integer;
public
procedure RaiseReadError(const Msg: String = '');
procedure RaiseSeekError;
procedure ReadBuffer(var Buffer; const Size: Integer);
function ReadStr(const Size: Integer): String; virtual;
function ReadWideStr(const Length: Integer): WideString; virtual;
function GetToEOF: String; virtual;
function GetAsString: String; virtual;
function ReadByte: Byte; virtual;
function ReadWord: Word;
function ReadLongWord: LongWord;
function ReadLongInt: LongInt;
function ReadInt64: Int64;
function ReadSingle: Single;
function ReadDouble: Double;
function ReadExtended: Extended;
function ReadPackedString: String;
function ReadPackedStringArray: StringArray;
function ReadPackedWideString: WideString;
function Peek(var Buffer; const Size: Integer): Integer; virtual;
procedure PeekBuffer(var Buffer; const Size: Integer);
function PeekStr(const Size: Integer): String; virtual;
function PeekByte: Byte; virtual;
function PeekWord: Word;
function PeekLongWord: LongWord;
function PeekLongInt: LongInt;
function PeekInt64: Int64;
function Match(const Buffer; const Size: Integer;
const CaseSensitive: Boolean = True): Integer; virtual;
function MatchBuffer(const Buffer; const Size: Integer;
const CaseSensitive: Boolean = True): Boolean;
function MatchStr(const S: String;
const CaseSensitive: Boolean = True): Boolean; virtual;
function MatchChar(const Ch: Char;
const MatchNonMatch: Boolean = False;
const SkipOnMatch: Boolean = False): Boolean; overload;
function MatchChar(const C: CharSet; var Ch: Char;
const MatchNonMatch: Boolean = False;
const SkipOnMatch: Boolean = False): Boolean; overload;
procedure Skip(const Count: Integer = 1); virtual;
procedure SkipByte;
function SkipAll(const Ch: Char; const MatchNonMatch: Boolean = False;
const MaxCount: Integer = -1): Integer; overload;
function SkipAll(const C: CharSet; const MatchNonMatch: Boolean = False;
const MaxCount: Integer = -1): Integer; overload;
function Locate(const Ch: Char;
const LocateNonMatch: Boolean = False;
const MaxOffset: Integer = -1): Integer; overload; virtual;
function Locate(const C: CharSet;
const LocateNonMatch: Boolean = False;
const MaxOffset: Integer = -1): Integer; overload; virtual;
function LocateBuffer(const Buffer; const Size: Integer;
const MaxOffset: Integer = -1;
const CaseSensitive: Boolean = True): Integer; virtual;
function LocateStr(const S: String;
const MaxOffset: Integer = -1;
const CaseSensitive: Boolean = True): Integer; virtual;
function ExtractAll(const C: CharSet;
const ExtractNonMatch: Boolean = False;
const MaxCount: Integer = -1): String;
function ExtractLine(const MaxLineLength: Integer = -1;
const EOLTypes: TReaderEOLTypes = DefaultReaderEOLTypes): String;
function SkipLine(const MaxLineLength: Integer = -1;
const EOLTypes: TReaderEOLTypes = DefaultReaderEOLTypes): Boolean;
end;
{ }
{ TMemoryReader }
{ Reader implementation for a memory block. }
{ }
{ If the reader is initialized with Size = -1, the content is unsized and }
{ EOF will always return False. }
{ }
type
TMemoryReader = class(AReaderEx)
protected
FData : Pointer;
FSize : Integer;
FPos : Integer;
function GetPosition: Int64; override;
procedure SetPosition(const Position: Int64); override;
function GetSize: Int64; override;
public
constructor Create(const Data: Pointer; const Size: Integer);
property Data: Pointer read FData;
property Size: Integer read FSize;
procedure SetData(const Data: Pointer; const Size: Integer);
function Read(var Buffer; const Size: Integer): Integer; override;
function EOF: Boolean; override;
function ReadByte: Byte; override;
function ReadLongInt: LongInt;
function ReadInt64: Int64;
function PeekByte: Byte; override;
function Match(const Buffer; const Size: Integer;
const CaseSensitive: Boolean = True): Integer; override;
procedure Skip(const Count: Integer = 1); override;
end;
EMemoryReader = class(EReader);
{ }
{ TStringReader }
{ Memory reader implementation for a reference counted string (long string). }
{ }
type
TStringReader = class(TMemoryReader)
protected
FDataString : String;
procedure SetDataString(const S: String);
public
constructor Create(const Data: String);
property DataString: String read FDataString write SetDataString;
function GetAsString: String; override;
end;
{ }
{ TFileReader }
{ Reader implementation for a file. }
{ }
type
TFileReaderAccessHint = (
frahNone,
frahRandomAccess,
frahSequentialAccess);
TFileReader = class(AReaderEx)
protected
FHandle : Integer;
FHandleOwner : Boolean;
function GetPosition: Int64; override;
procedure SetPosition(const Position: Int64); override;
function GetSize: Int64; override;
public
constructor Create(const FileName: String;
const AccessHint: TFileReaderAccessHint = frahNone); overload;
constructor Create(const FileHandle: Integer; const HandleOwner: Boolean = False); overload;
destructor Destroy; override;
property Handle: Integer read FHandle;
property HandleOwner: Boolean read FHandleOwner;
function Read(var Buffer; const Size: Integer): Integer; override;
function EOF: Boolean; override;
end;
EFileReader = class(EReader);
function ReadFileToStr(const FileName: String): String;
{ }
{ AReaderProxy }
{ Base class for Reader Proxies. }
{ }
type
AReaderProxy = class(AReaderEx)
protected
FReader : AReaderEx;
FReaderOwner : Boolean;
function GetPosition: Int64; override;
procedure SetPosition(const Position: Int64); override;
function GetSize: Int64; override;
public
constructor Create(const Reader: AReaderEx; const ReaderOwner: Boolean = True);
destructor Destroy; override;
function Read(var Buffer; const Size: Integer): Integer; override;
function EOF: Boolean; override;
property Reader: AReaderEx read FReader;
property ReaderOwner: Boolean read FReaderOwner write FReaderOwner;
end;
{ }
{ TReaderProxy }
{ Reader Proxy implementation. }
{ }
{ Proxies a block of data from Reader from the Reader's current position. }
{ Size specifies the size of the data to be proxied, or if Size = -1, }
{ all data up to EOF is proxied. }
{ }
type
TReaderProxy = class(AReaderProxy)
protected
FOffset : Int64;
FSize : Int64;
function GetPosition: Int64; override;
procedure SetPosition(const Position: Int64); override;
function GetSize: Int64; override;
public
constructor Create(const Reader: AReaderEx; const ReaderOwner: Boolean = True;
const Size: Int64 = -1);
function Read(var Buffer; const Size: Integer): Integer; override;
function EOF: Boolean; override;
end;
{ }
{ TBufferedReader }
{ ReaderProxy implementation for buffered reading. }
{ }
type
TBufferedReader = class(AReaderProxy)
protected
FBufferSize : Integer;
FPos : Int64;
FBuffer : Pointer;
FBufUsed : Integer;
FBufPos : Integer;
function GetPosition: Int64; override;
procedure SetPosition(const Position: Int64); override;
function GetSize: Int64; override;
function FillBuf: Boolean;
procedure BufferByte;
function PosBuf(const C: CharSet; const LocateNonMatch: Boolean;
const MaxOffset: Integer): Integer;
public
constructor Create(const Reader: AReaderEx; const BufferSize: Integer = 128;
const ReaderOwner: Boolean = True);
destructor Destroy; override;
function Read(var Buffer; const Size: Integer): Integer; override;
function EOF: Boolean; override;
function ReadByte: Byte; override;
function PeekByte: Byte; override;
procedure Skip(const Count: Integer = 1); override;
function Locate(const C: CharSet; const LocateNonMatch: Boolean = False;
const MaxOffset: Integer = -1): Integer; override;
property BufferSize: Integer read FBufferSize;
procedure InvalidateBuffer;
end;
{ }
{ TSplitBufferedReader }
{ ReaderProxy implementation for split buffered reading. }
{ }
{ One buffer is used for read-ahead buffering, the other for seek-back }
{ buffering. }
{ }
type
TSplitBufferedReader = class(AReaderProxy)
protected
FBufferSize : Integer;
FPos : Int64;
FBuffer : Array[0..1] of Pointer;
FBufUsed : Array[0..1] of Integer;
FBufNr : Integer;
FBufPos : Integer;
function GetPosition: Int64; override;
procedure SetPosition(const Position: Int64); override;
function GetSize: Int64; override;
function BufferStart: Integer;
function BufferRemaining: Integer;
function MoveBuf(var Dest: PByte; var Remaining: Integer): Boolean;
function FillBuf(var Dest: PByte; var Remaining: Integer): Boolean;
public
constructor Create(const Reader: AReaderEx; const BufferSize: Integer = 128;
const ReaderOwner: Boolean = True);
destructor Destroy; override;
property BufferSize: Integer read FBufferSize;
function Read(var Buffer; const Size: Integer): Integer; override;
function EOF: Boolean; override;
procedure InvalidateBuffer;
end;
{ }
{ TBufferedFileReader }
{ TBufferedReader instance using a TFileReader. }
{ }
type
TBufferedFileReader = class(TBufferedReader)
public
constructor Create(const FileName: String; const BufferSize: Integer = 512); overload;
constructor Create(const FileHandle: Integer; const HandleOwner: Boolean = False;
const BufferSize: Integer = 512); overload;
end;
{ }
{ TSplitBufferedFileReader }
{ TSplitBufferedReader instance using a TFileReader. }
{ }
type
TSplitBufferedFileReader = class(TSplitBufferedReader)
public
constructor Create(const FileName: String; const BufferSize: Integer = 512);
end;
{ }
{ Self-testing code }
{ }
procedure SelfTest;
implementation
{ }
{ AReaderEx }
{ }
const
DefaultBufSize = 2048;
procedure AReaderEx.RaiseReadError(const Msg: String);
var S : String;
begin
if Msg = '' then
S := 'Read error'
else
S := Msg;
raise EReader.Create(S);
end;
procedure AReaderEx.RaiseSeekError;
begin
raise EReader.Create('Seek error');
end;
procedure AReaderEx.ReadBuffer(var Buffer; const Size: Integer);
begin
if Size <= 0 then
exit;
if Read(Buffer, Size) <> Size then
RaiseReadError;
end;
function AReaderEx.ReadStr(const Size: Integer): String;
var L : Integer;
begin
if Size <= 0 then
begin
Result := '';
exit;
end;
SetLength(Result, Size);
L := Read(Pointer(Result)^, Size);
if L <= 0 then
begin
Result := '';
exit;
end;
if L < Size then
SetLength(Result, L);
end;
function AReaderEx.ReadWideStr(const Length: Integer): WideString;
var L : Integer;
begin
if Length <= 0 then
begin
Result := '';
exit;
end;
SetLength(Result, Length);
L := Read(Pointer(Result)^, Length * Sizeof(WideChar)) div Sizeof(WideChar);
if L <= 0 then
begin
Result := '';
exit;
end;
if L < Length then
SetLength(Result, L);
end;
function AReaderEx.GetToEOF: String;
var S : Int64;
B : Array[0..DefaultBufSize - 1] of Byte;
I, J : Integer;
L, N : Integer;
R : Boolean;
Q : PChar;
begin
S := GetSize;
if S < 0 then // size unknown
begin
Q := nil;
Result := '';
L := 0; // actual size
N := 0; // allocated size
R := EOF;
While not R do
begin
I := Read(B[0], DefaultBufSize);
if I > 0 then
begin
J := L + I;
if J > N then
begin
// allocate the exact buffer size for the first two reads
// allocate double the buffer size from the third read
if J <= DefaultBufSize * 2 then
N := J
else
N := J * 2;
SetLength(Result, N);
Q := Pointer(Result);
Inc(Q, L);
end;
Move(B[0], Q^, I);
Inc(Q, I);
Inc(L, I);
end
else
begin
R := EOF;
if not R then
RaiseReadError;
end;
end;
if L < N then
// set exact result size
SetLength(Result, L);
end
else
// size known
Result := ReadStr(S - GetPosition);
end;
function AReaderEx.GetAsString: String;
begin
SetPosition(0);
Result := GetToEOF;
end;
function AReaderEx.ReadByte: Byte;
begin
ReadBuffer(Result, Sizeof(Byte));
end;
function AReaderEx.ReadWord: Word;
begin
ReadBuffer(Result, Sizeof(Word));
end;
function AReaderEx.ReadLongWord: LongWord;
begin
ReadBuffer(Result, Sizeof(LongWord));
end;
function AReaderEx.ReadLongInt: LongInt;
begin
ReadBuffer(Result, Sizeof(LongInt));
end;
function AReaderEx.ReadInt64: Int64;
begin
ReadBuffer(Result, Sizeof(Int64));
end;
function AReaderEx.ReadSingle: Single;
begin
ReadBuffer(Result, Sizeof(Single));
end;
function AReaderEx.ReadDouble: Double;
begin
ReadBuffer(Result, Sizeof(Double));
end;
function AReaderEx.ReadExtended: Extended;
begin
ReadBuffer(Result, Sizeof(Extended));
end;
function AReaderEx.ReadPackedString: String;
var L : Integer;
begin
L := ReadLongInt;
if L < 0 then
raise EReader.Create('Invalid string');
Result := ReadStr(L);
end;
function AReaderEx.ReadPackedStringArray: StringArray;
var I, L : Integer;
begin
L := ReadLongInt;
if L < 0 then
raise EReader.Create('Invalid array');
SetLength(Result, L);
For I := 0 to L - 1 do
Result[I] := ReadPackedString;
end;
function AReaderEx.ReadPackedWideString: WideString;
var L : Integer;
begin
L := ReadLongInt;
if L < 0 then
raise EReader.Create('Invalid string');
Result := ReadWideStr(L);
end;
function AReaderEx.Peek(var Buffer; const Size: Integer): Integer;
var P : Int64;
begin
P := GetPosition;
Result := Read(Buffer, Size);
if Result > 0 then
SetPosition(P);
end;
procedure AReaderEx.PeekBuffer(var Buffer; const Size: Integer);
begin
if Size <= 0 then
exit;
if Peek(Buffer, Size) <> Size then
RaiseReadError;
end;
function AReaderEx.PeekStr(const Size: Integer): String;
var L : Integer;
begin
if Size <= 0 then
begin
Result := '';
exit;
end;
SetLength(Result, Size);
L := Peek(Pointer(Result)^, Size);
if L <= 0 then
begin
Result := '';
exit;
end;
if L < Size then
SetLength(Result, L);
end;
function AReaderEx.PeekByte: Byte;
begin
PeekBuffer(Result, Sizeof(Byte));
end;
function AReaderEx.PeekWord: Word;
begin
PeekBuffer(Result, Sizeof(Word));
end;
function AReaderEx.PeekLongWord: LongWord;
begin
PeekBuffer(Result, Sizeof(LongWord));
end;
function AReaderEx.PeekLongInt: LongInt;
begin
PeekBuffer(Result, Sizeof(LongInt));
end;
function AReaderEx.PeekInt64: Int64;
begin
PeekBuffer(Result, Sizeof(Int64));
end;
{ Returns the number of characters read and matched, or -1 if no match }
function AReaderEx.Match(const Buffer; const Size: Integer;
const CaseSensitive: Boolean): Integer;
var B : Pointer;
F : Array[0..DefaultBufSize - 1] of Byte;
M : Boolean;
R : Boolean;
begin
if Size <= 0 then
begin
Result := -1;
exit;
end;
M := Size > DefaultBufSize;
if M then
GetMem(B, Size)
else
B := @F[0];
try
Result := Peek(B^, Size);
if Result <= 0 then
exit;
if CaseSensitive then
R := CompareMem(Buffer, B^, Result)
else
R := CompareMemNoCase(Buffer, B^, Result) = crEqual;
if not R then
Result := -1;
finally
if M then
FreeMem(B);
end;
end;
function AReaderEx.MatchBuffer(const Buffer; const Size: Integer;
const CaseSensitive: Boolean): Boolean;
var I : Integer;
begin
I := Match(Buffer, Size, CaseSensitive);
if I < 0 then
begin
Result := False;
exit;
end;
if I < Size then
RaiseReadError;
Result := True;
end;
function AReaderEx.MatchStr(const S: String; const CaseSensitive: Boolean): Boolean;
begin
Result := MatchBuffer(Pointer(S)^, Length(S), CaseSensitive);
end;
function AReaderEx.MatchChar(const Ch: Char; const MatchNonMatch: Boolean;
const SkipOnMatch: Boolean): Boolean;
begin
if EOF then
begin
Result := False;
exit;
end;
Result := (Char(PeekByte) = Ch) xor MatchNonMatch;
if Result and SkipOnMatch then
Skip(Sizeof(Byte));
end;
function AReaderEx.MatchChar(const C: CharSet; var Ch: Char; const MatchNonMatch: Boolean;
const SkipOnMatch: Boolean): Boolean;
begin
if EOF then
begin
Result := False;
exit;
end;
Ch := Char(PeekByte);
Result := (Ch in C) xor MatchNonMatch;
if Result and SkipOnMatch then
Skip(Sizeof(Byte));
end;
procedure AReaderEx.Skip(const Count: Integer);
begin
if Count < 0 then
raise EReader.Create('Skip error');
if Count = 0 then
exit;
SetPosition(GetPosition + Count);
end;
procedure AReaderEx.SkipByte;
begin
Skip(Sizeof(Byte));
end;
function AReaderEx.SkipAll(const Ch: Char; const MatchNonMatch: Boolean;
const MaxCount: Integer): Integer;
begin
Result := 0;
While (MaxCount < 0) or (Result < MaxCount) do
if not MatchChar(Ch, MatchNonMatch, True) then
exit
else
Inc(Result);
end;
function AReaderEx.SkipAll(const C: CharSet; const MatchNonMatch: Boolean;
const MaxCount: Integer): Integer;
var Ch : Char;
begin
Result := 0;
While (MaxCount < 0) or (Result < MaxCount) do
if not MatchChar(C, Ch, MatchNonMatch, True) then
exit
else
Inc(Result);
end;
function AReaderEx.Locate(const Ch: Char; const LocateNonMatch: Boolean;
const MaxOffset: Integer): Integer;
var P : Int64;
I : Integer;
begin
P := GetPosition;
I := 0;
While not EOF and ((MaxOffset < 0) or (I <= MaxOffset)) do
if (Char(ReadByte) = Ch) xor LocateNonMatch then
begin
SetPosition(P);
Result := I;
exit;
end
else
Inc(I);
SetPosition(P);
Result := -1;
end;
function AReaderEx.Locate(const C: CharSet; const LocateNonMatch: Boolean;
const MaxOffset: Integer): Integer;
var P : Int64;
I : Integer;
begin
P := GetPosition;
I := 0;
While not EOF and ((MaxOffset < 0) or (I <= MaxOffset)) do
if (Char(ReadByte) in C) xor LocateNonMatch then
begin
SetPosition(P);
Result := I;
exit;
end
else
Inc(I);
SetPosition(P);
Result := -1;
end;
function AReaderEx.LocateBuffer(const Buffer; const Size: Integer;
const MaxOffset: Integer; const CaseSensitive: Boolean): Integer;
var P : Int64;
I, J : Integer;
B : Pointer;
R, M : Boolean;
F : Array[0..DefaultBufSize - 1] of Byte;
begin
if Size <= 0 then
begin
Result := -1;
exit;
end;
M := Size > DefaultBufSize;
if M then
GetMem(B, Size)
else
B := @F[0];
try
P := GetPosition;
I := 0;
While not EOF and
((MaxOffset < 0) or (I <= MaxOffset)) do
begin
J := Read(B^, Size);
if J < Size then
begin
if EOF then
begin
SetPosition(P);
Result := -1;
exit;
end
else
RaiseReadError;
end;
if CaseSensitive then
R := CompareMem(Buffer, B^, Size)
else
R := CompareMemNoCase(Buffer, B^, Size) = crEqual;
if R then
begin
SetPosition(P);
Result := I;
exit;
end
else
begin
Inc(I);
SetPosition(P + I);
end;
end;
SetPosition(P);
Result := -1;
finally
if M then
FreeMem(B);
end;
end;
function AReaderEx.LocateStr(const S: String; const MaxOffset: Integer;
const CaseSensitive: Boolean): Integer;
begin
Result := LocateBuffer(Pointer(S)^, Length(S), MaxOffset, CaseSensitive);
end;
function AReaderEx.ExtractAll(const C: CharSet; const ExtractNonMatch: Boolean;
const MaxCount: Integer): String;
var I : Integer;
begin
I := Locate(C, not ExtractNonMatch, MaxCount);
if I = -1 then
if MaxCount = -1 then
Result := GetToEOF
else
Result := ReadStr(MaxCount)
else
Result := ReadStr(I);
end;
const
csNewLineNone : CharSet = [];
csNewLineCR : CharSet = [#13];
csNewLineLF : CharSet = [#10];
csNewLineEOF : CharSet = [#26];
csNewLineCRLF : CharSet = [#10, #13];
csNewLineCREOF : CharSet = [#13, #26];
csNewLineLFEOF : CharSet = [#10, #26];
csNewLineCRLFEOF : CharSet = [#10, #13, #26];
procedure FirstNewLineCharsFromEOLTypes(const EOLTypes: TReaderEOLTypes;
var Chars: PCharSet);
begin
if [eolCR, eolCRLF] * EOLTypes <> [] then
if [eolLF, eolLFCR] * EOLTypes <> [] then
if eolEOFAtEOF in EOLTypes then
Chars := @csNewLineCRLFEOF else
Chars := @csNewLineCRLF
else
if eolEOFAtEOF in EOLTypes then
Chars := @csNewLineCREOF else
Chars := @csNewLineCR
else
if [eolLF, eolLFCR] * EOLTypes <> [] then
if eolEOFAtEOF in EOLTypes then
Chars := @csNewLineLFEOF else
Chars := @csNewLineLF
else
if eolEOFAtEOF in EOLTypes then
Chars := @csNewLineEOF
else
Chars := @csNewLineNone;
end;
function AReaderEx.SkipLineTerminator(const EOLTypes: TReaderEOLTypes): Integer;
var C, D : Char;
R : Boolean;
begin
C := Char(ReadByte);
if ((C = #10) and ([eolLFCR, eolLF] * EOLTypes = [eolLF])) or
((C = #13) and ([eolCRLF, eolCR] * EOLTypes = [eolCR])) then
begin
Result := 1;
exit;
end;
if not (((C = #10) and (eolLFCR in EOLTypes)) or
((C = #13) and (eolCRLF in EOLTypes))) then
begin
SetPosition(GetPosition - 1);
Result := 0;
exit;
end;
R := EOF;
if (C = #26) and (eolEOFAtEOF in EOLTypes) and R then
begin
Result := 1;
exit;
end;
if R then
begin
if ((C = #10) and (eolLF in EOLTypes)) or
((C = #13) and (eolCR in EOLTypes)) then
begin
Result := 1;
exit;
end;
SetPosition(GetPosition - 1);
Result := 0;
exit;
end;
D := Char(ReadByte);
if ((C = #13) and (D = #10) and (eolCRLF in EOLTypes)) or
((C = #10) and (D = #13) and (eolLFCR in EOLTypes)) then
begin
Result := 2;
exit;
end;
if ((C = #13) and (eolCR in EOLTypes)) or
((C = #10) and (eolLF in EOLTypes)) then
begin
SetPosition(GetPosition - 1);
Result := 1;
exit;
end;
SetPosition(GetPosition - 2);
Result := 0;
end;
function AReaderEx.ExtractLine(const MaxLineLength: Integer;
const EOLTypes: TReaderEOLTypes): String;
var NewLineChars : PCharSet;
Fin : Boolean;
begin
if EOLTypes = [] then
begin
Result := '';
exit;
end;
if EOLTypes = [eolEOF] then
begin
Result := GetToEOF;
exit;
end;
FirstNewLineCharsFromEOLTypes(EOLTypes, NewLineChars);
Result := '';
Repeat
Result := Result + ExtractAll(NewLineChars^, True, MaxLineLength);
if EOF then
if eolEOF in EOLTypes then
exit
else
RaiseReadError;
Fin := (MaxLineLength >= 0) and (Length(Result) >= MaxLineLength);
if not Fin then
begin
Fin := SkipLineTerminator(EOLTypes) > 0;
if not Fin then
Result := Result + Char(ReadByte);
end;
Until Fin;
end;
function AReaderEx.SkipLine(const MaxLineLength: Integer;
const EOLTypes: TReaderEOLTypes): Boolean;
var NewLineChars : PCharSet;
I : Integer;
P, Q : Int64;
Fin : Boolean;
begin
if EOLTypes = [] then
begin
Result := False;
exit;
end;
Result := True;
if EOLTypes = [eolEOF] then
begin
Position := Size;
exit;
end;
FirstNewLineCharsFromEOLTypes(EOLTypes, NewLineChars);
Fin := False;
Repeat
I := Locate(NewLineChars^, False, MaxLineLength);
if I < 0 then
if MaxLineLength < 0 then
begin
Position := Size;
exit;
end
else
begin
P := Position + MaxLineLength;
Q := Size;
if P > Q then
P := Q;
Position := P;
exit;
end
else
begin
Skip(I);
if EOF then
exit;
Fin := SkipLineTerminator(EOLTypes) > 0;
if not Fin then
SkipByte;
end;
Until Fin;
end;
{ }
{ TMemoryReader }
{ For Size < 0 the memory reader assumes no size limit. }
{ }
constructor TMemoryReader.Create(const Data: Pointer; const Size: Integer);
begin
inherited Create;
SetData(Data, Size);
end;
procedure TMemoryReader.SetData(const Data: Pointer; const Size: Integer);
begin
FData := Data;
FSize := Size;
FPos := 0;
end;
function TMemoryReader.GetPosition: Int64;
begin
Result := FPos;
end;
procedure TMemoryReader.SetPosition(const Position: Int64);
var S : Integer;
begin
S := FSize;
if (Position < 0) or ((S >= 0) and (Position > S)) then
RaiseSeekError;
FPos := Integer(Position);
end;
function TMemoryReader.GetSize: Int64;
begin
Result := FSize;
end;
function TMemoryReader.Read(var Buffer; const Size: Integer): Integer;
var L, S, I : Integer;
P : PByte;
begin
I := FPos;
S := FSize;
if (Size <= 0) or ((S >= 0) and (I >= S)) then
begin
Result := 0;
exit;
end;
if S < 0 then
L := Size
else
begin
L := S - I;
if Size < L then
L := Size;
end;
P := FData;
Inc(P, I);
MoveMem(P^, Buffer, L);
Result := L;
Inc(FPos, L);
end;
function TMemoryReader.EOF: Boolean;
var S : Integer;
begin
S := FSize;
if S < 0 then
Result := False
else
Result := FPos >= S;
end;
function TMemoryReader.ReadByte: Byte;
var I, S : Integer;
P : PByte;
begin
I := FPos;
S := FSize;
if (S >= 0) and (I >= S) then
RaiseReadError;
P := FData;
Inc(P, I);
Result := P^;
Inc(FPos);
end;
function TMemoryReader.ReadLongInt: LongInt;
var I, S : Integer;
begin
I := FPos;
S := FSize;
if (S >= 0) and (I + Sizeof(LongInt) > S) then
RaiseReadError;
Result := PLongInt(@PChar(FData)[I])^;
Inc(FPos, Sizeof(LongInt));
end;
function TMemoryReader.ReadInt64: Int64;
var I, S : Integer;
begin
I := FPos;
S := FSize;
if (S >= 0) and (I + Sizeof(Int64) > S) then
RaiseReadError;
Result := PInt64(@PChar(FData)[I])^;
Inc(FPos, Sizeof(Int64));
end;
function TMemoryReader.PeekByte: Byte;
var I, S : Integer;
P : PByte;
begin
I := FPos;
S := FSize;
if (S >= 0) and (I >= S) then
RaiseReadError;
P := FData;
Inc(P, I);
Result := P^;
end;
function TMemoryReader.Match(const Buffer; const Size: Integer;
const CaseSensitive: Boolean): Integer;
var L, S : Integer;
P : PByte;
R : Boolean;
begin
S := FSize;
if S < 0 then
L := Size
else
begin
L := S - FPos;
if L > Size then
L := Size;
end;
if L <= 0 then
begin
Result := -1;
exit;
end;
P := FData;
Inc(P, FPos);
if CaseSensitive then
R := CompareMem(Buffer, P^, L) else
R := CompareMemNoCase(Buffer, P^, L) = crEqual;
if R then
Result := L else
Result := -1;
end;
procedure TMemoryReader.Skip(const Count: Integer);
var S, I : Integer;
begin
if Count <= 0 then
exit;
S := FSize;
if S < 0 then
Inc(FPos, Count)
else
begin
I := FPos + Count;
if I > S then
RaiseSeekError;
FPos := I;
end;
end;
{ }
{ TStringReader }
{ }
constructor TStringReader.Create(const Data: String);
begin
FDataString := Data;
inherited Create(Pointer(FDataString), Length(FDataString));
end;
procedure TStringReader.SetDataString(const S: String);
begin
FDataString := S;
SetData(Pointer(FDataString), Length(FDataString));
end;
function TStringReader.GetAsString: String;
begin
Result := FDataString;
end;
{ }
{ TFileReader }
{ }
constructor TFileReader.Create(const FileName: String;
const AccessHint: TFileReaderAccessHint);
{$IFDEF OS_WIN32}
var F : LongWord;
{$ENDIF}
begin
inherited Create;
{$IFDEF OS_WIN32}
F := FILE_ATTRIBUTE_NORMAL;
Case AccessHint of
frahNone : ;
frahRandomAccess : F := F or FILE_FLAG_RANDOM_ACCESS;
frahSequentialAccess : F := F or FILE_FLAG_SEQUENTIAL_SCAN;
end;
FHandle := Integer(Windows.CreateFile(PChar(FileName), GENERIC_READ,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, F, 0));
{$ELSE}
FHandle := FileOpen(FileName, fmOpenRead or fmShareDenyNone);
{$ENDIF}
if FHandle = -1 then
raise EFileReader.Create('File open error');
FHandleOwner := True;
end;
constructor TFileReader.Create(const FileHandle: Integer; const HandleOwner: Boolean);
begin
inherited Create;
FHandle := FileHandle;
FHandleOwner := HandleOwner;
end;
destructor TFileReader.Destroy;
begin
if FHandleOwner and (FHandle <> -1) and (FHandle <> 0) then
FileClose(FHandle);
inherited Destroy;
end;
function TFileReader.GetPosition: Int64;
begin
Result := FileSeek(FHandle, Int64(0), 1);
if Result = -1 then
raise EFileReader.Create('File error');
end;
procedure TFileReader.SetPosition(const Position: Int64);
begin
if FileSeek(FHandle, Position, 0) = -1 then
raise EFileReader.Create('File seek error');
end;
function TFileReader.GetSize: Int64;
var I : Int64;
begin
I := GetPosition;
Result := FileSeek(FHandle, Int64(0), 2);
SetPosition(I);
if Result = -1 then
raise EFileReader.Create('File error');
end;
function TFileReader.Read(var Buffer; const Size: Integer): Integer;
var I : Integer;
begin
if Size <= 0 then
begin
Result := 0;
exit;
end;
I := FileRead(FHandle, Buffer, Size);
if I <= 0 then
begin
Result := 0;
exit;
end;
Result := I;
end;
function TFileReader.EOF: Boolean;
begin
Result := GetPosition >= GetSize;
end;
{ ReadFileToStr }
function ReadFileToStr(const FileName: String): String;
var F : TFileReader;
begin
F := TFileReader.Create(FileName);
try
Result := F.GetAsString;
finally
F.Free;
end;
end;
{ }
{ AReaderProxy }
{ }
constructor AReaderProxy.Create(const Reader: AReaderEx; const ReaderOwner: Boolean);
begin
Assert(Assigned(Reader));
inherited Create;
FReader := Reader;
FReaderOwner := ReaderOwner;
end;
destructor AReaderProxy.Destroy;
begin
if FReaderOwner then
FreeAndNil(FReader);
inherited Destroy;
end;
function AReaderProxy.Read(var Buffer; const Size: Integer): Integer;
begin
Result := FReader.Read(Buffer, Size);
end;
function AReaderProxy.EOF: Boolean;
begin
Result := FReader.EOF;
end;
function AReaderProxy.GetPosition: Int64;
begin
Result := FReader.GetPosition;
end;
procedure AReaderProxy.SetPosition(const Position: Int64);
begin
FReader.SetPosition(Position);
end;
function AReaderProxy.GetSize: Int64;
begin
Result := FReader.GetSize;
end;
{ }
{ TReaderProxy }
{ }
constructor TReaderProxy.Create(const Reader: AReaderEx; const ReaderOwner: Boolean;
const Size: Int64);
begin
inherited Create(Reader, ReaderOwner);
FOffset := Reader.GetPosition;
FSize := Size;
end;
function TReaderProxy.GetPosition: Int64;
begin
Result := FReader.GetPosition - FOffset;
end;
procedure TReaderProxy.SetPosition(const Position: Int64);
begin
if Position < 0 then
raise EReader.Create('Seek error');
if (FSize >= 0) and (Position > FOffset + FSize) then
raise EReader.Create('Seek error');
FReader.SetPosition(FOffset + Position);
end;
function TReaderProxy.GetSize: Int64;
begin
Result := FReader.GetSize;
if Result >= FOffset then
Dec(Result, FOffset)
else
Result := -1;
if (FSize >= 0) and (FSize < Result) then
Result := FSize;
end;
function TReaderProxy.EOF: Boolean;
begin
Result := FReader.EOF;
if Result or (FSize < 0) then
exit;
Result := FReader.Position >= FOffset + FSize;
end;
function TReaderProxy.Read(var Buffer; const Size: Integer): Integer;
var L : Integer;
M : Int64;
begin
L := Size;
if FSize >= 0 then
begin
M := FSize - (FReader.Position - FOffset);
if M < L then
L := Integer(M);
end;
if L <= 0 then
begin
Result := 0;
exit;
end;
Result := FReader.Read(Buffer, L);
end;
{ }
{ TBufferedReader }
{ }
constructor TBufferedReader.Create(const Reader: AReaderEx; const BufferSize: Integer;
const ReaderOwner: Boolean);
begin
inherited Create(Reader, ReaderOwner);
FBufferSize := BufferSize;
GetMem(FBuffer, BufferSize);
FPos := Reader.GetPosition;
end;
destructor TBufferedReader.Destroy;
begin
if Assigned(FBuffer) then
FreeMem(FBuffer);
inherited Destroy;
end;
function TBufferedReader.GetPosition: Int64;
begin
Result := FPos;
end;
function TBufferedReader.GetSize: Int64;
begin
Result := FReader.GetSize;
end;
procedure TBufferedReader.SetPosition(const Position: Int64);
var B, C : Int64;
begin
B := Position - FPos;
if B = 0 then
exit;
C := B + FBufPos;
if (C >= 0) and (C <= FBufUsed) then
begin
Inc(FBufPos, Integer(B));
FPos := Position;
exit;
end;
FReader.SetPosition(Position);
FPos := Position;
FBufPos := 0;
FBufUsed := 0;
end;
procedure TBufferedReader.Skip(const Count: Integer);
var I : Integer;
P : Int64;
begin
if Count < 0 then
raise EReader.Create('Seek error');
if Count = 0 then
exit;
I := FBufUsed - FBufPos;
if I >= Count then
begin
Inc(FBufPos, Count);
Inc(FPos, Count);
exit;
end;
P := GetPosition + Count;
FReader.SetPosition(P);
FPos := P;
FBufPos := 0;
FBufUsed := 0;
end;
// Internal function FillBuf
// Returns True if buffer was only partially filled
function TBufferedReader.FillBuf: Boolean;
var P : PByte;
L, N : Integer;
begin
L := FBufferSize - FBufUsed;
if L <= 0 then
begin
Result := False;
exit;
end;
P := FBuffer;
Inc(P, FBufPos);
N := FReader.Read(P^, L);
Inc(FBufUsed, N);
Result := N < L;
end;
function TBufferedReader.Read(var Buffer; const Size: Integer): Integer;
var L, M : Integer;
P, Q : PByte;
R : Boolean;
begin
if Size <= 0 then
begin
Result := 0;
exit;
end;
Q := @Buffer;
M := Size;
R := False;
Repeat
L := FBufUsed - FBufPos;
if L > M then
L := M;
if L > 0 then
begin
P := FBuffer;
Inc(P, FBufPos);
MoveMem(P^, Q^, L);
Inc(FBufPos, L);
Inc(FPos, L);
Dec(M, L);
if M = 0 then
begin
Result := Size;
exit;
end;
Inc(Q, L);
end;
FBufPos := 0;
FBufUsed := 0;
if R then
begin
Result := Size - M;
exit;
end;
R := FillBuf;
Until False;
end;
function TBufferedReader.EOF: Boolean;
begin
if FBufUsed > FBufPos then
Result := False else
Result := FReader.EOF;
end;
procedure TBufferedReader.InvalidateBuffer;
begin
FReader.SetPosition(FPos);
FBufPos := 0;
FBufUsed := 0;
end;
// Internal function BufferByte
// Fills buffer with at least one character, otherwise raises an exception
procedure TBufferedReader.BufferByte;
var I : Integer;
begin
I := FBufUsed;
if FBufPos < I then
exit;
if I >= FBufferSize then
begin
FBufPos := 0;
FBufUsed := 0;
end;
FillBuf;
if FBufPos >= FBufUsed then
RaiseReadError;
end;
function TBufferedReader.ReadByte: Byte;
var P : PByte;
begin
BufferByte;
P := FBuffer;
Inc(P, FBufPos);
Result := P^;
Inc(FBufPos);
Inc(FPos);
end;
function TBufferedReader.PeekByte: Byte;
var P : PByte;
begin
BufferByte;
P := FBuffer;
Inc(P, FBufPos);
Result := P^;
end;
function TBufferedReader.PosBuf(const C: CharSet; const LocateNonMatch: Boolean;
const MaxOffset: Integer): Integer;
var P : PChar;
L : Integer;
begin
P := FBuffer;
L := FBufPos;
Inc(P, L);
Result := 0;
While (L < FBufUsed) and ((MaxOffset < 0) or (Result <= MaxOffset)) do
if (P^ in C) xor LocateNonMatch then
exit else
begin
Inc(P);
Inc(L);
Inc(Result);
end;
Result := -1;
end;
function TBufferedReader.Locate(const C: CharSet; const LocateNonMatch: Boolean;
const MaxOffset: Integer): Integer;
var I, J, M, K : Integer;
P : Int64;
R : Boolean;
begin
P := GetPosition;
M := MaxOffset;
J := 0;
R := False;
Repeat
K := FBufUsed - FBufPos;
if K > 0 then
begin
I := PosBuf(C, LocateNonMatch, M);
if I >= 0 then
begin
SetPosition(P);
Result := J + I;
exit;
end;
end;
if R then
begin
SetPosition(P);
Result := -1;
exit;
end;
Inc(J, K);
Inc(FPos, K);
FBufPos := 0;
FBufUsed := 0;
if M >= 0 then
begin
Dec(M, K);
if M < 0 then
begin
SetPosition(P);
Result := -1;
exit;
end;
end;
R := FillBuf;
Until False;
end;
{ }
{ TSplitBufferedReader }
{ }
constructor TSplitBufferedReader.Create(const Reader: AReaderEx; const BufferSize: Integer;
const ReaderOwner: Boolean);
var I : Integer;
begin
inherited Create(Reader, ReaderOwner);
FBufferSize := BufferSize;
For I := 0 to 1 do
GetMem(FBuffer[I], BufferSize);
FPos := Reader.GetPosition;
end;
destructor TSplitBufferedReader.Destroy;
var I : Integer;
begin
For I := 0 to 1 do
if Assigned(FBuffer[I]) then
FreeMem(FBuffer[I]);
inherited Destroy;
end;
function TSplitBufferedReader.GetSize: Int64;
begin
Result := FReader.GetSize;
end;
function TSplitBufferedReader.GetPosition: Int64;
begin
Result := FPos;
end;
// Internal function BufferStart used by SetPosition
// Returns the relative offset of the first buffered byte
function TSplitBufferedReader.BufferStart: Integer;
begin
Result := -FBufPos;
if FBufNr = 1 then
Dec(Result, FBufUsed[0]);
end;
// Internal function BufferRemaining used by SetPosition
// Returns the length of the remaining buffered data
function TSplitBufferedReader.BufferRemaining: Integer;
begin
Result := FBufUsed[FBufNr] - FBufPos;
if FBufNr = 0 then
Inc(Result, FBufUsed[1]);
end;
procedure TSplitBufferedReader.SetPosition(const Position: Int64);
var D : Int64;
begin
D := Position - FPos;
if D = 0 then
exit;
if (D >= BufferStart) and (D <= BufferRemaining) then
begin
Inc(FBufPos, D);
if (FBufNr = 1) and (FBufPos < 0) then // Set position from Buf1 to Buf0
begin
Inc(FBufPos, FBufUsed[0]);
FBufNr := 0;
end else
if (FBufNr = 0) and (FBufPos > FBufUsed[0]) then // Set position from Buf0 to Buf1
begin
Dec(FBufPos, FBufUsed[0]);
FBufNr := 1;
end;
FPos := Position;
exit;
end;
FReader.SetPosition(Position);
FPos := Position;
FBufNr := 0;
FBufPos := 0;
FBufUsed[0] := 0;
FBufUsed[1] := 0;
end;
procedure TSplitBufferedReader.InvalidateBuffer;
begin
FReader.SetPosition(FPos);
FBufNr := 0;
FBufPos := 0;
FBufUsed[0] := 0;
FBufUsed[1] := 0;
end;
// Internal function MoveBuf used by Read
// Moves remaining data from Buffer[BufNr]^[BufPos] to Dest
// Returns True if complete (Remaining=0)
function TSplitBufferedReader.MoveBuf(var Dest: PByte; var Remaining: Integer): Boolean;
var P : PByte;
L, R, N, O : Integer;
begin
N := FBufNr;
O := FBufPos;
L := FBufUsed[N] - O;
if L <= 0 then
begin
Result := False;
exit;
end;
P := FBuffer[N];
Inc(P, O);
R := Remaining;
if R < L then
L := R;
MoveMem(P^, Dest^, L);
Inc(Dest, L);
Inc(FBufPos, L);
Dec(R, L);
if R <= 0 then
Result := True else
Result := False;
Remaining := R;
end;
// Internal function FillBuf used by Read
// Fill Buffer[BufNr]^[BufPos] with up to BufferSize bytes and moves
// the read data to Dest
// Returns True if complete (incomplete Read or Remaining=0)
function TSplitBufferedReader.FillBuf(var Dest: PByte; var Remaining: Integer): Boolean;
var P : PByte;
I, L, N : Integer;
begin
N := FBufNr;
I := FBufUsed[N];
L := FBufferSize - I;
if L <= 0 then
begin
Result := False;
exit;
end;
P := FBuffer[N];
Inc(P, I);
I := FReader.Read(P^, L);
if I > 0 then
begin
Inc(FBufUsed[N], I);
if MoveBuf(Dest, Remaining) then
begin
Result := True;
exit;
end;
end;
Result := I < L;
end;
function TSplitBufferedReader.Read(var Buffer; const Size: Integer): Integer;
var Dest : PByte;
Remaining : Integer;
begin
if Size <= 0 then
begin
Result := 0;
exit;
end;
Dest := @Buffer;
Remaining := Size;
Repeat
if MoveBuf(Dest, Remaining) then
begin
Result := Size;
Inc(FPos, Size);
exit;
end;
if FillBuf(Dest, Remaining) then
begin
Result := Size - Remaining;
Inc(FPos, Result);
exit;
end;
if FBufNr = 0 then
FBufNr := 1 else
begin
Swap(FBuffer[0], FBuffer[1]);
FBufUsed[0] := FBufUsed[1];
FBufUsed[1] := 0;
end;
FBufPos := 0;
Until False;
end;
function TSplitBufferedReader.EOF: Boolean;
begin
if FBufUsed[FBufNr] > FBufPos then
Result := False else
if FBufNr = 1 then
Result := FReader.EOF else
if FBufUsed[1] > 0 then
Result := False else
Result := FReader.EOF;
end;
{ }
{ TBufferedFileReader }
{ }
constructor TBufferedFileReader.Create(const FileName: String; const BufferSize: Integer);
begin
inherited Create(TFileReader.Create(FileName), BufferSize, True);
end;
constructor TBufferedFileReader.Create(const FileHandle: Integer;
const HandleOwner: Boolean; const BufferSize: Integer);
begin
inherited Create(TFileReader.Create(FileHandle, HandleOwner), BufferSize, True);
end;
{ }
{ TSplitBufferedFileReader }
{ }
constructor TSplitBufferedFileReader.Create(const FileName: String; const BufferSize: Integer);
begin
inherited Create(TFileReader.Create(FileName), BufferSize, True);
end;
{ }
{ Self-testing code }
{ }
{$ASSERTIONS ON}
procedure TestReader(const Reader: AReaderEx; const FreeReader: Boolean);
begin
try
Reader.Position := 0;
Assert(not Reader.EOF, 'Reader.EOF');
Assert(Reader.Size = 26, 'Reader.Size');
Assert(Reader.PeekStr(0) = '', 'Reader.PeekStr');
Assert(Reader.PeekStr(-1) = '', 'Reader.PeekStr');
Assert(Reader.PeekStr(2) = '01', 'Reader.PeekStr');
Assert(Char(Reader.PeekByte) = '0', 'Reader.PeekByte');
Assert(Char(Reader.ReadByte) = '0', 'Reader.ReadByte');
Assert(Char(Reader.PeekByte) = '1', 'Reader.PeekByte');
Assert(Char(Reader.ReadByte) = '1', 'Reader.ReadByte');
Assert(Reader.ReadStr(0) = '', 'Reader.ReadStr');
Assert(Reader.ReadStr(-1) = '', 'Reader.ReadStr');
Assert(Reader.ReadStr(1) = '2', 'Reader.ReadStr');
Assert(Reader.MatchChar('3'), 'Reader.MatchChar');
Assert(Reader.MatchStr('3', True), 'Reader.MatchStr');
Assert(Reader.MatchStr('345', True), 'Reader.MatchStr');
Assert(not Reader.MatchStr('35', True), 'Reader.MatchStr');
Assert(not Reader.MatchStr('4', True), 'Reader.MatchStr');
Assert(not Reader.MatchStr('', True), 'Reader.MatchStr');
Assert(Reader.ReadStr(2) = '34', 'Reader.ReadStr');
Assert(Reader.PeekStr(3) = '567', 'Reader.PeekStr');
Assert(Reader.Locate('5', False, 0) = 0, 'Reader.Locate');
Assert(Reader.Locate('8', False, -1) = 3, 'Reader.Locate');
Assert(Reader.Locate('8', False, 3) = 3, 'Reader.Locate');
Assert(Reader.Locate('8', False, 2) = -1, 'Reader.Locate');
Assert(Reader.Locate('8', False, 4) = 3, 'Reader.Locate');
Assert(Reader.Locate('0', False, -1) = -1, 'Reader.Locate');
Assert(Reader.Locate(['8'], False, -1) = 3, 'Reader.Locate');
Assert(Reader.Locate(['8'], False, 3) = 3, 'Reader.Locate');
Assert(Reader.Locate(['8'], False, 2) = -1, 'Reader.Locate');
Assert(Reader.Locate(['0'], False, -1) = -1, 'Reader.Locate');
Assert(Reader.LocateStr('8', -1, True) = 3, 'Reader.LocateStr');
Assert(Reader.LocateStr('8', 3, True) = 3, 'Reader.LocateStr');
Assert(Reader.LocateStr('8', 2, True) = -1, 'Reader.LocateStr');
Assert(Reader.LocateStr('89', -1, True) = 3, 'Reader.LocateStr');
Assert(Reader.LocateStr('0', -1, True) = -1, 'Reader.LocateStr');
Assert(not Reader.EOF, 'Reader.EOF');
Assert(Reader.Position = 5, 'Reader.Position');
Reader.Position := 7;
Reader.SkipByte;
Assert(Reader.Position = 8, 'Reader.Position');
Reader.Skip(2);
Assert(Reader.Position = 10, 'Reader.Position');
Assert(not Reader.EOF, 'Reader.EOF');
Assert(Reader.MatchStr('abcd', False), 'Reader.MatchStr');
Assert(not Reader.MatchStr('abcd', True), 'Reader.MatchStr');
Assert(Reader.LocateStr('d', -1, True) = 3, 'Reader.LocateStr');
Assert(Reader.LocateStr('d', 3, False) = 3, 'Reader.LocateStr');
Assert(Reader.LocateStr('D', -1, True) = -1, 'Reader.LocateStr');
Assert(Reader.LocateStr('D', -1, False) = 3, 'Reader.LocateStr');
Assert(Reader.SkipAll('X', False, -1) = 0, 'Reader.SkipAll');
Assert(Reader.SkipAll('A', False, -1) = 1, 'Reader.SkipAll');
Assert(Reader.SkipAll(['b', 'C'], False, -1) = 2, 'Reader.SkipAll');
Assert(Reader.SkipAll(['d'], False, 0) = 0, 'Reader.SkipAll');
Assert(Reader.ExtractAll(['d', 'E'], False, 1) = 'd', 'Reader.ExtractAll');
Assert(Reader.ExtractAll(['*'], True, 1) = 'E', 'Reader.ExtractAll');
Assert(Reader.ReadStr(2) = '*.', 'Reader.ReadStr');
Assert(Reader.ExtractAll(['X'], False, 1) = 'X', 'Reader.ExtractAll');
Assert(Reader.ExtractAll(['X'], False, -1) = 'XX', 'Reader.ExtractAll');
Assert(Reader.ExtractAll(['X', '*'], True, 1) = 'Y', 'Reader.ExtractAll');
Assert(Reader.ExtractAll(['X', '*'], True, -1) = 'YYY','Reader.ExtractAll');
Assert(Reader.ExtractAll(['X'], False, -1) = '', 'Reader.ExtractAll');
Assert(Reader.ExtractAll(['X'], True, -1) = '*.', 'Reader.ExtractAll');
Assert(Reader.EOF, 'Reader.EOF');
Assert(Reader.Position = 26, 'Reader.Position');
Reader.Position := Reader.Position - 2;
Assert(Reader.PeekStr(3) = '*.', 'Reader.PeekStr');
Assert(Reader.ReadStr(3) = '*.', 'Reader.ReadStr');
finally
if FreeReader then
Reader.Free;
end;
end;
procedure TestLineReader(const Reader: AReaderEx; const FreeReader: Boolean);
begin
try
Reader.Position := 0;
Assert(not Reader.EOF, 'Reader.EOF');
Assert(Reader.ExtractLine = '1', 'Reader.ExtractLine');
Assert(Reader.ExtractLine = '23', 'Reader.ExtractLine');
Assert(Reader.ExtractLine = '', 'Reader.ExtractLine');
Assert(Reader.ExtractLine = '4', 'Reader.ExtractLine');
Assert(Reader.ExtractLine = '5', 'Reader.ExtractLine');
Assert(Reader.ExtractLine = '6', 'Reader.ExtractLine');
Assert(Reader.EOF, 'Reader.EOF');
Reader.Position := 0;
Assert(Reader.ExtractLine(-1, [eolCRLF, eolEOF]) = '1', 'Reader.ExtractLine');
Assert(Reader.ExtractLine(-1, [eolCRLF, eolEOF]) = '23'#13#13'4'#10'5'#10#13'6', 'Reader.ExtractLine');
Assert(Reader.EOF, 'Reader.EOF');
Reader.Position := 0;
Assert(Reader.ExtractLine(-1, [eolCR, eolEOF]) = '1', 'Reader.ExtractLine');
Assert(Reader.ExtractLine(-1, [eolCR, eolEOF]) = #10'23', 'Reader.ExtractLine');
Assert(Reader.ExtractLine(-1, [eolCR, eolEOF]) = '', 'Reader.ExtractLine');
Assert(Reader.ExtractLine(-1, [eolCR, eolEOF]) = '4'#10'5'#10, 'Reader.ExtractLine');
Assert(Reader.ExtractLine(-1, [eolCR, eolEOF]) = '6', 'Reader.ExtractLine');
Assert(Reader.EOF, 'Reader.EOF');
Reader.Position := 0;
Assert(Reader.ExtractLine(-1, [eolCR, eolCRLF, eolEOF]) = '1', 'Reader.ExtractLine');
Assert(Reader.ExtractLine(-1, [eolCR, eolCRLF, eolEOF]) = '23', 'Reader.ExtractLine');
Assert(Reader.ExtractLine(-1, [eolCR, eolCRLF, eolEOF]) = '', 'Reader.ExtractLine');
Assert(Reader.ExtractLine(-1, [eolCR, eolCRLF, eolEOF]) = '4'#10'5'#10, 'Reader.ExtractLine');
Assert(Reader.ExtractLine(-1, [eolCR, eolCRLF, eolEOF]) = '6', 'Reader.ExtractLine');
Assert(Reader.EOF, 'Reader.EOF');
Reader.Position := 0;
Assert(Reader.SkipLine(-1, [eolCRLF, eolEOF]), 'Reader.SkipLine');
Assert(Reader.SkipLine(-1, [eolCRLF, eolEOF]), 'Reader.SkipLine');
Assert(Reader.EOF, 'Reader.EOF');
Reader.Position := 0;
Assert(Reader.SkipLine(-1, [eolCR, eolCRLF, eolEOF]), 'Reader.SkipLine');
Assert(Reader.SkipLine(-1, [eolCR, eolCRLF, eolEOF]), 'Reader.SkipLine');
Assert(Reader.SkipLine(-1, [eolCR, eolCRLF, eolEOF]), 'Reader.SkipLine');
Assert(Reader.SkipLine(-1, [eolCR, eolCRLF, eolEOF]), 'Reader.SkipLine');
Assert(Reader.SkipLine(-1, [eolCR, eolCRLF, eolEOF]), 'Reader.SkipLine');
Assert(Reader.EOF, 'Reader.EOF');
finally
if FreeReader then
Reader.Free;
end;
end;
type
TUnsizedStringReader = class(TStringReader)
protected
function GetSize: Int64; override;
end;
function TUnsizedStringReader.GetSize: Int64;
begin
Result := -1;
end;
procedure TestUnsizedReader(const Data: String);
var S : TUnsizedStringReader;
T : String;
begin
S := TUnsizedStringReader.Create(Data);
try
T := S.GetToEOF;
Assert(T = Data, 'UnsizedReader.GetToEOF');
Assert(S.EOF, 'UnsizedReader.EOF');
finally
S.Free;
end;
end;
procedure SelfTest;
var S : TStringReader;
I : Integer;
T : String;
begin
S := TStringReader.Create('0123456789AbCdE*.XXXYYYY*.');
try
TestReader(TReaderProxy.Create(S, False, -1), True);
TestReader(S, False);
TestReader(TBufferedReader.Create(S, 128, False), True);
For I := 1 to 16 do
TestReader(TBufferedReader.Create(S, I, False), True);
TestReader(TSplitBufferedReader.Create(S, 128, False), True);
For I := 1 to 16 do
TestReader(TSplitBufferedReader.Create(S, I, False), True);
finally
S.Free;
end;
S := TStringReader.Create('1'#13#10'23'#13#13'4'#10'5'#10#13'6');
try
TestLineReader(TReaderProxy.Create(S, False, -1), True);
For I := 1 to 32 do
TestLineReader(TBufferedReader.Create(S, I, False), True);
For I := 1 to 32 do
TestLineReader(TSplitBufferedReader.Create(S, I, False), True);
TestLineReader(S, False);
finally
S.Free;
end;
TestUnsizedReader('');
TestUnsizedReader('A');
TestUnsizedReader('ABC');
T := '';
For I := 1 to 1000 do
T := T + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
TestUnsizedReader(T);
end;
end.