Subversion Repositories autosfx

Rev

Blame | Last modification | View Log | RSS feed

  1. unit ZMHash19;
  2.  
  3. (*
  4.   ZMHash19.pas - Hash list for entries
  5.     Copyright (C) 2009, 2010  by Russell J. Peters, Roger Aelbrecht,
  6.       Eric W. Engler and Chris Vleghert.
  7.  
  8.         This file is part of TZipMaster Version 1.9.
  9.  
  10.     TZipMaster is free software: you can redistribute it and/or modify
  11.     it under the terms of the GNU Lesser General Public License as published by
  12.     the Free Software Foundation, either version 3 of the License, or
  13.     (at your option) any later version.
  14.  
  15.     TZipMaster is distributed in the hope that it will be useful,
  16.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.     GNU Lesser General Public License for more details.
  19.  
  20.     You should have received a copy of the GNU Lesser General Public License
  21.     along with TZipMaster.  If not, see <http://www.gnu.org/licenses/>.
  22.  
  23.     contact: problems@delphizip.org (include ZipMaster in the subject).
  24.     updates: http://www.delphizip.org
  25.     DelphiZip maillist subscribe at http://www.freelists.org/list/delphizip
  26.  
  27.   modified 2009-04-21
  28. ---------------------------------------------------------------------------*)
  29.  
  30. interface
  31.  
  32. uses
  33.   ZipMstr19, ZMIRec19, ZMCore19;
  34.  
  35. const
  36.   HDEBlockEntries = 511; // number of entries per block
  37.  
  38. type
  39.   PHashedDirEntry = ^THashedDirEntry;
  40.   THashedDirEntry = record
  41.     Next: PHashedDirEntry;
  42.     ZRec: TZMIRec;
  43.   end;
  44.  
  45.   // for speed and efficiency allocate blocks of entries
  46.   PHDEBlock = ^THDEBlock;
  47.   THDEBlock = packed record
  48.     Entries: array [0..(HDEBlockEntries -1)] of THashedDirEntry;
  49.     Next: PHDEBlock;
  50.   end;
  51.  
  52.   TZMDirHashList = class(TObject)
  53.   private
  54.     fLastBlock: PHDEBlock;
  55.     fNextEntry: Cardinal;  
  56. {$IFNDEF UNICODE}
  57.     FWorker: TZMCore;
  58. {$ENDIF}
  59.     function GetEmpty: boolean;
  60.     function GetSize: Cardinal;
  61.     procedure SetEmpty(const Value: boolean);
  62.     procedure SetSize(const Value: Cardinal);
  63.   protected
  64.     Chains: array of PHashedDirEntry;
  65.     fBlocks: Integer;
  66.     //1 chain of removed nodes
  67.     fEmpties: PHashedDirEntry;
  68.     procedure DisposeBlocks;
  69.     function GetEntry: PHashedDirEntry;
  70.     function Same(Entry: PHashedDirEntry; Hash: Cardinal; const Str: String):
  71.         Boolean;
  72.   public
  73.     function Add(const Rec: TZMIRec): TZMIRec;
  74.     procedure AfterConstruction; override;
  75.     procedure AutoSize(Req: Cardinal);
  76.     procedure BeforeDestruction; override;
  77.     procedure Clear;
  78.     function Find(const FileName: String): TZMIRec;
  79.     //1 return true if removed
  80.     function Remove(const ZDir: TZMIRec): boolean;
  81.     property Empty: boolean read GetEmpty write SetEmpty;
  82.     property Size: Cardinal read GetSize write SetSize;
  83. {$IFNDEF UNICODE}
  84.     property Worker: TZMCore read FWorker write FWorker;
  85. {$ENDIF}
  86.   end;
  87.  
  88. implementation
  89.  
  90. uses
  91.   SysUtils, Windows, ZMMatch19;
  92.  
  93. const
  94.   ChainsMax = 65537;
  95.   ChainsMin = 61;
  96.   CapacityMin = 64;
  97.  
  98. function TZMDirHashList.Add(const Rec: TZMIRec): TZMIRec;
  99. var
  100.   Entry: PHashedDirEntry;
  101.   Hash: Cardinal;
  102.   Idx: Integer;
  103.   Parent: PHashedDirEntry;
  104.   S: String;
  105. begin
  106.   Assert(Rec <> nil, 'nil ZipDirEntry');
  107.   if Chains = nil then
  108.     Size := 1283;
  109.   Result := nil;
  110.   S := Rec.FileName;
  111.   Hash := Rec.Hash;
  112.   Idx := Hash mod Cardinal(Length(Chains));
  113.   Entry := Chains[Idx];
  114.   if Entry = nil then
  115.   begin
  116.     Entry := GetEntry;
  117.     Entry.ZRec := Rec;
  118.     Entry.Next := nil;
  119.     Chains[Idx] := Entry;
  120.   end
  121.   else
  122.   begin
  123.     repeat
  124.       if Same(Entry, Hash, S) then
  125.       begin
  126.         Result := Entry.ZRec;   // duplicate name
  127.         exit;
  128.       end;
  129.       Parent := Entry;
  130.       Entry := Entry.Next;
  131.       if Entry = nil then
  132.       begin
  133.         Entry := GetEntry;
  134.         Entry.ZRec := nil;
  135.         Parent.Next := Entry;
  136.       end;
  137.     until (Entry.ZRec = nil);
  138.     // we have an entry so fill in the details
  139.     Entry.ZRec := Rec;
  140.     Entry.Next := nil;
  141.   end;
  142. end;
  143.  
  144. procedure TZMDirHashList.AfterConstruction;
  145. begin
  146.   inherited;
  147.   fBlocks := 0;
  148.   fLastBlock := nil;
  149.   fEmpties := nil;
  150.   fNextEntry := HIGH(Cardinal);
  151. end;
  152.  
  153. // set size to a reasonable prime number
  154. procedure TZMDirHashList.AutoSize(Req: Cardinal);
  155. const
  156.   PrimeSizes: array[0..29] of Cardinal =
  157.   (61, 131, 257, 389, 521, 641, 769, 1031, 1283, 1543, 2053, 2579, 3593,
  158.    4099, 5147, 6151, 7177, 8209, 10243, 12289, 14341, 16411, 18433, 20483,
  159.    22521, 24593, 28687, 32771, 40961, 65537);
  160. var
  161.   i: Integer;
  162. begin
  163.   if Req < 12000 then
  164.   begin
  165.     // use next higher size
  166.     for i := 0 to HIGH(PrimeSizes) do
  167.       if PrimeSizes[i] >= Req then
  168.       begin
  169.         Req := PrimeSizes[i];
  170.         break;
  171.       end;
  172.   end
  173.   else
  174.   begin
  175.     // use highest smaller size
  176.     for i := HIGH(PrimeSizes) downto 0 do
  177.       if PrimeSizes[i] < Req then
  178.       begin
  179.         Req := PrimeSizes[i];
  180.         break;
  181.       end;
  182.   end;
  183.   SetSize(Req);
  184. end;
  185.  
  186. procedure TZMDirHashList.BeforeDestruction;
  187. begin
  188.   Clear;
  189.   inherited;
  190. end;
  191.  
  192. procedure TZMDirHashList.Clear;
  193. begin
  194.   DisposeBlocks;
  195.   Chains := nil;  // empty it
  196. end;
  197.  
  198. procedure TZMDirHashList.DisposeBlocks;
  199. var
  200.   TmpBlock: PHDEBlock;
  201. begin
  202.   while fLastBlock <> nil do
  203.   begin
  204.     TmpBlock := fLastBlock;
  205.     fLastBlock := TmpBlock^.Next;
  206.     Dispose(TmpBlock);
  207.   end;
  208.   fBlocks := 0;
  209.   fLastBlock := nil;
  210.   fEmpties := nil;
  211.   fNextEntry := HIGH(Cardinal);
  212. end;
  213.  
  214. function TZMDirHashList.Find(const FileName: String): TZMIRec;
  215. var
  216.   Entry: PHashedDirEntry;
  217.   Hash: Cardinal;
  218.   idx:  Cardinal;
  219. begin
  220.   Result := nil;
  221.   if Chains = nil then
  222.     exit;
  223.   Hash := HashFunc(FileName);
  224.   idx  := Hash mod Cardinal(Length(Chains));
  225.   Entry := Chains[idx];
  226.   // check entries in this chain
  227.   while Entry <> nil do
  228.   begin
  229.     if Same(Entry, Hash, FileName) then
  230.     begin
  231.       Result := Entry.ZRec;
  232.       break;
  233.     end
  234.     else
  235.       Entry := Entry.Next;
  236.   end;
  237. end;
  238.  
  239. function TZMDirHashList.GetEmpty: boolean;
  240. begin
  241.   Result := Chains = nil;
  242. end;
  243.  
  244. // return address in allocated block
  245. function TZMDirHashList.GetEntry: PHashedDirEntry;
  246. var
  247.   TmpBlock: PHDEBlock;
  248. begin
  249.   if fEmpties <> nil then
  250.   begin
  251.     Result := fEmpties;         // last emptied
  252.     fEmpties := fEmpties.Next;
  253.   end
  254.   else
  255.   begin
  256.     if (fBlocks < 1) or (fNextEntry >= HDEBlockEntries) then
  257.     begin
  258.       // we need a new block
  259.       New(TmpBlock);
  260.       ZeroMemory(TmpBlock, sizeof(THDEBlock));
  261.       TmpBlock^.Next := fLastBlock;
  262.       fLastBlock := TmpBlock;
  263.       Inc(fBlocks);
  264.       fNextEntry := 0;
  265.     end;
  266.     Result := @fLastBlock^.Entries[fNextEntry];
  267.     Inc(fNextEntry);
  268.   end;
  269. end;
  270.  
  271. function TZMDirHashList.GetSize: Cardinal;
  272. begin
  273.   Result := Length(Chains);
  274. end;
  275.  
  276. function TZMDirHashList.Remove(const ZDir: TZMIRec): boolean;
  277. var
  278.   Entry: PHashedDirEntry;
  279.   FileName: String;
  280.   Hash: Cardinal;
  281.   idx:  Cardinal;
  282.   Prev: PHashedDirEntry;
  283. begin
  284.   Result := false;
  285.   if (ZDir = nil) or (Chains = nil) then
  286.     exit;
  287.   FileName := ZDir.FileName;
  288.   Hash := ZDir.Hash;
  289.   idx  := Hash mod Cardinal(Length(Chains));
  290.   Entry := Chains[idx];
  291.   Prev := nil;
  292.   while Entry <> nil do
  293.   begin
  294.     if Same(Entry, Hash, FileName) and (Entry.ZRec = ZDir) then
  295.     begin
  296.       // we found it so unlink it
  297.       if Prev = nil then
  298.       begin
  299.         // first in chain
  300.         Chains[idx] := Entry.Next;   // link to next
  301.       end
  302.       else
  303.       begin
  304.         Prev.Next := Entry.Next;   // link to next
  305.       end;
  306.       Entry.Next := fEmpties;    // link to removed
  307.       fEmpties := Entry;
  308.       Entry.ZRec := nil;
  309.       Result := True;
  310.       break;
  311.     end
  312.     else
  313.     begin
  314.       Prev := Entry;
  315.       Entry := Entry.Next;
  316.     end;
  317.   end;
  318. end;
  319.  
  320. function TZMDirHashList.Same(Entry: PHashedDirEntry; Hash: Cardinal; const Str:
  321.     String): Boolean;
  322. var
  323.   IRec: TZMIRec;
  324. begin
  325.   IRec := Entry^.ZRec;
  326.   Result := (Hash = IRec.Hash) and
  327. {$IFDEF UNICODE}
  328.     (FileNameComp(Str, IRec.FileName) = 0);
  329. {$ELSE}                      
  330.     (FileNameComp(Str, IRec.FileName, Worker.UseUTF8) = 0);
  331. {$ENDIF}
  332. end;
  333.  
  334. procedure TZMDirHashList.SetEmpty(const Value: boolean);
  335. begin
  336.   if Value then
  337.     Clear;
  338. end;
  339.  
  340. procedure TZMDirHashList.SetSize(const Value: Cardinal);
  341. var
  342.   TableSize: Integer;
  343. begin
  344.   Clear;
  345.   if Value > 0 then
  346.   begin
  347.     TableSize := Value;
  348.     // keep within reasonable limits
  349.     if TableSize < ChainsMin then
  350.       TableSize := ChainsMin
  351.     else
  352.     if TableSize > ChainsMax then
  353.       TableSize := ChainsMax;
  354.     SetLength(Chains, TableSize);
  355.     ZeroMemory(Chains, Size * sizeof(PHashedDirEntry));
  356.   end;
  357. end;
  358.  
  359. end.
  360.