LzmaEncodeStream.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. namespace SevenZip
  2. {
  3. using System;
  4. using System.IO;
  5. using SevenZip.Sdk.Compression.Lzma;
  6. /// <summary>
  7. /// The stream which compresses data with LZMA on the fly.
  8. /// </summary>
  9. public class LzmaEncodeStream : Stream
  10. {
  11. private const int MAX_BUFFER_CAPACITY = 1 << 30; //1 Gb
  12. private readonly MemoryStream _buffer = new MemoryStream();
  13. private readonly int _bufferCapacity = 1 << 18; //256 kb
  14. private readonly bool _ownOutput;
  15. private bool _disposed;
  16. private Encoder _lzmaEncoder;
  17. private Stream _output;
  18. /// <summary>
  19. /// Initializes a new instance of the LzmaEncodeStream class.
  20. /// </summary>
  21. public LzmaEncodeStream()
  22. {
  23. _output = new MemoryStream();
  24. _ownOutput = true;
  25. Init();
  26. }
  27. /// <summary>
  28. /// Initializes a new instance of the LzmaEncodeStream class.
  29. /// </summary>
  30. /// <param name="bufferCapacity">The buffer size. The bigger size, the better compression.</param>
  31. public LzmaEncodeStream(int bufferCapacity)
  32. {
  33. _output = new MemoryStream();
  34. _ownOutput = true;
  35. if (bufferCapacity > MAX_BUFFER_CAPACITY)
  36. {
  37. throw new ArgumentException("Too large capacity.", "bufferCapacity");
  38. }
  39. _bufferCapacity = bufferCapacity;
  40. Init();
  41. }
  42. /// <summary>
  43. /// Initializes a new instance of the LzmaEncodeStream class.
  44. /// </summary>
  45. /// <param name="outputStream">An output stream which supports writing.</param>
  46. public LzmaEncodeStream(Stream outputStream)
  47. {
  48. if (!outputStream.CanWrite)
  49. {
  50. throw new ArgumentException("The specified stream can not write.", "outputStream");
  51. }
  52. _output = outputStream;
  53. Init();
  54. }
  55. /// <summary>
  56. /// Initializes a new instance of the LzmaEncodeStream class.
  57. /// </summary>
  58. /// <param name="outputStream">An output stream which supports writing.</param>
  59. /// <param name="bufferCapacity">A buffer size. The bigger size, the better compression.</param>
  60. public LzmaEncodeStream(Stream outputStream, int bufferCapacity)
  61. {
  62. if (!outputStream.CanWrite)
  63. {
  64. throw new ArgumentException("The specified stream can not write.", "outputStream");
  65. }
  66. _output = outputStream;
  67. if (bufferCapacity > 1 << 30)
  68. {
  69. throw new ArgumentException("Too large capacity.", "bufferCapacity");
  70. }
  71. _bufferCapacity = bufferCapacity;
  72. Init();
  73. }
  74. /// <summary>
  75. /// Gets a value indicating whether the current stream supports reading.
  76. /// </summary>
  77. public override bool CanRead => false;
  78. /// <summary>
  79. /// Gets a value indicating whether the current stream supports seeking.
  80. /// </summary>
  81. public override bool CanSeek => false;
  82. /// <summary>
  83. /// Gets a value indicating whether the current stream supports writing.
  84. /// </summary>
  85. public override bool CanWrite
  86. {
  87. get
  88. {
  89. DisposedCheck();
  90. return _buffer.CanWrite;
  91. }
  92. }
  93. /// <summary>
  94. /// Gets the length in bytes of the output stream.
  95. /// </summary>
  96. public override long Length
  97. {
  98. get
  99. {
  100. DisposedCheck();
  101. if (_output.CanSeek)
  102. {
  103. return _output.Length;
  104. }
  105. return _buffer.Position;
  106. }
  107. }
  108. /// <summary>
  109. /// Gets or sets the position within the output stream.
  110. /// </summary>
  111. public override long Position
  112. {
  113. get
  114. {
  115. DisposedCheck();
  116. if (_output.CanSeek)
  117. {
  118. return _output.Position;
  119. }
  120. return _buffer.Position;
  121. }
  122. set => throw new NotSupportedException();
  123. }
  124. private void Init()
  125. {
  126. _buffer.Capacity = _bufferCapacity;
  127. SevenZipCompressor.LzmaDictionarySize = _bufferCapacity;
  128. _lzmaEncoder = new Encoder();
  129. SevenZipCompressor.WriteLzmaProperties(_lzmaEncoder);
  130. }
  131. /// <summary>
  132. /// Checked whether the class was disposed.
  133. /// </summary>
  134. /// <exception cref="System.ObjectDisposedException" />
  135. private void DisposedCheck()
  136. {
  137. if (_disposed)
  138. {
  139. throw new ObjectDisposedException("SevenZipExtractor");
  140. }
  141. }
  142. private void WriteChunk()
  143. {
  144. _lzmaEncoder.WriteCoderProperties(_output);
  145. long streamSize = _buffer.Position;
  146. if (_buffer.Length != _buffer.Position)
  147. {
  148. _buffer.SetLength(_buffer.Position);
  149. }
  150. _buffer.Position = 0;
  151. for (int i = 0; i < 8; i++)
  152. {
  153. _output.WriteByte((byte) (streamSize >> (8*i)));
  154. }
  155. _lzmaEncoder.Code(_buffer, _output, -1, -1, null);
  156. _buffer.Position = 0;
  157. }
  158. /// <summary>
  159. /// Converts the LzmaEncodeStream to the LzmaDecodeStream to read data.
  160. /// </summary>
  161. /// <returns></returns>
  162. public LzmaDecodeStream ToDecodeStream()
  163. {
  164. DisposedCheck();
  165. Flush();
  166. return new LzmaDecodeStream(_output);
  167. }
  168. /// <summary>
  169. /// Clears all buffers for this stream and causes any buffered data to be compressed and written.
  170. /// </summary>
  171. public override void Flush()
  172. {
  173. DisposedCheck();
  174. WriteChunk();
  175. }
  176. /// <summary>
  177. /// Releases all unmanaged resources used by LzmaEncodeStream.
  178. /// </summary>
  179. protected override void Dispose(bool disposing)
  180. {
  181. if (!_disposed)
  182. {
  183. if (disposing)
  184. {
  185. Flush();
  186. _buffer.Close();
  187. if (_ownOutput)
  188. {
  189. _output.Dispose();
  190. }
  191. _output = null;
  192. }
  193. _disposed = true;
  194. }
  195. }
  196. /// <summary>
  197. /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
  198. /// </summary>
  199. /// <param name="buffer">An array of bytes.</param>
  200. /// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream.</param>
  201. /// <param name="count">The maximum number of bytes to be read from the current stream.</param>
  202. /// <returns>The total number of bytes read into the buffer.</returns>
  203. public override int Read(byte[] buffer, int offset, int count)
  204. {
  205. DisposedCheck();
  206. throw new NotSupportedException();
  207. }
  208. /// <summary>
  209. /// Sets the position within the current stream.
  210. /// </summary>
  211. /// <param name="offset">A byte offset relative to the origin parameter.</param>
  212. /// <param name="origin">A value of type System.IO.SeekOrigin indicating the reference point used to obtain the new position.</param>
  213. /// <returns>The new position within the current stream.</returns>
  214. public override long Seek(long offset, SeekOrigin origin)
  215. {
  216. DisposedCheck();
  217. throw new NotSupportedException();
  218. }
  219. /// <summary>
  220. /// Sets the length of the current stream.
  221. /// </summary>
  222. /// <param name="value">The desired length of the current stream in bytes.</param>
  223. public override void SetLength(long value)
  224. {
  225. DisposedCheck();
  226. throw new NotSupportedException();
  227. }
  228. /// <summary>
  229. /// Writes a sequence of bytes to the current stream and compresses it if necessary.
  230. /// </summary>
  231. /// <param name="buffer">An array of bytes.</param>
  232. /// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream.</param>
  233. /// <param name="count">The maximum number of bytes to be read from the current stream.</param>
  234. public override void Write(byte[] buffer, int offset, int count)
  235. {
  236. DisposedCheck();
  237. int dataLength = Math.Min(buffer.Length - offset, count);
  238. while (_buffer.Position + dataLength >= _bufferCapacity)
  239. {
  240. int length = _bufferCapacity - (int) _buffer.Position;
  241. _buffer.Write(buffer, offset, length);
  242. offset = length + offset;
  243. dataLength -= length;
  244. WriteChunk();
  245. }
  246. _buffer.Write(buffer, offset, dataLength);
  247. }
  248. }
  249. }