2
0

Compiler.StringLists.pas 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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, 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. var
  138. I: Integer;
  139. begin
  140. if Assigned(FLines) then begin
  141. for I := FLines.Count-1 downto 0 do
  142. Dispose(PScriptFileLine(FLines[I]));
  143. FLines.Free;
  144. end;
  145. inherited;
  146. end;
  147. procedure TScriptFileLines.Add(const LineFilename: String;
  148. const LineNumber: Integer; const LineText: String);
  149. var
  150. L, PrevLine: PScriptFileLine;
  151. begin
  152. FLines.Expand;
  153. New(L);
  154. try
  155. { Memory usage optimization: If LineFilename is equal to the previous
  156. line's LineFilename, then make this line's LineFilename reference the
  157. same string (i.e. just increment its refcount). }
  158. PrevLine := nil;
  159. if (LineFilename <> '') and (FLines.Count > 0) then
  160. PrevLine := PScriptFileLine(FLines[FLines.Count-1]);
  161. if Assigned(PrevLine) and (PrevLine.LineFilename = LineFilename) then
  162. L.LineFilename := PrevLine.LineFilename
  163. else
  164. L.LineFilename := LineFilename;
  165. L.LineNumber := LineNumber;
  166. L.LineText := LineText;
  167. except
  168. Dispose(L);
  169. raise;
  170. end;
  171. FLines.Add(L);
  172. end;
  173. function TScriptFileLines.Get(Index: Integer): PScriptFileLine;
  174. begin
  175. Result := PScriptFileLine(FLines[Index]);
  176. end;
  177. function TScriptFileLines.GetCount: Integer;
  178. begin
  179. Result := FLines.Count;
  180. end;
  181. function TScriptFileLines.GetText: String;
  182. var
  183. I, L, Size, Count: Integer;
  184. P: PChar;
  185. S, LB: string;
  186. begin
  187. Count := GetCount;
  188. Size := 0;
  189. LB := sLineBreak;
  190. for I := 0 to Count-1 do
  191. Inc(Size, Length(Get(I).LineText) + Length(LB));
  192. Dec(Size, Length(LB));
  193. SetString(Result, nil, Size);
  194. P := Pointer(Result);
  195. for I := 0 to Count-1 do begin
  196. S := Get(I).LineText;
  197. L := Length(S);
  198. if L <> 0 then begin
  199. System.Move(Pointer(S)^, P^, L * SizeOf(Char));
  200. Inc(P, L);
  201. end;
  202. if I < Count-1 then begin
  203. L := Length(LB);
  204. if L <> 0 then begin
  205. System.Move(Pointer(LB)^, P^, L * SizeOf(Char));
  206. Inc(P, L);
  207. end;
  208. end;
  209. end;
  210. end;
  211. end.