123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- unit Compiler.StringLists;
- {
- Inno Setup
- Copyright (C) 1997-2025 Jordan Russell
- Portions by Martijn Laan
- For conditions of distribution and use, see LICENSE.TXT.
- Special string list classes used by TSetupCompiler
- }
- interface
- uses
- Classes;
- type
- THashStringItem = record
- Hash: Longint;
- Str: String;
- end;
- const
- MaxHashStringItemListSize = MaxInt div 16;
- type
- PHashStringItemList = ^THashStringItemList;
- THashStringItemList = array[0..MaxHashStringItemListSize-1] of THashStringItem;
- THashStringList = class
- private
- FCapacity: Integer;
- FCount: Integer;
- FIgnoreDuplicates: Boolean;
- FList: PHashStringItemList;
- procedure Grow;
- public
- destructor Destroy; override;
- function Add(const S: String): Integer;
- function CaseInsensitiveIndexOf(const S: String): Integer;
- procedure Clear;
- function Get(Index: Integer): String;
- property Count: Integer read FCount;
- property IgnoreDuplicates: Boolean read FIgnoreDuplicates write FIgnoreDuplicates;
- property Strings[Index: Integer]: String read Get; default;
- end;
- PScriptFileLine = ^TScriptFileLine;
- TScriptFileLine = record
- LineFilename: String;
- LineNumber: Integer;
- LineText: String;
- end;
- TScriptFileLines = class
- private
- FLines: TList;
- function Get(Index: Integer): PScriptFileLine;
- function GetCount: Integer;
- function GetText: String;
- public
- constructor Create;
- destructor Destroy; override;
- procedure Add(const LineFilename: String; const LineNumber: Integer;
- const LineText: String);
- property Count: Integer read GetCount;
- property Lines[Index: Integer]: PScriptFileLine read Get; default;
- property Text: String read GetText;
- end;
- implementation
- uses
- PathFunc, Compression.Base;
- { THashStringList }
- destructor THashStringList.Destroy;
- begin
- Clear;
- inherited;
- end;
- function THashStringList.Add(const S: String): Integer;
- var
- LS: String;
- begin
- if FIgnoreDuplicates and (CaseInsensitiveIndexOf(S) <> -1) then begin
- Result := -1;
- Exit;
- end;
- Result := FCount;
- if Result = FCapacity then
- Grow;
- LS := PathLowercase(S);
- Pointer(FList[Result].Str) := nil; { since Grow doesn't zero init }
- FList[Result].Str := S;
- FList[Result].Hash := GetCRC32(Pointer(LS)^, Length(LS)*SizeOf(LS[1]));
- Inc(FCount);
- end;
- procedure THashStringList.Clear;
- begin
- if FCount > 0 then
- Finalize(FList[0], FCount);
- FCount := 0;
- FCapacity := 0;
- ReallocMem(FList, 0);
- end;
- function THashStringList.Get(Index: Integer): String;
- begin
- if (Index < 0) or (Index >= FCount) then
- raise EStringListError.CreateFmt('THashStringList: Index %d is out of bounds',
- [Index]);
- Result := FList[Index].Str;
- end;
- procedure THashStringList.Grow;
- var
- Delta, NewCapacity: Integer;
- begin
- if FCapacity > 64 then Delta := FCapacity div 4 else
- if FCapacity > 8 then Delta := 16 else
- Delta := 4;
- NewCapacity := FCapacity + Delta;
- if NewCapacity > MaxHashStringItemListSize then
- raise EStringListError.Create('THashStringList: Exceeded maximum list size');
- ReallocMem(FList, NewCapacity * SizeOf(FList[0]));
- FCapacity := NewCapacity;
- end;
- function THashStringList.CaseInsensitiveIndexOf(const S: String): Integer;
- var
- LS: String;
- Hash: Longint;
- I: Integer;
- begin
- LS := PathLowercase(S);
- Hash := GetCRC32(Pointer(LS)^, Length(LS)*SizeOf(LS[1]));
- for I := 0 to FCount-1 do
- if (FList[I].Hash = Hash) and (PathLowercase(FList[I].Str) = LS) then begin
- Result := I;
- Exit;
- end;
- Result := -1;
- end;
- { TScriptFileLines }
- constructor TScriptFileLines.Create;
- begin
- inherited;
- FLines := TList.Create;
- end;
- destructor TScriptFileLines.Destroy;
- var
- I: Integer;
- begin
- if Assigned(FLines) then begin
- for I := FLines.Count-1 downto 0 do
- Dispose(PScriptFileLine(FLines[I]));
- FLines.Free;
- end;
- inherited;
- end;
- procedure TScriptFileLines.Add(const LineFilename: String;
- const LineNumber: Integer; const LineText: String);
- var
- L, PrevLine: PScriptFileLine;
- begin
- FLines.Expand;
- New(L);
- try
- { Memory usage optimization: If LineFilename is equal to the previous
- line's LineFilename, then make this line's LineFilename reference the
- same string (i.e. just increment its refcount). }
- PrevLine := nil;
- if (LineFilename <> '') and (FLines.Count > 0) then
- PrevLine := PScriptFileLine(FLines[FLines.Count-1]);
- if Assigned(PrevLine) and (PrevLine.LineFilename = LineFilename) then
- L.LineFilename := PrevLine.LineFilename
- else
- L.LineFilename := LineFilename;
- L.LineNumber := LineNumber;
- L.LineText := LineText;
- except
- Dispose(L);
- raise;
- end;
- FLines.Add(L);
- end;
- function TScriptFileLines.Get(Index: Integer): PScriptFileLine;
- begin
- Result := PScriptFileLine(FLines[Index]);
- end;
- function TScriptFileLines.GetCount: Integer;
- begin
- Result := FLines.Count;
- end;
- function TScriptFileLines.GetText: String;
- var
- I, L, Size, Count: Integer;
- P: PChar;
- S, LB: string;
- begin
- Count := GetCount;
- Size := 0;
- LB := sLineBreak;
- for I := 0 to Count-1 do
- Inc(Size, Length(Get(I).LineText) + Length(LB));
- Dec(Size, Length(LB));
- SetString(Result, nil, Size);
- P := Pointer(Result);
- for I := 0 to Count-1 do begin
- S := Get(I).LineText;
- L := Length(S);
- if L <> 0 then begin
- System.Move(Pointer(S)^, P^, L * SizeOf(Char));
- Inc(P, L);
- end;
- if I < Count-1 then begin
- L := Length(LB);
- if L <> 0 then begin
- System.Move(Pointer(LB)^, P^, L * SizeOf(Char));
- Inc(P, L);
- end;
- end;
- end;
- end;
- end.
|