StringScanner.pas 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. unit StringScanner;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. TStringScanner
  8. }
  9. interface
  10. uses
  11. SysUtils;
  12. type
  13. TStringScanner = record
  14. strict private
  15. FStr: String;
  16. FPosition: Integer;
  17. function GetReachedEnd: Boolean;
  18. function GetRemainingCount: Integer;
  19. public
  20. class function Create(const AString: String): TStringScanner; static;
  21. function Consume(const C: Char): Boolean; overload;
  22. function Consume(const S: String): Boolean; overload;
  23. function ConsumeMulti(const C: TSysCharSet; const AMinChars: Integer = 1;
  24. const AMaxChars: Integer = Maxint): Integer;
  25. function ConsumeMultiToString(const C: TSysCharSet;
  26. var ACapturedString: String; const AMinChars: Integer = 1;
  27. const AMaxChars: Integer = Maxint): Integer;
  28. property ReachedEnd: Boolean read GetReachedEnd;
  29. property RemainingCount: Integer read GetRemainingCount;
  30. property Str: String read FStr;
  31. end;
  32. implementation
  33. {$ZEROBASEDSTRINGS OFF}
  34. { TStringScanner }
  35. class function TStringScanner.Create(const AString: String): TStringScanner;
  36. begin
  37. Result.FPosition := 1;
  38. Result.FStr := AString;
  39. end;
  40. function TStringScanner.Consume(const C: Char): Boolean;
  41. begin
  42. Result := (GetRemainingCount > 0) and (FStr[FPosition] = C);
  43. if Result then
  44. Inc(FPosition);
  45. end;
  46. function TStringScanner.Consume(const S: String): Boolean;
  47. begin
  48. const SLen = Length(S);
  49. if SLen > GetRemainingCount then
  50. Exit(False);
  51. for var I := 0 to SLen-1 do
  52. if FStr[FPosition + I] <> S[I+1] then
  53. Exit(False);
  54. Inc(FPosition, SLen);
  55. Result := True;
  56. end;
  57. function TStringScanner.ConsumeMulti(const C: TSysCharSet;
  58. const AMinChars: Integer = 1; const AMaxChars: Integer = Maxint): Integer;
  59. begin
  60. if (AMinChars <= 0) or (AMinChars > AMaxChars) then
  61. raise Exception.Create('TStringScanner.ConsumeMulti: Invalid parameter');
  62. const Remain = GetRemainingCount;
  63. if Remain < AMinChars then
  64. Exit(0);
  65. Result := 0;
  66. while (Result < AMaxChars) and (Result < Remain) and
  67. CharInSet(FStr[FPosition + Result], C) do
  68. Inc(Result);
  69. if Result < AMinChars then
  70. Result := 0
  71. else
  72. Inc(FPosition, Result);
  73. end;
  74. function TStringScanner.ConsumeMultiToString(const C: TSysCharSet;
  75. var ACapturedString: String; const AMinChars: Integer = 1;
  76. const AMaxChars: Integer = Maxint): Integer;
  77. begin
  78. const StartPos = FPosition;
  79. Result := ConsumeMulti(C, AMinChars, AMaxChars);
  80. if Result > 0 then
  81. ACapturedString := Copy(FStr, StartPos, Result)
  82. else
  83. ACapturedString := '';
  84. end;
  85. function TStringScanner.GetReachedEnd: Boolean;
  86. begin
  87. Result := (GetRemainingCount = 0);
  88. end;
  89. function TStringScanner.GetRemainingCount: Integer;
  90. begin
  91. { The "<= 0" check exists to protect against OOB reads in case someone calls
  92. into an instance that was never properly initialized (via Create).
  93. Inside TStringScanner, FStr[FPosition] must not be accessed unless
  94. GetRemainingCount is called first and returns a nonzero value. }
  95. const Len = Length(FStr);
  96. if (FPosition <= 0) or (FPosition > Len) then
  97. Result := 0
  98. else
  99. Result := Len - FPosition + 1;
  100. end;
  101. end.