private async Task <ChunkBytes> Compress(ChunkBytes data)
        {
            var result = await Pool.GetAsync();

            using (MemoryStream memStream = new MemoryStream(result.Bytes))
            {
                using (GZipStream gzipStream = new GZipStream(memStream, CompressionLevel.Optimal, leaveOpen: true))
                    await gzipStream.WriteAsync(data.Bytes, 0, data.Length);
                result.Length = (int)memStream.Position;
            }
            return(result);
        }
        private async Task <ChunkBytes> Decompress(ChunkBytes data, int offset = 0)
        {
            var result = await Pool.GetAsync();

            using (MemoryStream memStream = new MemoryStream(result.Bytes))
            {
                using (MemoryStream input = new MemoryStream(data.Bytes, offset, data.Length - offset))
                    using (GZipStream gzipStream = new GZipStream(input, CompressionMode.Decompress))
                        await gzipStream.CopyToAsync(memStream);

                result.Length = (int)memStream.Position;
            }
            return(result);
        }
        private async Task <ChunkBytes> CryptoTransform(ChunkBytes data, ICryptoTransform transform)
        {
            var result = await Pool.GetAsync();

            using (var memoryStream = new MemoryStream(result.Bytes))
            {
                using (var cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
                {
                    await cryptoStream.WriteAsync(data.Bytes, 0, data.Length);

                    cryptoStream.FlushFinalBlock();
                    result.Length = (int)memoryStream.Position;
                }
            }
            return(result);
        }
 private Task <ChunkBytes> Decrypt(ChunkBytes data)
 {
     return(CryptoTransform(data, rijndaelAlgorithm.Value.CreateDecryptor()));
 }
 private static string MD5FromBytes(ChunkBytes data)
 {
     using (var md5 = MD5.Create())
         using (var stream = new MemoryStream(data.Bytes, 0, data.Length))
             return(BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", "").ToLower());
 }
        public async Task CompressAndEncrypt(
            Stream streamSource, Stream streamDestination,
            CancellationTokenSource cts = null)
        {
            cts = cts ?? new CancellationTokenSource();

            var compressorOptions = new ExecutionDataflowBlockOptions
            {
                MaxDegreeOfParallelism = Environment.ProcessorCount,
                BoundedCapacity        = _boundedCapacity,
                CancellationToken      = cts.Token
            };

            var inputBuffer = new BufferBlock <CompressingDetails>(
                new DataflowBlockOptions
            {
                CancellationToken = cts.Token,
                BoundedCapacity   = _boundedCapacity
            });

            var compressor = new TransformBlock <CompressingDetails, CompressedDetails>(
                async details =>
            {
                ChunkBytes compressedData = await Compress(details.Bytes);
                await Pool.PutAsync(details.Bytes);

                return(new CompressedDetails
                {
                    Bytes = compressedData,
                    ChunkSize = details.ChunkSize,
                    Sequence = details.Sequence,
                    CompressedDataSize = new ChunkBytes(BitConverter.GetBytes(compressedData.Length))
                });
            }, compressorOptions);

            var encryptor = new TransformBlock <CompressedDetails, EncryptDetails>(
                async details =>
            {
                var data = await CombineByteArrays(details.CompressedDataSize, details.ChunkSize, details.Bytes);
                await Pool.PutAsync(details.Bytes);

                var encryptedData = await Encrypt(data);
                await Pool.PutAsync(data);

                return(new EncryptDetails
                {
                    Bytes = encryptedData,
                    Sequence = details.Sequence,
                    EncryptedDataSize = new ChunkBytes(BitConverter.GetBytes(encryptedData.Length))
                });
            }, compressorOptions);

            var asOrderedAgent = Agent.Start((new Dictionary <int, EncryptDetails>(), 0),
                                             async((Dictionary <int, EncryptDetails>, int)state, EncryptDetails msg) =>
            {
                (Dictionary <int, EncryptDetails> details, int lastIndexProc) = state;
                details.Add(msg.Sequence, msg);
                while (details.ContainsKey(lastIndexProc + 1))
                {
                    msg = details[lastIndexProc + 1];
                    await streamDestination.WriteAsync(msg.EncryptedDataSize.Bytes, 0, msg.EncryptedDataSize.Length);
                    await streamDestination.WriteAsync(msg.Bytes.Bytes, 0, msg.Bytes.Length);
                    await Pool.PutAsync(msg.Bytes);
                    lastIndexProc = msg.Sequence;
                    details.Remove(lastIndexProc);
                }
                return(details, lastIndexProc);
            }, cts);