mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-10-31 23:30:32 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			553 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			553 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| {
 | |
|  **********************************************************************
 | |
|   This file is part of LazUtils.
 | |
| 
 | |
|   See the file COPYING.modifiedLGPL.txt, included in this distribution,
 | |
|   for details about the license.
 | |
|  **********************************************************************
 | |
| }
 | |
| unit LazFileCache;
 | |
| 
 | |
| {$mode objfpc}{$H+}
 | |
| 
 | |
| interface
 | |
| 
 | |
| uses
 | |
|   Classes, SysUtils, LazDbgLog, AVL_Tree, LazFileUtils;
 | |
| 
 | |
| type
 | |
|   TFileStateCacheItemFlag = (
 | |
|     fsciExists,    // file or directory exists
 | |
|     fsciDirectory, // file exists and is directory
 | |
|     fsciReadable,  // file is readable
 | |
|     fsciWritable,  // file is writable
 | |
|     fsciDirectoryReadable, // file is directory and can be searched
 | |
|     fsciDirectoryWritable, // file is directory and new files can be created
 | |
|     fsciText,      // file is text file (not binary)
 | |
|     fsciExecutable,// file is executable
 | |
|     fsciAge,        // file age is valid
 | |
|     fsciPhysical    // physical filename is valid
 | |
|     );
 | |
|   TFileStateCacheItemFlags = set of TFileStateCacheItemFlag;
 | |
| 
 | |
|   { TFileStateCacheItem }
 | |
| 
 | |
|   TFileStateCacheItem = class
 | |
|   private
 | |
|     FAge: longint;
 | |
|     FFilename: string;
 | |
|     FFlags: TFileStateCacheItemFlags;
 | |
|     FPhysicalFilename: string;
 | |
|     FTestedFlags: TFileStateCacheItemFlags;
 | |
|     FTimeStamp: int64;
 | |
|   public
 | |
|     constructor Create(const TheFilename: string; NewTimeStamp: int64);
 | |
|     function CalcMemSize: PtrUint;
 | |
|   public
 | |
|     property Filename: string read FFilename;
 | |
|     property PhysicalFilename: string read FPhysicalFilename;
 | |
|     property Flags: TFileStateCacheItemFlags read FFlags;
 | |
|     property TestedFlags: TFileStateCacheItemFlags read FTestedFlags;
 | |
|     property TimeStamp: int64 read FTimeStamp;
 | |
|     property Age: longint read FAge;
 | |
|   end;
 | |
| 
 | |
|   TOnChangeFileStateTimeStamp = procedure(Sender: TObject;
 | |
|                                           const AFilename: string) of object;
 | |
| 
 | |
|   { TFileStateCache }
 | |
| 
 | |
|   TFileStateCache = class
 | |
|   private
 | |
|     FFiles: TAVLTree; // tree of TFileStateCacheItem
 | |
|     FTimeStamp: int64;
 | |
|     FLockCount: integer;
 | |
|     FChangeTimeStampHandler: array of TOnChangeFileStateTimeStamp;
 | |
|     procedure SetFlag(AFile: TFileStateCacheItem;
 | |
|                       AFlag: TFileStateCacheItemFlag; NewValue: boolean);
 | |
|   public
 | |
|     constructor Create;
 | |
|     destructor Destroy; override;
 | |
|     procedure Lock;
 | |
|     procedure Unlock;
 | |
|     function Locked: boolean;
 | |
|     procedure IncreaseTimeStamp(const AFilename: string);
 | |
|     function FileExistsCached(const AFilename: string): boolean;
 | |
|     function DirPathExistsCached(const AFilename: string): boolean;
 | |
|     function DirectoryIsWritableCached(const DirectoryName: string): boolean;
 | |
|     function FileIsExecutableCached(const AFilename: string): boolean;
 | |
|     function FileIsReadableCached(const AFilename: string): boolean;
 | |
