DeflateStream.cs 12 KB

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