GZipStream.cs 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033
  1. // GZipStream.cs
  2. // ------------------------------------------------------------------
  3. //
  4. // Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
  5. // All rights reserved.
  6. //
  7. // This code module is part of DotNetZip, a zipfile class library.
  8. //
  9. // ------------------------------------------------------------------
  10. //
  11. // This code is licensed under the Microsoft Public License.
  12. // See the file License.txt for the license details.
  13. // More info on: http://dotnetzip.codeplex.com
  14. //
  15. // ------------------------------------------------------------------
  16. //
  17. // last saved (in emacs):
  18. // Time-stamp: <2011-August-08 18:14:39>
  19. //
  20. // ------------------------------------------------------------------
  21. //
  22. // This module defines the GZipStream class, which can be used as a replacement for
  23. // the System.IO.Compression.GZipStream class in the .NET BCL. NB: The design is not
  24. // completely OO clean: there is some intelligence in the ZlibBaseStream that reads the
  25. // GZip header.
  26. //
  27. // ------------------------------------------------------------------
  28. using System;
  29. using System.IO;
  30. namespace Ionic.Zlib
  31. {
  32. /// <summary>
  33. /// A class for compressing and decompressing GZIP streams.
  34. /// </summary>
  35. /// <remarks>
  36. ///
  37. /// <para>
  38. /// The <c>GZipStream</c> is a <see
  39. /// href="http://en.wikipedia.org/wiki/Decorator_pattern">Decorator</see> on a
  40. /// <see cref="Stream"/>. It adds GZIP compression or decompression to any
  41. /// stream.
  42. /// </para>
  43. ///
  44. /// <para>
  45. /// Like the <c>System.IO.Compression.GZipStream</c> in the .NET Base Class Library, the
  46. /// <c>Ionic.Zlib.GZipStream</c> can compress while writing, or decompress while
  47. /// reading, but not vice versa. The compression method used is GZIP, which is
  48. /// documented in <see href="http://www.ietf.org/rfc/rfc1952.txt">IETF RFC
  49. /// 1952</see>, "GZIP file format specification version 4.3".</para>
  50. ///
  51. /// <para>
  52. /// A <c>GZipStream</c> can be used to decompress data (through <c>Read()</c>) or
  53. /// to compress data (through <c>Write()</c>), but not both.
  54. /// </para>
  55. ///
  56. /// <para>
  57. /// If you wish to use the <c>GZipStream</c> to compress data, you must wrap it
  58. /// around a write-able stream. As you call <c>Write()</c> on the <c>GZipStream</c>, the
  59. /// data will be compressed into the GZIP format. If you want to decompress data,
  60. /// you must wrap the <c>GZipStream</c> around a readable stream that contains an
  61. /// IETF RFC 1952-compliant stream. The data will be decompressed as you call
  62. /// <c>Read()</c> on the <c>GZipStream</c>.
  63. /// </para>
  64. ///
  65. /// <para>
  66. /// Though the GZIP format allows data from multiple files to be concatenated
  67. /// together, this stream handles only a single segment of GZIP format, typically
  68. /// representing a single file.
  69. /// </para>
  70. ///
  71. /// <para>
  72. /// This class is similar to <see cref="ZlibStream"/> and <see cref="DeflateStream"/>.
  73. /// <c>ZlibStream</c> handles RFC1950-compliant streams. <see cref="DeflateStream"/>
  74. /// handles RFC1951-compliant streams. This class handles RFC1952-compliant streams.
  75. /// </para>
  76. ///
  77. /// </remarks>
  78. ///
  79. /// <seealso cref="DeflateStream" />
  80. /// <seealso cref="ZlibStream" />
  81. public class GZipStream : System.IO.Stream
  82. {
  83. // GZip format
  84. // source: http://tools.ietf.org/html/rfc1952
  85. //
  86. // header id: 2 bytes 1F 8B
  87. // compress method 1 byte 8= DEFLATE (none other supported)
  88. // flag 1 byte bitfield (See below)
  89. // mtime 4 bytes time_t (seconds since jan 1, 1970 UTC of the file.
  90. // xflg 1 byte 2 = max compress used , 4 = max speed (can be ignored)
  91. // OS 1 byte OS for originating archive. set to 0xFF in compression.
  92. // extra field length 2 bytes optional - only if FEXTRA is set.
  93. // extra field varies
  94. // filename varies optional - if FNAME is set. zero terminated. ISO-8859-1.
  95. // file comment varies optional - if FCOMMENT is set. zero terminated. ISO-8859-1.
  96. // crc16 1 byte optional - present only if FHCRC bit is set
  97. // compressed data varies
  98. // CRC32 4 bytes
  99. // isize 4 bytes data size modulo 2^32
  100. //
  101. // FLG (FLaGs)
  102. // bit 0 FTEXT - indicates file is ASCII text (can be safely ignored)
  103. // bit 1 FHCRC - there is a CRC16 for the header immediately following the header
  104. // bit 2 FEXTRA - extra fields are present
  105. // bit 3 FNAME - the zero-terminated filename is present. encoding; ISO-8859-1.
  106. // bit 4 FCOMMENT - a zero-terminated file comment is present. encoding: ISO-8859-1
  107. // bit 5 reserved
  108. // bit 6 reserved
  109. // bit 7 reserved
  110. //
  111. // On consumption:
  112. // Extra field is a bunch of nonsense and can be safely ignored.
  113. // Header CRC and OS, likewise.
  114. //
  115. // on generation:
  116. // all optional fields get 0, except for the OS, which gets 255.
  117. //
  118. /// <summary>
  119. /// The comment on the GZIP stream.
  120. /// </summary>
  121. ///
  122. /// <remarks>
  123. /// <para>
  124. /// The GZIP format allows for each file to optionally have an associated
  125. /// comment stored with the file. The comment is encoded with the ISO-8859-1
  126. /// code page. To include a comment in a GZIP stream you create, set this
  127. /// property before calling <c>Write()</c> for the first time on the
  128. /// <c>GZipStream</c>.
  129. /// </para>
  130. ///
  131. /// <para>
  132. /// When using <c>GZipStream</c> to decompress, you can retrieve this property
  133. /// after the first call to <c>Read()</c>. If no comment has been set in the
  134. /// GZIP bytestream, the Comment property will return <c>null</c>
  135. /// (<c>Nothing</c> in VB).
  136. /// </para>
  137. /// </remarks>
  138. public String Comment
  139. {
  140. get
  141. {
  142. return _Comment;
  143. }
  144. set
  145. {
  146. if (_disposed) throw new ObjectDisposedException("GZipStream");
  147. _Comment = value;
  148. }
  149. }
  150. /// <summary>
  151. /// The FileName for the GZIP stream.
  152. /// </summary>
  153. ///
  154. /// <remarks>
  155. ///
  156. /// <para>
  157. /// The GZIP format optionally allows each file to have an associated
  158. /// filename. When compressing data (through <c>Write()</c>), set this
  159. /// FileName before calling <c>Write()</c> the first time on the <c>GZipStream</c>.
  160. /// The actual filename is encoded into the GZIP bytestream with the
  161. /// ISO-8859-1 code page, according to RFC 1952. It is the application's
  162. /// responsibility to insure that the FileName can be encoded and decoded
  163. /// correctly with this code page.
  164. /// </para>
  165. ///
  166. /// <para>
  167. /// When decompressing (through <c>Read()</c>), you can retrieve this value
  168. /// any time after the first <c>Read()</c>. In the case where there was no filename
  169. /// encoded into the GZIP bytestream, the property will return <c>null</c> (<c>Nothing</c>
  170. /// in VB).
  171. /// </para>
  172. /// </remarks>
  173. public String FileName
  174. {
  175. get { return _FileName; }
  176. set
  177. {
  178. if (_disposed) throw new ObjectDisposedException("GZipStream");
  179. _FileName = value;
  180. if (_FileName == null) return;
  181. if (_FileName.IndexOf("/") != -1)
  182. {
  183. _FileName = _FileName.Replace("/", "\\");
  184. }
  185. if (_FileName.EndsWith("\\"))
  186. throw new Exception("Illegal filename");
  187. if (_FileName.IndexOf("\\") != -1)
  188. {
  189. // trim any leading path
  190. _FileName = Path.GetFileName(_FileName);
  191. }
  192. }
  193. }
  194. /// <summary>
  195. /// The last modified time for the GZIP stream.
  196. /// </summary>
  197. ///
  198. /// <remarks>
  199. /// GZIP allows the storage of a last modified time with each GZIP entry.
  200. /// When compressing data, you can set this before the first call to
  201. /// <c>Write()</c>. When decompressing, you can retrieve this value any time
  202. /// after the first call to <c>Read()</c>.
  203. /// </remarks>
  204. public DateTime? LastModified;
  205. /// <summary>
  206. /// The CRC on the GZIP stream.
  207. /// </summary>
  208. /// <remarks>
  209. /// This is used for internal error checking. You probably don't need to look at this property.
  210. /// </remarks>
  211. public int Crc32 { get { return _Crc32; } }
  212. private int _headerByteCount;
  213. internal ZlibBaseStream _baseStream;
  214. bool _disposed;
  215. bool _firstReadDone;
  216. string _FileName;
  217. string _Comment;
  218. int _Crc32;
  219. /// <summary>
  220. /// Create a <c>GZipStream</c> using the specified <c>CompressionMode</c>.
  221. /// </summary>
  222. /// <remarks>
  223. ///
  224. /// <para>
  225. /// When mode is <c>CompressionMode.Compress</c>, the <c>GZipStream</c> will use the
  226. /// default compression level.
  227. /// </para>
  228. ///
  229. /// <para>
  230. /// As noted in the class documentation, the <c>CompressionMode</c> (Compress
  231. /// or Decompress) also establishes the "direction" of the stream. A
  232. /// <c>GZipStream</c> with <c>CompressionMode.Compress</c> works only through
  233. /// <c>Write()</c>. A <c>GZipStream</c> with
  234. /// <c>CompressionMode.Decompress</c> works only through <c>Read()</c>.
  235. /// </para>
  236. ///
  237. /// </remarks>
  238. ///
  239. /// <example>
  240. /// This example shows how to use a GZipStream to compress data.
  241. /// <code>
  242. /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
  243. /// {
  244. /// using (var raw = System.IO.File.Create(outputFile))
  245. /// {
  246. /// using (Stream compressor = new GZipStream(raw, CompressionMode.Compress))
  247. /// {
  248. /// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
  249. /// int n;
  250. /// while ((n= input.Read(buffer, 0, buffer.Length)) != 0)
  251. /// {
  252. /// compressor.Write(buffer, 0, n);
  253. /// }
  254. /// }
  255. /// }
  256. /// }
  257. /// </code>
  258. /// <code lang="VB">
  259. /// Dim outputFile As String = (fileToCompress &amp; ".compressed")
  260. /// Using input As Stream = File.OpenRead(fileToCompress)
  261. /// Using raw As FileStream = File.Create(outputFile)
  262. /// Using compressor As Stream = New GZipStream(raw, CompressionMode.Compress)
  263. /// Dim buffer As Byte() = New Byte(4096) {}
  264. /// Dim n As Integer = -1
  265. /// Do While (n &lt;&gt; 0)
  266. /// If (n &gt; 0) Then
  267. /// compressor.Write(buffer, 0, n)
  268. /// End If
  269. /// n = input.Read(buffer, 0, buffer.Length)
  270. /// Loop
  271. /// End Using
  272. /// End Using
  273. /// End Using
  274. /// </code>
  275. /// </example>
  276. ///
  277. /// <example>
  278. /// This example shows how to use a GZipStream to uncompress a file.
  279. /// <code>
  280. /// private void GunZipFile(string filename)
  281. /// {
  282. /// if (!filename.EndsWith(".gz))
  283. /// throw new ArgumentException("filename");
  284. /// var DecompressedFile = filename.Substring(0,filename.Length-3);
  285. /// byte[] working = new byte[WORKING_BUFFER_SIZE];
  286. /// int n= 1;
  287. /// using (System.IO.Stream input = System.IO.File.OpenRead(filename))
  288. /// {
  289. /// using (Stream decompressor= new Ionic.Zlib.GZipStream(input, CompressionMode.Decompress, true))
  290. /// {
  291. /// using (var output = System.IO.File.Create(DecompressedFile))
  292. /// {
  293. /// while (n !=0)
  294. /// {
  295. /// n= decompressor.Read(working, 0, working.Length);
  296. /// if (n > 0)
  297. /// {
  298. /// output.Write(working, 0, n);
  299. /// }
  300. /// }
  301. /// }
  302. /// }
  303. /// }
  304. /// }
  305. /// </code>
  306. ///
  307. /// <code lang="VB">
  308. /// Private Sub GunZipFile(ByVal filename as String)
  309. /// If Not (filename.EndsWith(".gz)) Then
  310. /// Throw New ArgumentException("filename")
  311. /// End If
  312. /// Dim DecompressedFile as String = filename.Substring(0,filename.Length-3)
  313. /// Dim working(WORKING_BUFFER_SIZE) as Byte
  314. /// Dim n As Integer = 1
  315. /// Using input As Stream = File.OpenRead(filename)
  316. /// Using decompressor As Stream = new Ionic.Zlib.GZipStream(input, CompressionMode.Decompress, True)
  317. /// Using output As Stream = File.Create(UncompressedFile)
  318. /// Do
  319. /// n= decompressor.Read(working, 0, working.Length)
  320. /// If n > 0 Then
  321. /// output.Write(working, 0, n)
  322. /// End IF
  323. /// Loop While (n > 0)
  324. /// End Using
  325. /// End Using
  326. /// End Using
  327. /// End Sub
  328. /// </code>
  329. /// </example>
  330. ///
  331. /// <param name="stream">The stream which will be read or written.</param>
  332. /// <param name="mode">Indicates whether the GZipStream will compress or decompress.</param>
  333. public GZipStream(Stream stream, CompressionMode mode)
  334. : this(stream, mode, CompressionLevel.Default, false)
  335. {
  336. }
  337. /// <summary>
  338. /// Create a <c>GZipStream</c> using the specified <c>CompressionMode</c> and
  339. /// the specified <c>CompressionLevel</c>.
  340. /// </summary>
  341. /// <remarks>
  342. ///
  343. /// <para>
  344. /// The <c>CompressionMode</c> (Compress or Decompress) also establishes the
  345. /// "direction" of the stream. A <c>GZipStream</c> with
  346. /// <c>CompressionMode.Compress</c> works only through <c>Write()</c>. A
  347. /// <c>GZipStream</c> with <c>CompressionMode.Decompress</c> works only
  348. /// through <c>Read()</c>.
  349. /// </para>
  350. ///
  351. /// </remarks>
  352. ///
  353. /// <example>
  354. ///
  355. /// This example shows how to use a <c>GZipStream</c> to compress a file into a .gz file.
  356. ///
  357. /// <code>
  358. /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
  359. /// {
  360. /// using (var raw = System.IO.File.Create(fileToCompress + ".gz"))
  361. /// {
  362. /// using (Stream compressor = new GZipStream(raw,
  363. /// CompressionMode.Compress,
  364. /// CompressionLevel.BestCompression))
  365. /// {
  366. /// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
  367. /// int n;
  368. /// while ((n= input.Read(buffer, 0, buffer.Length)) != 0)
  369. /// {
  370. /// compressor.Write(buffer, 0, n);
  371. /// }
  372. /// }
  373. /// }
  374. /// }
  375. /// </code>
  376. ///
  377. /// <code lang="VB">
  378. /// Using input As Stream = File.OpenRead(fileToCompress)
  379. /// Using raw As FileStream = File.Create(fileToCompress &amp; ".gz")
  380. /// Using compressor As Stream = New GZipStream(raw, CompressionMode.Compress, CompressionLevel.BestCompression)
  381. /// Dim buffer As Byte() = New Byte(4096) {}
  382. /// Dim n As Integer = -1
  383. /// Do While (n &lt;&gt; 0)
  384. /// If (n &gt; 0) Then
  385. /// compressor.Write(buffer, 0, n)
  386. /// End If
  387. /// n = input.Read(buffer, 0, buffer.Length)
  388. /// Loop
  389. /// End Using
  390. /// End Using
  391. /// End Using
  392. /// </code>
  393. /// </example>
  394. /// <param name="stream">The stream to be read or written while deflating or inflating.</param>
  395. /// <param name="mode">Indicates whether the <c>GZipStream</c> will compress or decompress.</param>
  396. /// <param name="level">A tuning knob to trade speed for effectiveness.</param>
  397. public GZipStream(Stream stream, CompressionMode mode, CompressionLevel level)
  398. : this(stream, mode, level, false)
  399. {
  400. }
  401. /// <summary>
  402. /// Create a <c>GZipStream</c> using the specified <c>CompressionMode</c>, and
  403. /// explicitly specify whether the stream should be left open after Deflation
  404. /// or Inflation.
  405. /// </summary>
  406. ///
  407. /// <remarks>
  408. /// <para>
  409. /// This constructor allows the application to request that the captive stream
  410. /// remain open after the deflation or inflation occurs. By default, after
  411. /// <c>Close()</c> is called on the stream, the captive stream is also
  412. /// closed. In some cases this is not desired, for example if the stream is a
  413. /// memory stream that will be re-read after compressed data has been written
  414. /// to it. Specify true for the <paramref name="leaveOpen"/> parameter to leave
  415. /// the stream open.
  416. /// </para>
  417. ///
  418. /// <para>
  419. /// The <see cref="CompressionMode"/> (Compress or Decompress) also
  420. /// establishes the "direction" of the stream. A <c>GZipStream</c> with
  421. /// <c>CompressionMode.Compress</c> works only through <c>Write()</c>. A <c>GZipStream</c>
  422. /// with <c>CompressionMode.Decompress</c> works only through <c>Read()</c>.
  423. /// </para>
  424. ///
  425. /// <para>
  426. /// The <c>GZipStream</c> will use the default compression level. If you want
  427. /// to specify the compression level, see <see cref="GZipStream(Stream,
  428. /// CompressionMode, CompressionLevel, bool)"/>.
  429. /// </para>
  430. ///
  431. /// <para>
  432. /// See the other overloads of this constructor for example code.
  433. /// </para>
  434. ///
  435. /// </remarks>
  436. ///
  437. /// <param name="stream">
  438. /// The stream which will be read or written. This is called the "captive"
  439. /// stream in other places in this documentation.
  440. /// </param>
  441. ///
  442. /// <param name="mode">Indicates whether the GZipStream will compress or decompress.
  443. /// </param>
  444. ///
  445. /// <param name="leaveOpen">
  446. /// true if the application would like the base stream to remain open after
  447. /// inflation/deflation.
  448. /// </param>
  449. public GZipStream(Stream stream, CompressionMode mode, bool leaveOpen)
  450. : this(stream, mode, CompressionLevel.Default, leaveOpen)
  451. {
  452. }
  453. /// <summary>
  454. /// Create a <c>GZipStream</c> using the specified <c>CompressionMode</c> and the
  455. /// specified <c>CompressionLevel</c>, and explicitly specify whether the
  456. /// stream should be left open after Deflation or Inflation.
  457. /// </summary>
  458. ///
  459. /// <remarks>
  460. ///
  461. /// <para>
  462. /// This constructor allows the application to request that the captive stream
  463. /// remain open after the deflation or inflation occurs. By default, after
  464. /// <c>Close()</c> is called on the stream, the captive stream is also
  465. /// closed. In some cases this is not desired, for example if the stream is a
  466. /// memory stream that will be re-read after compressed data has been written
  467. /// to it. Specify true for the <paramref name="leaveOpen"/> parameter to
  468. /// leave the stream open.
  469. /// </para>
  470. ///
  471. /// <para>
  472. /// As noted in the class documentation, the <c>CompressionMode</c> (Compress
  473. /// or Decompress) also establishes the "direction" of the stream. A
  474. /// <c>GZipStream</c> with <c>CompressionMode.Compress</c> works only through
  475. /// <c>Write()</c>. A <c>GZipStream</c> with <c>CompressionMode.Decompress</c> works only
  476. /// through <c>Read()</c>.
  477. /// </para>
  478. ///
  479. /// </remarks>
  480. ///
  481. /// <example>
  482. /// This example shows how to use a <c>GZipStream</c> to compress data.
  483. /// <code>
  484. /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress))
  485. /// {
  486. /// using (var raw = System.IO.File.Create(outputFile))
  487. /// {
  488. /// using (Stream compressor = new GZipStream(raw, CompressionMode.Compress, CompressionLevel.BestCompression, true))
  489. /// {
  490. /// byte[] buffer = new byte[WORKING_BUFFER_SIZE];
  491. /// int n;
  492. /// while ((n= input.Read(buffer, 0, buffer.Length)) != 0)
  493. /// {
  494. /// compressor.Write(buffer, 0, n);
  495. /// }
  496. /// }
  497. /// }
  498. /// }
  499. /// </code>
  500. /// <code lang="VB">
  501. /// Dim outputFile As String = (fileToCompress &amp; ".compressed")
  502. /// Using input As Stream = File.OpenRead(fileToCompress)
  503. /// Using raw As FileStream = File.Create(outputFile)
  504. /// Using compressor As Stream = New GZipStream(raw, CompressionMode.Compress, CompressionLevel.BestCompression, True)
  505. /// Dim buffer As Byte() = New Byte(4096) {}
  506. /// Dim n As Integer = -1
  507. /// Do While (n &lt;&gt; 0)
  508. /// If (n &gt; 0) Then
  509. /// compressor.Write(buffer, 0, n)
  510. /// End If
  511. /// n = input.Read(buffer, 0, buffer.Length)
  512. /// Loop
  513. /// End Using
  514. /// End Using
  515. /// End Using
  516. /// </code>
  517. /// </example>
  518. /// <param name="stream">The stream which will be read or written.</param>
  519. /// <param name="mode">Indicates whether the GZipStream will compress or decompress.</param>
  520. /// <param name="leaveOpen">true if the application would like the stream to remain open after inflation/deflation.</param>
  521. /// <param name="level">A tuning knob to trade speed for effectiveness.</param>
  522. public GZipStream(Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen)
  523. {
  524. _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.GZIP, leaveOpen);
  525. }
  526. #region Zlib properties
  527. /// <summary>
  528. /// This property sets the flush behavior on the stream.
  529. /// </summary>
  530. virtual public FlushType FlushMode
  531. {
  532. get { return (this._baseStream._flushMode); }
  533. set {
  534. if (_disposed) throw new ObjectDisposedException("GZipStream");
  535. this._baseStream._flushMode = value;
  536. }
  537. }
  538. /// <summary>
  539. /// The size of the working buffer for the compression codec.
  540. /// </summary>
  541. ///
  542. /// <remarks>
  543. /// <para>
  544. /// The working buffer is used for all stream operations. The default size is
  545. /// 1024 bytes. The minimum size is 128 bytes. You may get better performance
  546. /// with a larger buffer. Then again, you might not. You would have to test
  547. /// it.
  548. /// </para>
  549. ///
  550. /// <para>
  551. /// Set this before the first call to <c>Read()</c> or <c>Write()</c> on the
  552. /// stream. If you try to set it afterwards, it will throw.
  553. /// </para>
  554. /// </remarks>
  555. public int BufferSize
  556. {
  557. get
  558. {
  559. return this._baseStream._bufferSize;
  560. }
  561. set
  562. {
  563. if (_disposed) throw new ObjectDisposedException("GZipStream");
  564. if (this._baseStream._workingBuffer != null)
  565. throw new ZlibException("The working buffer is already set.");
  566. if (value < ZlibConstants.WorkingBufferSizeMin)
  567. throw new ZlibException(String.Format("Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", value, ZlibConstants.WorkingBufferSizeMin));
  568. this._baseStream._bufferSize = value;
  569. }
  570. }
  571. /// <summary> Returns the total number of bytes input so far.</summary>
  572. virtual public long TotalIn
  573. {
  574. get
  575. {
  576. return this._baseStream._z.TotalBytesIn;
  577. }
  578. }
  579. /// <summary> Returns the total number of bytes output so far.</summary>
  580. virtual public long TotalOut
  581. {
  582. get
  583. {
  584. return this._baseStream._z.TotalBytesOut;
  585. }
  586. }
  587. #endregion
  588. #region Stream methods
  589. /// <summary>
  590. /// Dispose the stream.
  591. /// </summary>
  592. /// <remarks>
  593. /// <para>
  594. /// This may or may not result in a <c>Close()</c> call on the captive
  595. /// stream. See the constructors that have a <c>leaveOpen</c> parameter
  596. /// for more information.
  597. /// </para>
  598. /// <para>
  599. /// This method may be invoked in two distinct scenarios. If disposing
  600. /// == true, the method has been called directly or indirectly by a
  601. /// user's code, for example via the public Dispose() method. In this
  602. /// case, both managed and unmanaged resources can be referenced and
  603. /// disposed. If disposing == false, the method has been called by the
  604. /// runtime from inside the object finalizer and this method should not
  605. /// reference other objects; in that case only unmanaged resources must
  606. /// be referenced or disposed.
  607. /// </para>
  608. /// </remarks>
  609. /// <param name="disposing">
  610. /// indicates whether the Dispose method was invoked by user code.
  611. /// </param>
  612. protected override void Dispose(bool disposing)
  613. {
  614. try
  615. {
  616. if (!_disposed)
  617. {
  618. if (disposing && (this._baseStream != null))
  619. {
  620. this._baseStream.Dispose();
  621. this._Crc32 = _baseStream.Crc32;
  622. }
  623. _disposed = true;
  624. }
  625. }
  626. finally
  627. {
  628. base.Dispose(disposing);
  629. }
  630. }
  631. /// <summary>
  632. /// Indicates whether the stream can be read.
  633. /// </summary>
  634. /// <remarks>
  635. /// The return value depends on whether the captive stream supports reading.
  636. /// </remarks>
  637. public override bool CanRead
  638. {
  639. get
  640. {
  641. if (_disposed) throw new ObjectDisposedException("GZipStream");
  642. return _baseStream._stream.CanRead;
  643. }
  644. }
  645. /// <summary>
  646. /// Indicates whether the stream supports Seek operations.
  647. /// </summary>
  648. /// <remarks>
  649. /// Always returns false.
  650. /// </remarks>
  651. public override bool CanSeek
  652. {
  653. get { return false; }
  654. }
  655. /// <summary>
  656. /// Indicates whether the stream can be written.
  657. /// </summary>
  658. /// <remarks>
  659. /// The return value depends on whether the captive stream supports writing.
  660. /// </remarks>
  661. public override bool CanWrite
  662. {
  663. get
  664. {
  665. if (_disposed) throw new ObjectDisposedException("GZipStream");
  666. return _baseStream._stream.CanWrite;
  667. }
  668. }
  669. /// <summary>
  670. /// Flush the stream.
  671. /// </summary>
  672. public override void Flush()
  673. {
  674. if (_disposed) throw new ObjectDisposedException("GZipStream");
  675. _baseStream.Flush();
  676. }
  677. /// <summary>
  678. /// Reading this property always throws a <see cref="NotImplementedException"/>.
  679. /// </summary>
  680. public override long Length
  681. {
  682. get { throw new NotImplementedException(); }
  683. }
  684. /// <summary>
  685. /// The position of the stream pointer.
  686. /// </summary>
  687. ///
  688. /// <remarks>
  689. /// Setting this property always throws a <see
  690. /// cref="NotImplementedException"/>. Reading will return the total bytes
  691. /// written out, if used in writing, or the total bytes read in, if used in
  692. /// reading. The count may refer to compressed bytes or uncompressed bytes,
  693. /// depending on how you've used the stream.
  694. /// </remarks>
  695. public override long Position
  696. {
  697. get
  698. {
  699. if (this._baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Writer)
  700. return this._baseStream._z.TotalBytesOut + _headerByteCount;
  701. if (this._baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Reader)
  702. return this._baseStream._z.TotalBytesIn + this._baseStream._gzipHeaderByteCount;
  703. return 0;
  704. }
  705. set { throw new NotImplementedException(); }
  706. }
  707. /// <summary>
  708. /// Read and decompress data from the source stream.
  709. /// </summary>
  710. ///
  711. /// <remarks>
  712. /// With a <c>GZipStream</c>, decompression is done through reading.
  713. /// </remarks>
  714. ///
  715. /// <example>
  716. /// <code>
  717. /// byte[] working = new byte[WORKING_BUFFER_SIZE];
  718. /// using (System.IO.Stream input = System.IO.File.OpenRead(_CompressedFile))
  719. /// {
  720. /// using (Stream decompressor= new Ionic.Zlib.GZipStream(input, CompressionMode.Decompress, true))
  721. /// {
  722. /// using (var output = System.IO.File.Create(_DecompressedFile))
  723. /// {
  724. /// int n;
  725. /// while ((n= decompressor.Read(working, 0, working.Length)) !=0)
  726. /// {
  727. /// output.Write(working, 0, n);
  728. /// }
  729. /// }
  730. /// }
  731. /// }
  732. /// </code>
  733. /// </example>
  734. /// <param name="buffer">The buffer into which the decompressed data should be placed.</param>
  735. /// <param name="offset">the offset within that data array to put the first byte read.</param>
  736. /// <param name="count">the number of bytes to read.</param>
  737. /// <returns>the number of bytes actually read</returns>
  738. public override int Read(byte[] buffer, int offset, int count)
  739. {
  740. if (_disposed) throw new ObjectDisposedException("GZipStream");
  741. int n = _baseStream.Read(buffer, offset, count);
  742. // Console.WriteLine("GZipStream::Read(buffer, off({0}), c({1}) = {2}", offset, count, n);
  743. // Console.WriteLine( Util.FormatByteArray(buffer, offset, n) );
  744. if (!_firstReadDone)
  745. {
  746. _firstReadDone = true;
  747. FileName = _baseStream._GzipFileName;
  748. Comment = _baseStream._GzipComment;
  749. }
  750. return n;
  751. }
  752. /// <summary>
  753. /// Calling this method always throws a <see cref="NotImplementedException"/>.
  754. /// </summary>
  755. /// <param name="offset">irrelevant; it will always throw!</param>
  756. /// <param name="origin">irrelevant; it will always throw!</param>
  757. /// <returns>irrelevant!</returns>
  758. public override long Seek(long offset, SeekOrigin origin)
  759. {
  760. throw new NotImplementedException();
  761. }
  762. /// <summary>
  763. /// Calling this method always throws a <see cref="NotImplementedException"/>.
  764. /// </summary>
  765. /// <param name="value">irrelevant; this method will always throw!</param>
  766. public override void SetLength(long value)
  767. {
  768. throw new NotImplementedException();
  769. }
  770. /// <summary>
  771. /// Write data to the stream.
  772. /// </summary>
  773. ///
  774. /// <remarks>
  775. /// <para>
  776. /// If you wish to use the <c>GZipStream</c> to compress data while writing,
  777. /// you can create a <c>GZipStream</c> with <c>CompressionMode.Compress</c>, and a
  778. /// writable output stream. Then call <c>Write()</c> on that <c>GZipStream</c>,
  779. /// providing uncompressed data as input. The data sent to the output stream
  780. /// will be the compressed form of the data written.
  781. /// </para>
  782. ///
  783. /// <para>
  784. /// A <c>GZipStream</c> can be used for <c>Read()</c> or <c>Write()</c>, but not
  785. /// both. Writing implies compression. Reading implies decompression.
  786. /// </para>
  787. ///
  788. /// </remarks>
  789. /// <param name="buffer">The buffer holding data to write to the stream.</param>
  790. /// <param name="offset">the offset within that data array to find the first byte to write.</param>
  791. /// <param name="count">the number of bytes to write.</param>
  792. public override void Write(byte[] buffer, int offset, int count)
  793. {
  794. if (_disposed) throw new ObjectDisposedException("GZipStream");
  795. if (_baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Undefined)
  796. {
  797. //Console.WriteLine("GZipStream: First write");
  798. if (_baseStream._wantCompress)
  799. {
  800. // first write in compression, therefore, emit the GZIP header
  801. _headerByteCount = EmitHeader();
  802. }
  803. else
  804. {
  805. throw new InvalidOperationException();
  806. }
  807. }
  808. _baseStream.Write(buffer, offset, count);
  809. }
  810. #endregion
  811. internal static readonly System.DateTime _unixEpoch = new System.DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
  812. #if !PCL && (SILVERLIGHT || NETCF)
  813. internal static readonly System.Text.Encoding iso8859dash1 = new Ionic.Encoding.Iso8859Dash1Encoding();
  814. #else
  815. internal static readonly System.Text.Encoding iso8859dash1 = System.Text.Encoding.GetEncoding("iso-8859-1");
  816. #endif
  817. private int EmitHeader()
  818. {
  819. byte[] commentBytes = (Comment == null) ? null : iso8859dash1.GetBytes(Comment);
  820. byte[] filenameBytes = (FileName == null) ? null : iso8859dash1.GetBytes(FileName);
  821. int cbLength = (Comment == null) ? 0 : commentBytes.Length + 1;
  822. int fnLength = (FileName == null) ? 0 : filenameBytes.Length + 1;
  823. int bufferLength = 10 + cbLength + fnLength;
  824. byte[] header = new byte[bufferLength];
  825. int i = 0;
  826. // ID
  827. header[i++] = 0x1F;
  828. header[i++] = 0x8B;
  829. // compression method
  830. header[i++] = 8;
  831. byte flag = 0;
  832. if (Comment != null)
  833. flag ^= 0x10;
  834. if (FileName != null)
  835. flag ^= 0x8;
  836. // flag
  837. header[i++] = flag;
  838. // mtime
  839. if (!LastModified.HasValue) LastModified = DateTime.Now;
  840. System.TimeSpan delta = LastModified.Value - _unixEpoch;
  841. Int32 timet = (Int32)delta.TotalSeconds;
  842. Array.Copy(BitConverter.GetBytes(timet), 0, header, i, 4);
  843. i += 4;
  844. // xflg
  845. header[i++] = 0; // this field is totally useless
  846. // OS
  847. header[i++] = 0xFF; // 0xFF == unspecified
  848. // extra field length - only if FEXTRA is set, which it is not.
  849. //header[i++]= 0;
  850. //header[i++]= 0;
  851. // filename
  852. if (fnLength != 0)
  853. {
  854. Array.Copy(filenameBytes, 0, header, i, fnLength - 1);
  855. i += fnLength - 1;
  856. header[i++] = 0; // terminate
  857. }
  858. // comment
  859. if (cbLength != 0)
  860. {
  861. Array.Copy(commentBytes, 0, header, i, cbLength - 1);
  862. i += cbLength - 1;
  863. header[i++] = 0; // terminate
  864. }
  865. _baseStream._stream.Write(header, 0, header.Length);
  866. return header.Length; // bytes written
  867. }
  868. /// <summary>
  869. /// Compress a string into a byte array using GZip.
  870. /// </summary>
  871. ///
  872. /// <remarks>
  873. /// Uncompress it with <see cref="GZipStream.UncompressString(byte[])"/>.
  874. /// </remarks>
  875. ///
  876. /// <seealso cref="GZipStream.UncompressString(byte[])"/>
  877. /// <seealso cref="GZipStream.CompressBuffer(byte[])"/>
  878. ///
  879. /// <param name="s">
  880. /// A string to compress. The string will first be encoded
  881. /// using UTF8, then compressed.
  882. /// </param>
  883. ///
  884. /// <returns>The string in compressed form</returns>
  885. public static byte[] CompressString(String s)
  886. {
  887. using (var ms = new MemoryStream())
  888. {
  889. System.IO.Stream compressor =
  890. new GZipStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression);
  891. ZlibBaseStream.CompressString(s, compressor);
  892. return ms.ToArray();
  893. }
  894. }
  895. /// <summary>
  896. /// Compress a byte array into a new byte array using GZip.
  897. /// </summary>
  898. ///
  899. /// <remarks>
  900. /// Uncompress it with <see cref="GZipStream.UncompressBuffer(byte[])"/>.
  901. /// </remarks>
  902. ///
  903. /// <seealso cref="GZipStream.CompressString(string)"/>
  904. /// <seealso cref="GZipStream.UncompressBuffer(byte[])"/>
  905. ///
  906. /// <param name="b">
  907. /// A buffer to compress.
  908. /// </param>
  909. ///
  910. /// <returns>The data in compressed form</returns>
  911. public static byte[] CompressBuffer(byte[] b)
  912. {
  913. using (var ms = new MemoryStream())
  914. {
  915. System.IO.Stream compressor =
  916. new GZipStream( ms, CompressionMode.Compress, CompressionLevel.BestCompression );
  917. ZlibBaseStream.CompressBuffer(b, compressor);
  918. return ms.ToArray();
  919. }
  920. }
  921. /// <summary>
  922. /// Uncompress a GZip'ed byte array into a single string.
  923. /// </summary>
  924. ///
  925. /// <seealso cref="GZipStream.CompressString(String)"/>
  926. /// <seealso cref="GZipStream.UncompressBuffer(byte[])"/>
  927. ///
  928. /// <param name="compressed">
  929. /// A buffer containing GZIP-compressed data.
  930. /// </param>
  931. ///
  932. /// <returns>The uncompressed string</returns>
  933. public static String UncompressString(byte[] compressed)
  934. {
  935. using (var input = new MemoryStream(compressed))
  936. {
  937. Stream decompressor = new GZipStream(input, CompressionMode.Decompress);
  938. return ZlibBaseStream.UncompressString(compressed, decompressor);
  939. }
  940. }
  941. /// <summary>
  942. /// Uncompress a GZip'ed byte array into a byte array.
  943. /// </summary>
  944. ///
  945. /// <seealso cref="GZipStream.CompressBuffer(byte[])"/>
  946. /// <seealso cref="GZipStream.UncompressString(byte[])"/>
  947. ///
  948. /// <param name="compressed">
  949. /// A buffer containing data that has been compressed with GZip.
  950. /// </param>
  951. ///
  952. /// <returns>The data in uncompressed form</returns>
  953. public static byte[] UncompressBuffer(byte[] compressed)
  954. {
  955. using (var input = new System.IO.MemoryStream(compressed))
  956. {
  957. System.IO.Stream decompressor =
  958. new GZipStream( input, CompressionMode.Decompress );
  959. return ZlibBaseStream.UncompressBuffer(compressed, decompressor);
  960. }
  961. }
  962. }
  963. }