|     function FileIsWritableCached(const AFilename: string): boolean;
 | |
|     function FileIsTextCached(const AFilename: string): boolean;
 | |
|     function FileAgeCached(const AFileName: string): Longint;
 | |
|     function GetPhysicalFilenameCached(const AFileName: string; {%H-}EmptyOnError: boolean): string;
 | |
|     function FindFile(const Filename: string;
 | |
|                       CreateIfNotExists: boolean): TFileStateCacheItem;
 | |
|     function Check(const Filename: string; AFlag: TFileStateCacheItemFlag;
 | |
|                    out AFile: TFileStateCacheItem; var FlagIsSet: boolean): boolean;
 | |
|     procedure AddChangeTimeStampHandler(const Handler: TOnChangeFileStateTimeStamp);
 | |
|     procedure RemoveChangeTimeStampHandler(const Handler: TOnChangeFileStateTimeStamp);
 | |
|     function CalcMemSize: PtrUint;
 | |
|   public
 | |
|     property TimeStamp: int64 read FTimeStamp;
 | |
|   end;
 | |
| 
 | |
| var
 | |
|   FileStateCache: TFileStateCache = nil;
 | |
| 
 | |
| function FileExistsCached(const AFilename: string): boolean;
 | |
| function DirPathExistsCached(const AFilename: string): boolean;
 | |
| function DirectoryIsWritableCached(const ADirectoryName: string): boolean;
 | |
| function FileIsExecutableCached(const AFilename: string): boolean;
 | |
| function FileIsReadableCached(const AFilename: string): boolean;
 | |
| function FileIsWritableCached(const AFilename: string): boolean;
 | |
| function FileIsTextCached(const AFilename: string): boolean;
 | |
| function FileAgeCached(const AFileName: string): Longint;
 | |
| function GetPhysicalFilenameCached(const AFilename: string; EmptyOnError: boolean): string;
 | |
| 
 | |
| procedure InvalidateFileStateCache(const Filename: string = ''); inline;
 | |
| function CompareFileStateItems(Data1, Data2: Pointer): integer;
 | |
| function CompareFilenameWithFileStateCacheItem(Key, Data: Pointer): integer;
 | |
| 
 | |
| const
 | |
|   LUInvalidChangeStamp = Low(integer);
 | |
|   LUInvalidChangeStamp64 = Low(int64); // using a value outside integer to spot wrong types early
 | |
| procedure LUIncreaseChangeStamp(var ChangeStamp: integer); inline;
 | |
| procedure LUIncreaseChangeStamp64(var ChangeStamp: int64); inline;
 | |
| 
 | |
| type
 | |
|   TOnFileExistsCached = function(Filename: string): boolean of object;
 | |
|   TOnFileAgeCached = function(Filename: string): longint of object;
 | |
| var
 | |
|   OnFileExistsCached: TOnFileExistsCached = nil;
 | |
|   OnFileAgeCached: TOnFileAgeCached = nil;
 | |
| 
 | |
| implementation
 | |
| 
 | |
| 
 | |
| function FileExistsCached(const AFilename: string): boolean;
 | |
| begin
 | |
|   if OnFileExistsCached<>nil then
 | |
|     Result:=OnFileExistsCached(AFilename)
 | |
|   else if FileStateCache<>nil then
 | |
|     Result:=FileStateCache.FileExistsCached(AFilename)
 | |
|   else
 | |
|     Result:=FileExistsUTF8(AFilename);
 | |
| end;
 | |
| 
 | |
| function DirPathExistsCached(const AFilename: string): boolean;
 | |
| begin
 | |
|   if FileStateCache<>nil then
 | |
|     Result:=FileStateCache.DirPathExistsCached(AFilename)
 | |
|   else
 | |
|     Result:=DirPathExists(AFilename);
 | |
| end;
 | |
| 
 | |
| function DirectoryIsWritableCached(const ADirectoryName: string): boolean;
 | |
