public async Task DecompressEntryAsync(int index, Stream output)
{
var entry = Files[index];
using (var fs = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.None, 4096, useAsync: true))
{
fs.Seek(entry.BlockOffsets[0], SeekOrigin.Begin);
//not compressed
if (entry.BlockSizeIndex == 0xFFFFFFFF)
{
var uncompressed = new byte[entry.RealUncompressedSize];
await fs.ReadAsync(uncompressed, 0, uncompressed.Length).ConfigureAwait(continueOnCapturedContext: false);
await output.WriteAsync(uncompressed, 0, uncompressed.Length).ConfigureAwait(continueOnCapturedContext: false);
return;
}
var decompressor = new TransformBlock<InputBlock, byte[]>(
input => input.IsCompressed
? SevenZipHelper.Decompress(input.Data, input.UncompressedSize)
: input.Data
, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded }
);
var outputWriter = new ActionBlock<byte[]>(
data => output.Write(data, 0, data.Length)
);
decompressor.LinkTo(outputWriter, new DataflowLinkOptions { PropagateCompletion = true });
uint count = 0;
long left = entry.RealUncompressedSize;
while (left > 0)
{
uint compressedBlockSize = entry.BlockSizes[count];
if (compressedBlockSize == 0)
{
compressedBlockSize = Header.MaxBlockSize;
}
if (compressedBlockSize == Header.MaxBlockSize ||
compressedBlockSize == left)
{
left -= compressedBlockSize;
var uncompressedData = new byte[compressedBlockSize];
await fs.ReadAsync(uncompressedData, 0, uncompressedData.Length).ConfigureAwait(continueOnCapturedContext: false);
decompressor.Post(new InputBlock(uncompressedData, InputBlock.Uncompressed));
}
else
{
var uncompressedBlockSize = Math.Min(left, Header.MaxBlockSize);
left -= uncompressedBlockSize;
if (compressedBlockSize < 5)
{
throw new Exception("compressed block size smaller than 5");
}
var compressedData = new byte[compressedBlockSize];
await fs.ReadAsync(compressedData, 0, (int)compressedBlockSize).ConfigureAwait(continueOnCapturedContext: false);
decompressor.Post(new InputBlock(compressedData, uncompressedBlockSize));
}
count++;
}
decompressor.Complete();
await outputWriter.Completion;
}
}