Subversion Repositories autosfx

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 daniel-mar 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.