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. |