Compiler.StringLists.pas 5.7 KB

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