StringScanner.pas 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  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 AAllowedChars: TSysCharSet;
  24. const AAllowAllCharsAboveFF: Boolean = False; const AMinChars: Integer = 1;
  25. const AMaxChars: Integer = MaxInt): Integer;
  26. function ConsumeMultiToString(const AAllowedChars: TSysCharSet;
  27. var ACapturedString: String; const AAllowAllCharsAboveFF: Boolean = False;
  28. const AMinChars: Integer = 1; const AMaxChars: Integer = MaxInt): Integer;
  29. property ReachedEnd: Boolean read GetReachedEnd;
  30. property RemainingCount: Integer read GetRemainingCount;
  31. property Str: String read FStr;
  32. end;
  33. implementation
  34. {$ZEROBASEDSTRINGS OFF}
  35. { TStringScanner }
  36. class function TStringScanner.Create(const AString: String): TStringScanner;
  37. begin
  38. Result.FPosition := 1;
  39. Result.FStr := AString;
  40. end;
  41. function TStringScanner.Consume(const C: Char): Boolean;
  42. begin
  43. Result := (GetRemainingCount > 0) and (FStr[FPosition] = C);
  44. if Result then
  45. Inc(FPosition);
  46. end;
  47. function TStringScanner.Consume(const S: String): Boolean;
  48. begin
  49. const SLen = Length(S);
  50. if SLen > GetRemainingCount then
  51. Exit(False);
  52. for var I := 0 to SLen-1 do
  53. if FStr[FPosition + I] <> S[I+1] then
  54. Exit(False);
  55. Inc(FPosition, SLen);
  56. Result := True;
  57. end;
  58. function TStringScanner.ConsumeMulti(const AAllowedChars: TSysCharSet;
  59. const AAllowAllCharsAboveFF: Boolean = False; const AMinChars: Integer = 1;
  60. const AMaxChars: Integer = MaxInt): Integer;
  61. begin
  62. { AMinChars may be 0; it functions the same as 1 }
  63. if (AMinChars < 0) or (AMinChars > AMaxChars) then
  64. raise Exception.Create('TStringScanner.ConsumeMulti: Invalid parameter');
  65. const Remain = GetRemainingCount;
  66. if Remain < AMinChars then
  67. Exit(0);
  68. Result := 0;
  69. while (Result < AMaxChars) and (Result < Remain) and
  70. (CharInSet(FStr[FPosition + Result], AAllowedChars) or
  71. (AAllowAllCharsAboveFF and (Ord(FStr[FPosition + Result]) > $FF))) do
  72. Inc(Result);
  73. if Result < AMinChars then
  74. Result := 0
  75. else
  76. Inc(FPosition, Result);
  77. end;
  78. function TStringScanner.ConsumeMultiToString(const AAllowedChars: TSysCharSet;
  79. var ACapturedString: String; const AAllowAllCharsAboveFF: Boolean = False;
  80. const AMinChars: Integer = 1; const AMaxChars: Integer = MaxInt): Integer;
  81. begin
  82. const StartPos = FPosition;
  83. Result := ConsumeMulti(AAllowedChars, AAllowAllCharsAboveFF, AMinChars, AMaxChars);
  84. if Result > 0 then
  85. ACapturedString := Copy(FStr, StartPos, Result)
  86. else
  87. ACapturedString := '';
  88. end;
  89. function TStringScanner.GetReachedEnd: Boolean;
  90. begin
  91. Result := (GetRemainingCount = 0);
  92. end;
  93. function TStringScanner.GetRemainingCount: Integer;
  94. begin
  95. { The "<= 0" check exists to protect against OOB reads in case someone calls
  96. into an instance that was never properly initialized (via Create).
  97. Inside TStringScanner, FStr[FPosition] must not be accessed unless
  98. GetRemainingCount is called first and returns a nonzero value. }
  99. const Len = Length(FStr);
  100. if (FPosition <= 0) or (FPosition > Len) then
  101. Result := 0
  102. else
  103. Result := Len - FPosition + 1;
  104. end;
  105. end.