You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GZipStream.cs 41KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022
  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. /// Set 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. }
  763. /// <summary>
  764. /// Write data to the stream.
  765. /// </summary>
  766. ///
  767. /// <remarks>
  768. /// <para>
  769. /// If you wish to use the <c>GZipStream</c> to compress data while writing,
  770. /// you can create a <c>GZipStream</c> with <c>CompressionMode.Compress</c>, and a
  771. /// writable output stream. Then call <c>Write()</c> on that <c>GZipStream</c>,
  772. /// providing uncompressed data as input. The data sent to the output stream
  773. /// will be the compressed form of the data written.
  774. /// </para>
  775. ///
  776. /// <para>
  777. /// A <c>GZipStream</c> can be used for <c>Read()</c> or <c>Write()</c>, but not
  778. /// both. Writing implies compression. Reading implies decompression.
  779. /// </para>
  780. ///
  781. /// </remarks>
  782. /// <param name="buffer">The buffer holding data to write to the stream.</param>
  783. /// <param name="offset">the offset within that data array to find the first byte to write.</param>
  784. /// <param name="count">the number of bytes to write.</param>
  785. public override void Write(byte[] buffer, int offset, int count)
  786. {
  787. if (_disposed) throw new ObjectDisposedException("GZipStream");
  788. if (_baseStream._streamMode == BestHTTP.Decompression.Zlib.ZlibBaseStream.StreamMode.Undefined)
  789. {
  790. //Console.WriteLine("GZipStream: First write");
  791. if (_baseStream._wantCompress)
  792. {
  793. // first write in compression, therefore, emit the GZIP header
  794. _headerByteCount = EmitHeader();
  795. }
  796. else
  797. {
  798. throw new InvalidOperationException();
  799. }
  800. }
  801. _baseStream.Write(buffer, offset, count);
  802. }
  803. #endregion
  804. internal static readonly System.DateTime _unixEpoch = new System.DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
  805. internal static readonly System.Text.Encoding iso8859dash1 = System.Text.Encoding.GetEncoding("iso-8859-1");
  806. private int EmitHeader()
  807. {
  808. byte[] commentBytes = (Comment == null) ? null : iso8859dash1.GetBytes(Comment);
  809. byte[] filenameBytes = (FileName == null) ? null : iso8859dash1.GetBytes(FileName);
  810. int cbLength = (Comment == null) ? 0 : commentBytes.Length + 1;
  811. int fnLength = (FileName == null) ? 0 : filenameBytes.Length + 1;
  812. int bufferLength = 10 + cbLength + fnLength;
  813. byte[] header = new byte[bufferLength];
  814. int i = 0;
  815. // ID
  816. header[i++] = 0x1F;
  817. header[i++] = 0x8B;
  818. // compression method
  819. header[i++] = 8;
  820. byte flag = 0;
  821. if (Comment != null)
  822. flag ^= 0x10;
  823. if (FileName != null)
  824. flag ^= 0x8;
  825. // flag
  826. header[i++] = flag;
  827. // mtime
  828. if (!LastModified.HasValue) LastModified = DateTime.Now;
  829. System.TimeSpan delta = LastModified.Value - _unixEpoch;
  830. Int32 timet = (Int32)delta.TotalSeconds;
  831. Array.Copy(BitConverter.GetBytes(timet), 0, header, i, 4);
  832. i += 4;
  833. // xflg
  834. header[i++] = 0; // this field is totally useless
  835. // OS
  836. header[i++] = 0xFF; // 0xFF == unspecified
  837. // extra field length - only if FEXTRA is set, which it is not.
  838. //header[i++]= 0;
  839. //header[i++]= 0;
  840. // filename
  841. if (fnLength != 0)
  842. {
  843. Array.Copy(filenameBytes, 0, header, i, fnLength - 1);
  844. i += fnLength - 1;
  845. header[i++] = 0; // terminate
  846. }
  847. // comment
  848. if (cbLength != 0)
  849. {
  850. Array.Copy(commentBytes, 0, header, i, cbLength - 1);
  851. i += cbLength - 1;
  852. header[i++] = 0; // terminate
  853. }
  854. _baseStream._stream.Write(header, 0, header.Length);
  855. return header.Length; // bytes written
  856. }
  857. /// <summary>
  858. /// Compress a string into a byte array using GZip.
  859. /// </summary>
  860. ///
  861. /// <remarks>
  862. /// Uncompress it with <see cref="GZipStream.UncompressString(byte[])"/>.
  863. /// </remarks>
  864. ///
  865. /// <seealso cref="GZipStream.UncompressString(byte[])"/>
  866. /// <seealso cref="GZipStream.CompressBuffer(byte[])"/>
  867. ///
  868. /// <param name="s">
  869. /// A string to compress. The string will first be encoded
  870. /// using UTF8, then compressed.
  871. /// </param>
  872. ///
  873. /// <returns>The string in compressed form</returns>
  874. public static byte[] CompressString(String s)
  875. {
  876. using (var ms = new MemoryStream())
  877. {
  878. System.IO.Stream compressor =
  879. new GZipStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression);
  880. ZlibBaseStream.CompressString(s, compressor);
  881. return ms.ToArray();
  882. }
  883. }
  884. /// <summary>
  885. /// Compress a byte array into a new byte array using GZip.
  886. /// </summary>
  887. ///
  888. /// <remarks>
  889. /// Uncompress it with <see cref="GZipStream.UncompressBuffer(byte[])"/>.
  890. /// </remarks>
  891. ///
  892. /// <seealso cref="GZipStream.CompressString(string)"/>
  893. /// <seealso cref="GZipStream.UncompressBuffer(byte[])"/>
  894. ///
  895. /// <param name="b">
  896. /// A buffer to compress.
  897. /// </param>
  898. ///
  899. /// <returns>The data in compressed form</returns>
  900. public static byte[] CompressBuffer(byte[] b)
  901. {
  902. using (var ms = new MemoryStream())
  903. {
  904. System.IO.Stream compressor =
  905. new GZipStream( ms, CompressionMode.Compress, CompressionLevel.BestCompression );
  906. ZlibBaseStream.CompressBuffer(b, compressor);
  907. return ms.ToArray();
  908. }
  909. }
  910. /// <summary>
  911. /// Uncompress a GZip'ed byte array into a single string.
  912. /// </summary>
  913. ///
  914. /// <seealso cref="GZipStream.CompressString(String)"/>
  915. /// <seealso cref="GZipStream.UncompressBuffer(byte[])"/>
  916. ///
  917. /// <param name="compressed">
  918. /// A buffer containing GZIP-compressed data.
  919. /// </param>
  920. ///
  921. /// <returns>The uncompressed string</returns>
  922. public static String UncompressString(byte[] compressed)
  923. {
  924. using (var input = new MemoryStream(compressed))
  925. {
  926. Stream decompressor = new GZipStream(input, CompressionMode.Decompress);
  927. return ZlibBaseStream.UncompressString(compressed, decompressor);
  928. }
  929. }
  930. /// <summary>
  931. /// Uncompress a GZip'ed byte array into a byte array.
  932. /// </summary>
  933. ///
  934. /// <seealso cref="GZipStream.CompressBuffer(byte[])"/>
  935. /// <seealso cref="GZipStream.UncompressString(byte[])"/>
  936. ///
  937. /// <param name="compressed">
  938. /// A buffer containing data that has been compressed with GZip.
  939. /// </param>
  940. ///
  941. /// <returns>The data in uncompressed form</returns>
  942. public static byte[] UncompressBuffer(byte[] compressed)
  943. {
  944. using (var input = new System.IO.MemoryStream(compressed))
  945. {
  946. System.IO.Stream decompressor =
  947. new GZipStream( input, CompressionMode.Decompress );
  948. return ZlibBaseStream.UncompressBuffer(compressed, decompressor);
  949. }
  950. }
  951. }
  952. }