1 {*******************************************************} 2 { } 3 { Borland Delphi Supplemental Components } 4 { ZLIB Data Compression Interface Unit } 5 { } 6 { Copyright (c) 1997,99 Borland Corporation } 7 { } 8 {*******************************************************} 9 10 { Updated for zlib 1.2.x by Cosmin Truta <cosmint@cs.ubbcluj.ro> } 11 12 unit ZLib; 13 14 interface 15 16 uses SysUtils, Classes; 17 18 type 19 TAlloc = function (AppData: Pointer; Items, Size: Integer): Pointer; cdecl; 20 TFree = procedure (AppData, Block: Pointer); cdecl; 21 22 // Internal structure. Ignore. 23 TZStreamRec = packed record 24 next_in: PChar; // next input byte 25 avail_in: Integer; // number of bytes available at next_in 26 total_in: Longint; // total nb of input bytes read so far 27 28 next_out: PChar; // next output byte should be put here 29 avail_out: Integer; // remaining free space at next_out 30 total_out: Longint; // total nb of bytes output so far 31 32 msg: PChar; // last error message, NULL if no error 33 internal: Pointer; // not visible by applications 34 35 zalloc: TAlloc; // used to allocate the internal state 36 zfree: TFree; // used to free the internal state 37 AppData: Pointer; // private data object passed to zalloc and zfree 38 39 data_type: Integer; // best guess about the data type: ascii or binary 40 adler: Longint; // adler32 value of the uncompressed data 41 reserved: Longint; // reserved for future use 42 end; 43 44 // Abstract ancestor class 45 TCustomZlibStream = class(TStream) 46 private 47 FStrm: TStream; 48 FStrmPos: Integer; 49 FOnProgress: TNotifyEvent; 50 FZRec: TZStreamRec; 51 FBuffer: array [Word] of Char; 52 protected 53 procedure Progress(Sender: TObject); dynamic; 54 property OnProgress: TNotifyEvent read FOnProgress write FOnProgress; 55 constructor Create(Strm: TStream); 56 end; 57 58 { TCompressionStream compresses data on the fly as data is written to it, and 59 stores the compressed data to another stream. 60 61 TCompressionStream is write-only and strictly sequential. Reading from the 62 stream will raise an exception. Using Seek to move the stream pointer 63 will raise an exception. 64 65 Output data is cached internally, written to the output stream only when 66 the internal output buffer is full. All pending output data is flushed 67 when the stream is destroyed. 68 69 The Position property returns the number of uncompressed bytes of 70 data that have been written to the stream so far. 71 72 CompressionRate returns the on-the-fly percentage by which the original 73 data has been compressed: (1 - (CompressedBytes / UncompressedBytes)) * 100 74 If raw data size = 100 and compressed data size = 25, the CompressionRate 75 is 75% 76 77 The OnProgress event is called each time the output buffer is filled and 78 written to the output stream. This is useful for updating a progress 79 indicator when you are writing a large chunk of data to the compression 80 stream in a single call.} 81 82 83 TCompressionLevel = (clNone, clFastest, clDefault, clMax); 84 85 TCompressionStream = class(TCustomZlibStream) 86 private 87 function GetCompressionRate: Single; 88 public 89 constructor Create(CompressionLevel: TCompressionLevel; Dest: TStream); 90 destructor Destroy; override; 91 function Read(var Buffer; Count: Longint): Longint; override; 92 function Write(const Buffer; Count: Longint): Longint; override; 93 function Seek(Offset: Longint; Origin: Word): Longint; override; 94 property CompressionRate: Single read GetCompressionRate; 95 property OnProgress; 96 end; 97 98 { TDecompressionStream decompresses data on the fly as data is read from it. 99 100 Compressed data comes from a separate source stream. TDecompressionStream 101 is read-only and unidirectional; you can seek forward in the stream, but not 102 backwards. The special case of setting the stream position to zero is 103 allowed. Seeking forward decompresses data until the requested position in 104 the uncompressed data has been reached. Seeking backwards, seeking relative 105 to the end of the stream, requesting the size of the stream, and writing to 106 the stream will raise an exception. 107 108 The Position property returns the number of bytes of uncompressed data that 109 have been read from the stream so far. 110 111 The OnProgress event is called each time the internal input buffer of 112 compressed data is exhausted and the next block is read from the input stream. 113 This is useful for updating a progress indicator when you are reading a 114 large chunk of data from the decompression stream in a single call.} 115 116 TDecompressionStream = class(TCustomZlibStream) 117 public 118 constructor Create(Source: TStream); 119 destructor Destroy; override; 120 function Read(var Buffer; Count: Longint): Longint; override; 121 function Write(const Buffer; Count: Longint): Longint; override; 122 function Seek(Offset: Longint; Origin: Word): Longint; override; 123 property OnProgress; 124 end; 125 126 127 128 { CompressBuf compresses data, buffer to buffer, in one call. 129 In: InBuf = ptr to compressed data 130 InBytes = number of bytes in InBuf 131 Out: OutBuf = ptr to newly allocated buffer containing decompressed data 132 OutBytes = number of bytes in OutBuf } 133 procedure CompressBuf(const InBuf: Pointer; InBytes: Integer; 134 out OutBuf: Pointer; out OutBytes: Integer); 135 136 137 { DecompressBuf decompresses data, buffer to buffer, in one call. 138 In: InBuf = ptr to compressed data 139 InBytes = number of bytes in InBuf 140 OutEstimate = zero, or est. size of the decompressed data 141 Out: OutBuf = ptr to newly allocated buffer containing decompressed data 142 OutBytes = number of bytes in OutBuf } 143 procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer; 144 OutEstimate: Integer; out OutBuf: Pointer; out OutBytes: Integer); 145 146 { DecompressToUserBuf decompresses data, buffer to buffer, in one call. 147 In: InBuf = ptr to compressed data 148 InBytes = number of bytes in InBuf 149 Out: OutBuf = ptr to user-allocated buffer to contain decompressed data 150 BufSize = number of bytes in OutBuf } 151 procedure DecompressToUserBuf(const InBuf: Pointer; InBytes: Integer; 152 const OutBuf: Pointer; BufSize: Integer); 153 154 const 155 zlib_version = '1.2.8'; 156 157 type 158 EZlibError = class(Exception); 159 ECompressionError = class(EZlibError); 160 EDecompressionError = class(EZlibError); 161 162 implementation 163 164 uses ZLibConst; 165 166 const 167 Z_NO_FLUSH = 0; 168 Z_PARTIAL_FLUSH = 1; 169 Z_SYNC_FLUSH = 2; 170 Z_FULL_FLUSH = 3; 171 Z_FINISH = 4; 172 173 Z_OK = 0; 174 Z_STREAM_END = 1; 175 Z_NEED_DICT = 2; 176 Z_ERRNO = (-1); 177 Z_STREAM_ERROR = (-2); 178 Z_DATA_ERROR = (-3); 179 Z_MEM_ERROR = (-4); 180 Z_BUF_ERROR = (-5); 181 Z_VERSION_ERROR = (-6); 182 183 Z_NO_COMPRESSION = 0; 184 Z_BEST_SPEED = 1; 185 Z_BEST_COMPRESSION = 9; 186 Z_DEFAULT_COMPRESSION = (-1); 187 188 Z_FILTERED = 1; 189 Z_HUFFMAN_ONLY = 2; 190 Z_RLE = 3; 191 Z_DEFAULT_STRATEGY = 0; 192 193 Z_BINARY = 0; 194 Z_ASCII = 1; 195 Z_UNKNOWN = 2; 196 197 Z_DEFLATED = 8; 198 199 200 {$L adler32.obj} 201 {$L compress.obj} 202 {$L crc32.obj} 203 {$L deflate.obj} 204 {$L infback.obj} 205 {$L inffast.obj} 206 {$L inflate.obj} 207 {$L inftrees.obj} 208 {$L trees.obj} 209 {$L uncompr.obj} 210 {$L zutil.obj} 211 212 procedure adler32; external; 213 procedure compressBound; external; 214 procedure crc32; external; 215 procedure deflateInit2_; external; 216 procedure deflateParams; external; 217 218 function _malloc(Size: Integer): Pointer; cdecl; 219 begin 220 Result := AllocMem(Size); 221 end; 222 223 procedure _free(Block: Pointer); cdecl; 224 begin 225 FreeMem(Block); 226 end; 227 228 procedure _memset(P: Pointer; B: Byte; count: Integer); cdecl; 229 begin 230 FillChar(P^, count, B); 231 end; 232 233 procedure _memcpy(dest, source: Pointer; count: Integer); cdecl; 234 begin 235 Move(source^, dest^, count); 236 end; 237 238 239 240 // deflate compresses data 241 function deflateInit_(var strm: TZStreamRec; level: Integer; version: PChar; 242 recsize: Integer): Integer; external; 243 function deflate(var strm: TZStreamRec; flush: Integer): Integer; external; 244 function deflateEnd(var strm: TZStreamRec): Integer; external; 245 246 // inflate decompresses data 247 function inflateInit_(var strm: TZStreamRec; version: PChar; 248 recsize: Integer): Integer; external; 249 function inflate(var strm: TZStreamRec; flush: Integer): Integer; external; 250 function inflateEnd(var strm: TZStreamRec): Integer; external; 251 function inflateReset(var strm: TZStreamRec): Integer; external; 252 253 254 function zlibAllocMem(AppData: Pointer; Items, Size: Integer): Pointer; cdecl; 255 begin 256 // GetMem(Result, Items*Size); 257 Result := AllocMem(Items * Size); 258 end; 259 260 procedure zlibFreeMem(AppData, Block: Pointer); cdecl; 261 begin 262 FreeMem(Block); 263 end; 264 265 {function zlibCheck(code: Integer): Integer; 266 begin 267 Result := code; 268 if code < 0 then 269 raise EZlibError.Create('error'); //!! 270 end;} 271 272 function CCheck(code: Integer): Integer; 273 begin 274 Result := code; 275 if code < 0 then 276 raise ECompressionError.Create('error'); //!! 277 end; 278 279 function DCheck(code: Integer): Integer; 280 begin 281 Result := code; 282 if code < 0 then 283 raise EDecompressionError.Create('error'); //!! 284 end; 285 286 procedure CompressBuf(const InBuf: Pointer; InBytes: Integer; 287 out OutBuf: Pointer; out OutBytes: Integer); 288 var 289 strm: TZStreamRec; 290 P: Pointer; 291 begin 292 FillChar(strm, sizeof(strm), 0); 293 strm.zalloc := zlibAllocMem; 294 strm.zfree := zlibFreeMem; 295 OutBytes := ((InBytes + (InBytes div 10) + 12) + 255) and not 255; 296 GetMem(OutBuf, OutBytes); 297 try 298 strm.next_in := InBuf; 299 strm.avail_in := InBytes; 300 strm.next_out := OutBuf; 301 strm.avail_out := OutBytes; 302 CCheck(deflateInit_(strm, Z_BEST_COMPRESSION, zlib_version, sizeof(strm))); 303 try 304 while CCheck(deflate(strm, Z_FINISH)) <> Z_STREAM_END do 305 begin 306 P := OutBuf; 307 Inc(OutBytes, 256); 308 ReallocMem(OutBuf, OutBytes); 309 strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P))); 310 strm.avail_out := 256; 311 end; 312 finally 313 CCheck(deflateEnd(strm)); 314 end; 315 ReallocMem(OutBuf, strm.total_out); 316 OutBytes := strm.total_out; 317 except 318 FreeMem(OutBuf); 319 raise 320 end; 321 end; 322 323 324 procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer; 325 OutEstimate: Integer; out OutBuf: Pointer; out OutBytes: Integer); 326 var 327 strm: TZStreamRec; 328 P: Pointer; 329 BufInc: Integer; 330 begin 331 FillChar(strm, sizeof(strm), 0); 332 strm.zalloc := zlibAllocMem; 333 strm.zfree := zlibFreeMem; 334 BufInc := (InBytes + 255) and not 255; 335 if OutEstimate = 0 then 336 OutBytes := BufInc 337 else 338 OutBytes := OutEstimate; 339 GetMem(OutBuf, OutBytes); 340 try 341 strm.next_in := InBuf; 342 strm.avail_in := InBytes; 343 strm.next_out := OutBuf; 344 strm.avail_out := OutBytes; 345 DCheck(inflateInit_(strm, zlib_version, sizeof(strm))); 346 try 347 while DCheck(inflate(strm, Z_NO_FLUSH)) <> Z_STREAM_END do 348 begin 349 P := OutBuf; 350 Inc(OutBytes, BufInc); 351 ReallocMem(OutBuf, OutBytes); 352 strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P))); 353 strm.avail_out := BufInc; 354 end; 355 finally 356 DCheck(inflateEnd(strm)); 357 end; 358 ReallocMem(OutBuf, strm.total_out); 359 OutBytes := strm.total_out; 360 except 361 FreeMem(OutBuf); 362 raise 363 end; 364 end; 365 366 procedure DecompressToUserBuf(const InBuf: Pointer; InBytes: Integer; 367 const OutBuf: Pointer; BufSize: Integer); 368 var 369 strm: TZStreamRec; 370 begin 371 FillChar(strm, sizeof(strm), 0); 372 strm.zalloc := zlibAllocMem; 373 strm.zfree := zlibFreeMem; 374 strm.next_in := InBuf; 375 strm.avail_in := InBytes; 376 strm.next_out := OutBuf; 377 strm.avail_out := BufSize; 378 DCheck(inflateInit_(strm, zlib_version, sizeof(strm))); 379 try 380 if DCheck(inflate(strm, Z_FINISH)) <> Z_STREAM_END then 381 raise EZlibError.CreateRes(@sTargetBufferTooSmall); 382 finally 383 DCheck(inflateEnd(strm)); 384 end; 385 end; 386 387 // TCustomZlibStream 388 389 constructor TCustomZLibStream.Create(Strm: TStream); 390 begin 391 inherited Create; 392 FStrm := Strm; 393 FStrmPos := Strm.Position; 394 FZRec.zalloc := zlibAllocMem; 395 FZRec.zfree := zlibFreeMem; 396 end; 397 398 procedure TCustomZLibStream.Progress(Sender: TObject); 399 begin 400 if Assigned(FOnProgress) then FOnProgress(Sender); 401 end; 402 403 404 // TCompressionStream 405 406 constructor TCompressionStream.Create(CompressionLevel: TCompressionLevel; 407 Dest: TStream); 408 const 409 Levels: array [TCompressionLevel] of ShortInt = 410 (Z_NO_COMPRESSION, Z_BEST_SPEED, Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION); 411 begin 412 inherited Create(Dest); 413 FZRec.next_out := FBuffer; 414 FZRec.avail_out := sizeof(FBuffer); 415 CCheck(deflateInit_(FZRec, Levels[CompressionLevel], zlib_version, sizeof(FZRec))); 416 end; 417 418 destructor TCompressionStream.Destroy; 419 begin 420 FZRec.next_in := nil; 421 FZRec.avail_in := 0; 422 try 423 if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos; 424 while (CCheck(deflate(FZRec, Z_FINISH)) <> Z_STREAM_END) 425 and (FZRec.avail_out = 0) do 426 begin 427 FStrm.WriteBuffer(FBuffer, sizeof(FBuffer)); 428 FZRec.next_out := FBuffer; 429 FZRec.avail_out := sizeof(FBuffer); 430 end; 431 if FZRec.avail_out < sizeof(FBuffer) then 432 FStrm.WriteBuffer(FBuffer, sizeof(FBuffer) - FZRec.avail_out); 433 finally 434 deflateEnd(FZRec); 435 end; 436 inherited Destroy; 437 end; 438 439 function TCompressionStream.Read(var Buffer; Count: Longint): Longint; 440 begin 441 raise ECompressionError.CreateRes(@sInvalidStreamOp); 442 end; 443 444 function TCompressionStream.Write(const Buffer; Count: Longint): Longint; 445 begin 446 FZRec.next_in := @Buffer; 447 FZRec.avail_in := Count; 448 if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos; 449 while (FZRec.avail_in > 0) do 450 begin 451 CCheck(deflate(FZRec, 0)); 452 if FZRec.avail_out = 0 then 453 begin 454 FStrm.WriteBuffer(FBuffer, sizeof(FBuffer)); 455 FZRec.next_out := FBuffer; 456 FZRec.avail_out := sizeof(FBuffer); 457 FStrmPos := FStrm.Position; 458 Progress(Self); 459 end; 460 end; 461 Result := Count; 462 end; 463 464 function TCompressionStream.Seek(Offset: Longint; Origin: Word): Longint; 465 begin 466 if (Offset = 0) and (Origin = soFromCurrent) then 467 Result := FZRec.total_in 468 else 469 raise ECompressionError.CreateRes(@sInvalidStreamOp); 470 end; 471 472 function TCompressionStream.GetCompressionRate: Single; 473 begin 474 if FZRec.total_in = 0 then 475 Result := 0 476 else 477 Result := (1.0 - (FZRec.total_out / FZRec.total_in)) * 100.0; 478 end; 479 480 481 // TDecompressionStream 482 483 constructor TDecompressionStream.Create(Source: TStream); 484 begin 485 inherited Create(Source); 486 FZRec.next_in := FBuffer; 487 FZRec.avail_in := 0; 488 DCheck(inflateInit_(FZRec, zlib_version, sizeof(FZRec))); 489 end; 490 491 destructor TDecompressionStream.Destroy; 492 begin 493 FStrm.Seek(-FZRec.avail_in, 1); 494 inflateEnd(FZRec); 495 inherited Destroy; 496 end; 497 498 function TDecompressionStream.Read(var Buffer; Count: Longint): Longint; 499 begin 500 FZRec.next_out := @Buffer; 501 FZRec.avail_out := Count; 502 if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos; 503 while (FZRec.avail_out > 0) do 504 begin 505 if FZRec.avail_in = 0 then 506 begin 507 FZRec.avail_in := FStrm.Read(FBuffer, sizeof(FBuffer)); 508 if FZRec.avail_in = 0 then 509 begin 510 Result := Count - FZRec.avail_out; 511 Exit; 512 end; 513 FZRec.next_in := FBuffer; 514 FStrmPos := FStrm.Position; 515 Progress(Self); 516 end; 517 CCheck(inflate(FZRec, 0)); 518 end; 519 Result := Count; 520 end; 521 522 function TDecompressionStream.Write(const Buffer; Count: Longint): Longint; 523 begin 524 raise EDecompressionError.CreateRes(@sInvalidStreamOp); 525 end; 526 527 function TDecompressionStream.Seek(Offset: Longint; Origin: Word): Longint; 528 var 529 I: Integer; 530 Buf: array [0..4095] of Char; 531 begin 532 if (Offset = 0) and (Origin = soFromBeginning) then 533 begin 534 DCheck(inflateReset(FZRec)); 535 FZRec.next_in := FBuffer; 536 FZRec.avail_in := 0; 537 FStrm.Position := 0; 538 FStrmPos := 0; 539 end 540 else if ( (Offset >= 0) and (Origin = soFromCurrent)) or 541 ( ((Offset - FZRec.total_out) > 0) and (Origin = soFromBeginning)) then 542 begin 543 if Origin = soFromBeginning then Dec(Offset, FZRec.total_out); 544 if Offset > 0 then 545 begin 546 for I := 1 to Offset div sizeof(Buf) do 547 ReadBuffer(Buf, sizeof(Buf)); 548 ReadBuffer(Buf, Offset mod sizeof(Buf)); 549 end; 550 end 551 else 552 raise EDecompressionError.CreateRes(@sInvalidStreamOp); 553 Result := FZRec.total_out; 554 end; 555 556 557 end.