Merge branch 'opencode/clever-knight' into adam/async-creation

This commit is contained in:
Adam Hathcock
2026-01-25 15:04:07 +00:00
3 changed files with 367 additions and 3 deletions

View File

@@ -161,11 +161,12 @@ internal abstract partial class ZipFilePart
}
case ZipCompressionMethod.Explode:
{
return ExplodeStream.Create(
return await ExplodeStream.CreateAsync(
stream,
Header.CompressedSize,
Header.UncompressedSize,
Header.Flags
Header.Flags,
cancellationToken
);
}

View File

@@ -0,0 +1,363 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.IO;
namespace SharpCompress.Compressors.Explode;
public partial class ExplodeStream
{
internal static async ValueTask<ExplodeStream> CreateAsync(
Stream inStr,
long compressedSize,
long uncompressedSize,
HeaderFlags generalPurposeBitFlag,
CancellationToken cancellationToken = default
)
{
var ex = new ExplodeStream(inStr, compressedSize, uncompressedSize, generalPurposeBitFlag);
await ex.explode_SetTables_async(cancellationToken).ConfigureAwait(false);
ex.explode_var_init();
return ex;
}
private async Task<int> get_tree_async(
int[] arrBitLengths,
int numberExpected,
CancellationToken cancellationToken
)
{
int inIndex = (await ReadSingleByteAsync(cancellationToken).ConfigureAwait(false)) + 1;
int outIndex = 0;
do
{
int nextByte = await ReadSingleByteAsync(cancellationToken).ConfigureAwait(false);
int bitLengthOfCodes = (nextByte & 0xf) + 1;
int numOfCodes = ((nextByte & 0xf0) >> 4) + 1;
if (outIndex + numOfCodes > numberExpected)
{
return 4;
}
do
{
arrBitLengths[outIndex++] = bitLengthOfCodes;
} while ((--numOfCodes) != 0);
} while ((--inIndex) != 0);
return outIndex != numberExpected ? 4 : 0;
}
private async Task<int> ReadSingleByteAsync(CancellationToken cancellationToken)
{
var buffer = new byte[1];
int bytesRead = await inStream
.ReadAsync(buffer, 0, 1, cancellationToken)
.ConfigureAwait(false);
if (bytesRead == 0)
{
return -1;
}
return buffer[0];
}
private async Task<int> explode_SetTables_async(CancellationToken cancellationToken)
{
int returnCode;
int[] arrBitLengthsForCodes = new int[256];
bitsForLiteralCodeTable = 0;
bitsForLengthCodeTable = 7;
bitsForDistanceCodeTable = (compressedSize) > 200000 ? 8 : 7;
if ((generalPurposeBitFlag & HeaderFlags.Bit2) != 0)
{
bitsForLiteralCodeTable = 9;
if (
(
returnCode = await get_tree_async(arrBitLengthsForCodes, 256, cancellationToken)
.ConfigureAwait(false)
) != 0
)
{
return returnCode;
}
if (
(
returnCode = HuftTree.huftbuid(
arrBitLengthsForCodes,
256,
256,
[],
[],
out hufLiteralCodeTable,
ref bitsForLiteralCodeTable
)
) != 0
)
{
return returnCode;
}
if (
(
returnCode = await get_tree_async(arrBitLengthsForCodes, 64, cancellationToken)
.ConfigureAwait(false)
) != 0
)
{
return returnCode;
}
if (
(
returnCode = HuftTree.huftbuid(
arrBitLengthsForCodes,
64,
0,
cplen3,
extra,
out hufLengthCodeTable,
ref bitsForLengthCodeTable
)
) != 0
)
{
return returnCode;
}
}
else
{
if (
(
returnCode = await get_tree_async(arrBitLengthsForCodes, 64, cancellationToken)
.ConfigureAwait(false)
) != 0
)
{
return returnCode;
}
hufLiteralCodeTable = null;
if (
(
returnCode = HuftTree.huftbuid(
arrBitLengthsForCodes,
64,
0,
cplen2,
extra,
out hufLengthCodeTable,
ref bitsForLengthCodeTable
)
) != 0
)
{
return returnCode;
}
}
if (
(
returnCode = await get_tree_async(arrBitLengthsForCodes, 64, cancellationToken)
.ConfigureAwait(false)
) != 0
)
{
return (int)returnCode;
}
if ((generalPurposeBitFlag & HeaderFlags.Bit1) != 0)
{
numOfUncodedLowerDistanceBits = 7;
returnCode = HuftTree.huftbuid(
arrBitLengthsForCodes,
64,
0,
cpdist8,
extra,
out hufDistanceCodeTable,
ref bitsForDistanceCodeTable
);
}
else
{
numOfUncodedLowerDistanceBits = 6;
returnCode = HuftTree.huftbuid(
arrBitLengthsForCodes,
64,
0,
cpdist4,
extra,
out hufDistanceCodeTable,
ref bitsForDistanceCodeTable
);
}
return returnCode;
}
private async Task NeedBitsAsync(int numberOfBits, CancellationToken cancellationToken)
{
while (bitBufferCount < (numberOfBits))
{
int byteRead = await ReadSingleByteAsync(cancellationToken).ConfigureAwait(false);
bitBuffer |= (uint)byteRead << bitBufferCount;
bitBufferCount += 8;
}
}
public override async Task<int> ReadAsync(
byte[] buffer,
int offset,
int count,
CancellationToken cancellationToken
)
{
int countIndex = 0;
while (countIndex < count && outBytesCount < unCompressedSize)
{
if (length == 0)
{
await NeedBitsAsync(1, cancellationToken).ConfigureAwait(false);
bool literal = (bitBuffer & 1) == 1;
DumpBits(1);
huftNode huftPointer;
if (literal)
{
byte nextByte;
if (hufLiteralCodeTable != null)
{
if (
DecodeHuft(
hufLiteralCodeTable,
bitsForLiteralCodeTable,
maskForLiteralCodeTable,
out huftPointer,
out _
) != 0
)
{
throw new Exception("Error decoding literal value");
}
nextByte = (byte)huftPointer.Value;
}
else
{
await NeedBitsAsync(8, cancellationToken).ConfigureAwait(false);
nextByte = (byte)bitBuffer;
DumpBits(8);
}
buffer[offset + (countIndex++)] = nextByte;
windowsBuffer[windowIndex++] = nextByte;
outBytesCount++;
if (windowIndex == WSIZE)
{
windowIndex = 0;
}
continue;
}
await NeedBitsAsync(numOfUncodedLowerDistanceBits, cancellationToken)
.ConfigureAwait(false);
distance = (int)(bitBuffer & maskForDistanceLowBits);
DumpBits(numOfUncodedLowerDistanceBits);
if (
DecodeHuft(
hufDistanceCodeTable,
bitsForDistanceCodeTable,
maskForDistanceCodeTable,
out huftPointer,
out _
) != 0
)
{
throw new Exception("Error decoding distance high bits");
}
distance = windowIndex - (distance + huftPointer.Value);
if (
DecodeHuft(
hufLengthCodeTable,
bitsForLengthCodeTable,
maskForLengthCodeTable,
out huftPointer,
out int extraBitLength
) != 0
)
{
throw new Exception("Error decoding coded length");
}
length = huftPointer.Value;
if (extraBitLength != 0)
{
await NeedBitsAsync(8, cancellationToken).ConfigureAwait(false);
length += (int)(bitBuffer & 0xff);
DumpBits(8);
}
if (length > (unCompressedSize - outBytesCount))
{
length = (int)(unCompressedSize - outBytesCount);
}
distance &= WSIZE - 1;
}
while (length != 0 && countIndex < count)
{
byte nextByte = windowsBuffer[distance++];
buffer[offset + (countIndex++)] = nextByte;
windowsBuffer[windowIndex++] = nextByte;
outBytesCount++;
if (distance == WSIZE)
{
distance = 0;
}
if (windowIndex == WSIZE)
{
windowIndex = 0;
}
length--;
}
}
return countIndex;
}
#if !LEGACY_DOTNET
public override async ValueTask<int> ReadAsync(
Memory<byte> buffer,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
if (buffer.IsEmpty || outBytesCount >= unCompressedSize)
{
return 0;
}
byte[] arrayBuffer = new byte[buffer.Length];
int result = await ReadAsync(arrayBuffer, 0, arrayBuffer.Length, cancellationToken)
.ConfigureAwait(false);
arrayBuffer.AsMemory(0, result).CopyTo(buffer);
return result;
}
#endif
}

View File

@@ -5,7 +5,7 @@ using SharpCompress.IO;
namespace SharpCompress.Compressors.Explode;
public class ExplodeStream : Stream, IStreamStack
public partial class ExplodeStream : Stream, IStreamStack
{
#if DEBUG_STREAMS
long IStreamStack.InstanceId { get; set; }