| begin
 | |
|   if FileStateCache<>nil then
 | |
|     Result:=FileStateCache.DirectoryIsWritableCached(ADirectoryName)
 | |
|   else
 | |
|     Result:=DirectoryIsWritable(ADirectoryName);
 | |
| end;
 | |
| 
 | |
| function FileIsExecutableCached(const AFilename: string): boolean;
 | |
| begin
 | |
|   if FileStateCache<>nil then
 | |
|     Result:=FileStateCache.FileIsExecutableCached(AFilename)
 | |
|   else
 | |
|     Result:=FileIsExecutable(AFilename);
 | |
| end;
 | |
| 
 | |
| function FileIsReadableCached(const AFilename: string): boolean;
 | |
| begin
 | |
|   if FileStateCache<>nil then
 | |
|     Result:=FileStateCache.FileIsReadableCached(AFilename)
 | |
|   else
 | |
|     Result:=FileIsReadable(AFilename);
 | |
| end;
 | |
| 
 | |
| function FileIsWritableCached(const AFilename: string): boolean;
 | |
| begin
 | |
|   if FileStateCache<>nil then
 | |
|     Result:=FileStateCache.FileIsWritableCached(AFilename)
 | |
|   else
 | |
|     Result:=FileIsWritable(AFilename);
 | |
| end;
 | |
| 
 | |
| function FileIsTextCached(const AFilename: string): boolean;
 | |
| begin
 | |
|   if FileStateCache<>nil then
 | |
|     Result:=FileStateCache.FileIsTextCached(AFilename)
 | |
|   else
 | |
|     Result:=FileIsText(AFilename);
 | |
| end;
 | |
| 
 | |
| function FileAgeCached(const AFileName: string): Longint;
 | |
| begin
 | |
|   if OnFileAgeCached<>nil then
 | |
|     Result:=OnFileAgeCached(AFilename)
 | |
|   else if FileStateCache<>nil then
 | |
|     Result:=FileStateCache.FileAgeCached(AFilename)
 | |
|   else
 | |
|     Result:=FileAgeUTF8(AFileName);
 | |
| end;
 | |
| 
 | |
| function GetPhysicalFilenameCached(const AFilename: string;
 | |
|   EmptyOnError: boolean): string;
 | |
| var
 | |
|   OnError: TPhysicalFilenameOnError;
 | |
| begin
 | |
|   if FileStateCache<>nil then
 | |
|     Result:=FileStateCache.GetPhysicalFilenameCached(AFilename,EmptyOnError)
 | |
|   else begin
 | |
|     if EmptyOnError then
 | |
|       OnError:=pfeEmpty
 | |
|     else
 | |
|       OnError:=pfeOriginal;
 | |
|     writeln('GetPhysicalFilenameCached GGG1');
 | |
|     Result:=GetPhysicalFilename(AFilename,OnError);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure InvalidateFileStateCache(const Filename: string);
 | |
| begin
 | |
|   FileStateCache.IncreaseTimeStamp(Filename);
 | |
| end;
 | |
| 
 | |
| function CompareFileStateItems(Data1, Data2: Pointer): integer;
 | |
| begin
 | |
|   Result:=CompareFilenames(TFileStateCacheItem(Data1).FFilename,
 | |
|                            TFileStateCacheItem(Data2).FFilename);
 | |
| end;
 | |
| 
 | |
| function CompareFilenameWithFileStateCacheItem(Key, Data: Pointer): integer;
 | |
| begin
 | |
|   Result:=CompareFilenames(AnsiString(Key),TFileStateCacheItem(Data).FFilename);
 | |
|   //debugln('CompareFilenameWithFileStateCacheItem Key=',AnsiString(Key),' Data=',TFileStateCacheItem(Data).FFilename,' Result=',dbgs(Result));
 | |
| end;
 | |
| 
 | |
