GZipStream.cs 40 KB

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