public PipedMultiplexer(PipedStreamReader reader, PipedStreamOptions options)
        {
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }

            _writers                     = new ConcurrentBag <Stream>();
            _reader                      = reader;
            _optionsTemplate             = options.Clone();
            _optionsTemplate.Multiplexed = false;

            _task = Task.Run(async() => await MultiplexerTask().IgnoreContext());
        }
        public PipedStreamManager(PipedStreamOptions options)
        {
            _options = options.Clone();
            _options.EnsureValidity();

            _writersCreated = 0;
            _readersCreated = 0;
            _multiplexer    = _options.Multiplexed ? new Lazy <PipedMultiplexer>(CreateMultiplexer, LazyThreadSafetyMode.ExecutionAndPublication) : null;

            _buffer = new PipeBufferFactory(_options.BlockSize, _options.MaxBlocks + 2);

            _pipe = new BufferBlock <IPipeBufferItem>(new DataflowBlockOptions()
            {
                EnsureOrdered   = true,
                BoundedCapacity = _options.MaxBlocks
            });
            _closeCalled = 0;
        }
        public static Stream Create(Stream originalStream, string sharedSecret, string salt, bool leaveOpen)
        {
            var pipeOptions = new PipedStreamOptions()
            {
                WriteTimeout = TimeSpan.FromMinutes(10)
            };
            var pipe = new PipedStreamManager(pipeOptions);

            var saltBytes = Encoding.UTF8.GetBytes(salt);

            var encryptorTask = Task.Run(async() =>
            {
                try
                {
                    using (var writerStream = pipe.CreateWriter(throwsFailedWrite: true))
                        using (var aesAlg = new RijndaelManaged())
                            using (var key = new Rfc2898DeriveBytes(sharedSecret, saltBytes))
                            {
                                try
                                {
                                    aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);

                                    // prepends the IV
                                    await writerStream.WriteAsync(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int))
                                    .IgnoreContext();
                                    await writerStream.WriteAsync(aesAlg.IV, 0, aesAlg.IV.Length)
                                    .IgnoreContext();

                                    // Creates an encryptor to perform the stream transform.
                                    using (var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV))
                                        using (var cryptoStream = new CryptoStream(writerStream, encryptor, CryptoStreamMode.Write))
                                        {
                                            await originalStream.CopyToAsync(cryptoStream, bufferSize: pipeOptions.BlockSize)
                                            .IgnoreContext();

                                            await cryptoStream.FlushAsync()
                                            .IgnoreContext();

                                            await writerStream.FlushAsync()
                                            .IgnoreContext();

                                            if (!cryptoStream.HasFlushedFinalBlock)
                                            {
                                                cryptoStream.FlushFinalBlock();
                                            }

                                            await writerStream.FlushAsync()
                                            .IgnoreContext();
                                        }
                                }
                                catch (Exception ex)
                                {
                                    pipe.FaultPipe(ex);
                                }
                            }
                }
                finally
                {
                    if (!leaveOpen)
                    {
                        originalStream.Dispose();
                    }
                }
            });

            return(ReadOnlyStreamWrapper.Create(pipe.CreateReader(), onDisposingAction: (Stream s) => s?.Dispose()));
        }