| procedure LUIncreaseChangeStamp(var ChangeStamp: integer);
 | |
| begin
 | |
|   if ChangeStamp<High(ChangeStamp) then
 | |
|     inc(ChangeStamp)
 | |
|   else
 | |
|     ChangeStamp:=LUInvalidChangeStamp+1;
 | |
| end;
 | |
| 
 | |
| procedure LUIncreaseChangeStamp64(var ChangeStamp: int64);
 | |
| begin
 | |
|   if ChangeStamp<High(ChangeStamp) then
 | |
|     inc(ChangeStamp)
 | |
|   else
 | |
|     ChangeStamp:=LUInvalidChangeStamp64+1;
 | |
| end;
 | |
| 
 | |
| { TFileStateCacheItem }
 | |
| 
 | |
| constructor TFileStateCacheItem.Create(const TheFilename: string;
 | |
|   NewTimeStamp: int64);
 | |
| begin
 | |
|   FFilename:=TheFilename;
 | |
|   FTimeStamp:=NewTimeStamp;
 | |
| end;
 | |
| 
 | |
| function TFileStateCacheItem.CalcMemSize: PtrUint;
 | |
| begin
 | |
|   Result:=PtrUInt(InstanceSize)
 | |
|     +MemSizeString(FFilename)
 | |
|     +MemSizeString(FPhysicalFilename);
 | |
| end;
 | |
| 
 | |
| { TFileStateCache }
 | |
| 
 | |
| procedure TFileStateCache.SetFlag(AFile: TFileStateCacheItem;
 | |
|   AFlag: TFileStateCacheItemFlag; NewValue: boolean);
 | |
| begin
 | |
|   if AFile.FTimeStamp<>FTimeStamp then begin
 | |
|     AFile.FTestedFlags:=[];
 | |
|     AFile.FTimeStamp:=FTimeStamp;
 | |
|   end;
 | |
|   Include(AFile.FTestedFlags,AFlag);
 | |
|   if NewValue then
 | |
|     Include(AFile.FFlags,AFlag)
 | |
|   else
 | |
|     Exclude(AFile.FFlags,AFlag);
 | |
|   //WriteStr(s, AFlag);
 | |
|   //debugln('TFileStateCache.SetFlag AFile.Filename=',AFile.Filename,' ',s,'=',dbgs(AFlag in AFile.FFlags),' Valid=',dbgs(AFlag in AFile.FTestedFlags));
 | |
| end;
 | |
| 
 | |
| constructor TFileStateCache.Create;
 | |
| begin
 | |
|   FFiles:=TAVLTree.Create(@CompareFileStateItems);
 | |
|   LUIncreaseChangeStamp64(FTimeStamp); // one higher than default for new files
 | |
| end;
 | |
| 
 | |
| destructor TFileStateCache.Destroy;
 | |
| begin
 | |
|   FFiles.FreeAndClear;
 | |
|   FFiles.Free;
 | |
|   SetLength(FChangeTimeStampHandler,0);
 | |
|   inherited Destroy;
 | |
| end;
 | |
| 
 | |
| procedure TFileStateCache.Lock;
 | |
| begin
 | |
|   inc(FLockCount);
 | |
| end;
 | |
| 
 | |
| procedure TFileStateCache.Unlock;
 | |
| 
 | |
|   procedure RaiseTooManyUnlocks;
 | |
|   begin
 | |
|     raise Exception.Create('TFileStateCache.Unlock');
 | |
|   end;
 | |
| 
 | |
| begin
 | |
|   if FLockCount<=0 then RaiseTooManyUnlocks;
 | |
|   dec(FLockCount);
 | |
| end;
 | |
| 
 | |
| function TFileStateCache.Locked: boolean;
 | |
| begin
 | |
|   Result:=FLockCount>0;
 | |
| end;
 | |
| 
 | |
