DeflateStream.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. /* -*- Mode: csharp; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
  2. //
  3. // DeflateStream.cs
  4. //
  5. // Authors:
  6. // Christopher James Lahey <[email protected]>
  7. //
  8. // (c) 2004 Novell, Inc. <http://www.novell.com>
  9. //
  10. #if NET_2_0
  11. using System;
  12. using System.IO;
  13. using System.Runtime.InteropServices;
  14. using System.Runtime.Remoting.Messaging;
  15. namespace System.IO.Compression {
  16. public class DeflateStream : Stream
  17. {
  18. private Stream compressedStream;
  19. private CompressionMode mode;
  20. private bool leaveOpen;
  21. private bool open;
  22. private IntPtr z_stream;
  23. private const int BUFFER_SIZE = 4096;
  24. private IntPtr sized_buffer;
  25. static int bytes_read = 0;
  26. private bool finished = false;
  27. private enum ZReturnConsts {
  28. Z_OK = 0,
  29. Z_STREAM_END = 1,
  30. Z_NEED_DICT = 2,
  31. Z_STREAM_ERROR = -2,
  32. Z_DATA_ERROR = -3,
  33. Z_MEM_ERROR = -4,
  34. Z_BUF_ERROR = -5,
  35. }
  36. private enum ZFlushConsts {
  37. Z_NO_FLUSH = 0,
  38. Z_PARTIAL_FLUSH = 1, /* will be removed, use Z_SYNC_FLUSH instead */
  39. Z_SYNC_FLUSH = 2,
  40. Z_FULL_FLUSH = 3,
  41. Z_FINISH = 4,
  42. Z_BLOCK = 5,
  43. };
  44. [DllImport("MonoPosixHelper")]
  45. static extern IntPtr create_z_stream(CompressionMode compress, bool gzip);
  46. [DllImport("MonoPosixHelper")]
  47. static extern void free_z_stream(IntPtr z_stream, CompressionMode compress );
  48. [DllImport("MonoPosixHelper")]
  49. static extern void z_stream_set_next_in(IntPtr z_stream, IntPtr next_in);
  50. [DllImport("MonoPosixHelper")]
  51. static extern void z_stream_set_avail_in(IntPtr z_stream, int avail_in);
  52. [DllImport("MonoPosixHelper")]
  53. static extern int z_stream_get_avail_in(IntPtr z_stream);
  54. [DllImport("MonoPosixHelper")]
  55. static extern void z_stream_set_next_out(IntPtr z_stream, IntPtr next_out);
  56. //[DllImport("MonoPosixHelper")]
  57. //static extern void z_stream_set_avail_out(IntPtr z_stream, int avail_out);
  58. [DllImport("MonoPosixHelper")]
  59. static extern ZReturnConsts z_stream_inflate(IntPtr z_stream, ref int avail_out);
  60. [DllImport("MonoPosixHelper")]
  61. static extern ZReturnConsts z_stream_deflate(IntPtr z_stream, ZFlushConsts flush, IntPtr next_out, ref int avail_out);
  62. delegate int ReadMethod (byte[] array, int offset, int count);
  63. delegate void WriteMethod(byte[] array, int offset, int count);
  64. internal DeflateStream (Stream compressedStream, CompressionMode mode, bool leaveOpen, bool gzip)
  65. {
  66. if (compressedStream == null)
  67. throw new ArgumentNullException ("compressedStream");
  68. switch (mode) {
  69. case CompressionMode.Compress:
  70. case CompressionMode.Decompress:
  71. break;
  72. default:
  73. throw new ArgumentException ("mode");
  74. }
  75. this.compressedStream = compressedStream;
  76. this.mode = mode;
  77. this.leaveOpen = leaveOpen;
  78. this.sized_buffer = Marshal.AllocHGlobal (BUFFER_SIZE);
  79. this.z_stream = create_z_stream (mode, gzip);
  80. if (z_stream == IntPtr.Zero) {
  81. throw new NotImplementedException ("Failed to initialize zlib. You probably have an old zlib installed. Version 1.2.0.4 or later is required.");
  82. }
  83. this.open = true;
  84. if (mode == CompressionMode.Compress) {
  85. Flush();
  86. }
  87. }
  88. public DeflateStream (Stream compressedStream, CompressionMode mode) :
  89. this (compressedStream, mode, false, false) { }
  90. public DeflateStream (Stream compressedStream, CompressionMode mode, bool leaveOpen) :
  91. this (compressedStream, mode, leaveOpen, false) { }
  92. protected override void Dispose (bool disposing)
  93. {
  94. try {
  95. FlushInternal (true);
  96. base.Dispose (disposing);
  97. } finally {
  98. try {
  99. DisposeCore ();
  100. } finally {
  101. if (disposing)
  102. Marshal.FreeHGlobal (sized_buffer);
  103. }
  104. }
  105. }
  106. void DisposeCore ()
  107. {
  108. if (/*mode == CompressionMode.Decompress &&*/ compressedStream.CanSeek) {
  109. int avail_in = z_stream_get_avail_in (z_stream);
  110. if (avail_in != 0) {
  111. compressedStream.Seek (- avail_in, SeekOrigin.Current);
  112. z_stream_set_avail_in (z_stream, 0);
  113. }
  114. }
  115. free_z_stream (z_stream,mode);
  116. z_stream = IntPtr.Zero;
  117. if (!leaveOpen) {
  118. compressedStream.Close();
  119. }
  120. open = false;
  121. }
  122. private int ReadInternal(byte[] array, int offset, int count) {
  123. int buffer_size;
  124. if (finished)
  125. return 0;
  126. if (compressedStream.CanSeek)
  127. buffer_size = BUFFER_SIZE;
  128. else
  129. buffer_size = 1;
  130. IntPtr buffer = Marshal.AllocHGlobal(count);
  131. try {
  132. int avail_out;
  133. avail_out = count;
  134. z_stream_set_next_out (z_stream, buffer);
  135. while (avail_out != 0 && !finished) {
  136. if (z_stream_get_avail_in (z_stream) == 0) {
  137. byte[] read_buf = new byte[buffer_size];
  138. int length_read = compressedStream.Read (read_buf, 0, buffer_size);
  139. bytes_read += length_read;
  140. if (length_read == 0) {
  141. break;
  142. }
  143. Marshal.Copy (read_buf, 0, sized_buffer, length_read);
  144. z_stream_set_next_in (z_stream, sized_buffer);
  145. z_stream_set_avail_in (z_stream, length_read);
  146. }
  147. ZReturnConsts ret_val = z_stream_inflate(z_stream, ref avail_out);
  148. switch (ret_val) {
  149. case ZReturnConsts.Z_OK:
  150. break;
  151. case ZReturnConsts.Z_STREAM_END:
  152. finished = true;
  153. break;
  154. case ZReturnConsts.Z_NEED_DICT:
  155. throw new InvalidDataException ("ZLib stream requires a dictionary.");
  156. case ZReturnConsts.Z_DATA_ERROR:
  157. throw new InvalidDataException ("Invalid ZLib data.");
  158. case ZReturnConsts.Z_STREAM_ERROR:
  159. throw new InvalidOperationException ("Internal DeflateStream error.");
  160. case ZReturnConsts.Z_MEM_ERROR:
  161. throw new OutOfMemoryException ();
  162. case ZReturnConsts.Z_BUF_ERROR:
  163. throw new InvalidOperationException ("Internal DeflateStream error: Buf error.");
  164. }
  165. }
  166. if (count != avail_out)
  167. Marshal.Copy (buffer, array, offset, count - avail_out);
  168. return count - avail_out;
  169. } finally {
  170. Marshal.FreeHGlobal(buffer);
  171. }
  172. }
  173. public override int Read (byte[] dest, int dest_offset, int count)
  174. {
  175. if (!open)
  176. throw new ObjectDisposedException ("DeflateStream");
  177. if (dest == null)
  178. throw new ArgumentNullException ("Destination array is null.");
  179. if (!CanRead)
  180. throw new InvalidOperationException ("Stream does not support reading.");
  181. int len = dest.Length;
  182. if (dest_offset < 0 || count < 0)
  183. throw new ArgumentException ("Dest or count is negative.");
  184. if (dest_offset > len)
  185. throw new ArgumentException ("destination offset is beyond array size");
  186. if ((dest_offset + count) > len)
  187. throw new ArgumentException ("Reading would overrun buffer");
  188. return ReadInternal (dest, dest_offset, count);
  189. }
  190. private ZReturnConsts do_deflate (ZFlushConsts flush, out int avail_out) {
  191. avail_out = BUFFER_SIZE;
  192. ZReturnConsts ret_val = z_stream_deflate (z_stream, flush, sized_buffer, ref avail_out);
  193. switch (ret_val) {
  194. case ZReturnConsts.Z_STREAM_ERROR:
  195. throw new InvalidOperationException ("Internal error.");
  196. case ZReturnConsts.Z_MEM_ERROR:
  197. throw new InvalidOperationException ("Memory error.");
  198. }
  199. return ret_val;
  200. }
  201. private void WriteInternal(byte[] array, int offset, int count) {
  202. IntPtr buffer = Marshal.AllocHGlobal(count);
  203. try {
  204. int avail_in;
  205. avail_in = count;
  206. Marshal.Copy (array, offset, buffer, count);
  207. z_stream_set_next_in (z_stream, buffer);
  208. z_stream_set_avail_in (z_stream, avail_in);
  209. while (avail_in != 0) {
  210. int avail_out;
  211. do_deflate (ZFlushConsts.Z_NO_FLUSH, out avail_out);
  212. if (avail_out != BUFFER_SIZE) {
  213. byte[] output = new byte[BUFFER_SIZE - avail_out];
  214. Marshal.Copy (sized_buffer, output, 0, BUFFER_SIZE - avail_out);
  215. compressedStream.Write(output, 0, BUFFER_SIZE - avail_out);
  216. }
  217. avail_in = z_stream_get_avail_in (z_stream);
  218. }
  219. } finally {
  220. Marshal.FreeHGlobal(buffer);
  221. }
  222. }
  223. public override void Write (byte[] src, int src_offset, int count)
  224. {
  225. if (!open)
  226. throw new ObjectDisposedException ("DeflateStream");
  227. if (src == null)
  228. throw new ArgumentNullException ("src");
  229. if (src_offset < 0)
  230. throw new ArgumentOutOfRangeException ("src_offset");
  231. if (count < 0)
  232. throw new ArgumentOutOfRangeException ("count");
  233. if (!CanWrite)
  234. throw new NotSupportedException ("Stream does not support writing");
  235. WriteInternal (src, src_offset, count);
  236. }
  237. private void FlushInternal (bool finish)
  238. {
  239. if (!open)
  240. throw new ObjectDisposedException ("DeflateStream");
  241. int avail_out;
  242. ZReturnConsts ret_val;
  243. if (!(open && mode == CompressionMode.Compress && compressedStream.CanWrite))
  244. return;
  245. z_stream_set_next_in (z_stream, IntPtr.Zero);
  246. z_stream_set_avail_in (z_stream, 0);
  247. while (true) {
  248. ret_val = do_deflate (finish ? ZFlushConsts.Z_FINISH : ZFlushConsts.Z_SYNC_FLUSH, out avail_out);
  249. if (BUFFER_SIZE != avail_out) {
  250. byte[] output = new byte[BUFFER_SIZE - avail_out];
  251. Marshal.Copy (sized_buffer, output, 0, BUFFER_SIZE - avail_out);
  252. compressedStream.Write(output, 0, BUFFER_SIZE - avail_out);
  253. } else {
  254. if (!finish)
  255. break;
  256. }
  257. if (ret_val == ZReturnConsts.Z_STREAM_END)
  258. break;
  259. }
  260. compressedStream.Flush();
  261. }
  262. public override void Flush () {
  263. FlushInternal (false);
  264. }
  265. public override long Seek (long offset, SeekOrigin origin) {
  266. throw new System.NotSupportedException();
  267. }
  268. public override void SetLength (long value) {
  269. throw new System.NotSupportedException();
  270. }
  271. public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
  272. AsyncCallback cback, object state)
  273. {
  274. if (!open)
  275. throw new ObjectDisposedException ("DeflateStream");
  276. if (!CanRead)
  277. throw new NotSupportedException ("This stream does not support reading");
  278. if (buffer == null)
  279. throw new ArgumentNullException ("buffer");
  280. if (count < 0)
  281. throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
  282. if (offset < 0)
  283. throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
  284. if (count + offset > buffer.Length)
  285. throw new ArgumentException ("Buffer too small. count/offset wrong.");
  286. ReadMethod r = new ReadMethod (ReadInternal);
  287. return r.BeginInvoke (buffer, offset, count, cback, state);
  288. }
  289. public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count,
  290. AsyncCallback cback, object state)
  291. {
  292. if (!open)
  293. throw new ObjectDisposedException ("DeflateStream");
  294. if (!CanWrite)
  295. throw new InvalidOperationException ("This stream does not support writing");
  296. if (buffer == null)
  297. throw new ArgumentNullException ("buffer");
  298. if (count < 0)
  299. throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
  300. if (offset < 0)
  301. throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
  302. if (count + offset > buffer.Length)
  303. throw new ArgumentException ("Buffer too small. count/offset wrong.");
  304. WriteMethod w = new WriteMethod (WriteInternal);
  305. return w.BeginInvoke (buffer, offset, count, cback, state);
  306. }
  307. public override int EndRead(IAsyncResult async_result) {
  308. if (async_result == null)
  309. throw new ArgumentNullException ("async_result");
  310. AsyncResult ares = async_result as AsyncResult;
  311. if (ares == null)
  312. throw new ArgumentException ("Invalid IAsyncResult", "async_result");
  313. ReadMethod r = ares.AsyncDelegate as ReadMethod;
  314. if (r == null)
  315. throw new ArgumentException ("Invalid IAsyncResult", "async_result");
  316. return r.EndInvoke (async_result);
  317. }
  318. public override void EndWrite (IAsyncResult async_result)
  319. {
  320. if (async_result == null)
  321. throw new ArgumentNullException ("async_result");
  322. AsyncResult ares = async_result as AsyncResult;
  323. if (ares == null)
  324. throw new ArgumentException ("Invalid IAsyncResult", "async_result");
  325. WriteMethod w = ares.AsyncDelegate as WriteMethod;
  326. if (w == null)
  327. throw new ArgumentException ("Invalid IAsyncResult", "async_result");
  328. w.EndInvoke (async_result);
  329. return;
  330. }
  331. public Stream BaseStream {
  332. get {
  333. return compressedStream;
  334. }
  335. }
  336. public override bool CanRead {
  337. get {
  338. return open && mode == CompressionMode.Decompress && compressedStream.CanRead;
  339. }
  340. }
  341. public override bool CanSeek {
  342. get {
  343. return false;
  344. }
  345. }
  346. public override bool CanWrite {
  347. get {
  348. return open && mode == CompressionMode.Compress && compressedStream.CanWrite;
  349. }
  350. }
  351. public override long Length {
  352. get {
  353. throw new System.NotSupportedException();
  354. }
  355. }
  356. public override long Position {
  357. get {
  358. throw new System.NotSupportedException();
  359. }
  360. set {
  361. throw new System.NotSupportedException();
  362. }
  363. }
  364. }
  365. }
  366. #endif