GZipStream.cs 37 KB

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