zstream.pp 12 KB

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