zstream.pp 12 KB

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