IdCompressorZLib.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.9 3/5/2005 3:33:54 PM JPMugaas
  18. Fix for some compiler warnings having to do with TStream.Read being platform
  19. specific. This was fixed by changing the Compressor API to use TIdStreamVCL
  20. instead of TStream. I also made appropriate adjustments to other units for
  21. this.
  22. Rev 1.8 10/24/2004 2:40:28 PM JPMugaas
  23. Made a better fix for the problem with SmartFTP. It turns out that we may
  24. not be able to avoid a Z_BUF_ERROR in some cases.
  25. Rev 1.7 10/24/2004 11:17:08 AM JPMugaas
  26. Reimplemented ZLIB Decompression in FTP better. It now should work properly
  27. at ftp://ftp.smartftp.com.
  28. Rev 1.6 9/16/2004 3:24:04 AM JPMugaas
  29. TIdFTP now compresses to the IOHandler and decompresses from the IOHandler.
  30. Noted some that the ZLib code is based was taken from ZLibEx.
  31. Rev 1.4 9/11/2004 10:58:04 AM JPMugaas
  32. FTP now decompresses output directly to the IOHandler.
  33. Rev 1.3 6/21/2004 12:10:52 PM JPMugaas
  34. Attempt to expand the ZLib support for Int64 support.
  35. Rev 1.2 2/21/2004 3:32:58 PM JPMugaas
  36. Foxed for Unit rename.
  37. Rev 1.1 2/14/2004 9:59:50 PM JPMugaas
  38. Reworked the API. There is now a separate API for the Inflate_ and
  39. InflateInit2_ functions as well as separate functions for DeflateInit_ and
  40. DeflateInit2_. This was required for FTP. The API also includes an optional
  41. output stream for the servers.
  42. Rev 1.0 2/12/2004 11:27:22 PM JPMugaas
  43. New compressor based on ZLibEx.
  44. }
  45. unit IdCompressorZLib;
  46. interface
  47. {$i IdCompilerDefines.inc}
  48. uses
  49. Classes,
  50. IdException,
  51. IdIOHandler,
  52. IdZLibCompressorBase,
  53. IdZLibHeaders;
  54. type
  55. TIdCompressorZLib = class(TIdZLibCompressorBase)
  56. protected
  57. function GetIsReady : Boolean; override;
  58. procedure InternalDecompressStream(var LZstream: TZStreamRec; AIOHandler : TIdIOHandler;
  59. AOutStream: TStream);
  60. public
  61. procedure DeflateStream(AInStream, AOutStream : TStream;
  62. const ALevel : TIdCompressionLevel=0); override;
  63. procedure InflateStream(AInStream, AOutStream : TStream); override;
  64. procedure CompressStream(AInStream, AOutStream : TStream; const ALevel : TIdCompressionLevel; const AWindowBits, AMemLevel,
  65. AStrategy: Integer); override;
  66. procedure DecompressStream(AInStream, AOutStream : TStream; const AWindowBits : Integer); override;
  67. procedure CompressFTPToIO(AInStream : TStream; AIOHandler : TIdIOHandler;
  68. const ALevel, AWindowBits, AMemLevel, AStrategy: Integer); override;
  69. procedure DecompressFTPFromIO(AIOHandler : TIdIOHandler; AOutputStream : TStream;
  70. const AWindowBits : Integer); override;
  71. end;
  72. EIdCompressionException = class(EIdException);
  73. EIdCompressorInitFailure = class(EIdCompressionException);
  74. EIdDecompressorInitFailure = class(EIdCompressionException);
  75. EIdCompressionError = class(EIdCompressionException);
  76. EIdDecompressionError = class(EIdCompressionException);
  77. implementation
  78. uses
  79. IdAntiFreezeBase, IdComponent, IdResourceStringsProtocols, IdGlobal,
  80. IdGlobalProtocols, IdZLib, SysUtils;
  81. const
  82. bufferSize = 32768;
  83. { TIdCompressorZLib }
  84. procedure TIdCompressorZLib.InternalDecompressStream(
  85. var LZstream: TZStreamRec; AIOHandler: TIdIOHandler; AOutStream: TStream);
  86. {Note that much of this is taken from the ZLibEx unit and adapted to use the IOHandler}
  87. var
  88. zresult : Integer;
  89. outBuffer: Array [0..bufferSize-1] of TIdAnsiChar;
  90. inSize : Integer;
  91. outSize : Integer;
  92. LBuf : TIdBytes;
  93. function RawReadFromIOHandler(ABuffer : TIdBytes; AOIHandler : TIdIOHandler; AMax : Integer) : Integer;
  94. begin
  95. //We don't use the IOHandler.ReadBytes because that will check
  96. // for disconnect and raise an exception that we don't want.
  97. // RLebeau 3/26/09: we need to raise exceptions here! The socket component
  98. // that is performing the IO needs to know what is happening on the socket...
  99. {
  100. repeat
  101. AIOHandler.CheckForDataOnSource(1);
  102. Result := IndyMin(AIOHandler.InputBuffer.Size, AMax);
  103. if Result > 0 then begin
  104. AIOHandler.InputBuffer.ExtractToBytes(ABuffer, Result, False);
  105. Break;
  106. end;
  107. until not AIOHandler.Connected;
  108. }
  109. // TODO: ReadStream() has been re-written to not use ReadBytes() anymore, it
  110. // now reads directly from the InputBuffer into the target TStream via
  111. // IOHandler.ReadFromSource() and IOHandler.InputBuffer.ExtractToStream(),
  112. // so we should do something similar here...
  113. // copied from TIdIOHandler.ReadStream() and trimmed down...
  114. try
  115. AIOHandler.ReadBytes(ABuffer, AMax, False);
  116. except
  117. on E: Exception do begin
  118. // RLebeau - ReadFromSource() inside of ReadBytes()
  119. // could have filled the InputBuffer with more bytes
  120. // than actually requested, so don't extract too
  121. // many bytes here...
  122. AMax := IndyMin(AMax, AIOHandler.InputBuffer.Size);
  123. AIOHandler.InputBuffer.ExtractToBytes(ABuffer, AMax, False);
  124. if not (E is EIdConnClosedGracefully) then begin
  125. raise;
  126. end;
  127. end;
  128. end;
  129. TIdAntiFreezeBase.DoProcess;
  130. Result := AMax;
  131. end;
  132. begin
  133. SetLength(LBuf, bufferSize);
  134. repeat
  135. inSize := RawReadFromIOHandler(LBuf, AIOHandler, bufferSize);
  136. if inSize < 1 then begin
  137. Break;
  138. end;
  139. LZstream.next_in := PIdAnsiChar(@LBuf[0]);
  140. LZstream.avail_in := inSize;
  141. repeat
  142. LZstream.next_out := @outBuffer[0];
  143. LZstream.avail_out := bufferSize;
  144. zresult := inflate(LZstream, Z_NO_FLUSH);
  145. if zresult <> Z_BUF_ERROR then
  146. begin
  147. DCheck(zresult);
  148. end;
  149. outSize := bufferSize - LZstream.avail_out;
  150. AOutStream.Write(outBuffer, outSize);
  151. until (LZstream.avail_in = 0) and (LZstream.avail_out > 0);
  152. until False;
  153. { From the ZLIB FAQ at http://www.gzip.org/zlib/FAQ.txt
  154. 5. deflate() or inflate() returns Z_BUF_ERROR
  155. Before making the call, make sure that avail_in and avail_out are not
  156. zero. When setting the parameter flush equal to Z_FINISH, also make sure
  157. that avail_out is big enough to allow processing all pending input.
  158. Note that a Z_BUF_ERROR is not fatal--another call to deflate() or
  159. inflate() can be made with more input or output space. A Z_BUF_ERROR
  160. may in fact be unavoidable depending on how the functions are used, since
  161. it is not possible to tell whether or not there is more output pending
  162. when strm.avail_out returns with zero.
  163. }
  164. repeat
  165. LZstream.next_out := @outBuffer[0];
  166. LZstream.avail_out := bufferSize;
  167. zresult := inflate(LZstream, Z_FINISH);
  168. if zresult <> Z_BUF_ERROR then
  169. begin
  170. zresult := DCheck(zresult);
  171. end;
  172. outSize := bufferSize - LZstream.avail_out;
  173. AOutStream.Write(outBuffer, outSize);
  174. until ((zresult = Z_STREAM_END) and (LZstream.avail_out > 0)) or (zresult = Z_BUF_ERROR);
  175. DCheck(inflateEnd(LZstream));
  176. end;
  177. procedure TIdCompressorZLib.DecompressFTPFromIO(AIOHandler : TIdIOHandler; AOutputStream : TStream;
  178. const AWindowBits : Integer);
  179. {Note that much of this is taken from the ZLibEx unit and adapted to use the IOHandler}
  180. var
  181. Lzstream: TZStreamRec;
  182. LWinBits : Integer;
  183. begin
  184. AIOHandler.BeginWork(wmRead);
  185. try
  186. FillChar(Lzstream,SizeOf(TZStreamRec),0);
  187. {
  188. This is a workaround for some clients and servers that do not send decompression
  189. headers. The reason is that there's an inconsistancy in Internet Drafts for ZLIB
  190. compression. One says to include the headers while an older one says do not
  191. include the headers.
  192. If you add 32 to the Window Bits parameter,
  193. }
  194. LWinBits := AWindowBits;
  195. if LWinBits > 0 then
  196. begin
  197. LWinBits := Abs( LWinBits) + 32;
  198. end;
  199. LZstream.zalloc := zlibAllocMem;
  200. LZstream.zfree := zlibFreeMem;
  201. DCheck(inflateInit2_(Lzstream,LWinBits,ZLIB_VERSION,SizeOf(TZStreamRec)));
  202. InternalDecompressStream(Lzstream,AIOHandler,AOutputStream);
  203. finally
  204. AIOHandler.EndWork(wmRead);
  205. end;
  206. end;
  207. procedure TIdCompressorZLib.CompressFTPToIO(AInStream : TStream;
  208. AIOHandler : TIdIOHandler;
  209. const ALevel, AWindowBits, AMemLevel, AStrategy: Integer);
  210. {Note that much of this is taken from the ZLibEx unit and adapted to use the IOHandler}
  211. var
  212. LCompressRec : TZStreamRec;
  213. zresult : Integer;
  214. inBuffer : Array [0..bufferSize-1] of TIdAnsiChar;
  215. outBuffer: Array [0..bufferSize-1] of TIdAnsiChar;
  216. inSize : Integer;
  217. outSize : Integer;
  218. begin
  219. AIOHandler.BeginWork(wmWrite, AInStream.Size);
  220. try
  221. FillChar(LCompressRec, SizeOf(TZStreamRec), 0);
  222. CCheck( deflateInit2_(LCompressRec, ALevel, Z_DEFLATED, AWindowBits, AMemLevel,
  223. AStrategy, ZLIB_VERSION, SizeOf(LCompressRec)));
  224. inSize := AInStream.Read(inBuffer, bufferSize);
  225. while inSize > 0 do
  226. begin
  227. LCompressRec.next_in := @inBuffer[0];
  228. LCompressRec.avail_in := inSize;
  229. repeat
  230. LCompressRec.next_out := @outBuffer[0];
  231. LCompressRec.avail_out := bufferSize;
  232. CCheck(deflate(LCompressRec,Z_NO_FLUSH));
  233. // outSize := zstream.next_out - outBuffer;
  234. outSize := bufferSize - LCompressRec.avail_out;
  235. if outsize <> 0 then
  236. begin
  237. AIOHandler.Write(RawToBytes(outBuffer, outSize));
  238. end;
  239. until (LCompressRec.avail_in = 0) and (LCompressRec.avail_out > 0);
  240. inSize := AInStream.Read(inBuffer, bufferSize);
  241. end;
  242. repeat
  243. LCompressRec.next_out := @outBuffer[0];
  244. LCompressRec.avail_out := bufferSize;
  245. zresult := CCheck(deflate(LCompressRec,Z_FINISH));
  246. // outSize := zstream.next_out - outBuffer;
  247. outSize := bufferSize - LCompressRec.avail_out;
  248. // outStream.Write(outBuffer,outSize);
  249. if outSize <> 0 then
  250. begin
  251. AIOHandler.Write(RawToBytes(outBuffer, outSize));
  252. end;
  253. until (zresult = Z_STREAM_END) and (LCompressRec.avail_out > 0);
  254. CCheck(deflateEnd(LCompressRec));
  255. finally
  256. AIOHandler.EndWork(wmWrite);
  257. end;
  258. end;
  259. procedure TIdCompressorZLib.CompressStream(AInStream,AOutStream : TStream;
  260. const ALevel : TIdCompressionLevel;
  261. const AWindowBits, AMemLevel, AStrategy: Integer);
  262. begin
  263. IdZLib.IndyCompressStream(AInStream,AOutStream,ALevel,AWindowBits,AMemLevel,AStrategy);
  264. end;
  265. procedure TIdCompressorZLib.DecompressStream(AInStream, AOutStream : TStream; const AWindowBits : Integer);
  266. begin
  267. IdZLib.IndyDeCompressStream(AInStream,AOutStream, AWindowBits);
  268. end;
  269. procedure TIdCompressorZLib.DeflateStream(AInStream, AOutStream : TStream; const ALevel : TIdCompressionLevel=0);
  270. begin
  271. IdZLib.IndyCompressStream(AInStream,AOutStream,ALevel);
  272. end;
  273. function TIdCompressorZLib.GetIsReady: Boolean;
  274. begin
  275. Result := IdZLibHeaders.Load;
  276. end;
  277. procedure TIdCompressorZLib.InflateStream(AInStream, AOutStream : TStream);
  278. begin
  279. IdZlib.DeCompressStream(AInStream,AOutStream);
  280. end;
  281. end.