zstream.pp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  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; ASkipHeader : Boolean = False);
  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[0];
  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[0];
  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[0];
  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; ASkipHeader : Boolean = False);
  268. begin
  269. inherited Create(ASource);
  270. FZRec.next_in := @FBuffer[0];
  271. If ASkipHeader then
  272. DeCompressionCheck(inflateInit2(FZRec,-MAX_WBITS))
  273. else
  274. DeCompressionCheck(inflateInit(FZRec));
  275. end;
  276. destructor TDecompressionStream.Destroy;
  277. begin
  278. if FZRec.avail_in <> 0 then
  279. Source.Seek(-FZRec.avail_in, soFromCurrent);
  280. inflateEnd(FZRec);
  281. inherited Destroy;
  282. end;
  283. function TDecompressionStream.DecompressionCheck(code: Integer): Integer;
  284. begin
  285. Result := code;
  286. If Code<0 then
  287. if code < -6 then
  288. raise EDecompressionError.CreateFmt(Errorstrings[0],[Code])
  289. else
  290. raise EDecompressionError.Create(ErrorStrings[Abs(Code)]);
  291. end;
  292. function TDecompressionStream.Read(var Buffer; Count: Longint): Longint;
  293. begin
  294. FZRec.next_out := @Buffer;
  295. FZRec.avail_out := Count;
  296. if Source.Position <> FStrmPos then Source.Position := FStrmPos;
  297. while (FZRec.avail_out > 0) do
  298. begin
  299. if FZRec.avail_in = 0 then
  300. begin
  301. FZRec.avail_in := Source.Read(FBuffer, sizeof(FBuffer));
  302. if FZRec.avail_in = 0 then
  303. begin
  304. Result := Count - FZRec.avail_out;
  305. Exit;
  306. end;
  307. FZRec.next_in := @FBuffer[0];
  308. FStrmPos := Source.Position;
  309. Progress(Self);
  310. end;
  311. if DeCompressionCheck(inflate(FZRec, 0)) = Z_STREAM_END then
  312. begin
  313. Result := Count - FZRec.avail_out;
  314. Exit;
  315. end;
  316. end;
  317. Result := Count;
  318. end;
  319. function TDecompressionStream.Write(const Buffer; Count: Longint): Longint;
  320. begin
  321. raise EDecompressionError.Create('Invalid stream operation');
  322. result:=0;
  323. end;
  324. function TDecompressionStream.Seek(Offset: Longint; Origin: Word): Longint;
  325. var
  326. I: Integer;
  327. Buf: array [0..4095] of Char;
  328. begin
  329. if (Offset = 0) and (Origin = soFromBeginning) then
  330. begin
  331. DecompressionCheck(inflateReset(FZRec));
  332. FZRec.next_in := @FBuffer[0];
  333. FZRec.avail_in := 0;
  334. Source.Position := 0;
  335. FStrmPos := 0;
  336. end
  337. else if ( (Offset >= 0) and (Origin = soFromCurrent)) or
  338. ( ((Offset - FZRec.total_out) > 0) and (Origin = soFromBeginning)) then
  339. begin
  340. if Origin = soFromBeginning then Dec(Offset, FZRec.total_out);
  341. if Offset > 0 then
  342. begin
  343. for I := 1 to Offset div sizeof(Buf) do
  344. ReadBuffer(Buf, sizeof(Buf));
  345. ReadBuffer(Buf, Offset mod sizeof(Buf));
  346. end;
  347. end
  348. else
  349. raise EDecompressionError.Create(SInvalidSeek);
  350. Result := FZRec.total_out;
  351. end;
  352. // TGZFileStream
  353. Constructor TGZFileStream.Create(FileName: String;FileMode: TGZOpenMode);
  354. Const OpenStrings : array[TGZOpenMode] of pchar = ('rb','wb');
  355. begin
  356. FOpenMode:=FileMode;
  357. FFile:=gzopen (PChar(FileName),Openstrings[FileMode]);
  358. If FFile=Nil then
  359. Raise ezlibError.CreateFmt (SCouldntOpenFIle,[FileName]);
  360. end;
  361. Destructor TGZFileStream.Destroy;
  362. begin
  363. gzclose(FFile);
  364. Inherited Destroy;
  365. end;
  366. Function TGZFileStream.Read(Var Buffer; Count : longint): longint;
  367. begin
  368. If FOpenMode=gzOpenWrite then
  369. Raise ezliberror.create(SWriteOnlyStream);
  370. Result:=gzRead(FFile,@Buffer,Count);
  371. end;
  372. function TGZFileStream.Write(const Buffer; Count: Longint): Longint;
  373. begin
  374. If FOpenMode=gzOpenRead then
  375. Raise EzlibError.Create(SReadonlyStream);
  376. Result:=gzWrite(FFile,@Buffer,Count);
  377. end;
  378. function TGZFileStream.Seek(Offset: Longint; Origin: Word): Longint;
  379. begin
  380. Result:=gzseek(FFile,Offset,Origin);
  381. If Result=-1 then
  382. Raise eZlibError.Create(SSeekError);
  383. end;
  384. end.