PageRenderTime 373ms CodeModel.GetById 333ms RepoModel.GetById 12ms app.codeStats 0ms

/Utilities/Compression/Streams/DeflaterOutputStream.cs

#
C# | 586 lines | 333 code | 51 blank | 202 comment | 26 complexity | 0a3ef4efa0bcf319e00336bc82e719cd MD5 | raw file
Possible License(s): Apache-2.0
  1. // Based on Mike Krueger's SharpZipLib, Copyright (C) 2001 (GNU license).
  2. // Authors of the original java version: Jochen Hoenicke, John Leuner
  3. // See http://www.ISeeSharpCode.com for more information.
  4. using System;
  5. using System.IO;
  6. using Delta.Utilities.Compression.Checksums;
  7. using Delta.Utilities.Compression.Deflaters;
  8. namespace Delta.Utilities.Compression.Streams
  9. {
  10. /// <summary>
  11. /// A special stream deflating or compressing the bytes that are
  12. /// written to it. It uses a Deflater to perform actual deflating.<br/>
  13. /// Authors of the original java version: Tom Tromey, Jochen Hoenicke
  14. /// </summary>
  15. public class DeflaterOutputStream : Stream
  16. {
  17. #region Length (Public)
  18. /// <summary>
  19. /// Get current length of stream.
  20. /// </summary>
  21. public override long Length
  22. {
  23. get
  24. {
  25. return baseOutputStream.Length;
  26. } // get
  27. }
  28. #endregion
  29. #region Position (Public)
  30. /// <summary>
  31. /// The current position within the stream. If trying to change position,
  32. /// this property throws a NotSupportedExceptionNotSupportedException.
  33. /// </summary>
  34. /// <exception cref="NotSupportedException">
  35. /// Any attempt to set position
  36. /// </exception>
  37. public override long Position
  38. {
  39. get
  40. {
  41. return baseOutputStream.Position;
  42. } // get
  43. set
  44. {
  45. throw new NotSupportedException("DeflaterOutputStream Position" +
  46. " not supported");
  47. } // set
  48. }
  49. #endregion
  50. #region CanRead (Public)
  51. /// <summary>
  52. /// Gets value indicating stream can be read from
  53. /// </summary>
  54. public override bool CanRead
  55. {
  56. get
  57. {
  58. return baseOutputStream.CanRead;
  59. } // get
  60. }
  61. #endregion
  62. #region CanSeek (Public)
  63. /// <summary>
  64. /// Gets a value indicating if seeking is supported for this stream.
  65. /// This property always returns false
  66. /// </summary>
  67. public override bool CanSeek
  68. {
  69. get
  70. {
  71. return false;
  72. } // get
  73. }
  74. #endregion
  75. #region CanWrite (Public)
  76. /// <summary>
  77. /// Get value indicating if this stream supports writing.
  78. /// </summary>
  79. public override bool CanWrite
  80. {
  81. get
  82. {
  83. return baseOutputStream.CanWrite;
  84. } // get
  85. }
  86. #endregion
  87. #region IsStreamOwner (Public)
  88. /// <summary>
  89. /// Get/set flag indicating ownership of underlying stream.
  90. /// When the flag is true <see cref="Close"></see> will close the
  91. /// underlying stream also.
  92. /// </summary>
  93. public bool IsStreamOwner
  94. {
  95. get
  96. {
  97. return isStreamOwner;
  98. } // get
  99. set
  100. {
  101. isStreamOwner = value;
  102. } // set
  103. }
  104. #endregion
  105. #region CanPatchEntries (Public)
  106. /// <summary>
  107. /// Allows client to determine if an entry can be patched after its added.
  108. /// </summary>
  109. public bool CanPatchEntries
  110. {
  111. get
  112. {
  113. return baseOutputStream.CanSeek;
  114. } // get
  115. }
  116. #endregion
  117. #region Password (Public)
  118. /// <summary>
  119. /// Get/set the password used for encryption.
  120. /// When null no encryption is performed.
  121. /// </summary>
  122. public string Password
  123. {
  124. get
  125. {
  126. return password;
  127. }
  128. set
  129. {
  130. if (value != null &&
  131. value.Length == 0)
  132. {
  133. password = null;
  134. }
  135. else
  136. {
  137. password = value;
  138. }
  139. }
  140. }
  141. #endregion
  142. #region Protected
  143. #region buffer (Protected)
  144. /// <summary>
  145. /// This buffer is used temporarily to retrieve the bytes from the
  146. /// deflater and write them to the underlying output stream.
  147. /// </summary>
  148. protected byte[] buffer;
  149. #endregion
  150. #region deflater (Protected)
  151. /// <summary>
  152. /// The deflater which is used to deflate the stream.
  153. /// </summary>
  154. protected Deflater deflater;
  155. #endregion
  156. #region baseOutputStream (Protected)
  157. /// <summary>
  158. /// Base stream the deflater depends on.
  159. /// </summary>
  160. protected Stream baseOutputStream;
  161. #endregion
  162. #endregion
  163. #region Private
  164. #region isClosed (Private)
  165. /// <summary>
  166. /// Is closed
  167. /// </summary>
  168. /// <returns>False</returns>
  169. private bool isClosed;
  170. #endregion
  171. #region isStreamOwner (Private)
  172. /// <summary>
  173. /// Is stream owner
  174. /// </summary>
  175. /// <returns>True</returns>
  176. private bool isStreamOwner = true;
  177. #endregion
  178. #region password (Private)
  179. /// <summary>
  180. /// Note: Refactor this code: The presence of Zip specific code in this
  181. /// low level class is not good.
  182. /// </summary>
  183. private string password;
  184. #endregion
  185. #region keys (Private)
  186. /// <summary>
  187. /// Keys
  188. /// </summary>
  189. /// <returns>Null</returns>
  190. private uint[] keys;
  191. #endregion
  192. #endregion
  193. #region Constructors
  194. /// <summary>
  195. /// Creates a new DeflaterOutputStream with a default Deflater and default
  196. /// buffer size.
  197. /// </summary>
  198. /// <param name="setBaseOutputStream">
  199. /// the output stream where deflated output should be written.
  200. /// </param>
  201. public DeflaterOutputStream(Stream setBaseOutputStream)
  202. : this(setBaseOutputStream, new Deflater(), 512)
  203. {
  204. }
  205. /// <summary>
  206. /// Creates a new DeflaterOutputStream with the given Deflater and
  207. /// default buffer size.
  208. /// </summary>
  209. /// <param name="setBaseOutputStream">
  210. /// the output stream where deflated output should be written.
  211. /// </param>
  212. /// <param name="setDeflater">
  213. /// the underlying deflater.
  214. /// </param>
  215. public DeflaterOutputStream(Stream setBaseOutputStream,
  216. Deflater setDeflater)
  217. : this(setBaseOutputStream, setDeflater, 512)
  218. {
  219. deflater = setDeflater;
  220. }
  221. /// <summary>
  222. /// Creates a new DeflaterOutputStream with the given Deflater and
  223. /// buffer size.
  224. /// </summary>
  225. /// <param name="baseOutputStream">
  226. /// The output stream where deflated output is written.
  227. /// </param>
  228. /// <param name="deflater">
  229. /// The underlying deflater to use
  230. /// </param>
  231. /// <param name="bufsize">
  232. /// The buffer size to use when deflating
  233. /// </param>
  234. /// <exception cref="ArgumentOutOfRangeException">
  235. /// bufsize is less than or equal to zero.
  236. /// </exception>
  237. /// <exception cref="ArgumentException">
  238. /// baseOutputStream does not support writing
  239. /// </exception>
  240. /// <exception cref="ArgumentNullException">
  241. /// deflater instance is null
  242. /// </exception>
  243. public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater,
  244. int bufsize)
  245. {
  246. if (baseOutputStream.CanWrite == false)
  247. {
  248. throw new ArgumentException("baseOutputStream", "must support writing");
  249. }
  250. if (deflater == null)
  251. {
  252. throw new ArgumentNullException("deflater");
  253. }
  254. if (bufsize <= 0)
  255. {
  256. throw new ArgumentOutOfRangeException("bufsize");
  257. }
  258. this.baseOutputStream = baseOutputStream;
  259. buffer = new byte[bufsize];
  260. //does nothing: deflater = deflater;
  261. }
  262. #endregion
  263. #region Seek (Public)
  264. /// <summary>
  265. /// Sets the current position of this stream to the given value.
  266. /// Not supported by this class!
  267. /// </summary>
  268. /// <exception cref="NotSupportedException">Any access</exception>
  269. public override long Seek(long offset, SeekOrigin origin)
  270. {
  271. throw new NotSupportedException(
  272. "DeflaterOutputStream Seek not supported");
  273. }
  274. #endregion
  275. #region SetLength (Public)
  276. /// <summary>
  277. /// Sets the length of this stream to the given value.
  278. /// Not supported by this class!
  279. /// </summary>
  280. /// <exception cref="NotSupportedException">Any access</exception>
  281. public override void SetLength(long value)
  282. {
  283. throw new NotSupportedException(
  284. "DeflaterOutputStream SetLength not supported");
  285. }
  286. #endregion
  287. #region ReadByte (Public)
  288. /// <summary>
  289. /// Read a byte from stream advancing position by one
  290. /// </summary>
  291. /// <exception cref="NotSupportedException">Any access</exception>
  292. public override int ReadByte()
  293. {
  294. throw new NotSupportedException(
  295. "DeflaterOutputStream ReadByte not supported");
  296. }
  297. #endregion
  298. #region Read (Public)
  299. /// <summary>
  300. /// Read a block of bytes from stream
  301. /// </summary>
  302. /// <exception cref="NotSupportedException">Any access</exception>
  303. public override int Read(byte[] buffer, int offset, int count)
  304. {
  305. throw new NotSupportedException(
  306. "DeflaterOutputStream Read not supported");
  307. }
  308. #endregion
  309. #region BeginRead (Public)
  310. /// <summary>
  311. /// Asynchronous reads are not supported a NotSupportedException is
  312. /// always thrown
  313. /// </summary>
  314. /// <param name="buffer"></param>
  315. /// <param name="offset"></param>
  316. /// <param name="count"></param>
  317. /// <param name="callback"></param>
  318. /// <param name="state"></param>
  319. /// <returns></returns>
  320. /// <exception cref="NotSupportedException">Any access</exception>
  321. public override IAsyncResult BeginRead(
  322. byte[] buffer, int offset, int count,
  323. AsyncCallback callback, object state)
  324. {
  325. throw new NotSupportedException(
  326. "DeflaterOutputStream BeginRead not currently supported");
  327. }
  328. #endregion
  329. #region BeginWrite (Public)
  330. /// <summary>
  331. /// Asynchronous writes arent supported, a NotSupportedException is always
  332. /// thrown.
  333. /// </summary>
  334. /// <param name="buffer"></param>
  335. /// <param name="offset"></param>
  336. /// <param name="count"></param>
  337. /// <param name="callback"></param>
  338. /// <param name="state"></param>
  339. /// <returns></returns>
  340. /// <exception cref="NotSupportedException">Any access</exception>
  341. public override IAsyncResult BeginWrite(
  342. byte[] buffer, int offset, int count,
  343. AsyncCallback callback, object state)
  344. {
  345. throw new NotSupportedException(
  346. "DeflaterOutputStream BeginWrite not currently supported");
  347. }
  348. #endregion
  349. #region Flush (Public)
  350. /// <summary>
  351. /// Flushes the stream by calling flush() on the deflater and then
  352. /// on the underlying stream. This ensures that all bytes are
  353. /// flushed.
  354. /// </summary>
  355. public override void Flush()
  356. {
  357. deflater.Flush();
  358. Deflate();
  359. baseOutputStream.Flush();
  360. }
  361. #endregion
  362. #region Finish (Public)
  363. /// <summary>
  364. /// Finishes the stream by calling finish() on the deflater.
  365. /// </summary>
  366. /// <exception cref="CompressionException">
  367. /// Not all input is deflated
  368. /// </exception>
  369. public virtual void Finish()
  370. {
  371. deflater.Finish();
  372. while (deflater.IsFinished == false)
  373. {
  374. int len = deflater.Deflate(buffer, 0, buffer.Length);
  375. if (len <= 0)
  376. {
  377. break;
  378. }
  379. if (keys != null)
  380. {
  381. EncryptBlock(buffer, 0, len);
  382. }
  383. baseOutputStream.Write(buffer, 0, len);
  384. }
  385. if (!deflater.IsFinished)
  386. {
  387. throw new CompressionException("Can't deflate all input?");
  388. }
  389. baseOutputStream.Flush();
  390. keys = null;
  391. }
  392. #endregion
  393. #region Close (Public)
  394. /// <summary>
  395. /// Calls finish() and closes the underlying
  396. /// stream when <see cref="IsStreamOwner"></see> is true.
  397. /// </summary>
  398. public override void Close()
  399. {
  400. if (isClosed == false)
  401. {
  402. isClosed = true;
  403. Finish();
  404. if (isStreamOwner)
  405. {
  406. baseOutputStream.Close();
  407. }
  408. }
  409. }
  410. #endregion
  411. #region WriteByte (Public)
  412. /// <summary>
  413. /// Writes a single byte to the compressed output stream.
  414. /// </summary>
  415. /// <param name="value">The byte value.</param>
  416. public override void WriteByte(byte value)
  417. {
  418. byte[] b = new byte[1];
  419. b[0] = value;
  420. Write(b, 0, 1);
  421. }
  422. #endregion
  423. #region Write (Public)
  424. /// <summary>
  425. /// Writes bytes from an array to the compressed stream.
  426. /// </summary>
  427. /// <param name="buffer">The byte array</param>
  428. /// <param name="offset">The offset into the byte array where to start.
  429. /// </param>
  430. /// <param name="count">The number of bytes to write.</param>
  431. public override void Write(byte[] buffer, int offset, int count)
  432. {
  433. deflater.SetInput(buffer, offset, count);
  434. Deflate();
  435. }
  436. #endregion
  437. #region Methods (Private)
  438. #region Deflate
  439. /// <summary>
  440. /// Deflates everything in the input buffers. This will call
  441. /// <code>def.deflate()</code> until all bytes from the input buffers
  442. /// are processed.
  443. /// </summary>
  444. protected void Deflate()
  445. {
  446. while (deflater.IsNeedingInput == false)
  447. {
  448. int len = deflater.Deflate(buffer, 0, buffer.Length);
  449. if (len <= 0)
  450. {
  451. break;
  452. }
  453. if (keys != null)
  454. {
  455. EncryptBlock(buffer, 0, len);
  456. }
  457. baseOutputStream.Write(buffer, 0, len);
  458. }
  459. if (deflater.IsNeedingInput == false)
  460. {
  461. throw new CompressionException(
  462. "DeflaterOutputStream can't deflate all input?");
  463. }
  464. }
  465. #endregion
  466. #region EncryptByte
  467. /// <summary>
  468. /// Encrypt a single byte
  469. /// </summary>
  470. /// <returns>
  471. /// The encrypted value
  472. /// </returns>
  473. protected byte EncryptByte()
  474. {
  475. uint temp = ((keys[2] & 0xFFFF) | 2);
  476. return (byte)((temp * (temp ^ 1)) >> 8);
  477. }
  478. #endregion
  479. #region EncryptBlock
  480. /// <summary>
  481. /// Encrypt a block of data
  482. /// </summary>
  483. /// <param name="buffer">
  484. /// Data to encrypt. NOTE the original contents of the buffer are lost
  485. /// </param>
  486. /// <param name="offset">
  487. /// Offset of first byte in buffer to encrypt
  488. /// </param>
  489. /// <param name="length">
  490. /// Number of bytes in buffer to encrypt
  491. /// </param>
  492. protected void EncryptBlock(byte[] buffer, int offset, int length)
  493. {
  494. // Note: refactor to use crypto transform
  495. for (int i = offset; i < offset + length; ++i)
  496. {
  497. byte oldbyte = buffer[i];
  498. buffer[i] ^= EncryptByte();
  499. UpdateKeys(oldbyte);
  500. }
  501. }
  502. #endregion
  503. #region InitializePassword
  504. /// <summary>
  505. /// Initializes encryption keys based on given password
  506. /// </summary>
  507. protected void InitializePassword(string setPassword)
  508. {
  509. keys = new uint[]
  510. {
  511. 0x12345678,
  512. 0x23456789,
  513. 0x34567890
  514. };
  515. for (int i = 0; i < setPassword.Length; ++i)
  516. {
  517. UpdateKeys((byte)setPassword[i]);
  518. }
  519. }
  520. #endregion
  521. #region UpdateKeys
  522. /// <summary>
  523. /// Update encryption keys
  524. /// </summary>
  525. protected void UpdateKeys(byte ch)
  526. {
  527. keys[0] = Crc32.ComputeCrc32(keys[0], ch);
  528. keys[1] = keys[1] + (byte)keys[0];
  529. keys[1] = keys[1] * 134775813 + 1;
  530. keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24));
  531. }
  532. #endregion
  533. #endregion
  534. }
  535. }