| procedure TFileStateCache.IncreaseTimeStamp(const AFilename: string);
 | |
| var
 | |
|   i: Integer;
 | |
|   AFile: TFileStateCacheItem;
 | |
| begin
 | |
|   if Self=nil then exit;
 | |
|   if AFilename='' then begin
 | |
|     // invalidate all
 | |
|     LUIncreaseChangeStamp64(FTimeStamp);
 | |
|   end else begin
 | |
|     // invalidate single file
 | |
|     AFile:=FindFile(AFilename,false);
 | |
|     if AFile<>nil then
 | |
|       AFile.FTestedFlags:=[];
 | |
|   end;
 | |
|   for i:=0 to length(FChangeTimeStampHandler)-1 do
 | |
|     FChangeTimeStampHandler[i](Self,AFilename);
 | |
|   //debugln('TFileStateCache.IncreaseTimeStamp FTimeStamp=',dbgs(FTimeStamp));
 | |
| end;
 | |
| 
 | |
| function TFileStateCache.FileExistsCached(const AFilename: string): boolean;
 | |
| var
 | |
|   AFile: TFileStateCacheItem;
 | |
| begin
 | |
|   Result := False;
 | |
|   if Check(AFilename,fsciExists,AFile,Result) then exit;
 | |
|   Result:=FileExistsUTF8(AFile.Filename);
 | |
|   SetFlag(AFile,fsciExists,Result);
 | |
|   {if not Check(Filename,fsciExists,AFile,Result) then begin
 | |
|     WriteDebugReport;
 | |
|     raise Exception.Create('');
 | |
|   end;}
 | |
| end;
 | |
| 
 | |
| function TFileStateCache.DirPathExistsCached(const AFilename: string): boolean;
 | |
| var
 | |
|   AFile: TFileStateCacheItem;
 | |
| begin
 | |
|   Result := False;
 | |
|   if Check(AFilename,fsciDirectory,AFile,Result) then exit;
 | |
|   Result:=DirPathExists(AFile.Filename);
 | |
|   SetFlag(AFile,fsciDirectory,Result);
 | |
| end;
 | |
| 
 | |
| function TFileStateCache.DirectoryIsWritableCached(const DirectoryName: string
 | |
|   ): boolean;
 | |
| var
 | |
|   AFile: TFileStateCacheItem;
 | |
| begin
 | |
|   Result := False;
 | |
|   if Check(DirectoryName,fsciDirectoryWritable,AFile,Result) then exit;
 | |
|   Result:=DirectoryIsWritable(AFile.Filename);
 | |
|   SetFlag(AFile,fsciDirectoryWritable,Result);
 | |
| end;
 | |
| 
 | |
| function TFileStateCache.FileIsExecutableCached(
 | |
|   const AFilename: string): boolean;
 | |
| var
 | |
|   AFile: TFileStateCacheItem;
 | |
| begin
 | |
|   Result := False;
 | |
|   if Check(AFilename,fsciExecutable,AFile,Result) then exit;
 | |
|   Result:=FileIsExecutable(AFile.Filename);
 | |
|   SetFlag(AFile,fsciExecutable,Result);
 | |
| end;
 | |
| 
 | |
| function TFileStateCache.FileIsReadableCached(const AFilename: string): boolean;
 | |
| var
 | |
|   AFile: TFileStateCacheItem;
 | |
| begin
 | |
|   Result := False;
 | |
|   if Check(AFilename,fsciReadable,AFile,Result) then exit;
 | |
|   Result:=FileIsReadable(AFile.Filename);
 | |
|   SetFlag(AFile,fsciReadable,Result);
 | |
| end;
 | |
| 
 | |
| function TFileStateCache.FileIsWritableCached(const AFilename: string): boolean;
 | |
| var
 | |
|   AFile: TFileStateCacheItem;
 | |
| begin
 | |
|   Result := False;
 | |
|   if Check(AFilename,fsciWritable,AFile,Result) then exit;
 | |
