Compiler.StringLists.pas 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. unit Compiler.StringLists;
  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. Special string list classes used by TSetupCompiler
  8. }
  9. interface
  10. uses
  11. Classes;
  12. type
  13. THashStringItem = record
  14. Hash: Longint;
  15. Str: String;
  16. end;
  17. const
  18. MaxHashStringItemListSize = MaxInt div 16;
  19. type
  20. PHashStringItemList = ^THashStringItemList;
  21. THashStringItemList = array[0..MaxHashStringItemListSize-1] of THashStringItem;
  22. THashStringList = class
  23. private
  24. FCapacity: Integer;
  25. FCount: Integer;
  26. FIgnoreDuplicates: Boolean;
  27. FList: PHashStringItemList;
  28. procedure Grow;
  29. public
  30. destructor Destroy; override;
  31. function Add(const S: String): Integer;
  32. function CaseInsensitiveIndexOf(const S: String): Integer;
  33. procedure Clear;
  34. function Get(Index: Integer): String;
  35. property Count: Integer read FCount;
  36. property IgnoreDuplicates: Boolean read FIgnoreDuplicates write FIgnoreDuplicates;
  37. property Strings[Index: Integer]: String read Get; default;
  38. end;
  39. PScriptFileLine = ^TScriptFileLine;
  40. TScriptFileLine = record
  41. LineFilename: String;
  42. LineNumber: Integer;
  43. LineText: String;
  44. end;
  45. TScriptFileLines = class
  46. private
  47. FLines: TList;
  48. function Get(Index: Integer): PScriptFileLine;
  49. function GetCount: Integer;
  50. function GetText: String;
  51. public
  52. constructor Create;
  53. destructor Destroy; override;
  54. procedure Add(const LineFilename: String; const LineNumber: Integer;
  55. const LineText: String);
  56. property Count: Integer read GetCount;
  57. property Lines[Index: Integer]: PScriptFileLine read Get; default;
  58. property Text: String read GetText;
  59. end;
  60. implementation
  61. uses
  62. PathFunc, Compression.Base;
  63. { THashStringList }
  64. destructor THashStringList.Destroy;
  65. begin
  66. Clear;
  67. inherited;
  68. end;
  69. function THashStringList.Add(const S: String): Integer;
  70. var
  71. LS: String;
  72. begin
  73. if FIgnoreDuplicates and (CaseInsensitiveIndexOf(S) <> -1) then begin
  74. Result := -1;
  75. Exit;
  76. end;
  77. Result := FCount;
  78. if Result = FCapacity then
  79. Grow;
  80. LS := PathLowercase(S);
  81. Pointer(FList[Result].Str) := nil; { since Grow doesn't zero init }
  82. FList[Result].Str := S;
  83. FList[Result].Hash := GetCRC32(Pointer(LS)^, Length(LS)*SizeOf(LS[1]));
  84. Inc(FCount);
  85. end;
  86. procedure THashStringList.Clear;
  87. begin
  88. if FCount > 0 then
  89. Finalize(FList[0], FCount);
  90. FCount := 0;
  91. FCapacity := 0;
  92. ReallocMem(FList, 0);
  93. end;
  94. function THashStringList.Get(Index: Integer): String;
  95. begin
  96. if (Index < 0) or (Index >= FCount) then
  97. raise EStringListError.CreateFmt('THashStringList: Index %d is out of bounds',
  98. [Index]);
  99. Result := FList[Index].Str;
  100. end;
  101. procedure THashStringList.Grow;
  102. var
  103. Delta, NewCapacity: Integer;
  104. begin
  105. if FCapacity > 64 then Delta := FCapacity div 4 else
  106. if FCapacity > 8 then Delta := 16 else
  107. Delta := 4;
  108. NewCapacity := FCapacity + Delta;
  109. if NewCapacity > MaxHashStringItemListSize then
  110. raise EStringListError.Create('THashStringList: Exceeded maximum list size');
  111. ReallocMem(FList, NewCapacity * SizeOf(FList[0]));
  112. FCapacity := NewCapacity;
  113. end;
  114. function THashStringList.CaseInsensitiveIndexOf(const S: String): Integer;
  115. var
  116. LS: String;
  117. Hash: Longint;
  118. I: Integer;
  119. begin
  120. LS := PathLowercase(S);
  121. Hash := GetCRC32(Pointer(LS)^, Length(LS)*SizeOf(LS[1]));
  122. for I := 0 to FCount-1 do
  123. if (FList[I].Hash = Hash) and (PathLowercase(FList[I].Str) = LS) then begin
  124. Result := I;
  125. Exit;
  126. end;
  127. Result := -1;
  128. end;
  129. { TScriptFileLines }
  130. constructor TScriptFileLines.Create;
  131. begin
  132. inherited;
  133. FLines := TList.Create;
  134. end;
  135. destructor TScriptFileLines.Destroy;
  136. var
  137. I: Integer;
  138. begin
  139. if Assigned(FLines) then begin
  140. for I := FLines.Count-1 downto 0 do
  141. Dispose(PScriptFileLine(FLines[I]));
  142. FLines.Free;
  143. end;
  144. inherited;
  145. end;
  146. procedure TScriptFileLines.Add(const LineFilename: String;
  147. const LineNumber: Integer; const LineText: String);
  148. var
  149. L, PrevLine: PScriptFileLine;
  150. begin
  151. FLines.Expand;
  152. New(L);
  153. try
  154. { Memory usage optimization: If LineFilename is equal to the previous
  155. line's LineFilename, then make this line's LineFilename reference the
  156. same string (i.e. just increment its refcount). }
  157. PrevLine := nil;
  158. if (LineFilename <> '') and (FLines.Count > 0) then
  159. PrevLine := PScriptFileLine(FLines[FLines.Count-1]);
  160. if Assigned(PrevLine) and (PrevLine.LineFilename = LineFilename) then
  161. L.LineFilename := PrevLine.LineFilename
  162. else
  163. L.LineFilename := LineFilename;
  164. L.LineNumber := LineNumber;
  165. L.LineText := LineText;
  166. except
  167. Dispose(L);
  168. raise;
  169. end;
  170. FLines.Add(L);
  171. end;
  172. function TScriptFileLines.Get(Index: Integer): PScriptFileLine;
  173. begin
  174. Result := PScriptFileLine(FLines[Index]);
  175. end;
  176. function TScriptFileLines.GetCount: Integer;
  177. begin
  178. Result := FLines.Count;
  179. end;
  180. function TScriptFileLines.GetText: String;
  181. var
  182. I, L, Size, Count: Integer;
  183. P: PChar;
  184. S, LB: string;
  185. begin
  186. Count := GetCount;
  187. Size := 0;
  188. LB := sLineBreak;
  189. for I := 0 to Count-1 do
  190. Inc(Size, Length(Get(I).LineText) + Length(LB));
  191. Dec(Size, Length(LB));
  192. SetString(Result, nil, Size);
  193. P := Pointer(Result);
  194. for I := 0 to Count-1 do begin
  195. S := Get(I).LineText;
  196. L := Length(S);
  197. if L <> 0 then begin
  198. System.Move(Pointer(S)^, P^, L * SizeOf(Char));
  199. Inc(P, L);
  200. end;
  201. if I < Count-1 then begin
  202. L := Length(LB);
  203. if L <> 0 then begin
  204. System.Move(Pointer(LB)^, P^, L * SizeOf(Char));
  205. Inc(P, L);
  206. end;
  207. end;
  208. end;
  209. end;
  210. end.