| //#define Trace | 
 |  | 
 | // ParallelDeflateOutputStream.cs | 
 | // ------------------------------------------------------------------ | 
 | // | 
 | // A DeflateStream that does compression only, it uses a | 
 | // divide-and-conquer approach with multiple threads to exploit multiple | 
 | // CPUs for the DEFLATE computation. | 
 | // | 
 | // last saved: <2011-July-31 14:49:40> | 
 | // | 
 | // ------------------------------------------------------------------ | 
 | // | 
 | // Copyright (c) 2009-2011 by Dino Chiesa | 
 | // All rights reserved! | 
 | // | 
 | // This code module is part of DotNetZip, a zipfile class library. | 
 | // | 
 | // ------------------------------------------------------------------ | 
 | // | 
 | // This code is licensed under the Microsoft Public License. | 
 | // See the file License.txt for the license details. | 
 | // More info on: http://dotnetzip.codeplex.com | 
 | // | 
 | // ------------------------------------------------------------------ | 
 |  | 
 | using System; | 
 | using System.Collections.Generic; | 
 | using System.Threading; | 
 | using System.IO; | 
 |  | 
 |  | 
 | namespace OfficeOpenXml.Packaging.Ionic.Zlib | 
 | { | 
 |     internal class WorkItem | 
 |     { | 
 |         public byte[] buffer; | 
 |         public byte[] compressed; | 
 |         public int crc; | 
 |         public int index; | 
 |         public int ordinal; | 
 |         public int inputBytesAvailable; | 
 |         public int compressedBytesAvailable; | 
 |         public ZlibCodec compressor; | 
 |  | 
 |         public WorkItem(int size, | 
 |                         Ionic.Zlib.CompressionLevel compressLevel, | 
 |                         CompressionStrategy strategy, | 
 |                         int ix) | 
 |         { | 
 |             this.buffer= new byte[size]; | 
 |             // alloc 5 bytes overhead for every block (margin of safety= 2) | 
 |             int n = size + ((size / 32768)+1) * 5 * 2; | 
 |             this.compressed = new byte[n]; | 
 |             this.compressor = new ZlibCodec(); | 
 |             this.compressor.InitializeDeflate(compressLevel, false); | 
 |             this.compressor.OutputBuffer = this.compressed; | 
 |             this.compressor.InputBuffer = this.buffer; | 
 |             this.index = ix; | 
 |         } | 
 |     } | 
 |  | 
 |     /// <summary> | 
 |     ///   A class for compressing streams using the | 
 |     ///   Deflate algorithm with multiple threads. | 
 |     /// </summary> | 
 |     /// | 
 |     /// <remarks> | 
 |     /// <para> | 
 |     ///   This class performs DEFLATE compression through writing.  For | 
 |     ///   more information on the Deflate algorithm, see IETF RFC 1951, | 
 |     ///   "DEFLATE Compressed Data Format Specification version 1.3." | 
 |     /// </para> | 
 |     /// | 
 |     /// <para> | 
 |     ///   This class is similar to <see cref="Ionic.Zlib.DeflateStream"/>, except | 
 |     ///   that this class is for compression only, and this implementation uses an | 
 |     ///   approach that employs multiple worker threads to perform the DEFLATE.  On | 
 |     ///   a multi-cpu or multi-core computer, the performance of this class can be | 
 |     ///   significantly higher than the single-threaded DeflateStream, particularly | 
 |     ///   for larger streams.  How large?  Anything over 10mb is a good candidate | 
 |     ///   for parallel compression. | 
 |     /// </para> | 
 |     /// | 
 |     /// <para> | 
 |     ///   The tradeoff is that this class uses more memory and more CPU than the | 
 |     ///   vanilla DeflateStream, and also is less efficient as a compressor. For | 
 |     ///   large files the size of the compressed data stream can be less than 1% | 
 |     ///   larger than the size of a compressed data stream from the vanialla | 
 |     ///   DeflateStream.  For smaller files the difference can be larger.  The | 
 |     ///   difference will also be larger if you set the BufferSize to be lower than | 
 |     ///   the default value.  Your mileage may vary. Finally, for small files, the | 
 |     ///   ParallelDeflateOutputStream can be much slower than the vanilla | 
 |     ///   DeflateStream, because of the overhead associated to using the thread | 
 |     ///   pool. | 
 |     /// </para> | 
 |     /// | 
 |     /// </remarks> | 
 |     /// <seealso cref="Ionic.Zlib.DeflateStream" /> | 
 |     public class ParallelDeflateOutputStream : System.IO.Stream | 
 |     { | 
 |  | 
 |         private static readonly int IO_BUFFER_SIZE_DEFAULT = 64 * 1024;  // 128k | 
 |         private static readonly int BufferPairsPerCore = 4; | 
 |  | 
 |         private System.Collections.Generic.List<WorkItem> _pool; | 
 |         private bool                        _leaveOpen; | 
 |         private bool                        emitting; | 
 |         private System.IO.Stream            _outStream; | 
 |         private int                         _maxBufferPairs; | 
 |         private int                         _bufferSize = IO_BUFFER_SIZE_DEFAULT; | 
 |         private AutoResetEvent              _newlyCompressedBlob; | 
 |         //private ManualResetEvent            _writingDone; | 
 |         //private ManualResetEvent            _sessionReset; | 
 |         private object                      _outputLock = new object(); | 
 |         private bool                        _isClosed; | 
 |         private bool                        _firstWriteDone; | 
 |         private int                         _currentlyFilling; | 
 |         private int                         _lastFilled; | 
 |         private int                         _lastWritten; | 
 |         private int                         _latestCompressed; | 
 |         private int                         _Crc32; | 
 |         private Ionic.Crc.CRC32             _runningCrc; | 
 |         private object                      _latestLock = new object(); | 
 |         private System.Collections.Generic.Queue<int>     _toWrite; | 
 |         private System.Collections.Generic.Queue<int>     _toFill; | 
 |         private Int64                       _totalBytesProcessed; | 
 |         private Ionic.Zlib.CompressionLevel _compressLevel; | 
 |         private volatile Exception          _pendingException; | 
 |         private bool                        _handlingException; | 
 |         private object                      _eLock = new Object();  // protects _pendingException | 
 |  | 
 |         // This bitfield is used only when Trace is defined. | 
 |         //private TraceBits _DesiredTrace = TraceBits.Write | TraceBits.WriteBegin | | 
 |         //TraceBits.WriteDone | TraceBits.Lifecycle | TraceBits.Fill | TraceBits.Flush | | 
 |         //TraceBits.Session; | 
 |  | 
 |         //private TraceBits _DesiredTrace = TraceBits.WriteBegin | TraceBits.WriteDone | TraceBits.Synch | TraceBits.Lifecycle  | TraceBits.Session ; | 
 |  | 
 |         private TraceBits _DesiredTrace = | 
 |             TraceBits.Session | | 
 |             TraceBits.Compress | | 
 |             TraceBits.WriteTake | | 
 |             TraceBits.WriteEnter | | 
 |             TraceBits.EmitEnter | | 
 |             TraceBits.EmitDone | | 
 |             TraceBits.EmitLock | | 
 |             TraceBits.EmitSkip | | 
 |             TraceBits.EmitBegin; | 
 |  | 
 |         /// <summary> | 
 |         /// Create a ParallelDeflateOutputStream. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         /// | 
 |         /// <para> | 
 |         ///   This stream compresses data written into it via the DEFLATE | 
 |         ///   algorithm (see RFC 1951), and writes out the compressed byte stream. | 
 |         /// </para> | 
 |         /// | 
 |         /// <para> | 
 |         ///   The instance will use the default compression level, the default | 
 |         ///   buffer sizes and the default number of threads and buffers per | 
 |         ///   thread. | 
 |         /// </para> | 
 |         /// | 
 |         /// <para> | 
 |         ///   This class is similar to <see cref="Ionic.Zlib.DeflateStream"/>, | 
 |         ///   except that this implementation uses an approach that employs | 
 |         ///   multiple worker threads to perform the DEFLATE.  On a multi-cpu or | 
 |         ///   multi-core computer, the performance of this class can be | 
 |         ///   significantly higher than the single-threaded DeflateStream, | 
 |         ///   particularly for larger streams.  How large?  Anything over 10mb is | 
 |         ///   a good candidate for parallel compression. | 
 |         /// </para> | 
 |         /// | 
 |         /// </remarks> | 
 |         /// | 
 |         /// <example> | 
 |         /// | 
 |         /// This example shows how to use a ParallelDeflateOutputStream to compress | 
 |         /// data.  It reads a file, compresses it, and writes the compressed data to | 
 |         /// a second, output file. | 
 |         /// | 
 |         /// <code> | 
 |         /// byte[] buffer = new byte[WORKING_BUFFER_SIZE]; | 
 |         /// int n= -1; | 
 |         /// String outputFile = fileToCompress + ".compressed"; | 
 |         /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress)) | 
 |         /// { | 
 |         ///     using (var raw = System.IO.File.Create(outputFile)) | 
 |         ///     { | 
 |         ///         using (Stream compressor = new ParallelDeflateOutputStream(raw)) | 
 |         ///         { | 
 |         ///             while ((n= input.Read(buffer, 0, buffer.Length)) != 0) | 
 |         ///             { | 
 |         ///                 compressor.Write(buffer, 0, n); | 
 |         ///             } | 
 |         ///         } | 
 |         ///     } | 
 |         /// } | 
 |         /// </code> | 
 |         /// <code lang="VB"> | 
 |         /// Dim buffer As Byte() = New Byte(4096) {} | 
 |         /// Dim n As Integer = -1 | 
 |         /// Dim outputFile As String = (fileToCompress & ".compressed") | 
 |         /// Using input As Stream = File.OpenRead(fileToCompress) | 
 |         ///     Using raw As FileStream = File.Create(outputFile) | 
 |         ///         Using compressor As Stream = New ParallelDeflateOutputStream(raw) | 
 |         ///             Do While (n <> 0) | 
 |         ///                 If (n > 0) Then | 
 |         ///                     compressor.Write(buffer, 0, n) | 
 |         ///                 End If | 
 |         ///                 n = input.Read(buffer, 0, buffer.Length) | 
 |         ///             Loop | 
 |         ///         End Using | 
 |         ///     End Using | 
 |         /// End Using | 
 |         /// </code> | 
 |         /// </example> | 
 |         /// <param name="stream">The stream to which compressed data will be written.</param> | 
 |         public ParallelDeflateOutputStream(System.IO.Stream stream) | 
 |             : this(stream, CompressionLevel.Default, CompressionStrategy.Default, false) | 
 |         { | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///   Create a ParallelDeflateOutputStream using the specified CompressionLevel. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         ///   See the <see cref="ParallelDeflateOutputStream(System.IO.Stream)"/> | 
 |         ///   constructor for example code. | 
 |         /// </remarks> | 
 |         /// <param name="stream">The stream to which compressed data will be written.</param> | 
 |         /// <param name="level">A tuning knob to trade speed for effectiveness.</param> | 
 |         public ParallelDeflateOutputStream(System.IO.Stream stream, CompressionLevel level) | 
 |             : this(stream, level, CompressionStrategy.Default, false) | 
 |         { | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// Create a ParallelDeflateOutputStream and specify whether to leave the captive stream open | 
 |         /// when the ParallelDeflateOutputStream is closed. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         ///   See the <see cref="ParallelDeflateOutputStream(System.IO.Stream)"/> | 
 |         ///   constructor for example code. | 
 |         /// </remarks> | 
 |         /// <param name="stream">The stream to which compressed data will be written.</param> | 
 |         /// <param name="leaveOpen"> | 
 |         ///    true if the application would like the stream to remain open after inflation/deflation. | 
 |         /// </param> | 
 |         public ParallelDeflateOutputStream(System.IO.Stream stream, bool leaveOpen) | 
 |             : this(stream, CompressionLevel.Default, CompressionStrategy.Default, leaveOpen) | 
 |         { | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// Create a ParallelDeflateOutputStream and specify whether to leave the captive stream open | 
 |         /// when the ParallelDeflateOutputStream is closed. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         ///   See the <see cref="ParallelDeflateOutputStream(System.IO.Stream)"/> | 
 |         ///   constructor for example code. | 
 |         /// </remarks> | 
 |         /// <param name="stream">The stream to which compressed data will be written.</param> | 
 |         /// <param name="level">A tuning knob to trade speed for effectiveness.</param> | 
 |         /// <param name="leaveOpen"> | 
 |         ///    true if the application would like the stream to remain open after inflation/deflation. | 
 |         /// </param> | 
 |         public ParallelDeflateOutputStream(System.IO.Stream stream, CompressionLevel level, bool leaveOpen) | 
 |             : this(stream, CompressionLevel.Default, CompressionStrategy.Default, leaveOpen) | 
 |         { | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// Create a ParallelDeflateOutputStream using the specified | 
 |         /// CompressionLevel and CompressionStrategy, and specifying whether to | 
 |         /// leave the captive stream open when the ParallelDeflateOutputStream is | 
 |         /// closed. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         ///   See the <see cref="ParallelDeflateOutputStream(System.IO.Stream)"/> | 
 |         ///   constructor for example code. | 
 |         /// </remarks> | 
 |         /// <param name="stream">The stream to which compressed data will be written.</param> | 
 |         /// <param name="level">A tuning knob to trade speed for effectiveness.</param> | 
 |         /// <param name="strategy"> | 
 |         ///   By tweaking this parameter, you may be able to optimize the compression for | 
 |         ///   data with particular characteristics. | 
 |         /// </param> | 
 |         /// <param name="leaveOpen"> | 
 |         ///    true if the application would like the stream to remain open after inflation/deflation. | 
 |         /// </param> | 
 |         public ParallelDeflateOutputStream(System.IO.Stream stream, | 
 |                                            CompressionLevel level, | 
 |                                            CompressionStrategy strategy, | 
 |                                            bool leaveOpen) | 
 |         { | 
 |             TraceOutput(TraceBits.Lifecycle | TraceBits.Session, "-------------------------------------------------------"); | 
 |             TraceOutput(TraceBits.Lifecycle | TraceBits.Session, "Create {0:X8}", this.GetHashCode()); | 
 |             _outStream = stream; | 
 |             _compressLevel= level; | 
 |             Strategy = strategy; | 
 |             _leaveOpen = leaveOpen; | 
 |             this.MaxBufferPairs = 16; // default | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         ///   The ZLIB strategy to be used during compression. | 
 |         /// </summary> | 
 |         /// | 
 |         public CompressionStrategy Strategy | 
 |         { | 
 |             get; | 
 |             private set; | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///   The maximum number of buffer pairs to use. | 
 |         /// </summary> | 
 |         /// | 
 |         /// <remarks> | 
 |         /// <para> | 
 |         ///   This property sets an upper limit on the number of memory buffer | 
 |         ///   pairs to create.  The implementation of this stream allocates | 
 |         ///   multiple buffers to facilitate parallel compression.  As each buffer | 
 |         ///   fills up, this stream uses <see | 
 |         ///   cref="System.Threading.ThreadPool.QueueUserWorkItem(WaitCallback)"> | 
 |         ///   ThreadPool.QueueUserWorkItem()</see> | 
 |         ///   to compress those buffers in a background threadpool thread. After a | 
 |         ///   buffer is compressed, it is re-ordered and written to the output | 
 |         ///   stream. | 
 |         /// </para> | 
 |         /// | 
 |         /// <para> | 
 |         ///   A higher number of buffer pairs enables a higher degree of | 
 |         ///   parallelism, which tends to increase the speed of compression on | 
 |         ///   multi-cpu computers.  On the other hand, a higher number of buffer | 
 |         ///   pairs also implies a larger memory consumption, more active worker | 
 |         ///   threads, and a higher cpu utilization for any compression. This | 
 |         ///   property enables the application to limit its memory consumption and | 
 |         ///   CPU utilization behavior depending on requirements. | 
 |         /// </para> | 
 |         /// | 
 |         /// <para> | 
 |         ///   For each compression "task" that occurs in parallel, there are 2 | 
 |         ///   buffers allocated: one for input and one for output.  This property | 
 |         ///   sets a limit for the number of pairs.  The total amount of storage | 
 |         ///   space allocated for buffering will then be (N*S*2), where N is the | 
 |         ///   number of buffer pairs, S is the size of each buffer (<see | 
 |         ///   cref="BufferSize"/>).  By default, DotNetZip allocates 4 buffer | 
 |         ///   pairs per CPU core, so if your machine has 4 cores, and you retain | 
 |         ///   the default buffer size of 128k, then the | 
 |         ///   ParallelDeflateOutputStream will use 4 * 4 * 2 * 128kb of buffer | 
 |         ///   memory in total, or 4mb, in blocks of 128kb.  If you then set this | 
 |         ///   property to 8, then the number will be 8 * 2 * 128kb of buffer | 
 |         ///   memory, or 2mb. | 
 |         /// </para> | 
 |         /// | 
 |         /// <para> | 
 |         ///   CPU utilization will also go up with additional buffers, because a | 
 |         ///   larger number of buffer pairs allows a larger number of background | 
 |         ///   threads to compress in parallel. If you find that parallel | 
 |         ///   compression is consuming too much memory or CPU, you can adjust this | 
 |         ///   value downward. | 
 |         /// </para> | 
 |         /// | 
 |         /// <para> | 
 |         ///   The default value is 16. Different values may deliver better or | 
 |         ///   worse results, depending on your priorities and the dynamic | 
 |         ///   performance characteristics of your storage and compute resources. | 
 |         /// </para> | 
 |         /// | 
 |         /// <para> | 
 |         ///   This property is not the number of buffer pairs to use; it is an | 
 |         ///   upper limit. An illustration: Suppose you have an application that | 
 |         ///   uses the default value of this property (which is 16), and it runs | 
 |         ///   on a machine with 2 CPU cores. In that case, DotNetZip will allocate | 
 |         ///   4 buffer pairs per CPU core, for a total of 8 pairs.  The upper | 
 |         ///   limit specified by this property has no effect. | 
 |         /// </para> | 
 |         /// | 
 |         /// <para> | 
 |         ///   The application can set this value at any time, but it is effective | 
 |         ///   only before the first call to Write(), which is when the buffers are | 
 |         ///   allocated. | 
 |         /// </para> | 
 |         /// </remarks> | 
 |         public int MaxBufferPairs | 
 |         { | 
 |             get | 
 |             { | 
 |                 return _maxBufferPairs; | 
 |             } | 
 |             set | 
 |             { | 
 |                 if (value < 4) | 
 |                     throw new ArgumentException("MaxBufferPairs", | 
 |                                                 "Value must be 4 or greater."); | 
 |                 _maxBufferPairs = value; | 
 |             } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///   The size of the buffers used by the compressor threads. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         /// | 
 |         /// <para> | 
 |         ///   The default buffer size is 128k. The application can set this value | 
 |         ///   at any time, but it is effective only before the first Write(). | 
 |         /// </para> | 
 |         /// | 
 |         /// <para> | 
 |         ///   Larger buffer sizes implies larger memory consumption but allows | 
 |         ///   more efficient compression. Using smaller buffer sizes consumes less | 
 |         ///   memory but may result in less effective compression.  For example, | 
 |         ///   using the default buffer size of 128k, the compression delivered is | 
 |         ///   within 1% of the compression delivered by the single-threaded <see | 
 |         ///   cref="Ionic.Zlib.DeflateStream"/>.  On the other hand, using a | 
 |         ///   BufferSize of 8k can result in a compressed data stream that is 5% | 
 |         ///   larger than that delivered by the single-threaded | 
 |         ///   <c>DeflateStream</c>.  Excessively small buffer sizes can also cause | 
 |         ///   the speed of the ParallelDeflateOutputStream to drop, because of | 
 |         ///   larger thread scheduling overhead dealing with many many small | 
 |         ///   buffers. | 
 |         /// </para> | 
 |         /// | 
 |         /// <para> | 
 |         ///   The total amount of storage space allocated for buffering will be | 
 |         ///   (N*S*2), where N is the number of buffer pairs, and S is the size of | 
 |         ///   each buffer (this property). There are 2 buffers used by the | 
 |         ///   compressor, one for input and one for output.  By default, DotNetZip | 
 |         ///   allocates 4 buffer pairs per CPU core, so if your machine has 4 | 
 |         ///   cores, then the number of buffer pairs used will be 16. If you | 
 |         ///   accept the default value of this property, 128k, then the | 
 |         ///   ParallelDeflateOutputStream will use 16 * 2 * 128kb of buffer memory | 
 |         ///   in total, or 4mb, in blocks of 128kb.  If you set this property to | 
 |         ///   64kb, then the number will be 16 * 2 * 64kb of buffer memory, or | 
 |         ///   2mb. | 
 |         /// </para> | 
 |         /// | 
 |         /// </remarks> | 
 |         public int BufferSize | 
 |         { | 
 |             get { return _bufferSize;} | 
 |             set | 
 |             { | 
 |                 if (value < 1024) | 
 |                     throw new ArgumentOutOfRangeException("BufferSize", | 
 |                                                           "BufferSize must be greater than 1024 bytes"); | 
 |                 _bufferSize = value; | 
 |             } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// The CRC32 for the data that was written out, prior to compression. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         /// This value is meaningful only after a call to Close(). | 
 |         /// </remarks> | 
 |         public int Crc32 { get { return _Crc32; } } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         /// The total number of uncompressed bytes processed by the ParallelDeflateOutputStream. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         /// This value is meaningful only after a call to Close(). | 
 |         /// </remarks> | 
 |         public Int64 BytesProcessed { get { return _totalBytesProcessed; } } | 
 |  | 
 |  | 
 |         private void _InitializePoolOfWorkItems() | 
 |         { | 
 |             _toWrite = new Queue<int>(); | 
 |             _toFill = new Queue<int>(); | 
 |             _pool = new System.Collections.Generic.List<WorkItem>(); | 
 |             int nTasks = BufferPairsPerCore * Environment.ProcessorCount; | 
 |             nTasks = Math.Min(nTasks, _maxBufferPairs); | 
 |             for(int i=0; i < nTasks; i++) | 
 |             { | 
 |                 _pool.Add(new WorkItem(_bufferSize, _compressLevel, Strategy, i)); | 
 |                 _toFill.Enqueue(i); | 
 |             } | 
 |  | 
 |             _newlyCompressedBlob = new AutoResetEvent(false); | 
 |             _runningCrc = new Ionic.Crc.CRC32(); | 
 |             _currentlyFilling = -1; | 
 |             _lastFilled = -1; | 
 |             _lastWritten = -1; | 
 |             _latestCompressed = -1; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         ///   Write data to the stream. | 
 |         /// </summary> | 
 |         /// | 
 |         /// <remarks> | 
 |         /// | 
 |         /// <para> | 
 |         ///   To use the ParallelDeflateOutputStream to compress data, create a | 
 |         ///   ParallelDeflateOutputStream with CompressionMode.Compress, passing a | 
 |         ///   writable output stream.  Then call Write() on that | 
 |         ///   ParallelDeflateOutputStream, providing uncompressed data as input.  The | 
 |         ///   data sent to the output stream will be the compressed form of the data | 
 |         ///   written. | 
 |         /// </para> | 
 |         /// | 
 |         /// <para> | 
 |         ///   To decompress data, use the <see cref="Ionic.Zlib.DeflateStream"/> class. | 
 |         /// </para> | 
 |         /// | 
 |         /// </remarks> | 
 |         /// <param name="buffer">The buffer holding data to write to the stream.</param> | 
 |         /// <param name="offset">the offset within that data array to find the first byte to write.</param> | 
 |         /// <param name="count">the number of bytes to write.</param> | 
 |         public override void Write(byte[] buffer, int offset, int count) | 
 |         { | 
 |             bool mustWait = false; | 
 |  | 
 |             // This method does this: | 
 |             //   0. handles any pending exceptions | 
 |             //   1. write any buffers that are ready to be written, | 
 |             //   2. fills a work buffer; when full, flip state to 'Filled', | 
 |             //   3. if more data to be written,  goto step 1 | 
 |  | 
 |             if (_isClosed) | 
 |                 throw new InvalidOperationException(); | 
 |  | 
 |             // dispense any exceptions that occurred on the BG threads | 
 |             if (_pendingException != null) | 
 |             { | 
 |                 _handlingException = true; | 
 |                 var pe = _pendingException; | 
 |                 _pendingException = null; | 
 |                 throw pe; | 
 |             } | 
 |  | 
 |             if (count == 0) return; | 
 |  | 
 |             if (!_firstWriteDone) | 
 |             { | 
 |                 // Want to do this on first Write, first session, and not in the | 
 |                 // constructor.  We want to allow MaxBufferPairs to | 
 |                 // change after construction, but before first Write. | 
 |                 _InitializePoolOfWorkItems(); | 
 |                 _firstWriteDone = true; | 
 |             } | 
 |  | 
 |  | 
 |             do | 
 |             { | 
 |                 // may need to make buffers available | 
 |                 EmitPendingBuffers(false, mustWait); | 
 |  | 
 |                 mustWait = false; | 
 |                 // use current buffer, or get a new buffer to fill | 
 |                 int ix = -1; | 
 |                 if (_currentlyFilling >= 0) | 
 |                 { | 
 |                     ix = _currentlyFilling; | 
 |                     TraceOutput(TraceBits.WriteTake, | 
 |                                 "Write    notake   wi({0}) lf({1})", | 
 |                                 ix, | 
 |                                 _lastFilled); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     TraceOutput(TraceBits.WriteTake, "Write    take?"); | 
 |                     if (_toFill.Count == 0) | 
 |                     { | 
 |                         // no available buffers, so... need to emit | 
 |                         // compressed buffers. | 
 |                         mustWait = true; | 
 |                         continue; | 
 |                     } | 
 |  | 
 |                     ix = _toFill.Dequeue(); | 
 |                     TraceOutput(TraceBits.WriteTake, | 
 |                                 "Write    take     wi({0}) lf({1})", | 
 |                                 ix, | 
 |                                 _lastFilled); | 
 |                     ++_lastFilled; // TODO: consider rollover? | 
 |                 } | 
 |  | 
 |                 WorkItem workitem = _pool[ix]; | 
 |  | 
 |                 int limit = ((workitem.buffer.Length - workitem.inputBytesAvailable) > count) | 
 |                     ? count | 
 |                     : (workitem.buffer.Length - workitem.inputBytesAvailable); | 
 |  | 
 |                 workitem.ordinal = _lastFilled; | 
 |  | 
 |                 TraceOutput(TraceBits.Write, | 
 |                             "Write    lock     wi({0}) ord({1}) iba({2})", | 
 |                             workitem.index, | 
 |                             workitem.ordinal, | 
 |                             workitem.inputBytesAvailable | 
 |                             ); | 
 |  | 
 |                 // copy from the provided buffer to our workitem, starting at | 
 |                 // the tail end of whatever data we might have in there currently. | 
 |                 Buffer.BlockCopy(buffer, | 
 |                                  offset, | 
 |                                  workitem.buffer, | 
 |                                  workitem.inputBytesAvailable, | 
 |                                  limit); | 
 |  | 
 |                 count -= limit; | 
 |                 offset += limit; | 
 |                 workitem.inputBytesAvailable += limit; | 
 |                 if (workitem.inputBytesAvailable == workitem.buffer.Length) | 
 |                 { | 
 |                     // No need for interlocked.increment: the Write() | 
 |                     // method is documented as not multi-thread safe, so | 
 |                     // we can assume Write() calls come in from only one | 
 |                     // thread. | 
 |                     TraceOutput(TraceBits.Write, | 
 |                                 "Write    QUWI     wi({0}) ord({1}) iba({2}) nf({3})", | 
 |                                 workitem.index, | 
 |                                 workitem.ordinal, | 
 |                                 workitem.inputBytesAvailable ); | 
 |  | 
 |                     if (!ThreadPool.QueueUserWorkItem( _DeflateOne, workitem )) | 
 |                         throw new Exception("Cannot enqueue workitem"); | 
 |  | 
 |                     _currentlyFilling = -1; // will get a new buffer next time | 
 |                 } | 
 |                 else | 
 |                     _currentlyFilling = ix; | 
 |  | 
 |                 if (count > 0) | 
 |                     TraceOutput(TraceBits.WriteEnter, "Write    more"); | 
 |             } | 
 |             while (count > 0);  // until no more to write | 
 |  | 
 |             TraceOutput(TraceBits.WriteEnter, "Write    exit"); | 
 |             return; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         private void _FlushFinish() | 
 |         { | 
 |             // After writing a series of compressed buffers, each one closed | 
 |             // with Flush.Sync, we now write the final one as Flush.Finish, | 
 |             // and then stop. | 
 |             byte[] buffer = new byte[128]; | 
 |             var compressor = new ZlibCodec(); | 
 |             int rc = compressor.InitializeDeflate(_compressLevel, false); | 
 |             compressor.InputBuffer = null; | 
 |             compressor.NextIn = 0; | 
 |             compressor.AvailableBytesIn = 0; | 
 |             compressor.OutputBuffer = buffer; | 
 |             compressor.NextOut = 0; | 
 |             compressor.AvailableBytesOut = buffer.Length; | 
 |             rc = compressor.Deflate(FlushType.Finish); | 
 |  | 
 |             if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK) | 
 |                 throw new Exception("deflating: " + compressor.Message); | 
 |  | 
 |             if (buffer.Length - compressor.AvailableBytesOut > 0) | 
 |             { | 
 |                 TraceOutput(TraceBits.EmitBegin, | 
 |                             "Emit     begin    flush bytes({0})", | 
 |                             buffer.Length - compressor.AvailableBytesOut); | 
 |  | 
 |                 _outStream.Write(buffer, 0, buffer.Length - compressor.AvailableBytesOut); | 
 |  | 
 |                 TraceOutput(TraceBits.EmitDone, | 
 |                             "Emit     done     flush"); | 
 |             } | 
 |  | 
 |             compressor.EndDeflate(); | 
 |  | 
 |             _Crc32 = _runningCrc.Crc32Result; | 
 |         } | 
 |  | 
 |  | 
 |         private void _Flush(bool lastInput) | 
 |         { | 
 |             if (_isClosed) | 
 |                 throw new InvalidOperationException(); | 
 |  | 
 |             if (emitting) return; | 
 |  | 
 |             // compress any partial buffer | 
 |             if (_currentlyFilling >= 0) | 
 |             { | 
 |                 WorkItem workitem = _pool[_currentlyFilling]; | 
 |                 _DeflateOne(workitem); | 
 |                 _currentlyFilling = -1; // get a new buffer next Write() | 
 |             } | 
 |  | 
 |             if (lastInput) | 
 |             { | 
 |                 EmitPendingBuffers(true, false); | 
 |                 _FlushFinish(); | 
 |             } | 
 |             else | 
 |             { | 
 |                 EmitPendingBuffers(false, false); | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         /// Flush the stream. | 
 |         /// </summary> | 
 |         public override void Flush() | 
 |         { | 
 |             if (_pendingException != null) | 
 |             { | 
 |                 _handlingException = true; | 
 |                 var pe = _pendingException; | 
 |                 _pendingException = null; | 
 |                 throw pe; | 
 |             } | 
 |             if (_handlingException) | 
 |                 return; | 
 |  | 
 |             _Flush(false); | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         /// Close the stream. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         /// You must call Close on the stream to guarantee that all of the data written in has | 
 |         /// been compressed, and the compressed data has been written out. | 
 |         /// </remarks> | 
 |         public override void Close() | 
 |         { | 
 |             TraceOutput(TraceBits.Session, "Close {0:X8}", this.GetHashCode()); | 
 |  | 
 |             if (_pendingException != null) | 
 |             { | 
 |                 _handlingException = true; | 
 |                 var pe = _pendingException; | 
 |                 _pendingException = null; | 
 |                 throw pe; | 
 |             } | 
 |  | 
 |             if (_handlingException) | 
 |                 return; | 
 |  | 
 |             if (_isClosed) return; | 
 |  | 
 |             _Flush(true); | 
 |  | 
 |             if (!_leaveOpen) | 
 |                 _outStream.Close(); | 
 |  | 
 |             _isClosed= true; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         // workitem 10030 - implement a new Dispose method | 
 |  | 
 |         /// <summary>Dispose the object</summary> | 
 |         /// <remarks> | 
 |         ///   <para> | 
 |         ///     Because ParallelDeflateOutputStream is IDisposable, the | 
 |         ///     application must call this method when finished using the instance. | 
 |         ///   </para> | 
 |         ///   <para> | 
 |         ///     This method is generally called implicitly upon exit from | 
 |         ///     a <c>using</c> scope in C# (<c>Using</c> in VB). | 
 |         ///   </para> | 
 |         /// </remarks> | 
 |         new public void Dispose() | 
 |         { | 
 |             TraceOutput(TraceBits.Lifecycle, "Dispose  {0:X8}", this.GetHashCode()); | 
 |             Close(); | 
 |             _pool = null; | 
 |             Dispose(true); | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         /// <summary>The Dispose method</summary> | 
 |         /// <param name="disposing"> | 
 |         ///   indicates whether the Dispose method was invoked by user code. | 
 |         /// </param> | 
 |         protected override void Dispose(bool disposing) | 
 |         { | 
 |             base.Dispose(disposing); | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         ///   Resets the stream for use with another stream. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         ///   Because the ParallelDeflateOutputStream is expensive to create, it | 
 |         ///   has been designed so that it can be recycled and re-used.  You have | 
 |         ///   to call Close() on the stream first, then you can call Reset() on | 
 |         ///   it, to use it again on another stream. | 
 |         /// </remarks> | 
 |         /// | 
 |         /// <param name="stream"> | 
 |         ///   The new output stream for this era. | 
 |         /// </param> | 
 |         /// | 
 |         /// <example> | 
 |         /// <code> | 
 |         /// ParallelDeflateOutputStream deflater = null; | 
 |         /// foreach (var inputFile in listOfFiles) | 
 |         /// { | 
 |         ///     string outputFile = inputFile + ".compressed"; | 
 |         ///     using (System.IO.Stream input = System.IO.File.OpenRead(inputFile)) | 
 |         ///     { | 
 |         ///         using (var outStream = System.IO.File.Create(outputFile)) | 
 |         ///         { | 
 |         ///             if (deflater == null) | 
 |         ///                 deflater = new ParallelDeflateOutputStream(outStream, | 
 |         ///                                                            CompressionLevel.Best, | 
 |         ///                                                            CompressionStrategy.Default, | 
 |         ///                                                            true); | 
 |         ///             deflater.Reset(outStream); | 
 |         /// | 
 |         ///             while ((n= input.Read(buffer, 0, buffer.Length)) != 0) | 
 |         ///             { | 
 |         ///                 deflater.Write(buffer, 0, n); | 
 |         ///             } | 
 |         ///         } | 
 |         ///     } | 
 |         /// } | 
 |         /// </code> | 
 |         /// </example> | 
 |         public void Reset(Stream stream) | 
 |         { | 
 |             TraceOutput(TraceBits.Session, "-------------------------------------------------------"); | 
 |             TraceOutput(TraceBits.Session, "Reset {0:X8} firstDone({1})", this.GetHashCode(), _firstWriteDone); | 
 |  | 
 |             if (!_firstWriteDone) return; | 
 |  | 
 |             // reset all status | 
 |             _toWrite.Clear(); | 
 |             _toFill.Clear(); | 
 |             foreach (var workitem in _pool) | 
 |             { | 
 |                 _toFill.Enqueue(workitem.index); | 
 |                 workitem.ordinal = -1; | 
 |             } | 
 |  | 
 |             _firstWriteDone = false; | 
 |             _totalBytesProcessed = 0L; | 
 |             _runningCrc = new Ionic.Crc.CRC32(); | 
 |             _isClosed= false; | 
 |             _currentlyFilling = -1; | 
 |             _lastFilled = -1; | 
 |             _lastWritten = -1; | 
 |             _latestCompressed = -1; | 
 |             _outStream = stream; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |  | 
 |         private void EmitPendingBuffers(bool doAll, bool mustWait) | 
 |         { | 
 |             // When combining parallel deflation with a ZipSegmentedStream, it's | 
 |             // possible for the ZSS to throw from within this method.  In that | 
 |             // case, Close/Dispose will be called on this stream, if this stream | 
 |             // is employed within a using or try/finally pair as required. But | 
 |             // this stream is unaware of the pending exception, so the Close() | 
 |             // method invokes this method AGAIN.  This can lead to a deadlock. | 
 |             // Therefore, failfast if re-entering. | 
 |  | 
 |             if (emitting) return; | 
 |             emitting = true; | 
 |             if (doAll || mustWait) | 
 |                 _newlyCompressedBlob.WaitOne(); | 
 |  | 
 |             do | 
 |             { | 
 |                 int firstSkip = -1; | 
 |                 int millisecondsToWait = doAll ? 200 : (mustWait ? -1 : 0); | 
 |                 int nextToWrite = -1; | 
 |  | 
 |                 do | 
 |                 { | 
 |                     if (Monitor.TryEnter(_toWrite, millisecondsToWait)) | 
 |                     { | 
 |                         nextToWrite = -1; | 
 |                         try | 
 |                         { | 
 |                             if (_toWrite.Count > 0) | 
 |                                 nextToWrite = _toWrite.Dequeue(); | 
 |                         } | 
 |                         finally | 
 |                         { | 
 |                             Monitor.Exit(_toWrite); | 
 |                         } | 
 |  | 
 |                         if (nextToWrite >= 0) | 
 |                         { | 
 |                             WorkItem workitem = _pool[nextToWrite]; | 
 |                             if (workitem.ordinal != _lastWritten + 1) | 
 |                             { | 
 |                                 // out of order. requeue and try again. | 
 |                                 TraceOutput(TraceBits.EmitSkip, | 
 |                                             "Emit     skip     wi({0}) ord({1}) lw({2}) fs({3})", | 
 |                                             workitem.index, | 
 |                                             workitem.ordinal, | 
 |                                             _lastWritten, | 
 |                                             firstSkip); | 
 |  | 
 |                                 lock(_toWrite) | 
 |                                 { | 
 |                                     _toWrite.Enqueue(nextToWrite); | 
 |                                 } | 
 |  | 
 |                                 if (firstSkip == nextToWrite) | 
 |                                 { | 
 |                                     // We went around the list once. | 
 |                                     // None of the items in the list is the one we want. | 
 |                                     // Now wait for a compressor to signal again. | 
 |                                     _newlyCompressedBlob.WaitOne(); | 
 |                                     firstSkip = -1; | 
 |                                 } | 
 |                                 else if (firstSkip == -1) | 
 |                                     firstSkip = nextToWrite; | 
 |  | 
 |                                 continue; | 
 |                             } | 
 |  | 
 |                             firstSkip = -1; | 
 |  | 
 |                             TraceOutput(TraceBits.EmitBegin, | 
 |                                         "Emit     begin    wi({0}) ord({1})              cba({2})", | 
 |                                         workitem.index, | 
 |                                         workitem.ordinal, | 
 |                                         workitem.compressedBytesAvailable); | 
 |  | 
 |                             _outStream.Write(workitem.compressed, 0, workitem.compressedBytesAvailable); | 
 |                             _runningCrc.Combine(workitem.crc, workitem.inputBytesAvailable); | 
 |                             _totalBytesProcessed += workitem.inputBytesAvailable; | 
 |                             workitem.inputBytesAvailable = 0; | 
 |  | 
 |                             TraceOutput(TraceBits.EmitDone, | 
 |                                         "Emit     done     wi({0}) ord({1})              cba({2}) mtw({3})", | 
 |                                         workitem.index, | 
 |                                         workitem.ordinal, | 
 |                                         workitem.compressedBytesAvailable, | 
 |                                         millisecondsToWait); | 
 |  | 
 |                             _lastWritten = workitem.ordinal; | 
 |                             _toFill.Enqueue(workitem.index); | 
 |  | 
 |                             // don't wait next time through | 
 |                             if (millisecondsToWait == -1) millisecondsToWait = 0; | 
 |                         } | 
 |                     } | 
 |                     else | 
 |                         nextToWrite = -1; | 
 |  | 
 |                 } while (nextToWrite >= 0); | 
 |  | 
 |             //} while (doAll && (_lastWritten != _latestCompressed)); | 
 |             } while (doAll && (_lastWritten != _latestCompressed || _lastWritten != _lastFilled)); | 
 |  | 
 |             emitting = false; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 | #if OLD | 
 |         private void _PerpetualWriterMethod(object state) | 
 |         { | 
 |             TraceOutput(TraceBits.WriterThread, "_PerpetualWriterMethod START"); | 
 |  | 
 |             try | 
 |             { | 
 |                 do | 
 |                 { | 
 |                     // wait for the next session | 
 |                     TraceOutput(TraceBits.Synch | TraceBits.WriterThread, "Synch    _sessionReset.WaitOne(begin) PWM"); | 
 |                     _sessionReset.WaitOne(); | 
 |                     TraceOutput(TraceBits.Synch | TraceBits.WriterThread, "Synch    _sessionReset.WaitOne(done)  PWM"); | 
 |  | 
 |                     if (_isDisposed) break; | 
 |  | 
 |                     TraceOutput(TraceBits.Synch | TraceBits.WriterThread, "Synch    _sessionReset.Reset()        PWM"); | 
 |                     _sessionReset.Reset(); | 
 |  | 
 |                     // repeatedly write buffers as they become ready | 
 |                     WorkItem workitem = null; | 
 |                     Ionic.Zlib.CRC32 c= new Ionic.Zlib.CRC32(); | 
 |                     do | 
 |                     { | 
 |                         workitem = _pool[_nextToWrite % _pc]; | 
 |                         lock(workitem) | 
 |                         { | 
 |                             if (_noMoreInputForThisSegment) | 
 |                                 TraceOutput(TraceBits.Write, | 
 |                                                "Write    drain    wi({0}) stat({1}) canuse({2})  cba({3})", | 
 |                                                workitem.index, | 
 |                                                workitem.status, | 
 |                                                (workitem.status == (int)WorkItem.Status.Compressed), | 
 |                                                workitem.compressedBytesAvailable); | 
 |  | 
 |                             do | 
 |                             { | 
 |                                 if (workitem.status == (int)WorkItem.Status.Compressed) | 
 |                                 { | 
 |                                     TraceOutput(TraceBits.WriteBegin, | 
 |                                                    "Write    begin    wi({0}) stat({1})              cba({2})", | 
 |                                                    workitem.index, | 
 |                                                    workitem.status, | 
 |                                                    workitem.compressedBytesAvailable); | 
 |  | 
 |                                     workitem.status = (int)WorkItem.Status.Writing; | 
 |                                     _outStream.Write(workitem.compressed, 0, workitem.compressedBytesAvailable); | 
 |                                     c.Combine(workitem.crc, workitem.inputBytesAvailable); | 
 |                                     _totalBytesProcessed += workitem.inputBytesAvailable; | 
 |                                     _nextToWrite++; | 
 |                                     workitem.inputBytesAvailable= 0; | 
 |                                     workitem.status = (int)WorkItem.Status.Done; | 
 |  | 
 |                                     TraceOutput(TraceBits.WriteDone, | 
 |                                                    "Write    done     wi({0}) stat({1})              cba({2})", | 
 |                                                    workitem.index, | 
 |                                                    workitem.status, | 
 |                                                    workitem.compressedBytesAvailable); | 
 |  | 
 |  | 
 |                                     Monitor.Pulse(workitem); | 
 |                                     break; | 
 |                                 } | 
 |                                 else | 
 |                                 { | 
 |                                     int wcycles = 0; | 
 |                                     // I've locked a workitem I cannot use. | 
 |                                     // Therefore, wake someone else up, and then release the lock. | 
 |                                     while (workitem.status != (int)WorkItem.Status.Compressed) | 
 |                                     { | 
 |                                         TraceOutput(TraceBits.WriteWait, | 
 |                                                        "Write    waiting  wi({0}) stat({1}) nw({2}) nf({3}) nomore({4})", | 
 |                                                        workitem.index, | 
 |                                                        workitem.status, | 
 |                                                        _nextToWrite, _nextToFill, | 
 |                                                        _noMoreInputForThisSegment ); | 
 |  | 
 |                                         if (_noMoreInputForThisSegment && _nextToWrite == _nextToFill) | 
 |                                             break; | 
 |  | 
 |                                         wcycles++; | 
 |  | 
 |                                         // wake up someone else | 
 |                                         Monitor.Pulse(workitem); | 
 |                                         // release and wait | 
 |                                         Monitor.Wait(workitem); | 
 |  | 
 |                                         if (workitem.status == (int)WorkItem.Status.Compressed) | 
 |                                             TraceOutput(TraceBits.WriteWait, | 
 |                                                            "Write    A-OK     wi({0}) stat({1}) iba({2}) cba({3}) cyc({4})", | 
 |                                                            workitem.index, | 
 |                                                            workitem.status, | 
 |                                                            workitem.inputBytesAvailable, | 
 |                                                            workitem.compressedBytesAvailable, | 
 |                                                            wcycles); | 
 |                                     } | 
 |  | 
 |                                     if (_noMoreInputForThisSegment && _nextToWrite == _nextToFill) | 
 |                                         break; | 
 |  | 
 |                                 } | 
 |                             } | 
 |                             while (true); | 
 |                         } | 
 |  | 
 |                         if (_noMoreInputForThisSegment) | 
 |                             TraceOutput(TraceBits.Write, | 
 |                                            "Write    nomore  nw({0}) nf({1}) break({2})", | 
 |                                            _nextToWrite, _nextToFill, (_nextToWrite == _nextToFill)); | 
 |  | 
 |                         if (_noMoreInputForThisSegment && _nextToWrite == _nextToFill) | 
 |                             break; | 
 |  | 
 |                     } while (true); | 
 |  | 
 |  | 
 |                     // Finish: | 
 |                     // After writing a series of buffers, closing each one with | 
 |                     // Flush.Sync, we now write the final one as Flush.Finish, and | 
 |                     // then stop. | 
 |                     byte[] buffer = new byte[128]; | 
 |                     ZlibCodec compressor = new ZlibCodec(); | 
 |                     int rc = compressor.InitializeDeflate(_compressLevel, false); | 
 |                     compressor.InputBuffer = null; | 
 |                     compressor.NextIn = 0; | 
 |                     compressor.AvailableBytesIn = 0; | 
 |                     compressor.OutputBuffer = buffer; | 
 |                     compressor.NextOut = 0; | 
 |                     compressor.AvailableBytesOut = buffer.Length; | 
 |                     rc = compressor.Deflate(FlushType.Finish); | 
 |  | 
 |                     if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK) | 
 |                         throw new Exception("deflating: " + compressor.Message); | 
 |  | 
 |                     if (buffer.Length - compressor.AvailableBytesOut > 0) | 
 |                     { | 
 |                         TraceOutput(TraceBits.WriteBegin, | 
 |                                        "Write    begin    flush bytes({0})", | 
 |                                        buffer.Length - compressor.AvailableBytesOut); | 
 |  | 
 |                         _outStream.Write(buffer, 0, buffer.Length - compressor.AvailableBytesOut); | 
 |  | 
 |                         TraceOutput(TraceBits.WriteBegin, | 
 |                                        "Write    done     flush"); | 
 |                     } | 
 |  | 
 |                     compressor.EndDeflate(); | 
 |  | 
 |                     _Crc32 = c.Crc32Result; | 
 |  | 
 |                     // signal that writing is complete: | 
 |                     TraceOutput(TraceBits.Synch, "Synch    _writingDone.Set()           PWM"); | 
 |                     _writingDone.Set(); | 
 |                 } | 
 |                 while (true); | 
 |             } | 
 |             catch (System.Exception exc1) | 
 |             { | 
 |                 lock(_eLock) | 
 |                 { | 
 |                     // expose the exception to the main thread | 
 |                     if (_pendingException!=null) | 
 |                         _pendingException = exc1; | 
 |                 } | 
 |             } | 
 |  | 
 |             TraceOutput(TraceBits.WriterThread, "_PerpetualWriterMethod FINIS"); | 
 |         } | 
 | #endif | 
 |  | 
 |  | 
 |  | 
 |  | 
 |         private void _DeflateOne(Object wi) | 
 |         { | 
 |             // compress one buffer | 
 |             WorkItem workitem = (WorkItem) wi; | 
 |             try | 
 |             { | 
 |                 int myItem = workitem.index; | 
 |                 Ionic.Crc.CRC32 crc = new Ionic.Crc.CRC32(); | 
 |  | 
 |                 // calc CRC on the buffer | 
 |                 crc.SlurpBlock(workitem.buffer, 0, workitem.inputBytesAvailable); | 
 |  | 
 |                 // deflate it | 
 |                 DeflateOneSegment(workitem); | 
 |  | 
 |                 // update status | 
 |                 workitem.crc = crc.Crc32Result; | 
 |                 TraceOutput(TraceBits.Compress, | 
 |                             "Compress          wi({0}) ord({1}) len({2})", | 
 |                             workitem.index, | 
 |                             workitem.ordinal, | 
 |                             workitem.compressedBytesAvailable | 
 |                             ); | 
 |  | 
 |                 lock(_latestLock) | 
 |                 { | 
 |                     if (workitem.ordinal > _latestCompressed) | 
 |                         _latestCompressed = workitem.ordinal; | 
 |                 } | 
 |                 lock (_toWrite) | 
 |                 { | 
 |                     _toWrite.Enqueue(workitem.index); | 
 |                 } | 
 |                 _newlyCompressedBlob.Set(); | 
 |             } | 
 |             catch (System.Exception exc1) | 
 |             { | 
 |                 lock(_eLock) | 
 |                 { | 
 |                     // expose the exception to the main thread | 
 |                     if (_pendingException!=null) | 
 |                         _pendingException = exc1; | 
 |                 } | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |  | 
 |         private bool DeflateOneSegment(WorkItem workitem) | 
 |         { | 
 |             ZlibCodec compressor = workitem.compressor; | 
 |             int rc= 0; | 
 |             compressor.ResetDeflate(); | 
 |             compressor.NextIn = 0; | 
 |  | 
 |             compressor.AvailableBytesIn = workitem.inputBytesAvailable; | 
 |  | 
 |             // step 1: deflate the buffer | 
 |             compressor.NextOut = 0; | 
 |             compressor.AvailableBytesOut =  workitem.compressed.Length; | 
 |             do | 
 |             { | 
 |                 compressor.Deflate(FlushType.None); | 
 |             } | 
 |             while (compressor.AvailableBytesIn > 0 || compressor.AvailableBytesOut == 0); | 
 |  | 
 |             // step 2: flush (sync) | 
 |             rc = compressor.Deflate(FlushType.Sync); | 
 |  | 
 |             workitem.compressedBytesAvailable= (int) compressor.TotalBytesOut; | 
 |             return true; | 
 |         } | 
 |  | 
 |  | 
 |         [System.Diagnostics.ConditionalAttribute("Trace")] | 
 |         private void TraceOutput(TraceBits bits, string format, params object[] varParams) | 
 |         { | 
 |             if ((bits & _DesiredTrace) != 0) | 
 |             { | 
 |                 lock(_outputLock) | 
 |                 { | 
 |                     int tid = Thread.CurrentThread.GetHashCode(); | 
 | #if !SILVERLIGHT | 
 |                     Console.ForegroundColor = (ConsoleColor) (tid % 8 + 8); | 
 | #endif | 
 |                     Console.Write("{0:000} PDOS ", tid); | 
 |                     Console.WriteLine(format, varParams); | 
 | #if !SILVERLIGHT | 
 |                     Console.ResetColor(); | 
 | #endif | 
 |                 } | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |         // used only when Trace is defined | 
 |         [Flags] | 
 |         enum TraceBits : uint | 
 |         { | 
 |             None         = 0, | 
 |             NotUsed1     = 1, | 
 |             EmitLock     = 2, | 
 |             EmitEnter    = 4,    // enter _EmitPending | 
 |             EmitBegin    = 8,    // begin to write out | 
 |             EmitDone     = 16,   // done writing out | 
 |             EmitSkip     = 32,   // writer skipping a workitem | 
 |             EmitAll      = 58,   // All Emit flags | 
 |             Flush        = 64, | 
 |             Lifecycle    = 128,  // constructor/disposer | 
 |             Session      = 256,  // Close/Reset | 
 |             Synch        = 512,  // thread synchronization | 
 |             Instance     = 1024, // instance settings | 
 |             Compress     = 2048,  // compress task | 
 |             Write        = 4096,    // filling buffers, when caller invokes Write() | 
 |             WriteEnter   = 8192,    // upon entry to Write() | 
 |             WriteTake    = 16384,    // on _toFill.Take() | 
 |             All          = 0xffffffff, | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         /// Indicates whether the stream supports Seek operations. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         /// Always returns false. | 
 |         /// </remarks> | 
 |         public override bool CanSeek | 
 |         { | 
 |             get { return false; } | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         /// Indicates whether the stream supports Read operations. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         /// Always returns false. | 
 |         /// </remarks> | 
 |         public override bool CanRead | 
 |         { | 
 |             get {return false;} | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// Indicates whether the stream supports Write operations. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         /// Returns true if the provided stream is writable. | 
 |         /// </remarks> | 
 |         public override bool CanWrite | 
 |         { | 
 |             get { return _outStream.CanWrite; } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// Reading this property always throws a NotSupportedException. | 
 |         /// </summary> | 
 |         public override long Length | 
 |         { | 
 |             get { throw new NotSupportedException(); } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// Returns the current position of the output stream. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         ///   <para> | 
 |         ///     Because the output gets written by a background thread, | 
 |         ///     the value may change asynchronously.  Setting this | 
 |         ///     property always throws a NotSupportedException. | 
 |         ///   </para> | 
 |         /// </remarks> | 
 |         public override long Position | 
 |         { | 
 |             get { return _outStream.Position; } | 
 |             set { throw new NotSupportedException(); } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// This method always throws a NotSupportedException. | 
 |         /// </summary> | 
 |         /// <param name="buffer"> | 
 |         ///   The buffer into which data would be read, IF THIS METHOD | 
 |         ///   ACTUALLY DID ANYTHING. | 
 |         /// </param> | 
 |         /// <param name="offset"> | 
 |         ///   The offset within that data array at which to insert the | 
 |         ///   data that is read, IF THIS METHOD ACTUALLY DID | 
 |         ///   ANYTHING. | 
 |         /// </param> | 
 |         /// <param name="count"> | 
 |         ///   The number of bytes to write, IF THIS METHOD ACTUALLY DID | 
 |         ///   ANYTHING. | 
 |         /// </param> | 
 |         /// <returns>nothing.</returns> | 
 |         public override int Read(byte[] buffer, int offset, int count) | 
 |         { | 
 |             throw new NotSupportedException(); | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// This method always throws a NotSupportedException. | 
 |         /// </summary> | 
 |         /// <param name="offset"> | 
 |         ///   The offset to seek to.... | 
 |         ///   IF THIS METHOD ACTUALLY DID ANYTHING. | 
 |         /// </param> | 
 |         /// <param name="origin"> | 
 |         ///   The reference specifying how to apply the offset....  IF | 
 |         ///   THIS METHOD ACTUALLY DID ANYTHING. | 
 |         /// </param> | 
 |         /// <returns>nothing. It always throws.</returns> | 
 |         public override long Seek(long offset, System.IO.SeekOrigin origin) | 
 |         { | 
 |             throw new NotSupportedException(); | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// This method always throws a NotSupportedException. | 
 |         /// </summary> | 
 |         /// <param name="value"> | 
 |         ///   The new value for the stream length....  IF | 
 |         ///   THIS METHOD ACTUALLY DID ANYTHING. | 
 |         /// </param> | 
 |         public override void SetLength(long value) | 
 |         { | 
 |             throw new NotSupportedException(); | 
 |         } | 
 |  | 
 |     } | 
 |  | 
 | } | 
 |  | 
 |  |