776 lines
22 KiB
ObjectPascal
776 lines
22 KiB
ObjectPascal
{**************************************************************************************************}
|
|
{ }
|
|
{ Delphi language Preprocessor (dpp32) }
|
|
{ }
|
|
{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
|
|
{ you may not use this file except in compliance with the License. You may obtain a copy of the }
|
|
{ License at http://www.mozilla.org/MPL/ }
|
|
{ }
|
|
{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
|
|
{ ANY KIND, either express or implied. See the License for the specific language governing rights }
|
|
{ and limitations under the License. }
|
|
{ }
|
|
{ The Original Code is dpp_Utils.pas }
|
|
{ }
|
|
{ The Initial Developer of the Original Code is Andreas Hausladen }
|
|
{ Portions created by these individuals are Copyright (C) of these individuals. }
|
|
{ }
|
|
{**************************************************************************************************}
|
|
unit dpp_Utils;
|
|
{$define HASHTABLE}
|
|
interface
|
|
uses
|
|
{$ifdef MSWINDOWS}
|
|
Windows, SysUtils, Classes, RTLConsts;
|
|
{$endif}
|
|
{$ifdef LINUX}
|
|
Libc, SysUtils, Classes, RTLConsts;
|
|
{$endif}
|
|
|
|
type
|
|
TBooleanList = class(TObject)
|
|
private
|
|
FCount: Integer;
|
|
FList: array of Boolean;
|
|
FLast: Boolean;
|
|
function GetItems(Index: Integer): Boolean;
|
|
procedure SetItems(Index: Integer; const Value: Boolean);
|
|
public
|
|
constructor Create;
|
|
function Add(Value: Boolean): Integer;
|
|
procedure Delete(Index: Integer);
|
|
procedure Clear;
|
|
|
|
procedure DeleteLast;
|
|
procedure ToggleLast;
|
|
|
|
property Last: Boolean read FLast;
|
|
property Count: Integer read FCount;
|
|
property Items[Index: Integer]: Boolean read GetItems write SetItems;
|
|
end;
|
|
|
|
TRedirectTable = array of record
|
|
Text: string;
|
|
Index: Integer;
|
|
end;
|
|
|
|
|
|
procedure MakeStringHash(const Text: string; Index: Integer; var Table: TRedirectTable);
|
|
function FindStringHash(const Text: string; const Table: TRedirectTable; CaseSensitive: Boolean): Integer;
|
|
procedure DelStringHash(const Text: string; var Table: TRedirectTable; CaseSensitive: Boolean); overload;
|
|
procedure DelStringHash(Index: Integer; var Table: TRedirectTable); overload;
|
|
|
|
type
|
|
TFilenameMapper = class(TStringList)
|
|
private
|
|
FHashTable: TRedirectTable;
|
|
public
|
|
procedure AddFilename(const Name, Filename: string);
|
|
function FindFilename(const Name: string; var Filename: string): Boolean;
|
|
|
|
procedure Clear; override;
|
|
end;
|
|
|
|
|
|
// *****************************************************************************
|
|
// **************************** File handling **********************************
|
|
// *****************************************************************************
|
|
|
|
function CopyFile(const SourceFileName, DestFileName: String;
|
|
NativeCopy: Boolean = True): Boolean;
|
|
// Copy Source to Dest using a native copy function or the built in.
|
|
|
|
function MoveFile(const SourceFilename, DestFilename: String): Boolean;
|
|
// Moves the file Source to Dest. If renaming is not possible the file is moved
|
|
// by copy and delete.
|
|
|
|
function FileExistsX(const Filename: string): Boolean;
|
|
// Like SysUtils.FileExists() but on Windows it is faster.
|
|
|
|
procedure FileToString(const Filename: string; out Content: string);
|
|
// Reads a file into the string Content.
|
|
|
|
procedure StringToFile(const Filename, Content: string);
|
|
// Writes a file from the string Content.
|
|
|
|
function GetPreProcessedFilename(const Filename: string; IncludeIndex: Integer = 0): string;
|
|
// Returns the corresponding preprocessed Filename.
|
|
// IncludeIndex = 0 -> *.i.*
|
|
// IncludeIndex > 0 -> *.iX.* where X=IncludeIndex
|
|
|
|
|
|
type
|
|
TExTestMethod = function(const Filename: string): Boolean of object;
|
|
|
|
function TestFilenames(const Paths, Filename: string; ExTestMethod: TExTestMethod = nil): string;
|
|
// Returns the file name of an existing file by seaching Paths.
|
|
|
|
function FollowRelativePath(BaseDir, Filename: string): string;
|
|
// Expands the relative file path Filename based on BaseDir.
|
|
|
|
function CompareFileNames(const FileName1, FileName2: string): Integer;
|
|
// Compares the two file names
|
|
|
|
// *****************************************************************************
|
|
// ************************** String handling **********************************
|
|
// *****************************************************************************
|
|
|
|
function CountCharsStop(Ch, StopCh: Char; P: PChar): Integer;
|
|
function CountChars(Ch: Char; const S: string): Integer;
|
|
// CountChars() gets the number of char Ch in P. It stops seeking on char StopCh.
|
|
|
|
function PosCharSet(CS: TSysCharSet; const S: string): Integer;
|
|
// Returns the index of the first char in S which is in CS.
|
|
|
|
function PosChar(Ch: Char; const S: string): Integer;
|
|
// Returns the index of the first char Ch in S.
|
|
|
|
function StartsText(const StartText, Text: string): Boolean;
|
|
// Returns TRUE if StartText is the beginning of Text
|
|
|
|
function RemoveQuotes(const Text: string): string;
|
|
// Removes the embracing quotes ( " and ' )
|
|
|
|
function IsStrEmpty(const Text: string): Boolean;
|
|
// Returns true if all chars in Text are in [#1..#32].
|
|
|
|
function IndexOfStrText(List: TStrings; const StrText: string;
|
|
CaseSensitive: Boolean): Integer;
|
|
// Returns the index of StrText in List.
|
|
|
|
function IndexOfFilename(Files: TStrings; const Filename: string): Integer;
|
|
// Returns the index of the file name in Files (uses CompareFileNames).
|
|
|
|
procedure PathListToStrings(const Paths: string; List: TStrings);
|
|
// Converts Paths (path;path or path:path) to a string list.
|
|
|
|
|
|
implementation
|
|
|
|
{ TBooleanList }
|
|
|
|
constructor TBooleanList.Create;
|
|
begin
|
|
inherited Create;
|
|
FLast := True;
|
|
end;
|
|
|
|
function TBooleanList.Add(Value: Boolean): Integer;
|
|
begin
|
|
if FCount >= Length(FList) then
|
|
SetLength(FList, FCount + 10); // allocate more than 1 saves some memory with SysMemoryManger
|
|
FList[FCount] := Value;
|
|
Result := FCount;
|
|
Inc(FCount);
|
|
|
|
FLast := Value;
|
|
end;
|
|
|
|
procedure TBooleanList.Clear;
|
|
begin
|
|
FCount := 0;
|
|
SetLength(FList, 0);
|
|
FLast := True;
|
|
end;
|
|
|
|
procedure TBooleanList.Delete(Index: Integer);
|
|
begin
|
|
Dec(FCount);
|
|
if FCount mod 10 = 0 then
|
|
SetLength(FList, FCount);
|
|
if FCount > 0 then
|
|
FLast := FList[FCount - 1]
|
|
else
|
|
FLast := True;
|
|
end;
|
|
|
|
procedure TBooleanList.DeleteLast;
|
|
begin
|
|
if Count > 0 then
|
|
Delete(Count - 1)
|
|
else
|
|
FLast := True;
|
|
end;
|
|
|
|
function TBooleanList.GetItems(Index: Integer): Boolean;
|
|
begin
|
|
{ if Cardinal(Index) >= Cardinal(Count) then
|
|
TList.Error(@SListIndexError, Index);}
|
|
Result := FList[Index];
|
|
end;
|
|
|
|
procedure TBooleanList.SetItems(Index: Integer; const Value: Boolean);
|
|
begin
|
|
{ if Cardinal(Index) >= Cardinal(Count) then
|
|
TList.Error(@SListIndexError, Index);}
|
|
FList[Index] := Value;
|
|
if Index = Count - 1 then FLast := Value;
|
|
end;
|
|
|
|
procedure TBooleanList.ToggleLast;
|
|
begin
|
|
if Count > 0 then
|
|
Items[Count - 1] := not Items[Count - 1];
|
|
end;
|
|
|
|
{ TFilenameMapper }
|
|
|
|
procedure TFilenameMapper.AddFilename(const Name, Filename: string);
|
|
begin
|
|
MakeStringHash(Name, Add(Name) + 1, FHashTable);
|
|
Add(Filename);
|
|
end;
|
|
|
|
procedure TFilenameMapper.Clear;
|
|
begin
|
|
SetLength(FHashTable, 0);
|
|
inherited Clear;
|
|
end;
|
|
|
|
function TFilenameMapper.FindFilename(const Name: string; var Filename: string): Boolean;
|
|
{$ifdef HASHTABLE}
|
|
var Index: Integer;
|
|
begin
|
|
Index := FindStringHash(Name, FHashTable, {$ifdef MSWINDOWS}False{$endif}{$ifdef LINUX}True{$endif});
|
|
if Index > 0 then
|
|
begin
|
|
Result := True;
|
|
Filename := Strings[Index];
|
|
end
|
|
else
|
|
Result := False;
|
|
end;
|
|
{$else}
|
|
var
|
|
i: Integer;
|
|
cnt: Integer;
|
|
begin
|
|
cnt := Count;
|
|
i := 0;
|
|
while i < cnt do
|
|
begin
|
|
if CompareFileNames(Name, Strings[i]) = 0 then
|
|
begin
|
|
Result := True;
|
|
Filename := Strings[i + 1];
|
|
Exit;
|
|
end;
|
|
Inc(i, 2);
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{$endif}
|
|
|
|
// *****************************************************************************
|
|
// **************************** File handling **********************************
|
|
// *****************************************************************************
|
|
|
|
function CopyFile(const SourceFileName, DestFileName: String;
|
|
NativeCopy: Boolean = True): Boolean;
|
|
|
|
function BuiltInCopyFile: Boolean;
|
|
var
|
|
InFile, OutFile: TFileStream;
|
|
{$ifdef MSWINDOWS}
|
|
CreationTime, LastWriteTime, LastAccessTime: TFileTime;
|
|
{$endif}
|
|
{$ifdef LINUX}
|
|
st: TStatBuf;
|
|
{$endif}
|
|
begin
|
|
Result := True;
|
|
try
|
|
InFile := TFileStream.Create(SourceFileName, fmOpenRead or fmShareDenyWrite);
|
|
try
|
|
OutFile := TFileStream.Create(DestFileName, fmCreate or fmShareExclusive);
|
|
try
|
|
try
|
|
OutFile.CopyFrom(InFile, 0);
|
|
except
|
|
Result := False;
|
|
end;
|
|
{$ifdef MSWINDOWS}
|
|
GetFileTime(InFile.Handle, @CreationTime, @LastAccessTime, @LastWriteTime);
|
|
SetFileTime(OutFile.Handle, @CreationTime, @LastAccessTime, @LastWriteTime);
|
|
{$endif}
|
|
finally
|
|
OutFile.Free;
|
|
end;
|
|
{$ifdef LINUX}
|
|
FileSetDate(DestFileName, FileGetDate(InFile.Handle));
|
|
if fstat(InFile.Handle, st) = 0 then
|
|
Libc.chmod(PChar(DestFileName), st.st_mode);
|
|
{$endif}
|
|
finally
|
|
InFile.Free;
|
|
end;
|
|
{$ifdef MSWINDOWS}
|
|
SetFileAttributes(PChar(DestFileName), GetFileAttributes(PChar(SourceFileName)));
|
|
{$endif}
|
|
except
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
{$ifdef MSWINDOWS}
|
|
if NativeCopy then
|
|
Result := Windows.CopyFile(PChar(SourceFileName), PChar(DestFileName), False)
|
|
else
|
|
{$endif}
|
|
Result := BuiltInCopyFile;
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
|
|
function MoveFile(const SourceFilename, DestFilename: String): Boolean;
|
|
begin
|
|
Result := False;
|
|
if (SourceFilename = '') or (DestFilename = '') or
|
|
(not FileExists(SourceFilename)) then Exit;
|
|
|
|
ForceDirectories(ExtractFilePath(DestFilename)); // create directories
|
|
|
|
if FileExists(DestFilename) then // delete destination file if exist
|
|
begin
|
|
{$ifdef MSWINDOWS}
|
|
SetFileAttributes(PChar(DestFilename), 0);
|
|
{$endif}
|
|
{$ifdef LINUX}
|
|
FileSetReadOnly(DestFilename, False);
|
|
{$endif}
|
|
DeleteFile(DestFilename);
|
|
end;
|
|
|
|
if not RenameFile(SourceFilename, DestFilename) then
|
|
begin
|
|
if CopyFile(SourceFilename, DestFilename, True) then
|
|
begin
|
|
// delete source file
|
|
{$ifdef MSWINDOWS}
|
|
SetFileAttributes(PChar(SourceFilename), 0);
|
|
{$endif}
|
|
{$ifdef LINUX}
|
|
FileSetReadOnly(SourceFilename, False);
|
|
{$endif LINUX}
|
|
DeleteFile(SourceFilename);
|
|
Result := True;
|
|
end;
|
|
end
|
|
else
|
|
Result := True;
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
|
|
function FileExistsX(const Filename: string): Boolean;
|
|
{$ifdef MSWINDOWS}
|
|
var Attrib: Cardinal;
|
|
begin
|
|
Attrib := GetFileAttributes(PChar(Filename));
|
|
Result := (Attrib <> $FFFFFFFF) and (Attrib and FILE_ATTRIBUTE_DIRECTORY = 0);
|
|
end;
|
|
{$else}
|
|
asm
|
|
JMP FileExists
|
|
end;
|
|
{$endif}
|
|
|
|
// *****************************************************************************
|
|
|
|
procedure FileToString(const Filename: string; out Content: string);
|
|
var
|
|
Stream: TStream;
|
|
Len: Integer;
|
|
begin
|
|
Stream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
|
|
try
|
|
Len := Stream.Size;
|
|
SetLength(Content, Len);
|
|
if Len > 0 then Stream.ReadBuffer(Content[1], Len);
|
|
finally
|
|
Stream.Free;
|
|
end;
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
|
|
function GetPreProcessedFilename(const Filename: string; IncludeIndex: Integer = 0): string;
|
|
var Ext, NewExt: string;
|
|
begin
|
|
if IncludeIndex = 0 then NewExt := '.i' else NewExt := Format('.i%d', [IncludeIndex]);
|
|
|
|
Ext := ExtractFileExt(Filename);
|
|
Result := ChangeFileExt(Filename, NewExt) + Ext;
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
|
|
function TestFilenames(const Paths, Filename: string; ExTestMethod: TExTestMethod): string;
|
|
var
|
|
List: TStrings;
|
|
i: Integer;
|
|
begin
|
|
List := TStringList.Create;
|
|
try
|
|
PathListToStrings(Paths, List); // does never return empty list items
|
|
for i := 0 to List.Count - 1 do
|
|
begin
|
|
Result := List.Strings[i];
|
|
if Result[Length(Result)] <> PathDelim then
|
|
Result := Result + PathDelim + Filename
|
|
else
|
|
Result := Result + Filename;
|
|
if Assigned(ExTestMethod) then if ExTestMethod(Result) then Exit;
|
|
if FileExistsX(Result) then Exit;
|
|
end;
|
|
finally
|
|
List.Free;
|
|
end;
|
|
Result := '';
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
|
|
function FollowRelativePath(BaseDir, Filename: string): string;
|
|
var
|
|
ps: Integer;
|
|
s: string;
|
|
begin
|
|
Result := Filename;
|
|
if Filename = '' then Exit;
|
|
if Filename[1] = PathDelim then
|
|
Result := ExtractFileDrive(BaseDir) + Filename
|
|
else
|
|
begin
|
|
BaseDir := ExcludeTrailingPathDelimiter(BaseDir);
|
|
ps := PosChar(PathDelim, Filename);
|
|
while ps > 0 do
|
|
begin
|
|
s := Copy(Filename, 1, ps - 1);
|
|
Delete(Filename, 1, ps);
|
|
if s = '..' then
|
|
BaseDir := ExtractFileDir(BaseDir)
|
|
else if s <> '.' then
|
|
BaseDir := BaseDir + PathDelim + s;
|
|
ps := PosChar(PathDelim, Filename);
|
|
end;
|
|
end;
|
|
if Pointer(Filename) <> nil then // <= => Length(Filename) > 0 then
|
|
Result := BaseDir + PathDelim + Filename
|
|
else
|
|
Result := BaseDir;
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
|
|
procedure StringToFile(const Filename, Content: string);
|
|
var Stream: TStream;
|
|
begin
|
|
Stream := TFileStream.Create(FileName, fmCreate);
|
|
try
|
|
if Length(Content) > 0 then
|
|
Stream.WriteBuffer(Content[1], Length(Content));
|
|
finally
|
|
Stream.Free;
|
|
end;
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
|
|
function CompareFileNames(const FileName1, FileName2: string): Integer; assembler
|
|
asm
|
|
{$ifdef MSWINDOWS}
|
|
JMP CompareText
|
|
{$endif}
|
|
{$ifdef LINUX}
|
|
JMP CompareStr
|
|
{$endif}
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
// ************************** String handling **********************************
|
|
// *****************************************************************************
|
|
|
|
function CountCharsStop(Ch, StopCh: Char; P: PChar): Integer;
|
|
begin
|
|
Result := 0;
|
|
while not (P[0] in [#0, StopCh]) do
|
|
begin
|
|
if P[0] = Ch then Inc(Result);
|
|
Inc(P);
|
|
end;
|
|
end;
|
|
|
|
function CountChars(Ch: Char; const S: string): Integer;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := 0;
|
|
for i := 1 to Length(S) do
|
|
if S[i] = Ch then
|
|
Inc(Result);
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
|
|
function PosCharSet(CS: TSysCharSet; const S: string): Integer;
|
|
begin
|
|
for Result := 1 to Length(S) do
|
|
if S[Result] in CS then Exit;
|
|
Result := 0;
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
|
|
function PosChar(Ch: Char; const S: string): Integer; assembler;
|
|
asm
|
|
// AL = Ch
|
|
// EDX = const S
|
|
PUSH ESI
|
|
MOV ESI, EDX // ESI = S
|
|
|
|
// String empty ?
|
|
OR EDX, EDX
|
|
JZ @@TheEnd
|
|
|
|
@@loop:
|
|
MOV AH, [ESI]
|
|
INC ESI
|
|
// Char found ?
|
|
CMP AL, AH
|
|
JZ @@TheEnd
|
|
// String-End ?
|
|
OR AH, AH
|
|
JNZ @@loop
|
|
|
|
MOV ESI, EDX
|
|
|
|
@@TheEnd:
|
|
MOV EAX, ESI
|
|
SUB EAX, EDX
|
|
|
|
POP ESI
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
|
|
type
|
|
PStrRec = ^StrRec;
|
|
StrRec = packed record
|
|
refCnt: Longint;
|
|
length: Longint;
|
|
end;
|
|
|
|
function StartsText(const StartText, Text: string): Boolean;
|
|
begin
|
|
if Pointer(Text) = nil then
|
|
Result := Pointer(StartText) = nil
|
|
else
|
|
Result := StrLIComp(Pointer(Text),
|
|
Pointer(StartText),
|
|
PStrRec(Integer(StartText) - SizeOf(StrRec)).length) = 0;
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
|
|
function RemoveQuotes(const Text: string): string;
|
|
var Len: Integer;
|
|
begin
|
|
Result := Text;
|
|
Len := Length(Result);
|
|
if (Len > 0) and
|
|
(Result[1] = Result[Len]) and (Result[1] in ['''', '"']) then
|
|
begin
|
|
Delete(Result, Len, 1);
|
|
Delete(Result, 1, 1);
|
|
end;
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
|
|
function IsStrEmpty(const Text: string): Boolean;
|
|
var i: Integer;
|
|
begin
|
|
Result := False;
|
|
for i := 1 to Length(Text) do
|
|
if Text[i] > ' ' then Exit;
|
|
Result := True;
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
|
|
function IndexOfStrText(List: TStrings; const StrText: string;
|
|
CaseSensitive: Boolean): Integer;
|
|
var cmp: function(const S1, S2: string): Integer;
|
|
begin
|
|
if List <> nil then
|
|
begin
|
|
if CaseSensitive then cmp := CompareStr else cmp := CompareText;
|
|
|
|
for Result := 0 to List.Count - 1 do
|
|
if cmp(List.Strings[Result], StrText) = 0 then Exit;
|
|
end;
|
|
Result := -1;
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
|
|
function IndexOfFilename(Files: TStrings; const Filename: string): Integer;
|
|
begin
|
|
if Files <> nil then
|
|
begin
|
|
for Result := 0 to Files.Count - 1 do
|
|
if CompareFileNames(Files.Strings[Result], Filename) = 0 then Exit;
|
|
end;
|
|
Result := -1;
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
|
|
procedure PathListToStrings(const Paths: string; List: TStrings);
|
|
var
|
|
s: string;
|
|
F, P: PChar;
|
|
begin
|
|
P := PChar(Paths);
|
|
while P[0] <> #0 do
|
|
begin
|
|
F := P;
|
|
while not (P[0] in [#0, PathSep]) do Inc(P);
|
|
if F < P then
|
|
begin
|
|
SetString(s, F, P - F);
|
|
s := RemoveQuotes(s);
|
|
if Length(s) > 0 then List.Add(s);
|
|
end;
|
|
if P[0] <> #0 then Inc(P);
|
|
end;
|
|
end;
|
|
|
|
// *****************************************************************************
|
|
// ***************************** String Hash ***********************************
|
|
// *****************************************************************************
|
|
|
|
function StringHash(const Text: string): Integer;
|
|
var a, i, ch, Len: Integer;
|
|
begin
|
|
Len := Length(Text);
|
|
a := Len - 6;
|
|
if a <= 0 then a := 1;
|
|
Result := 0;
|
|
for i := Len downto a do
|
|
begin
|
|
ch := Byte(Text[i]);
|
|
if ch >= Byte('a') then Dec(ch, 32);
|
|
Dec(ch, 48); // no chars below '0' are allowed
|
|
Inc(Result, ch);
|
|
end;
|
|
end;
|
|
|
|
{procedure NextHash(var hash: Integer);
|
|
begin
|
|
Inc(hash, 29);
|
|
end;}
|
|
|
|
procedure MakeStringHash(const Text: string; Index: Integer; var Table: TRedirectTable);
|
|
var hash, len: Integer;
|
|
begin
|
|
hash := StringHash(Text);
|
|
len := Length(Table);
|
|
repeat
|
|
if hash >= len then
|
|
begin
|
|
SetLength(Table, hash + 1);
|
|
Table[hash].Text := Text;
|
|
Table[hash].Index := Index;
|
|
Exit;
|
|
end
|
|
else
|
|
if Table[hash].Index = 0 then
|
|
begin
|
|
Table[hash].Text := Text;
|
|
Table[hash].Index := Index;
|
|
Exit;
|
|
end;
|
|
|
|
// NextHash(hash);
|
|
Inc(hash, 29);
|
|
until False;
|
|
end;
|
|
|
|
function GetStringHashTableIndex(const Text: string; const Table: TRedirectTable; CaseSensitive: Boolean): Integer;
|
|
var hash, len: Integer;
|
|
begin
|
|
Result := -1;
|
|
hash := StringHash(Text);
|
|
len := Length(Table);
|
|
|
|
if CaseSensitive then
|
|
begin
|
|
while (hash < len) and (Table[hash].Index <> 0) do
|
|
begin
|
|
if Table[hash].Text = Text then
|
|
begin
|
|
Result := hash;
|
|
Exit;
|
|
end;
|
|
// NextHash(hash);
|
|
Inc(hash, 29);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
while (hash < len) and (Table[hash].Index <> 0) do
|
|
begin
|
|
if (SameText(Table[hash].Text, Text)) then
|
|
begin
|
|
Result := hash;
|
|
Exit;
|
|
end;
|
|
// NextHash(hash);
|
|
Inc(hash, 29);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function FindStringHash(const Text: string; const Table: TRedirectTable; CaseSensitive: Boolean): Integer;
|
|
begin
|
|
Result := GetStringHashTableIndex(Text, Table, CaseSensitive);
|
|
if Result = -1 then Result := 0 else Result := Table[Result].Index;
|
|
end;
|
|
|
|
procedure DelStringHash(const Text: string; var Table: TRedirectTable; CaseSensitive: Boolean);
|
|
var Index, len: Integer;
|
|
begin
|
|
Index := GetStringHashTableIndex(Text, Table, CaseSensitive);
|
|
if Index >= 0 then
|
|
begin
|
|
// Table[Index].Text := '';
|
|
Table[Index].Index := 0;
|
|
|
|
// shrink table
|
|
len := Length(Table);
|
|
while (len > 0) and (Table[len - 1].Index = 0) do Dec(len);
|
|
SetLength(Table, len);
|
|
end;
|
|
end;
|
|
|
|
procedure DelStringHash(Index: Integer; var Table: TRedirectTable);
|
|
var i, len: Integer;
|
|
begin
|
|
for i := 0 to High(Table) do
|
|
if Table[i].Index = Index then
|
|
begin
|
|
// Table[i].Text := '';
|
|
Table[i].Index := 0;
|
|
end;
|
|
|
|
// shrink table
|
|
len := Length(Table);
|
|
while (len > 0) and (Table[len - 1].Index = 0) do Dec(len);
|
|
SetLength(Table, len);
|
|
end;
|
|
|
|
end.
|