GzipOutputStream.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. using ICSharpCode.SharpZipLib.Checksum;
  2. using ICSharpCode.SharpZipLib.Zip.Compression;
  3. using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
  4. using System;
  5. using System.IO;
  6. namespace ICSharpCode.SharpZipLib.GZip
  7. {
  8. /// <summary>
  9. /// This filter stream is used to compress a stream into a "GZIP" stream.
  10. /// The "GZIP" format is described in RFC 1952.
  11. ///
  12. /// author of the original java version : John Leuner
  13. /// </summary>
  14. /// <example> This sample shows how to gzip a file
  15. /// <code>
  16. /// using System;
  17. /// using System.IO;
  18. ///
  19. /// using ICSharpCode.SharpZipLib.GZip;
  20. /// using ICSharpCode.SharpZipLib.Core;
  21. ///
  22. /// class MainClass
  23. /// {
  24. /// public static void Main(string[] args)
  25. /// {
  26. /// using (Stream s = new GZipOutputStream(File.Create(args[0] + ".gz")))
  27. /// using (FileStream fs = File.OpenRead(args[0])) {
  28. /// byte[] writeData = new byte[4096];
  29. /// Streamutils.Copy(s, fs, writeData);
  30. /// }
  31. /// }
  32. /// }
  33. /// }
  34. /// </code>
  35. /// </example>
  36. public class GZipOutputStream : DeflaterOutputStream
  37. {
  38. private enum OutputState
  39. {
  40. Header,
  41. Footer,
  42. Finished,
  43. Closed,
  44. };
  45. #region Instance Fields
  46. /// <summary>
  47. /// CRC-32 value for uncompressed data
  48. /// </summary>
  49. protected Crc32 crc = new Crc32();
  50. private OutputState state_ = OutputState.Header;
  51. #endregion Instance Fields
  52. #region Constructors
  53. /// <summary>
  54. /// Creates a GzipOutputStream with the default buffer size
  55. /// </summary>
  56. /// <param name="baseOutputStream">
  57. /// The stream to read data (to be compressed) from
  58. /// </param>
  59. public GZipOutputStream(Stream baseOutputStream)
  60. : this(baseOutputStream, 4096)
  61. {
  62. }
  63. /// <summary>
  64. /// Creates a GZipOutputStream with the specified buffer size
  65. /// </summary>
  66. /// <param name="baseOutputStream">
  67. /// The stream to read data (to be compressed) from
  68. /// </param>
  69. /// <param name="size">
  70. /// Size of the buffer to use
  71. /// </param>
  72. public GZipOutputStream(Stream baseOutputStream, int size) : base(baseOutputStream, new Deflater(Deflater.DEFAULT_COMPRESSION, true), size)
  73. {
  74. }
  75. #endregion Constructors
  76. #region Public API
  77. /// <summary>
  78. /// Sets the active compression level (0-9). The new level will be activated
  79. /// immediately.
  80. /// </summary>
  81. /// <param name="level">The compression level to set.</param>
  82. /// <exception cref="ArgumentOutOfRangeException">
  83. /// Level specified is not supported.
  84. /// </exception>
  85. /// <see cref="Deflater"/>
  86. public void SetLevel(int level)
  87. {
  88. if (level < Deflater.NO_COMPRESSION || level > Deflater.BEST_COMPRESSION)
  89. throw new ArgumentOutOfRangeException(nameof(level), "Compression level must be 0-9");
  90. deflater_.SetLevel(level);
  91. }
  92. /// <summary>
  93. /// Get the current compression level.
  94. /// </summary>
  95. /// <returns>The current compression level.</returns>
  96. public int GetLevel()
  97. {
  98. return deflater_.GetLevel();
  99. }
  100. #endregion Public API
  101. #region Stream overrides
  102. /// <summary>
  103. /// Write given buffer to output updating crc
  104. /// </summary>
  105. /// <param name="buffer">Buffer to write</param>
  106. /// <param name="offset">Offset of first byte in buf to write</param>
  107. /// <param name="count">Number of bytes to write</param>
  108. public override void Write(byte[] buffer, int offset, int count)
  109. {
  110. if (state_ == OutputState.Header)
  111. {
  112. WriteHeader();
  113. }
  114. if (state_ != OutputState.Footer)
  115. {
  116. throw new InvalidOperationException("Write not permitted in current state");
  117. }
  118. crc.Update(new ArraySegment<byte>(buffer, offset, count));
  119. base.Write(buffer, offset, count);
  120. }
  121. /// <summary>
  122. /// Writes remaining compressed output data to the output stream
  123. /// and closes it.
  124. /// </summary>
  125. protected override void Dispose(bool disposing)
  126. {
  127. try
  128. {
  129. Finish();
  130. }
  131. finally
  132. {
  133. if (state_ != OutputState.Closed)
  134. {
  135. state_ = OutputState.Closed;
  136. if (IsStreamOwner)
  137. {
  138. baseOutputStream_.Dispose();
  139. }
  140. }
  141. }
  142. }
  143. #endregion Stream overrides
  144. #region DeflaterOutputStream overrides
  145. /// <summary>
  146. /// Finish compression and write any footer information required to stream
  147. /// </summary>
  148. public override void Finish()
  149. {
  150. // If no data has been written a header should be added.
  151. if (state_ == OutputState.Header)
  152. {
  153. WriteHeader();
  154. }
  155. if (state_ == OutputState.Footer)
  156. {
  157. state_ = OutputState.Finished;
  158. base.Finish();
  159. var totalin = (uint)(deflater_.TotalIn & 0xffffffff);
  160. var crcval = (uint)(crc.Value & 0xffffffff);
  161. byte[] gzipFooter;
  162. unchecked
  163. {
  164. gzipFooter = new byte[] {
  165. (byte) crcval, (byte) (crcval >> 8),
  166. (byte) (crcval >> 16), (byte) (crcval >> 24),
  167. (byte) totalin, (byte) (totalin >> 8),
  168. (byte) (totalin >> 16), (byte) (totalin >> 24)
  169. };
  170. }
  171. baseOutputStream_.Write(gzipFooter, 0, gzipFooter.Length);
  172. }
  173. }
  174. #endregion DeflaterOutputStream overrides
  175. #region Support Routines
  176. private void WriteHeader()
  177. {
  178. if (state_ == OutputState.Header)
  179. {
  180. state_ = OutputState.Footer;
  181. var mod_time = (int)((DateTime.Now.Ticks - new DateTime(1970, 1, 1).Ticks) / 10000000L); // Ticks give back 100ns intervals
  182. byte[] gzipHeader = {
  183. // The two magic bytes
  184. (byte) (GZipConstants.GZIP_MAGIC >> 8), (byte) (GZipConstants.GZIP_MAGIC & 0xff),
  185. // The compression type
  186. (byte) Deflater.DEFLATED,
  187. // The flags (not set)
  188. 0,
  189. // The modification time
  190. (byte) mod_time, (byte) (mod_time >> 8),
  191. (byte) (mod_time >> 16), (byte) (mod_time >> 24),
  192. // The extra flags
  193. 0,
  194. // The OS type (unknown)
  195. (byte) 255
  196. };
  197. baseOutputStream_.Write(gzipHeader, 0, gzipHeader.Length);
  198. }
  199. }
  200. #endregion Support Routines
  201. }
  202. }