DeflateStream.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  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);
  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. this.compressedStream = compressedStream;
  66. this.mode = mode;
  67. this.leaveOpen = leaveOpen;
  68. this.sized_buffer = Marshal.AllocHGlobal (BUFFER_SIZE);
  69. this.z_stream = create_z_stream (mode, gzip);
  70. if (z_stream == IntPtr.Zero) {
  71. throw new OutOfMemoryException ();
  72. }
  73. this.open = true;
  74. if (mode == CompressionMode.Compress) {
  75. Flush();
  76. }
  77. }
  78. public DeflateStream (Stream compressedStream, CompressionMode mode) :
  79. this (compressedStream, mode, false, false) { }
  80. public DeflateStream (Stream compressedStream, CompressionMode mode, bool leaveOpen) :
  81. this (compressedStream, mode, leaveOpen, false) { }
  82. ~DeflateStream () {
  83. Marshal.FreeHGlobal (sized_buffer);
  84. }
  85. public override void Close () {
  86. FlushInternal (true);
  87. if (mode == CompressionMode.Decompress && compressedStream.CanSeek) {
  88. int avail_in = z_stream_get_avail_in (z_stream);
  89. if (avail_in != 0) {
  90. compressedStream.Seek (- avail_in, SeekOrigin.Current);
  91. z_stream_set_avail_in (z_stream, 0);
  92. }
  93. }
  94. free_z_stream (z_stream);
  95. z_stream = IntPtr.Zero;
  96. if (!leaveOpen) {
  97. compressedStream.Close();
  98. }
  99. open = false;
  100. }
  101. private int ReadInternal(byte[] array, int offset, int count) {
  102. int buffer_size;
  103. if (finished)
  104. return 0;
  105. if (compressedStream.CanSeek)
  106. buffer_size = BUFFER_SIZE;
  107. else
  108. buffer_size = 1;
  109. IntPtr buffer = Marshal.AllocHGlobal(count);
  110. try {
  111. int avail_out;
  112. avail_out = count;
  113. z_stream_set_next_out (z_stream, buffer);
  114. while (avail_out != 0 && !finished) {
  115. if (z_stream_get_avail_in (z_stream) == 0) {
  116. byte[] read_buf = new byte[buffer_size];
  117. int length_read = compressedStream.Read (read_buf, 0, buffer_size);
  118. bytes_read += length_read;
  119. if (length_read == 0) {
  120. break;
  121. }
  122. Marshal.Copy (read_buf, 0, sized_buffer, length_read);
  123. z_stream_set_next_in (z_stream, sized_buffer);
  124. z_stream_set_avail_in (z_stream, length_read);
  125. }
  126. ZReturnConsts ret_val = z_stream_inflate(z_stream, ref avail_out);
  127. switch (ret_val) {
  128. case ZReturnConsts.Z_OK:
  129. break;
  130. case ZReturnConsts.Z_STREAM_END:
  131. finished = true;
  132. break;
  133. case ZReturnConsts.Z_NEED_DICT:
  134. throw new InvalidDataException ("ZLib stream requires a dictionary.");
  135. case ZReturnConsts.Z_DATA_ERROR:
  136. throw new InvalidDataException ("Invalid ZLib data.");
  137. case ZReturnConsts.Z_STREAM_ERROR:
  138. throw new InvalidOperationException ("Internal DeflateStream error.");
  139. case ZReturnConsts.Z_MEM_ERROR:
  140. throw new OutOfMemoryException ();
  141. case ZReturnConsts.Z_BUF_ERROR:
  142. throw new InvalidOperationException ("Internal DeflateStream error: Buf error.");
  143. }
  144. }
  145. if (count != avail_out)
  146. Marshal.Copy (buffer, array, offset, count - avail_out);
  147. return count - avail_out;
  148. } finally {
  149. Marshal.FreeHGlobal(buffer);
  150. }
  151. }
  152. public override int Read (byte[] dest, int dest_offset, int count)
  153. {
  154. if (dest == null)
  155. throw new ArgumentNullException ("Destination array is null.");
  156. if (!CanRead)
  157. throw new NotSupportedException ("Stream does not support reading.");
  158. int len = dest.Length;
  159. if (dest_offset < 0 || count < 0)
  160. throw new ArgumentException ("Dest or count is negative.");
  161. if (dest_offset > len)
  162. throw new ArgumentException ("destination offset is beyond array size");
  163. if ((dest_offset + count) > len)
  164. throw new ArgumentException ("Reading would overrun buffer");
  165. return ReadInternal (dest, dest_offset, count);
  166. }
  167. private ZReturnConsts do_deflate (ZFlushConsts flush, out int avail_out) {
  168. avail_out = BUFFER_SIZE;
  169. ZReturnConsts ret_val = z_stream_deflate (z_stream, flush, sized_buffer, ref avail_out);
  170. switch (ret_val) {
  171. case ZReturnConsts.Z_STREAM_ERROR:
  172. throw new InvalidOperationException ("Internal error.");
  173. case ZReturnConsts.Z_MEM_ERROR:
  174. throw new InvalidOperationException ("Memory error.");
  175. }
  176. return ret_val;
  177. }
  178. private void WriteInternal(byte[] array, int offset, int count) {
  179. IntPtr buffer = Marshal.AllocHGlobal(count);
  180. try {
  181. int avail_in;
  182. avail_in = count;
  183. Marshal.Copy (array, offset, buffer, count);
  184. z_stream_set_next_in (z_stream, buffer);
  185. z_stream_set_avail_in (z_stream, avail_in);
  186. while (avail_in != 0) {
  187. int avail_out;
  188. do_deflate (ZFlushConsts.Z_NO_FLUSH, out avail_out);
  189. if (avail_out != BUFFER_SIZE) {
  190. byte[] output = new byte[BUFFER_SIZE - avail_out];
  191. Marshal.Copy (sized_buffer, output, 0, BUFFER_SIZE - avail_out);
  192. compressedStream.Write(output, 0, BUFFER_SIZE - avail_out);
  193. }
  194. avail_in = z_stream_get_avail_in (z_stream);
  195. }
  196. } finally {
  197. Marshal.FreeHGlobal(buffer);
  198. }
  199. }
  200. public override void Write (byte[] src, int src_offset, int count)
  201. {
  202. if (src == null)
  203. throw new ArgumentNullException ("src");
  204. if (src_offset < 0)
  205. throw new ArgumentOutOfRangeException ("src_offset");
  206. if (count < 0)
  207. throw new ArgumentOutOfRangeException ("count");
  208. if (!CanWrite)
  209. throw new NotSupportedException ("Stream does not support writing");
  210. WriteInternal (src, src_offset, count);
  211. }
  212. private void FlushInternal (bool finish) {
  213. int avail_out;
  214. ZReturnConsts ret_val;
  215. if (!(open && mode == CompressionMode.Compress && compressedStream.CanWrite))
  216. return;
  217. z_stream_set_next_in (z_stream, IntPtr.Zero);
  218. z_stream_set_avail_in (z_stream, 0);
  219. while (true) {
  220. ret_val = do_deflate (finish ? ZFlushConsts.Z_FINISH : ZFlushConsts.Z_SYNC_FLUSH, out avail_out);
  221. if (BUFFER_SIZE != avail_out) {
  222. byte[] output = new byte[BUFFER_SIZE - avail_out];
  223. Marshal.Copy (sized_buffer, output, 0, BUFFER_SIZE - avail_out);
  224. compressedStream.Write(output, 0, BUFFER_SIZE - avail_out);
  225. } else {
  226. if (!finish)
  227. break;
  228. }
  229. if (ret_val == ZReturnConsts.Z_STREAM_END)
  230. break;
  231. }
  232. compressedStream.Flush();
  233. }
  234. public override void Flush () {
  235. FlushInternal (false);
  236. }
  237. public override long Seek (long offset, SeekOrigin origin) {
  238. throw new System.NotSupportedException();
  239. }
  240. public override void SetLength (long value) {
  241. throw new System.NotSupportedException();
  242. }
  243. public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
  244. AsyncCallback cback, object state)
  245. {
  246. if (!CanRead)
  247. throw new NotSupportedException ("This stream does not support reading");
  248. if (buffer == null)
  249. throw new ArgumentNullException ("buffer");
  250. if (count < 0)
  251. throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
  252. if (offset < 0)
  253. throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
  254. if (count + offset > buffer.Length)
  255. throw new ArgumentException ("Buffer too small. count/offset wrong.");
  256. ReadMethod r = new ReadMethod (ReadInternal);
  257. return r.BeginInvoke (buffer, offset, count, cback, state);
  258. }
  259. public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count,
  260. AsyncCallback cback, object state)
  261. {
  262. if (!CanWrite)
  263. throw new NotSupportedException ("This stream does not support writing");
  264. if (buffer == null)
  265. throw new ArgumentNullException ("buffer");
  266. if (count < 0)
  267. throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
  268. if (offset < 0)
  269. throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
  270. if (count + offset > buffer.Length)
  271. throw new ArgumentException ("Buffer too small. count/offset wrong.");
  272. WriteMethod w = new WriteMethod (WriteInternal);
  273. return w.BeginInvoke (buffer, offset, count, cback, state);
  274. }
  275. public override int EndRead(IAsyncResult async_result) {
  276. if (async_result == null)
  277. throw new ArgumentNullException ("async_result");
  278. AsyncResult ares = async_result as AsyncResult;
  279. if (ares == null)
  280. throw new ArgumentException ("Invalid IAsyncResult", "async_result");
  281. ReadMethod r = ares.AsyncDelegate as ReadMethod;
  282. if (r == null)
  283. throw new ArgumentException ("Invalid IAsyncResult", "async_result");
  284. return r.EndInvoke (async_result);
  285. }
  286. public override void EndWrite (IAsyncResult async_result)
  287. {
  288. if (async_result == null)
  289. throw new ArgumentNullException ("async_result");
  290. AsyncResult ares = async_result as AsyncResult;
  291. if (ares == null)
  292. throw new ArgumentException ("Invalid IAsyncResult", "async_result");
  293. WriteMethod w = ares.AsyncDelegate as WriteMethod;
  294. if (w == null)
  295. throw new ArgumentException ("Invalid IAsyncResult", "async_result");
  296. w.EndInvoke (async_result);
  297. return;
  298. }
  299. public Stream BaseStream {
  300. get {
  301. return compressedStream;
  302. }
  303. }
  304. public override bool CanRead {
  305. get {
  306. return open && mode == CompressionMode.Decompress && compressedStream.CanRead;
  307. }
  308. }
  309. public override bool CanSeek {
  310. get {
  311. return false;
  312. }
  313. }
  314. public override bool CanWrite {
  315. get {
  316. return open && mode == CompressionMode.Compress && compressedStream.CanWrite;
  317. }
  318. }
  319. public override long Length {
  320. get {
  321. throw new System.NotSupportedException();
  322. }
  323. }
  324. public override long Position {
  325. get {
  326. throw new System.NotSupportedException();
  327. }
  328. set {
  329. throw new System.NotSupportedException();
  330. }
  331. }
  332. }
  333. }
  334. #endif