zstream.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. unit zstream;
  2. {
  3. $Id$
  4. This file is part of the Free Pascal run time library.
  5. Copyright (c) 1998 by the Free Pascal development team
  6. Implementation of compression streams.
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. { ---------------------------------------------------------------------
  14. On linux, the default is to use the zlib libraries.
  15. On all other platforms we use paszlib. If you want to use
  16. paszlib in all cases, just define -dUsePasZlib
  17. ---------------------------------------------------------------------}
  18. {$ifndef linux}
  19. {$define usepaszlib}
  20. {$endif}
  21. interface
  22. uses Sysutils, Classes,zlib
  23. {$ifdef usepaszlib}
  24. ,gzio,ZUtil,ZInflate,ZDeflate
  25. {$endif}
  26. ;
  27. {$H+}
  28. type
  29. // Error reporting.
  30. {$ifdef usepaszlib}
  31. TZStream=Z_Stream;
  32. PZStream=Z_StreamP;
  33. {$endif}
  34. EZlibError = class(EStreamError);
  35. ECompressionError = class(EZlibError);
  36. EDecompressionError = class(EZlibError);
  37. TCustomZlibStream = class(TStream)
  38. private
  39. FStrm: TStream;
  40. FStrmPos: Integer;
  41. FOnProgress: TNotifyEvent;
  42. FZRec: TZStream;
  43. FBuffer: array [Word] of Char;
  44. protected
  45. procedure Progress(Sender: TObject); dynamic;
  46. property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
  47. constructor Create(Strm: TStream);
  48. end;
  49. TCompressionLevel = (clNone, clFastest, clDefault, clMax);
  50. TCompressionStream = class(TCustomZlibStream)
  51. private
  52. function GetCompressionRate: extended;
  53. function CompressionCheck(code: Integer): Integer;
  54. procedure CompressBuf(const InBuf: Pointer; InBytes: Integer;
  55. var OutBuf: Pointer; var OutBytes: Integer);
  56. public
  57. constructor Create(CompressionLevel: TCompressionLevel; Dest: TStream);
  58. destructor Destroy; override;
  59. function Read(var Buffer; Count: Longint): Longint; override;
  60. function Write(const Buffer; Count: Longint): Longint; override;
  61. function Seek(Offset: Longint; Origin: Word): Longint; override;
  62. property CompressionRate: extended read GetCompressionRate;
  63. property OnProgress;
  64. end;
  65. TDecompressionStream = class(TCustomZlibStream)
  66. private
  67. function DecompressionCheck(code: Integer): Integer;
  68. procedure DecompressBuf(const InBuf: Pointer; InBytes: Integer;
  69. OutEstimate: Integer; var OutBuf: Pointer; var OutBytes: Integer);
  70. public
  71. constructor Create(Source: TStream);
  72. destructor Destroy; override;
  73. function Read(var Buffer; Count: Longint): Longint; override;
  74. function Write(const Buffer; Count: Longint): Longint; override;
  75. function Seek(Offset: Longint; Origin: Word): Longint; override;
  76. property OnProgress;
  77. end;
  78. TGZOpenMode = (gzOpenRead,gzOpenWrite);
  79. TGZFileStream = Class(TStream)
  80. Private
  81. FOpenMode : TGZOpenmode;
  82. FFIle : gzfile;
  83. Public
  84. Constructor Create(FileName: String;FileMode: TGZOpenMode);
  85. Destructor Destroy;override;
  86. Function Read(Var Buffer; Count : longint): longint;override;
  87. function Write(const Buffer; Count: Longint): Longint; override;
  88. function Seek(Offset: Longint; Origin: Word): Longint; override;
  89. end;
  90. implementation
  91. Const
  92. ErrorStrings : array [0..6] of string =
  93. ('Unknown error %d','Z_ERRNO','Z_STREAM_ERROR',
  94. 'Z_DATA_ERROR','Z_MEM_ERROR','Z_BUF_ERROR','Z_VERSION_ERROR');
  95. SCouldntOpenFile = 'Couldn''t open file : %s';
  96. SReadOnlyStream = 'Decompression streams are read-only';
  97. SWriteOnlyStream = 'Compression streams are write-only';
  98. SSeekError = 'Compression stream seek error';
  99. SInvalidSeek = 'Invalid Compression seek operation';
  100. Type PLongint = ^Longint;
  101. Function DGetmem (Size : Longint) : pointer;
  102. begin
  103. Inc(Size,SizeOf(Longint));
  104. GetMem(Result,Size);
  105. If Result<>Nil then
  106. begin
  107. Plongint(Result)^:=Size;
  108. Inc(Result,SizeOf(Integer));
  109. end;
  110. end;
  111. Procedure DFreeMem(P : Pointer);
  112. begin
  113. // Get Stored length
  114. Dec(P,SizeOf(Integer));
  115. FreeMem(P,Plongint(P)^);
  116. end;
  117. Procedure DReallocMem (var P : Pointer; NewSize : Longint);
  118. // Reallocates memory pointed to by P.
  119. Var T : pointer;
  120. OldSize : longint;
  121. begin
  122. // Should raise an exception if no memory.
  123. T:=DGetMem(NewSize);
  124. OldSize:=PLongint(P-SizeOf(Integer))^;
  125. If oldSize<NewSize then
  126. Move(P^,T^,OldSize)
  127. else
  128. Move(P^,T^,NewSize);
  129. DFreeMem(P);
  130. end;
  131. function zlibAllocMem(opaque:pointer; items:uInt; size:uInt):pointer;cdecl;
  132. begin
  133. Result:=DGetMem(Items*Size);
  134. end;
  135. procedure zlibFreeMem(opaque:pointer; address:pointer);cdecl;
  136. begin
  137. DFreeMem(address);
  138. end;
  139. procedure TCompressionStream.CompressBuf(const InBuf: Pointer; InBytes: Integer;
  140. var OutBuf: Pointer; var OutBytes: Integer);
  141. var
  142. strm: TZStream;
  143. P: Pointer;
  144. oldout : longint;
  145. begin
  146. FillChar(strm, sizeof(strm), 0);
  147. {$ifndef usepaszlib}
  148. strm.zalloc := @zlibAllocMem;
  149. strm.zfree := @zlibFreeMem;
  150. {$else}
  151. strm.zalloc := @zcalloc;
  152. strm.zfree := @zcfree;
  153. {$endif}
  154. OutBytes := ((InBytes + (InBytes div 10) + 12) + 255) and not 255;
  155. OutBuf:=DGetMem(OutBytes);
  156. try
  157. strm.next_in := InBuf;
  158. strm.avail_in := InBytes;
  159. strm.next_out := OutBuf;
  160. strm.avail_out := OutBytes;
  161. {$ifndef usepaszlib}
  162. CompressionCheck(deflateInit_(strm, Z_BEST_COMPRESSION, zlibversion, sizeof(strm)));
  163. {$else}
  164. CompressionCheck(deflateInit_(@strm, Z_BEST_COMPRESSION, zlibversion, sizeof(strm)));
  165. {$endif}
  166. try
  167. while CompressionCheck(deflate(strm, Z_FINISH)) <> Z_STREAM_END do
  168. begin
  169. P := OutBuf;
  170. Inc(OutBytes, 256);
  171. DReallocMem(OutBuf,OutBytes);
  172. {$ifndef usepaszlib}
  173. strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
  174. {$else}
  175. strm.next_out := PByteF(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
  176. {$endif}
  177. strm.avail_out := 256;
  178. end;
  179. finally
  180. CompressionCheck(deflateEnd(strm));
  181. end;
  182. DReallocMem(OutBuf,strm.total_out);
  183. OutBytes := strm.total_out;
  184. except
  185. DFreeMem(OutBuf);
  186. raise;
  187. end;
  188. end;
  189. procedure TDecompressionStream.DecompressBuf(const InBuf: Pointer; InBytes: Integer;
  190. OutEstimate: Integer; var OutBuf: Pointer; var OutBytes: Integer);
  191. var
  192. strm: TZStream;
  193. P: Pointer;
  194. BufInc: Integer;
  195. begin
  196. FillChar(strm, sizeof(strm), 0);
  197. {$ifndef usepaszlib}
  198. strm.zalloc := @zlibAllocMem;
  199. strm.zfree := @zlibFreeMem;
  200. {$else}
  201. strm.zalloc := @zcalloc;
  202. strm.zfree := @zcfree;
  203. {$endif}
  204. BufInc := (InBytes + 255) and not 255;
  205. if OutEstimate = 0 then
  206. OutBytes := BufInc
  207. else
  208. OutBytes := OutEstimate;
  209. OutBuf:=DGetMem(OutBytes);
  210. try
  211. strm.next_in := InBuf;
  212. strm.avail_in := InBytes;
  213. strm.next_out := OutBuf;
  214. strm.avail_out := OutBytes;
  215. {$ifndef usepaszlib}
  216. DecompressionCheck(inflateInit_(strm, zlibversion, sizeof(strm)));
  217. {$else}
  218. DecompressionCheck(inflateInit_(@strm, zlibversion, sizeof(strm)));
  219. {$endif}
  220. try
  221. while DecompressionCheck(inflate(strm, Z_FINISH)) <> Z_STREAM_END do
  222. begin
  223. P := OutBuf;
  224. Inc(OutBytes, BufInc);
  225. DReallocMem(OutBuf, OutBytes);
  226. {$ifndef usepaszlib}
  227. strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
  228. {$else}
  229. strm.next_out := Pbytef(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
  230. {$endif}
  231. strm.avail_out := BufInc;
  232. end;
  233. finally
  234. DecompressionCheck(inflateEnd(strm));
  235. end;
  236. DReallocMem(OutBuf, strm.total_out);
  237. OutBytes := strm.total_out;
  238. except
  239. DFreeMem(OutBuf);
  240. raise;
  241. end;
  242. end;
  243. // TCustomZlibStream
  244. constructor TCustomZLibStream.Create(Strm: TStream);
  245. begin
  246. inherited Create;
  247. FStrm := Strm;
  248. FStrmPos := Strm.Position;
  249. {$ifndef usepaszlib}
  250. FZRec.zalloc := @zlibAllocMem;
  251. FZRec.zfree := @zlibFreeMem;
  252. {$else}
  253. FZRec.zalloc := @zcalloc;
  254. FZRec.zfree := @zcfree;
  255. {$endif}
  256. end;
  257. procedure TCustomZLibStream.Progress(Sender: TObject);
  258. begin
  259. if Assigned(FOnProgress) then FOnProgress(Sender);
  260. end;
  261. // TCompressionStream
  262. constructor TCompressionStream.Create(CompressionLevel: TCompressionLevel;
  263. Dest: TStream);
  264. const
  265. Levels: array [TCompressionLevel] of ShortInt =
  266. (Z_NO_COMPRESSION, Z_BEST_SPEED, Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION);
  267. begin
  268. inherited Create(Dest);
  269. {$ifndef usepaszlib}
  270. FZRec.next_out := FBuffer;
  271. FZRec.avail_out := sizeof(FBuffer);
  272. CompressionCheck(deflateInit_(FZRec, Levels[CompressionLevel], zlibversion, sizeof(FZRec)));
  273. {$else}
  274. FZRec.next_out :=@FBuffer;
  275. FZRec.avail_out := sizeof(FBuffer);
  276. CompressionCheck(deflateInit_(@FZRec, Levels[CompressionLevel], zlibversion, sizeof(FZRec)));
  277. {$endif}
  278. end;
  279. destructor TCompressionStream.Destroy;
  280. begin
  281. FZRec.next_in := nil;
  282. FZRec.avail_in := 0;
  283. try
  284. if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
  285. while (CompressionCheck(deflate(FZRec, Z_FINISH)) <> Z_STREAM_END)
  286. and (FZRec.avail_out = 0) do
  287. begin
  288. FStrm.WriteBuffer(FBuffer, sizeof(FBuffer));
  289. {$ifndef usepaszlib}
  290. FZRec.next_out := FBuffer;
  291. {$else}
  292. FZRec.next_out := @FBuffer;
  293. {$endif}
  294. FZRec.avail_out := sizeof(FBuffer);
  295. end;
  296. if FZRec.avail_out < sizeof(FBuffer) then
  297. FStrm.WriteBuffer(FBuffer, sizeof(FBuffer) - FZRec.avail_out);
  298. finally
  299. deflateEnd(FZRec);
  300. end;
  301. inherited Destroy;
  302. end;
  303. function TCompressionStream.CompressionCheck(code: Integer): Integer;
  304. begin
  305. Result := code;
  306. if (code < 0) then
  307. if code < -6 then
  308. raise ECompressionError.CreateFmt(Errorstrings[0],[Code])
  309. else
  310. raise ECompressionError.Create(ErrorStrings[Abs(Code)]);
  311. end;
  312. function TCompressionStream.Read(var Buffer; Count: Longint): Longint;
  313. begin
  314. raise ECompressionError.Create('Invalid stream operation');
  315. end;
  316. function TCompressionStream.Write(const Buffer; Count: Longint): Longint;
  317. begin
  318. FZRec.next_in := @Buffer;
  319. FZRec.avail_in := Count;
  320. if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
  321. while (FZRec.avail_in > 0) do
  322. begin
  323. CompressionCheck(deflate(FZRec, 0));
  324. if FZRec.avail_out = 0 then
  325. begin
  326. FStrm.WriteBuffer(FBuffer, sizeof(FBuffer));
  327. {$ifndef usepaszlib}
  328. FZRec.next_out := FBuffer;
  329. {$else}
  330. FZRec.next_out := @FBuffer;
  331. {$endif}
  332. FZRec.avail_out := sizeof(FBuffer);
  333. FStrmPos := FStrm.Position;
  334. Progress(Self);
  335. end;
  336. end;
  337. Result := Count;
  338. end;
  339. function TCompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
  340. begin
  341. if (Offset = 0) and (Origin = soFromCurrent) then
  342. Result := FZRec.total_in
  343. else
  344. raise ECompressionError.Create(SInvalidSeek);
  345. end;
  346. function TCompressionStream.GetCompressionRate: extended;
  347. begin
  348. Result:=0.0;
  349. { With FZrec do
  350. if total_in = 0 then
  351. GetCompressionRate:=0.0
  352. else
  353. GetCompressionRate:=1.0E2*(1.0E0-(total_out/total_in));
  354. }
  355. end;
  356. // TDecompressionStream
  357. constructor TDecompressionStream.Create(Source: TStream);
  358. begin
  359. inherited Create(Source);
  360. {$ifndef usepaszlib}
  361. FZRec.next_in := FBuffer;
  362. FZRec.avail_in := 0;
  363. DecompressionCheck(inflateInit_(FZRec, zlibversion, sizeof(FZRec)));
  364. {$else}
  365. FZRec.next_in := @FBuffer;
  366. FZRec.avail_in := 0;
  367. DecompressionCheck(inflateInit_(@FZRec, zlibversion, sizeof(FZRec)));
  368. {$endif}
  369. end;
  370. destructor TDecompressionStream.Destroy;
  371. begin
  372. inflateEnd(FZRec);
  373. inherited Destroy;
  374. end;
  375. function TDecompressionStream.DecompressionCheck(code: Integer): Integer;
  376. begin
  377. Result := code;
  378. If Code<0 then
  379. if code < -6 then
  380. raise EDecompressionError.CreateFmt(Errorstrings[0],[Code])
  381. else
  382. raise EDecompressionError.Create(ErrorStrings[Abs(Code)]);
  383. end;
  384. function TDecompressionStream.Read(var Buffer; Count: Longint): Longint;
  385. begin
  386. FZRec.next_out := @Buffer;
  387. FZRec.avail_out := Count;
  388. if FStrm.Position <> FStrmPos then FStrm.Position := FStrmPos;
  389. while (FZRec.avail_out > 0) do
  390. begin
  391. if FZRec.avail_in = 0 then
  392. begin
  393. FZRec.avail_in := FStrm.Read(FBuffer, sizeof(FBuffer));
  394. if FZRec.avail_in = 0 then
  395. begin
  396. Result := Count - FZRec.avail_out;
  397. Exit;
  398. end;
  399. {$ifndef usepaszlib}
  400. FZRec.next_in := FBuffer;
  401. {$else}
  402. FZRec.next_in := @FBuffer;
  403. {$endif}
  404. FStrmPos := FStrm.Position;
  405. Progress(Self);
  406. end;
  407. DeCompressionCheck(inflate(FZRec, 0));
  408. end;
  409. Result := Count;
  410. end;
  411. function TDecompressionStream.Write(const Buffer; Count: Longint): Longint;
  412. begin
  413. raise EDecompressionError.Create('Invalid stream operation');
  414. end;
  415. function TDecompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
  416. var
  417. I: Integer;
  418. Buf: array [0..4095] of Char;
  419. begin
  420. if (Offset = 0) and (Origin = soFromBeginning) then
  421. begin
  422. DecompressionCheck(inflateReset(FZRec));
  423. {$ifndef usepaszlib}
  424. FZRec.next_in := FBuffer;
  425. {$else}
  426. FZRec.next_in := @FBuffer;
  427. {$endif}
  428. FZRec.avail_in := 0;
  429. FStrm.Position := 0;
  430. FStrmPos := 0;
  431. end
  432. else if ( (Offset >= 0) and (Origin = soFromCurrent)) or
  433. ( ((Offset - FZRec.total_out) > 0) and (Origin = soFromBeginning)) then
  434. begin
  435. if Origin = soFromBeginning then Dec(Offset, FZRec.total_out);
  436. if Offset > 0 then
  437. begin
  438. for I := 1 to Offset div sizeof(Buf) do
  439. ReadBuffer(Buf, sizeof(Buf));
  440. ReadBuffer(Buf, Offset mod sizeof(Buf));
  441. end;
  442. end
  443. else
  444. raise EDecompressionError.Create(SInvalidSeek);
  445. Result := FZRec.total_out;
  446. end;
  447. // TGZFileStream
  448. Constructor TGZFileStream.Create(FileName: String;FileMode: TGZOpenMode);
  449. Const OpenStrings : array[TGZOpenMode] of pchar = ('rb','wb');
  450. begin
  451. FOpenMode:=FileMode;
  452. FFile:=gzopen (Pchar(FileName),Openstrings[FileMode]);
  453. If FFile=Nil then
  454. Raise ezlibError.CreateFmt (SCouldntOpenFIle,[FileName]);
  455. end;
  456. Destructor TGZFileStream.Destroy;
  457. begin
  458. gzclose(FFile);
  459. Inherited Destroy;
  460. end;
  461. Function TGZFileStream.Read(Var Buffer; Count : longint): longint;
  462. begin
  463. If FOpenMode=gzOpenWrite then
  464. Raise ezliberror.create(SWriteOnlyStream);
  465. Result:=gzRead(FFile,@Buffer,Count);
  466. end;
  467. function TGZFileStream.Write(const Buffer; Count: Longint): Longint;
  468. begin
  469. If FOpenMode=gzOpenRead then
  470. Raise EzlibError.Create(SReadonlyStream);
  471. Result:=gzWrite(FFile,@Buffer,Count);
  472. end;
  473. function TGZFileStream.Seek(Offset: Longint; Origin: Word): Longint;
  474. begin
  475. Result:=gzseek(FFile,Offset,Origin);
  476. If Result=-1 then
  477. Raise eZlibError.Create(SSeekError);
  478. end;
  479. end.