GzipOutputStream.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. using System;
  2. using System.IO;
  3. using ICSharpCode.SharpZipLib.Checksum;
  4. using ICSharpCode.SharpZipLib.Zip.Compression;
  5. using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
  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. 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. OutputState state_ = OutputState.Header;
  51. #endregion
  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
  76. #region Public API
  77. /// <summary>
  78. /// Sets the active compression level (1-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.BEST_SPEED) {
  89. throw new ArgumentOutOfRangeException("nameof(level)");
  90. }
  91. deflater_.SetLevel(level);
  92. }
  93. /// <summary>
  94. /// Get the current compression level.
  95. /// </summary>
  96. /// <returns>The current compression level.</returns>
  97. public int GetLevel()
  98. {
  99. return deflater_.GetLevel();
  100. }
  101. #endregion
  102. #region Stream overrides
  103. /// <summary>
  104. /// Write given buffer to output updating crc
  105. /// </summary>
  106. /// <param name="buffer">Buffer to write</param>
  107. /// <param name="offset">Offset of first byte in buf to write</param>
  108. /// <param name="count">Number of bytes to write</param>
  109. public override void Write(byte[] buffer, int offset, int count)
  110. {
  111. if (state_ == OutputState.Header) {
  112. WriteHeader();
  113. }
  114. if (state_ != OutputState.Footer) {
  115. throw new InvalidOperationException("Write not permitted in current state");
  116. }
  117. crc.Update(buffer, offset, count);
  118. base.Write(buffer, offset, count);
  119. }
  120. /// <summary>
  121. /// Writes remaining compressed output data to the output stream
  122. /// and closes it.
  123. /// </summary>
  124. protected override void Dispose(bool disposing)
  125. {
  126. try {
  127. Finish();
  128. } finally {
  129. if (state_ != OutputState.Closed) {
  130. state_ = OutputState.Closed;
  131. if (IsStreamOwner) {
  132. baseOutputStream_.Dispose();
  133. }
  134. }
  135. }
  136. }
  137. #endregion
  138. #region DeflaterOutputStream overrides
  139. /// <summary>
  140. /// Finish compression and write any footer information required to stream
  141. /// </summary>
  142. public override void Finish()
  143. {
  144. // If no data has been written a header should be added.
  145. if (state_ == OutputState.Header) {
  146. WriteHeader();
  147. }
  148. if (state_ == OutputState.Footer) {
  149. state_ = OutputState.Finished;
  150. base.Finish();
  151. var totalin = (uint)(deflater_.TotalIn & 0xffffffff);
  152. var crcval = (uint)(crc.Value & 0xffffffff);
  153. byte[] gzipFooter;
  154. unchecked {
  155. gzipFooter = new byte[] {
  156. (byte) crcval, (byte) (crcval >> 8),
  157. (byte) (crcval >> 16), (byte) (crcval >> 24),
  158. (byte) totalin, (byte) (totalin >> 8),
  159. (byte) (totalin >> 16), (byte) (totalin >> 24)
  160. };
  161. }
  162. baseOutputStream_.Write(gzipFooter, 0, gzipFooter.Length);
  163. }
  164. }
  165. #endregion
  166. #region Support Routines
  167. void WriteHeader()
  168. {
  169. if (state_ == OutputState.Header) {
  170. state_ = OutputState.Footer;
  171. var mod_time = (int)((DateTime.Now.Ticks - new DateTime(1970, 1, 1).Ticks) / 10000000L); // Ticks give back 100ns intervals
  172. byte[] gzipHeader = {
  173. // The two magic bytes
  174. (byte) (GZipConstants.GZIP_MAGIC >> 8), (byte) (GZipConstants.GZIP_MAGIC & 0xff),
  175. // The compression type
  176. (byte) Deflater.DEFLATED,
  177. // The flags (not set)
  178. 0,
  179. // The modification time
  180. (byte) mod_time, (byte) (mod_time >> 8),
  181. (byte) (mod_time >> 16), (byte) (mod_time >> 24),
  182. // The extra flags
  183. 0,
  184. // The OS type (unknown)
  185. (byte) 255
  186. };
  187. baseOutputStream_.Write(gzipHeader, 0, gzipHeader.Length);
  188. }
  189. }
  190. #endregion
  191. }
  192. }