A circular buffer represented as a stream that one producer can write to and one consumer can read from simultaneously.
Do not use more than one producer or consumer thread simultaneously!
Inheritance: Stream
Esempio n. 1
0
        /// <summary>
        /// Provides a filter for compressing a <see cref="Stream"/> with LZMA.
        /// </summary>
        /// <param name="stream">The underlying <see cref="Stream"/> to write the compressed data to.</param>
        /// <param name="bufferSize">The maximum number of uncompressed bytes to buffer. 32k (the step size of <see cref="SevenZip"/>) is a sensible minimum.</param>
        /// <remarks>
        /// This method internally uses multi-threading and a <see cref="CircularBufferStream"/>.
        /// The <paramref name="stream"/> may be closed with a delay.
        /// </remarks>
        private static Stream GetCompressionStream(Stream stream, int bufferSize = 128 * 1024)
        {
            var bufferStream = new CircularBufferStream(bufferSize);
            var encoder = new Encoder();

            var consumerThread = new Thread(() =>
            {
                try
                {
                    // Write LZMA header
                    encoder.SetCoderProperties(
                        new[] {CoderPropId.DictionarySize, CoderPropId.PosStateBits, CoderPropId.LitContextBits, CoderPropId.LitPosBits, CoderPropId.Algorithm, CoderPropId.NumFastBytes, CoderPropId.MatchFinder, CoderPropId.EndMarker},
                        new object[] {1 << 23, 2, 3, 0, 2, 128, "bt4", true});
                    encoder.WriteCoderProperties(stream);

                    // Write "uncompressed length" header
                    var uncompressedLengthData = BitConverter.GetBytes(TarLzmaExtractor.UnknownSize);
                    if (!BitConverter.IsLittleEndian) Array.Reverse(uncompressedLengthData);
                    stream.Write(uncompressedLengthData);

                    encoder.Code(
                        inStream: bufferStream, outStream: stream,
                        inSize: TarLzmaExtractor.UnknownSize, outSize: TarLzmaExtractor.UnknownSize,
                        progress: null);
                }
                catch (ObjectDisposedException)
                {
                    // If the buffer stream is closed too early the user probably just canceled the compression process
                }
                finally
                {
                    stream.Dispose();
                }
            }) {IsBackground = true};
            consumerThread.Start();

            return new DisposeWarpperStream(bufferStream, disposeHandler: () =>
            {
                bufferStream.DoneWriting();
                consumerThread.Join();
            });
        }
 public void SetUp()
 {
     _stream = new CircularBufferStream(6);
 }
        /// <summary>
        /// Provides a filter for decompressing an LZMA encoded <see cref="Stream"/>.
        /// </summary>
        /// <param name="stream">The underlying <see cref="Stream"/> providing the compressed data.</param>
        /// <param name="bufferSize">The maximum number of uncompressed bytes to buffer. 32k (the step size of <see cref="SevenZip"/>) is a sensible minimum.</param>
        /// <exception cref="IOException">The <paramref name="stream"/> doesn't start with a valid 5-bit LZMA header.</exception>
        /// <remarks>
        /// This method internally uses multi-threading and a <see cref="CircularBufferStream"/>.
        /// The <paramref name="stream"/> may be closed with a delay.
        /// </remarks>
        private static Stream GetDecompressionStream(Stream stream, int bufferSize = 128 * 1024)
        {
            var bufferStream = new CircularBufferStream(bufferSize);
            var decoder = new Decoder();

            // Read LZMA header
            if (stream.CanSeek) stream.Position = 0;
            try
            {
                decoder.SetDecoderProperties(stream.Read(5));
            }
                #region Error handling
            catch (IOException ex)
            {
                // Wrap exception to add context
                throw new IOException(Resources.ArchiveInvalid, ex);
            }
            catch (ApplicationException ex)
            {
                // Wrap exception since only certain exception types are allowed
                throw new IOException(Resources.ArchiveInvalid, ex);
            }
            #endregion

            // Detmerine uncompressed length
            long uncompressedLength = 0;
            if (BitConverter.IsLittleEndian)
            {
                for (int i = 0; i < 8; i++)
                {
                    int v = stream.ReadByte();
                    if (v < 0) throw new IOException(Resources.ArchiveInvalid);

                    uncompressedLength |= ((long)(byte)v) << (8 * i);
                }
            }

            // If the uncompressed length is unknown, use original size * 1.5 as an estimate
            unchecked
            {
                bufferStream.SetLength(uncompressedLength == -1 ? stream.Length : (long)(uncompressedLength * 1.5));
            }

            // Initialize the producer thread that will deliver uncompressed data
            var thread = new Thread(() =>
            {
                try
                {
                    decoder.Code(stream, bufferStream, stream.Length, uncompressedLength, null);
                }
                    #region Error handling
                catch (ThreadAbortException)
                {}
                catch (ObjectDisposedException)
                {
                    // If the buffer stream is closed too early the user probably just canceled the extraction process
                }
                catch (ApplicationException ex)
                {
                    // Wrap exception since only certain exception types are allowed
                    bufferStream.RelayErrorToReader(new IOException(ex.Message, ex));
                }
                    #endregion

                finally
                {
                    bufferStream.DoneWriting();
                }
            }) {IsBackground = true};
            thread.Start();

            return new DisposeWarpperStream(bufferStream, () =>
            {
                // Stop producer thread when the buffer stream is closed
                thread.Abort();
                thread.Join();

                stream.Dispose();
            });
        }
