Inno-Setup-issrc/Components/StringScanner.pas
2025-04-15 01:53:36 -05:00

124 lines
3.3 KiB
ObjectPascal

unit StringScanner;
{
Inno Setup
Copyright (C) 1997-2025 Jordan Russell
Portions by Martijn Laan
For conditions of distribution and use, see LICENSE.TXT.
TStringScanner
}
interface
uses
SysUtils;
type
TStringScanner = record
strict private
FStr: String;
FPosition: Integer;
function GetReachedEnd: Boolean;
function GetRemainingCount: Integer;
public
class function Create(const AString: String): TStringScanner; static;
function Consume(const C: Char): Boolean; overload;
function Consume(const S: String): Boolean; overload;
function ConsumeMulti(const C: TSysCharSet; const AMinChars: Integer = 1;
const AMaxChars: Integer = Maxint): Integer;
function ConsumeMultiToString(const C: TSysCharSet;
var ACapturedString: String; const AMinChars: Integer = 1;
const AMaxChars: Integer = Maxint): Integer;
property ReachedEnd: Boolean read GetReachedEnd;
property RemainingCount: Integer read GetRemainingCount;
property Str: String read FStr;
end;
implementation
{$ZEROBASEDSTRINGS OFF}
{ TStringScanner }
class function TStringScanner.Create(const AString: String): TStringScanner;
begin
Result.FPosition := 1;
Result.FStr := AString;
end;
function TStringScanner.Consume(const C: Char): Boolean;
begin
Result := (GetRemainingCount > 0) and (FStr[FPosition] = C);
if Result then
Inc(FPosition);
end;
function TStringScanner.Consume(const S: String): Boolean;
begin
const SLen = Length(S);
if SLen > GetRemainingCount then
Exit(False);
for var I := 0 to SLen-1 do
if FStr[FPosition + I] <> S[I+1] then
Exit(False);
Inc(FPosition, SLen);
Result := True;
end;
function TStringScanner.ConsumeMulti(const C: TSysCharSet;
const AMinChars: Integer = 1; const AMaxChars: Integer = Maxint): Integer;
begin
if (AMinChars <= 0) or (AMinChars > AMaxChars) then
raise Exception.Create('TStringScanner.ConsumeMulti: Invalid parameter');
const Remain = GetRemainingCount;
if Remain < AMinChars then
Exit(0);
Result := 0;
while (Result < AMaxChars) and (Result < Remain) and
CharInSet(FStr[FPosition + Result], C) do
Inc(Result);
if Result < AMinChars then
Result := 0
else
Inc(FPosition, Result);
end;
function TStringScanner.ConsumeMultiToString(const C: TSysCharSet;
var ACapturedString: String; const AMinChars: Integer = 1;
const AMaxChars: Integer = Maxint): Integer;
begin
const StartPos = FPosition;
Result := ConsumeMulti(C, AMinChars, AMaxChars);
if Result > 0 then
ACapturedString := Copy(FStr, StartPos, Result)
else
ACapturedString := '';
end;
function TStringScanner.GetReachedEnd: Boolean;
begin
Result := (GetRemainingCount = 0);
end;
function TStringScanner.GetRemainingCount: Integer;
begin
{ The "<= 0" check exists to protect against OOB reads in case someone calls
into an instance that was never properly initialized (via Create).
Inside TStringScanner, FStr[FPosition] must not be accessed unless
GetRemainingCount is called first and returns a nonzero value. }
const Len = Length(FStr);
if (FPosition <= 0) or (FPosition > Len) then
Result := 0
else
Result := Len - FPosition + 1;
end;
end.