zstream.pp 12 KB

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