zstream.pp 12 KB

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