Subversion Repositories delphiutils

Rev

Rev 9 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. unit HighPerfFileComparator;
  2.  
  3. (*
  4.  
  5.   HighPerfFileComparator.pas
  6.   (C) 2010 ViaThinkSoft, Daniel Marschall
  7.  
  8.   Last modified: January, 21th 2010
  9.  
  10.   THighPerfFileComparator.compare(filenameA, filenameB: string): boolean;
  11.  
  12.   Compares two files primary with size comparison and
  13.   secundary with MD5 hash comparison. All results will be cached.
  14.  
  15.   Note: If you want to use the cache for every file, please do not
  16.   destroy the instance of THighPerfFileComparator after done your job.
  17.   Use in a field of your form class and free it when the application
  18.   closes.
  19.  
  20.   Example of usage:
  21.  
  22.       var
  23.         comparator: THighPerfFileComparator;
  24.  
  25.       procedure TForm1.FormCreate(Sender: TObject);
  26.       begin
  27.         comparator := THighPerfFileComparator.Create;
  28.       end;
  29.  
  30.       procedure TForm1.Button1Click(Sender: TObject);
  31.       begin
  32.         // This deletes all cached file hashs, so that the result will be
  33.         // new calculated. Alternatively you can create a new
  34.         // THighPerfFileComparator at the beginning of every new job.
  35.         comparator.clearCache;
  36.  
  37.         if comparator.Compare('C:\a.txt', 'C:\b.txt') then
  38.           ShowMessage('Files are equal')
  39.         else
  40.           ShowMessage('Files are not equal');
  41.       end;
  42.  
  43.       procedure TForm1.FormDestroy(Sender: TObject);
  44.       begin
  45.         comparator.Free;
  46.       end;
  47.  
  48.   Class hierarchie:
  49.  
  50.       Exception
  51.           EFileNotFound
  52.           ENoRegisteredComparators
  53.       TObject
  54.           (TContainer)
  55.               (TStringContainer)
  56.               (TInteger64Container)
  57.           TCacheManager
  58.               TFilenameCacheManager
  59.                   TInteger64CacheManager
  60.                   TStringCacheManager
  61.       TInterfacedObject
  62.           TComparator
  63.               TFileComparator
  64.                   THashMD5Comparator
  65.                       TCachedHashMD5Comparator [ICachedComparator]
  66.                   TSizeComparator
  67.                       TCachedSizeComparator [ICachedComparator]
  68.                   TMultipleFileComparators
  69.                       TCachedSizeHashMD5FileComparator [ICachedComparator]
  70.                       = THighPerfFileComparator
  71.  
  72. *)
  73.  
  74. interface
  75.  
  76. uses
  77.   SysUtils, Classes, Contnrs;
  78.  
  79. type
  80.   ICachedComparator = interface(IInterface)
  81.   // private
  82.     procedure SetCacheEnabled(Value: boolean);
  83.     function GetCacheEnabled: boolean;
  84.   // public
  85.     property CacheEnabled: boolean read getCacheEnabled write setCacheEnabled;
  86.     procedure ClearCache;
  87.   end;
  88.  
  89.   EFileNotFound = class(Exception);
  90.  
  91.   ENoRegisteredComparators = class(Exception);
  92.  
  93.   TCacheManager = class(TObject)
  94.   private
  95.     FCache: TStringList;
  96.   public
  97.     procedure SetCache(identifier: string; cacheObject: TObject);
  98.     function GetCache(identifier: string): TObject;
  99.     function IsCached(identifier: string): boolean;
  100.     procedure Clear;
  101.     constructor Create;
  102.     destructor Destroy; override;
  103.   end;
  104.  
  105.   // TFilenameCacheManager extends every filename to a unique identifier
  106.   TFilenameCacheManager = class(TCacheManager)
  107.   protected
  108.     function FullQualifiedFilename(filename: string): string;
  109.   public
  110.     procedure SetCache(filename: string; cacheObject: TObject);
  111.     function GetCache(filename: string): TObject;
  112.     function IsCached(filename: string): boolean;
  113.   end;
  114.  
  115.   // Wäre eigentlich ein guter Ansatz für Mehrfachvererbung...
  116.   TInteger64CacheManager = class(TFilenameCacheManager)
  117.   public
  118.     procedure SetCache(filename: string; content: int64);
  119.     function GetCache(filename: string): int64;
  120.   end;
  121.  
  122.   TStringCacheManager = class(TFilenameCacheManager)
  123.   public
  124.     procedure SetCache(filename: string; content: string);
  125.     function GetCache(filename: string): string;
  126.   end;
  127.  
  128.   TComparator = class(TInterfacedObject) // abstract
  129.   public
  130.     function Compare(a, b: string): boolean; virtual; abstract;
  131.   end;
  132.  
  133.   TFileComparator = class(TComparator) // abstract
  134.   protected
  135.     // Please call this method for both filenames at every Compare()
  136.     // call of your derivates.
  137.     procedure CheckFileExistence(filename: string);
  138.   public
  139.     // This is an abstract method since it only checks filenames and returns
  140.     // always false.
  141.     // function Compare(filenameA, filenameB: string): boolean; override;
  142.   end;
  143.  
  144.   TSizeComparator = class(TFileComparator)
  145.   protected
  146.     function GetFileSize(filename: string): Int64; virtual;
  147.   public
  148.     function Compare(filenameA, filenameB: string): boolean; override;
  149.   end;
  150.  
  151.   TCachedSizeComparator = class(TSizeComparator, ICachedComparator)
  152.   private
  153.     FCacheManager: TInteger64CacheManager;
  154.     FCacheEnabled: boolean;
  155.     procedure SetCacheEnabled(Value: boolean);
  156.     function GetCacheEnabled: boolean;
  157.   protected
  158.     function GetFileSize(filename: string): Int64; override;
  159.   public
  160.     property CacheEnabled: boolean read getCacheEnabled write setCacheEnabled;
  161.     procedure ClearCache;
  162.     constructor Create;
  163.     destructor Destroy; override;
  164.   end;
  165.  
  166.   THashMD5Comparator = class(TFileComparator)
  167.   protected
  168.     function GetFileHashMD5(filename: string): String; virtual;
  169.   public
  170.     function Compare(filenameA, filenameB: string): boolean; override;
  171.   end;
  172.  
  173.   TCachedHashMD5Comparator = class(THashMD5Comparator, ICachedComparator)
  174.   private
  175.     FCacheManager: TStringCacheManager;
  176.     FCacheEnabled: boolean;
  177.     procedure SetCacheEnabled(Value: boolean);
  178.     function GetCacheEnabled: boolean;
  179.   protected
  180.     function GetFileHashMD5(filename: string): String; override;
  181.   public
  182.     property CacheEnabled: boolean read getCacheEnabled write setCacheEnabled;
  183.     procedure ClearCache;
  184.     constructor Create;
  185.     destructor Destroy; override;
  186.   end;
  187.  
  188.   TMultipleFileComparators = class(TFileComparator) // abstract
  189.   // This is an abstract class since no comparators are registered and so
  190.   // compare() will throw an ENoRegisteredComparators exception.
  191.   protected
  192.     // WARNING: DOES *NOT* OWNS ITS OBJECTS. PLEASE FREE THEM ON DESTROY.
  193.     FRegisteredComparators: TObjectList; // of TFileComparator
  194.     procedure RegisterComparator(comparator: TFileComparator);
  195.   public
  196.     function Compare(filenameA, filenameB: string): boolean; override;
  197.     constructor Create;
  198.     destructor Destroy; override;
  199.   end;
  200.  
  201.   TCachedSizeHashMD5FileComparator = class(TMultipleFileComparators,
  202.     ICachedComparator)
  203.   private
  204.     FHashComparator: TCachedHashMD5Comparator;
  205.     FSizeComparator: TCachedSizeComparator;
  206.     procedure SetCacheEnabled(Value: boolean);
  207.     function GetCacheEnabled: boolean;
  208.   public
  209.     property CacheEnabled: boolean read getCacheEnabled write setCacheEnabled;
  210.     procedure ClearCache;
  211.     constructor Create;
  212.     destructor Destroy; override;
  213.   end;
  214.  
  215.   THighPerfFileComparator = TCachedSizeHashMD5FileComparator;
  216.  
  217. implementation
  218.  
  219. // Please download MD5.pas from
  220. // http://www.koders.com/delphi/fid1C4B47A76F8C7172FDCFE7B3A74863D6FB7FC2BA.aspx
  221.  
  222. uses
  223.   MD5;
  224.  
  225. resourcestring
  226.   LNG_E_NO_REGISTERED_COMPARATORS = 'No comparators registered. Please use ' +
  227.     'a derivate of the class TMultipleFileComparators which does register ' +
  228.     'comparators.';
  229.   LNG_E_FILE_NOT_FOUND = 'The file "%s" was not found.';
  230.  
  231. type
  232.   TContainer = class(TObject);
  233.  
  234.   TStringContainer = class(TContainer)
  235.   public
  236.     Content: string;
  237.     constructor Create(AContent: string);
  238.   end;
  239.  
  240.   TInteger64Container = class(TContainer)
  241.   public
  242.     Content: int64;
  243.     constructor Create(AContent: int64);
  244.   end;
  245.  
  246. { Functions }
  247.  
  248. function _MD5File(filename: string): string;
  249. begin
  250.   result := MD5Print(MD5File(filename));
  251. end;
  252.  
  253. { TStringContainer }
  254.  
  255. constructor TStringContainer.Create(AContent: string);
  256. begin
  257.   inherited Create;
  258.  
  259.   content := AContent;
  260. end;
  261.  
  262. { TInteger64Container }
  263.  
  264. constructor TInteger64Container.Create(AContent: int64);
  265. begin
  266.   inherited Create;
  267.  
  268.   content := AContent;
  269. end;
  270.  
  271. { TCacheManager }
  272.  
  273. procedure TCacheManager.SetCache(identifier: string; cacheObject: TObject);
  274. begin
  275.   FCache.AddObject(identifier, cacheObject);
  276. end;
  277.  
  278. function TCacheManager.GetCache(identifier: string): TObject;
  279. begin
  280.   if isCached(identifier) then
  281.     result := FCache.Objects[FCache.IndexOf(identifier)] as TContainer
  282.   else
  283.     result := nil;
  284. end;
  285.  
  286. function TCacheManager.IsCached(identifier: string): boolean;
  287. begin
  288.   result := FCache.IndexOf(identifier) <> -1;
  289. end;
  290.  
  291. procedure TCacheManager.Clear;
  292. begin
  293.   FCache.Clear;
  294. end;
  295.  
  296. constructor TCacheManager.Create;
  297. begin
  298.   inherited Create;
  299.  
  300.   FCache := TStringList.Create;
  301. end;
  302.  
  303. destructor TCacheManager.Destroy;
  304. begin
  305.   FCache.Free;
  306.  
  307.   inherited Destroy;
  308. end;
  309.  
  310. { TFilenameCacheManager }
  311.  
  312. function TFilenameCacheManager.FullQualifiedFilename(filename: string): string;
  313. begin
  314.   result := ExpandUNCFileName(filename);
  315. end;
  316.  
  317. procedure TFilenameCacheManager.SetCache(filename: string;
  318.   cacheObject: TObject);
  319. begin
  320.   inherited setCache(FullQualifiedFilename(filename), cacheObject);
  321. end;
  322.  
  323. function TFilenameCacheManager.GetCache(filename: string): TObject;
  324. begin
  325.   result := inherited getCache(FullQualifiedFilename(filename));
  326. end;
  327.  
  328. function TFilenameCacheManager.IsCached(filename: string): boolean;
  329. begin
  330.   result := inherited isCached(FullQualifiedFilename(filename));
  331. end;
  332.  
  333. { TInteger64CacheManager }
  334.  
  335. procedure TInteger64CacheManager.SetCache(filename: string; content: int64);
  336. begin
  337.   inherited setCache(filename, TInteger64Container.Create(content));
  338. end;
  339.  
  340. function TInteger64CacheManager.GetCache(filename: string): int64;
  341. begin
  342.   result := (inherited getCache(filename) as TInteger64Container).content;
  343. end;
  344.  
  345. { TStringCacheManager }
  346.  
  347. procedure TStringCacheManager.SetCache(filename: string; content: string);
  348. begin
  349.   inherited setCache(filename, TStringContainer.Create(content));
  350. end;
  351.  
  352. function TStringCacheManager.GetCache(filename: string): string;
  353. begin
  354.   result := (inherited getCache(filename) as TStringContainer).content;
  355. end;
  356.  
  357. { TFileComparator }
  358.  
  359. procedure TFileComparator.CheckFileExistence(filename: string);
  360. begin
  361.   if not fileExists(filename) then
  362.     raise EFileNotFound.CreateFmt(LNG_E_FILE_NOT_FOUND, [filename]);
  363. end;
  364.  
  365. (* function TFileComparator.Compare(filenameA, filenameB: string): boolean;
  366. begin
  367.   if not fileExists(filenameA) then
  368.     raise EFileNotFound.CreateFmt(LNG_E_FILE_NOT_FOUND, [filenameA]);
  369.  
  370.   if not fileExists(filenameB) then
  371.     raise EFileNotFound.CreateFmt(LNG_E_FILE_NOT_FOUND, [filenameB]);
  372.  
  373.   // Leider keine Überprüfung, ob Methode überschrieben wurde
  374.   // (da sonst result immer false ist!)
  375.   if Self.ClassType = TFileComparator then
  376.     raise EDirectCall.CreateFmt(LNG_E_DIRECT_CALL, [Self.ClassName]);
  377.  
  378.   result := false;
  379. end; *)
  380.  
  381. { TSizeComparator }
  382.  
  383. function TSizeComparator.GetFileSize(filename: string): Int64;
  384. var
  385.   f: TFileStream;
  386. begin
  387.   f := TFileStream.Create(filename, fmOpenRead);
  388.   try
  389.     result := f.Size
  390.   finally
  391.     f.Free;
  392.   end;
  393. end;
  394.  
  395. function TSizeComparator.Compare(filenameA, filenameB: string): boolean;
  396. begin
  397.   //inherited compare(filenameA, filenameB);
  398.   CheckFileExistence(filenameA);
  399.   CheckFileExistence(filenameB);
  400.  
  401.   result := getFileSize(filenameA) = getFileSize(filenameB);
  402. end;
  403.  
  404. { TCachedSizeComparator }
  405.  
  406. procedure TCachedSizeComparator.SetCacheEnabled(Value: boolean);
  407. begin
  408.   if FCacheEnabled <> Value then
  409.     FCacheEnabled := Value;
  410. end;
  411.  
  412. function TCachedSizeComparator.GetCacheEnabled: boolean;
  413. begin
  414.   result := FCacheEnabled;
  415. end;
  416.  
  417. function TCachedSizeComparator.GetFileSize(filename: string): Int64;
  418. begin
  419.   if FCacheEnabled then
  420.   begin
  421.     if FCacheManager.isCached(filename) then
  422.     begin
  423.       result := FCacheManager.getCache(filename);
  424.     end
  425.     else
  426.     begin
  427.       result := inherited getFileSize(filename);
  428.       FCacheManager.setCache(filename, result);
  429.     end;
  430.   end
  431.   else
  432.     result := inherited getFileSize(filename);
  433. end;
  434.  
  435. procedure TCachedSizeComparator.ClearCache;
  436. begin
  437.   FCacheManager.clear;
  438. end;
  439.  
  440. constructor TCachedSizeComparator.Create;
  441. begin
  442.   inherited Create;
  443.  
  444.   FCacheManager := TInteger64CacheManager.Create;
  445.   FCacheEnabled := true;
  446. end;
  447.  
  448. destructor TCachedSizeComparator.Destroy;
  449. begin
  450.   FCacheManager.Free;
  451.  
  452.   inherited Destroy;
  453. end;
  454.  
  455. { THashMD5Comparator }
  456.  
  457. function THashMD5Comparator.GetFileHashMD5(filename: string): String;
  458. begin
  459.   result := _MD5File(filename);
  460. end;
  461.  
  462. function THashMD5Comparator.Compare(filenameA, filenameB: string): boolean;
  463. begin
  464.   //inherited Compare(filenameA, filenameB);
  465.   CheckFileExistence(filenameA);
  466.   CheckFileExistence(filenameB);
  467.  
  468.   result := GetFileHashMD5(filenameA) = GetFileHashMD5(filenameB);
  469. end;
  470.  
  471. { TCachedHashMD5Comparator }
  472.  
  473. procedure TCachedHashMD5Comparator.SetCacheEnabled(Value: boolean);
  474. begin
  475.   if FCacheEnabled <> Value then
  476.     FCacheEnabled := Value;
  477. end;
  478.  
  479. function TCachedHashMD5Comparator.GetCacheEnabled: boolean;
  480. begin
  481.   result := FCacheEnabled;
  482. end;
  483.  
  484. function TCachedHashMD5Comparator.GetFileHashMD5(filename: string): String;
  485. begin
  486.   if FCacheEnabled then
  487.   begin
  488.     if FCacheManager.IsCached(filename) then
  489.     begin
  490.       result := FCacheManager.GetCache(filename);
  491.     end
  492.     else
  493.     begin
  494.       result := inherited GetFileHashMD5(filename);
  495.       FCacheManager.SetCache(filename, result);
  496.     end;
  497.   end
  498.   else
  499.     result := inherited GetFileHashMD5(filename);
  500. end;
  501.  
  502. procedure TCachedHashMD5Comparator.ClearCache;
  503. begin
  504.   FCacheManager.Clear;
  505. end;
  506.  
  507. constructor TCachedHashMD5Comparator.Create;
  508. begin
  509.   inherited Create;
  510.  
  511.   FCacheManager := TStringCacheManager.Create;
  512.   FCacheEnabled := true;
  513. end;
  514.  
  515. destructor TCachedHashMD5Comparator.Destroy;
  516. begin
  517.   FCacheManager.Free;
  518.  
  519.   inherited Destroy;
  520. end;
  521.  
  522. { TMultipleFileComparators }
  523.  
  524. procedure TMultipleFileComparators.RegisterComparator(comparator: TFileComparator);
  525. begin
  526.   FRegisteredComparators.Add(comparator)
  527. end;
  528.  
  529. function TMultipleFileComparators.Compare(filenameA,
  530.   filenameB: string): boolean;
  531. var
  532.   i: integer;
  533. begin
  534.   //inherited Compare(filenameA, filenameB);
  535.   CheckFileExistence(filenameA);
  536.   CheckFileExistence(filenameB);
  537.  
  538.   if FRegisteredComparators.Count = 0 then
  539.     raise ENoRegisteredComparators.Create(LNG_E_NO_REGISTERED_COMPARATORS);
  540.  
  541.   for i := 0 to FRegisteredComparators.Count - 1 do
  542.   begin
  543.     if not (FRegisteredComparators.Items[i] as TFileComparator).
  544.       Compare(filenameA, filenameB) then
  545.     begin
  546.       result := false;
  547.       exit;
  548.     end;
  549.   end;
  550.   result := true;
  551. end;
  552.  
  553. constructor TMultipleFileComparators.Create;
  554. begin
  555.   inherited Create;
  556.  
  557.   FRegisteredComparators := TObjectList.Create(false);
  558. end;
  559.  
  560. destructor TMultipleFileComparators.Destroy;
  561. begin
  562.   FRegisteredComparators.Free;
  563.  
  564.   inherited Destroy;
  565. end;
  566.  
  567. { TCachedSizeHashMD5FileComparator }
  568.  
  569. procedure TCachedSizeHashMD5FileComparator.SetCacheEnabled(Value: boolean);
  570. begin
  571.   FSizeComparator.SetCacheEnabled(Value);
  572.   FHashComparator.SetCacheEnabled(Value);
  573. end;
  574.  
  575. function TCachedSizeHashMD5FileComparator.getCacheEnabled: boolean;
  576. begin
  577.   result := FSizeComparator.GetCacheEnabled and FHashComparator.GetCacheEnabled;
  578. end;
  579.  
  580. procedure TCachedSizeHashMD5FileComparator.ClearCache;
  581. begin
  582.   FSizeComparator.ClearCache;
  583.   FHashComparator.ClearCache;
  584. end;
  585.  
  586. constructor TCachedSizeHashMD5FileComparator.Create;
  587. begin
  588.   inherited Create;
  589.  
  590.   FSizeComparator := TCachedSizeComparator.Create;
  591.   RegisterComparator(FSizeComparator);
  592.  
  593.   FHashComparator := TCachedHashMD5Comparator.Create;
  594.   RegisterComparator(FHashComparator);
  595. end;
  596.  
  597. destructor TCachedSizeHashMD5FileComparator.Destroy;
  598. begin
  599.   FHashComparator.Free;
  600.   FSizeComparator.Free;
  601.  
  602.   inherited Destroy;
  603. end;
  604.  
  605. end.
  606.