Esempio n. 4
0
        /// <summary>
        /// Provides a filter for decompressing an LZMA encoded <see cref="Stream"/>.
        /// </summary>
        /// <param name="stream">The underlying <see cref="Stream"/> providing the compressed data.</param>
        /// <param name="bufferSize">The maximum number of uncompressed bytes to buffer. 32k (the step size of <see cref="SevenZip"/>) is a sensible minimum.</param>
        /// <exception cref="IOException">The <paramref name="stream"/> doesn't start with a valid 5-bit LZMA header.</exception>
        /// <remarks>
        /// This method internally uses multi-threading and a <see cref="CircularBufferStream"/>.
        /// The <paramref name="stream"/> may be closed with a delay.
        /// </remarks>
        internal static Stream GetDecompressionStream(Stream stream, int bufferSize = 128 * 1024)
        {
            var bufferStream = new CircularBufferStream(bufferSize);
            var decoder = new Decoder();

            // Read LZMA header
            if (stream.CanSeek) stream.Position = 0;
            try
            {
                decoder.SetDecoderProperties(stream.Read(5));
            }
                #region Error handling
            catch (IOException ex)
            {
                // Wrap exception to add context
                throw new IOException(Resources.ArchiveInvalid, ex);
            }
            catch (ApplicationException ex)
            {
                // Wrap exception since only certain exception types are allowed
                throw new IOException(Resources.ArchiveInvalid, ex);
            }
            #endregion

            // Read "uncompressed length" header
            var uncompressedLengthData = stream.Read(8);
            if (!BitConverter.IsLittleEndian) Array.Reverse(uncompressedLengthData);
            long uncompressedLength = BitConverter.ToInt64(uncompressedLengthData, startIndex: 0);

            bufferStream.SetLength((uncompressedLength == UnknownSize)
                ? (long)(stream.Length * 1.5)
                : uncompressedLength);

            var producerThread = new Thread(() =>
            {
                try
                {
                    decoder.Code(
                        inStream: stream, outStream: bufferStream,
                        inSize: UnknownSize, outSize: uncompressedLength,
                        progress: null);
                }
                catch (ThreadAbortException)
                {}
                catch (ObjectDisposedException)
                {
                    // If the buffer stream is closed too early the user probably just canceled the extraction process
                }
                catch (ApplicationException ex)
                {
                    bufferStream.RelayErrorToReader(new IOException(ex.Message, ex));
                }
                finally
                {
                    bufferStream.DoneWriting();
                }
            }) {IsBackground = true};
            producerThread.Start();

            return new DisposeWarpperStream(bufferStream, disposeHandler: () =>
            {
                producerThread.Abort();
                producerThread.Join();
                stream.Dispose();
            });
        }