|   Result:=FileIsWritable(AFile.Filename);
 | |
|   SetFlag(AFile,fsciWritable,Result);
 | |
| end;
 | |
| 
 | |
| function TFileStateCache.FileIsTextCached(const AFilename: string): boolean;
 | |
| var
 | |
|   AFile: TFileStateCacheItem;
 | |
| begin
 | |
|   Result := False;
 | |
|   if Check(AFilename,fsciText,AFile,Result) then exit;
 | |
|   Result:=FileIsText(AFile.Filename);
 | |
|   SetFlag(AFile,fsciText,Result);
 | |
| end;
 | |
| 
 | |
| function TFileStateCache.FileAgeCached(const AFileName: string): Longint;
 | |
| var
 | |
|   AFile: TFileStateCacheItem;
 | |
|   Dummy: Boolean;
 | |
| begin
 | |
|   Dummy := False;
 | |
|   if Check(AFilename,fsciAge,AFile,Dummy) then begin
 | |
|     Result:=AFile.Age;
 | |
|     exit;
 | |
|   end;
 | |
|   Result:=FileAge(AFile.Filename);
 | |
|   AFile.FAge:=Result;
 | |
|   Include(AFile.FTestedFlags,fsciAge);
 | |
| end;
 | |
| 
 | |
| function TFileStateCache.GetPhysicalFilenameCached(const AFileName: string;
 | |
|   EmptyOnError: boolean): string;
 | |
| {$IFDEF Unix}
 | |
| var
 | |
|   AFile: TFileStateCacheItem;
 | |
|   Dummy: Boolean;
 | |
| {$ENDIF}
 | |
| begin
 | |
|   {$IFDEF Unix}
 | |
|   Dummy := False;
 | |
|   if Check(AFilename,fsciPhysical,AFile,Dummy) then begin
 | |
|     Result:=AFile.PhysicalFilename;
 | |
|     exit;
 | |
|   end;
 | |
|   Result:=ExtractFilePath(AFile.Filename);
 | |
|   if Result<>'' then begin
 | |
|     // use cache recursively for directory
 | |
|     if (Result='.') or (Result='..') or (Result='/') then begin
 | |
|       // no query required
 | |
|     end else begin
 | |
|       Result:=GetPhysicalFilenameCached(Result,true);
 | |
|     end;
 | |
|     if Result<>'' then begin
 | |
|       Result:=AppendPathDelim(Result)+ExtractFilename(AFile.Filename);
 | |
|       Result:=ReadAllLinks(Result,false);
 | |
|     end;
 | |
|   end else begin
 | |
|     // no path
 | |
|     Result:=ReadAllLinks(AFile.Filename,false);
 | |
|   end;
 | |
|   AFile.FPhysicalFilename:=Result;
 | |
|   Include(AFile.FTestedFlags,fsciPhysical);
 | |
|   if (Result='') and (not EmptyOnError) then
 | |
|     Result:=AFileName;
 | |
|   {$ELSE}
 | |
|   Result:=AFileName;
 | |
|   {$ENDIF}
 | |
| end;
 | |
| 
 | |
| function TFileStateCache.FindFile(const Filename: string;
 | |
|   CreateIfNotExists: boolean): TFileStateCacheItem;
 | |
| var
 | |
|   NormedFilename: String;
 | |
|   ANode: TAVLTreeNode;
 | |
| begin
 | |
|   // make filename unique
 | |
|   NormedFilename:=ChompPathDelim(ResolveDots(Filename));
 | |
|   ANode:=FFiles.FindKey(Pointer(NormedFilename),
 | |
|                         @CompareFilenameWithFileStateCacheItem);
 | |
|   if ANode<>nil then
 | |
|     Result:=TFileStateCacheItem(ANode.Data)
 | |
|   else if CreateIfNotExists then begin
 | |
