-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsggzip.pas
365 lines (315 loc) · 9.77 KB
/
sggzip.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
unit sggzip;
{$WARN UNSAFE_TYPE OFF}
{$WARN UNSAFE_CODE OFF}
interface
uses
SysUtils, Classes, gzIO, ZUtil, sgstreams;
const
// Large buffer significantly improves speed - regardless of
// kind of working streams!
cDefZipBuffer=1024*256;
type
TZipCompressionLevel=0..9;
TZipProgressEvent=procedure(Sender: TObject; DoneBytes: integer) of object;
// This is a Universal Compression/Decompression Engine :)
// TGZip is a perfect solution if you wnat to compress or decompress
// something at once.
TGZip=class
protected
buf: Pointer;
BUFLEN: cardinal;
vOnProgress: TZipProgressEvent;
procedure AllocBuf(sz: cardinal);
// Low level (Stream<->gzFile)...
procedure Compress(infile: TStream; outfile: gzFile); overload;
procedure UnCompress(infile: gzFile; outfile: TStream); overload;
public
GZ_SUFFIX: string;
EraseSource: boolean;
CompressionLevel: 0..9;
constructor Create; overload;
constructor Create(bufsz: cardinal); overload;
procedure Free;
// "Normal" level (Stream<->Stream)...
procedure Compress(infile: TStream; outfile: TStream); overload;
procedure UnCompress(infile: TStream; outfile: TStream); overload;
// High level (Filename<->Filename)...
procedure Compress(inf, outf: string); overload;
procedure UnCompress(inf, outf: string); overload;
// These two functions work as real gzip:
// when file is compressed, GZ_SUFFIX added
// when file is decompressed, GZ_SUFFIX is removed
// (if there is no GZ_SUFFIX, an exception is raised).
procedure Compress(filename: string); overload;
procedure UnCompress(filename: string); overload;
// Next two functions do the same as Compress/UnCompress, but also
// add/check StreamBlockHeader, which allows to write a number
// of objects into one stream. Temporary TMemoryStream is used.
procedure CompressAsBlock(inpstr, blockstr: TStream);
procedure UnCompressFromBlock(blockstr, outstr: TStream);
property BufferSize: cardinal read BUFLEN write AllocBuf;
// This event notifies about total bytes processed from input stream.
// If program needs to know %done, it should calculate it itself
// from <input-stream>.Size and DoneBytes.
property OnProgress: TZipProgressEvent read vOnProgress write vOnProgress;
end;
// I/O classes below (on-the-fly compression and decompression)
// can help to reduce memory usage in some cases.
// They don't offer any additional buffering and can be used on top of
// only one Stream object (associated with any compressed storage).
// This is just a base class for TGZipStream and TGUnzipStream.
// s parameter of Create is a stream which will be associated with
// [underlying] input/output target of the stream, e.g. TFileStream.
// It is not closed in this classes and must be freed somewhere else!
TGZipStrClass=class(TStream)
protected
gzf: gzFile;
Pos: int64;
public
constructor Create(s: TStream); virtual;
procedure Free; virtual;
function Seek(Offset: Longint; Origin: Word): Longint; override;
property Position: int64 read Pos;
// Below are stubs
function Write(const Buffer; Count: Longint): Longint; override;
function Read(var Buffer; Count: Longint): Longint; override;
end;
// Compression stream, output only.
// Large blocks in Write are highly recommended!
TGZipStream=class(TGZipStrClass)
public
constructor Create(s: TStream); overload; override;
constructor CreateC(s: TStream; Compression: TZipCompressionLevel); overload;
function Write(const Buffer; Count: Longint): Longint; override;
end;
// Decompression stream, input only.
// Large blocks in Read are highly recommended!
TGUnzipStream=class(TGZipStrClass)
public
constructor Create(s: TStream); override;
function Read(var Buffer; Count: Longint): Longint; override;
end;
implementation
procedure TGZip.AllocBuf(sz: cardinal);
begin
if Assigned(buf) then FreeMem(buf);
GetMem(buf, sz);
BUFLEN:=sz;
end;
constructor TGZip.Create;
begin
Create(cDefZipBuffer);
end;
constructor TGZip.Create(bufsz: cardinal);
begin
inherited Create;
vOnProgress:=nil;
GZ_SUFFIX:='.gz';
EraseSource:=false;
CompressionLevel:=9;
buf:=nil;
AllocBuf(bufsz);
end;
procedure TGZip.Free;
begin
FreeMem(buf);
inherited Free;
end;
procedure TGZip.Compress(infile: TStream; outfile: gzFile);
var total, len, err: integer;
begin
total:=0;
try
while true do begin
//blockread(infile, buf, BUFLEN, len);
len:=infile.Read(buf^, BUFLEN);
total:=total+len;
if Assigned(vOnProgress) then vOnProgress(Self, total);
if len=0 then break;
if gzwrite(outfile, buf, len)<>len then
raise Exception.Create('gzwrite error: '+gzerror(outfile, err));
end; {WHILE}
except
on E: Exception do raise Exception.Create('Compress(Stream->gzFile): '+E.Message);
end;
end;
procedure TGZip.UnCompress(infile: gzFile; outfile: TStream);
var len, total, written, err: integer;
begin
total:=0;
try
while true do begin
len:=gzread(infile, buf, BUFLEN);
if len<0 then raise Exception.Create(gzerror(infile, err));
total:=total+len;
if Assigned(vOnProgress) then vOnProgress(Self, total);
if len=0 then break;
//blockwrite (outfile, buf, len, written);
written:=outfile.Write(buf^, len);
if written<>len then
raise Exception.Create('write error');
end; {WHILE}
except
on E: Exception do raise Exception.Create('Uncompress(gzFile->Stream): '+E.Message);
end;
end;
procedure TGZip.Compress(infile: TStream; outfile: TStream);
var outgzfile : gzFile; mode: string;
begin
try
mode:='w'+intToStr(CompressionLevel);
outgzfile:=gzopen(outfile, mode, false);
if outgzfile=nil then raise Exception.Create('can''t gzopen');
try
Compress(infile, outgzfile); // calling lower-level function
finally
if (gzclose(outgzfile) <> 0{Z_OK}) then
raise Exception.Create('gzclose error');
end;
except
on E: Exception do raise Exception.Create('Compress(Stream->Stream): '+E.Message);
end;
end;
procedure TGZip.UnCompress(infile: TStream; outfile: TStream);
var ingzfile: gzFile;
begin
try
ingzfile:=gzopen(infile, 'r', false);
if ingzfile=nil then raise Exception.Create('can''t gzopen');
try
Uncompress (ingzfile, outfile); // calling lower-level function
finally
if (gzclose (ingzfile) <> 0{Z_OK}) then raise Exception.Create('gzclose error');
end;
except
on E: Exception do raise Exception.Create('UnCompress(Stream->Stream): '+E.Message);
end;
end;
procedure TGZip.Compress(inf, outf: string);
var infile, outfile: TFileStream;
begin
try
infile:=TFileStream.Create(inf, fmOpenRead);
try
outfile:=TFileStream.Create(outf, fmCreate);
try
Compress(infile, outfile);
if EraseSource then DeleteFile(inf);
finally
outfile.Free;
end;
finally
infile.Free;
end;
except
on E: Exception do raise Exception.Create('Compress(File->File): '+E.Message);
end;
end;
procedure TGZip.UnCompress(inf, outf: string);
var infile, outfile: TFileStream;
begin
try
infile:=TFileStream.Create(inf, fmOpenRead);
try
outfile:=TFileStream.Create(outf, fmCreate);
try
Uncompress (infile, outfile);
if EraseSource then DeleteFile(inf);
finally
outfile.Free;
end;
finally
infile.Free;
end;
except
on E: Exception do raise Exception.Create('UnCompress(File->File): '+E.Message);
end;
end;
procedure TGZip.Compress(filename: string);
begin
Compress(filename, filename+GZ_SUFFIX);
end;
procedure TGZip.UnCompress(filename: string);
var l, g: integer;
begin
l:=Length(filename);
g:=Length(GZ_SUFFIX);
if Copy(filename, l-g+1, g)<>GZ_SUFFIX then
raise Exception.Create('UnCompress(gzip mode): file '+filename+' doesn''t have '+GZ_SUFFIX+' suffix.')
else
UnCompress(filename, Copy(filename, 1, l-g));
end;
procedure TGZip.CompressAsBlock(inpstr, blockstr: TStream);
var tmp: TStreamBlock;
begin
tmp:=TStreamBlock.Create;
try
Compress(inpstr, tmp);
tmp.SaveToStream(blockstr);
finally
tmp.Free;
end;
end;
procedure TGZip.UnCompressFromBlock(blockstr, outstr: TStream);
var tmp: TStreamBlock;
begin
tmp:=TStreamBlock.Create;
try
tmp.LoadFromStream(blockstr);
UnCompress(tmp, outstr);
finally
tmp.Free;
end;
end;
constructor TGZipStrClass.Create(s: TStream);
begin
inherited Create;
end;
procedure TGZipStrClass.Free;
begin
if (gzclose(gzf) <> 0{Z_OK}) then
raise Exception.Create('gzclose error!');
inherited Free;
end;
function TGZipStrClass.Seek(Offset: Longint; Origin: Word): Longint;
begin
Result:=Position;
end;
function TGZipStrClass.Write(const Buffer; Count: Longint): Longint;
begin
Result:=0;
end;
function TGZipStrClass.Read(var Buffer; Count: Longint): Longint;
begin
Result:=0;
end;
constructor TGZipStream.CreateC(s: TStream; Compression: TZipCompressionLevel);
begin
inherited Create(s);
Pos:=0;
gzf:=gzopen(s, 'w'+IntToStr(Compression), false);
if gzf=nil then raise Exception.Create('Can''t gzopen.');
end;
constructor TGZipStream.Create(s: TStream);
begin
CreateC(s, 9);
end;
function TGZipStream.Write(const Buffer; Count: Longint): Longint;
begin
Pos:=Pos+Count;
Result:=gzwrite(gzf, @Buffer, Count);
end;
constructor TGUnzipStream.Create(s: TStream);
begin
inherited Create(s);
gzf:=gzopen(s, 'r', false);
if gzf=nil then raise Exception.Create('Can''t gzopen.');
end;
function TGUnzipStream.Read(var Buffer; Count: Longint): Longint;
var err: integer;
begin
Pos:=Pos+Count;
Result:=gzread(gzf, @Buffer, Count);
if Result<0 then raise Exception.Create(gzerror(gzf, err));
end;
end.