|     Result:=TFileStateCacheItem.Create(NormedFilename,FTimeStamp);
 | |
|     FFiles.Add(Result);
 | |
|     if FFiles.FindKey(Pointer(NormedFilename),
 | |
|                       @CompareFilenameWithFileStateCacheItem)=nil
 | |
|     then begin
 | |
|       //DebugLn(format('FileStateCache.FindFile: "%s"',[FileName]));
 | |
|       raise Exception.Create('');
 | |
|     end;
 | |
|   end else
 | |
|     Result:=nil;
 | |
| end;
 | |
| 
 | |
| function TFileStateCache.Check(const Filename: string;
 | |
|   AFlag: TFileStateCacheItemFlag; out AFile: TFileStateCacheItem;
 | |
|   var FlagIsSet: boolean): boolean;
 | |
| begin
 | |
|   AFile:=FindFile(Filename,true);
 | |
|   if FTimeStamp=AFile.FTimeStamp then begin
 | |
|     Result:=AFlag in AFile.FTestedFlags;
 | |
|     FlagIsSet:=AFlag in AFile.FFlags;
 | |
|   end else begin
 | |
|     AFile.FTestedFlags:=[];
 | |
|     AFile.FTimeStamp:=FTimeStamp;
 | |
|     Result:=false;
 | |
|     FlagIsSet:=false;
 | |
|   end;
 | |
|   //WriteStr(s, AFlag);
 | |
|   //debugln('TFileStateCache.Check Filename=',Filename,' AFile.Filename=',AFile.Filename,' ',s,'=',dbgs(FlagIsSet),' Valid=',dbgs(Result));
 | |
| end;
 | |
| 
 | |
| procedure TFileStateCache.AddChangeTimeStampHandler(
 | |
|   const Handler: TOnChangeFileStateTimeStamp);
 | |
| begin
 | |
|   SetLength(FChangeTimeStampHandler,length(FChangeTimeStampHandler)+1);
 | |
|   FChangeTimeStampHandler[length(FChangeTimeStampHandler)-1]:=Handler;
 | |
| end;
 | |
| 
 | |
| procedure TFileStateCache.RemoveChangeTimeStampHandler(
 | |
|   const Handler: TOnChangeFileStateTimeStamp);
 | |
| var
 | |
|   i: Integer;
 | |
| begin
 | |
|   for i:=length(FChangeTimeStampHandler)-1 downto 0 do begin
 | |
|     if Handler=FChangeTimeStampHandler[i] then begin
 | |
|       if i<length(FChangeTimeStampHandler)-1 then
 | |
|         System.Move(FChangeTimeStampHandler[i+1],FChangeTimeStampHandler[i],
 | |
|                     SizeOf(TNotifyEvent)*(length(FChangeTimeStampHandler)-i-1));
 | |
|       SetLength(FChangeTimeStampHandler,length(FChangeTimeStampHandler)-1);
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TFileStateCache.CalcMemSize: PtrUint;
 | |
| var
 | |
|   Node: TAVLTreeNode;
 | |
| begin
 | |
|   Result:=PtrUInt(InstanceSize)
 | |
|     +PtrUInt(length(FChangeTimeStampHandler))*SizeOf(TNotifyEvent);
 | |
|   if FFiles<>nil then begin
 | |
|     inc(Result,PtrUInt(FFiles.InstanceSize)
 | |
|       +PtrUInt(FFiles.Count)*PtrUInt(TAVLTreeNode.InstanceSize));
 | |
|     Node:=FFiles.FindLowest;
 | |
|     while Node<>nil do begin
 | |
|       inc(Result,TFileStateCacheItem(Node.Data).CalcMemSize);
 | |
|       Node:=FFiles.FindSuccessor(Node);
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| initialization
 | |
|   OnInvalidateFileStateCache:=@InvalidateFileStateCache;
 | |
| 
 | |
| end.
 | |
| 
 | 
