diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs index 3d550431..ac935576 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs @@ -217,17 +217,15 @@ internal sealed partial class Unpack : BitInput, IRarUnpack await UnstoreFileAsync(cancellationToken).ConfigureAwait(false); return; } - // TODO: When compression methods are converted to async, call them here - // For now, fall back to synchronous version switch (fileHeader.CompressionAlgorithm) { case 15: // rar 1.5 compression - unpack15(fileHeader.IsSolid); + await unpack15Async(fileHeader.IsSolid, cancellationToken).ConfigureAwait(false); break; case 20: // rar 2.x compression case 26: // files larger than 2GB - unpack20(fileHeader.IsSolid); + await unpack20Async(fileHeader.IsSolid, cancellationToken).ConfigureAwait(false); break; case 29: // rar 3.x compression diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack15.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack15.cs index ac962b67..4b9db7d6 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack15.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack15.cs @@ -316,6 +316,110 @@ internal partial class Unpack oldUnpWriteBuf(); } + private async System.Threading.Tasks.Task unpack15Async( + bool solid, + System.Threading.CancellationToken cancellationToken = default + ) + { + if (suspended) + { + unpPtr = wrPtr; + } + else + { + UnpInitData(solid); + oldUnpInitData(solid); + await unpReadBufAsync(cancellationToken).ConfigureAwait(false); + if (!solid) + { + initHuff(); + unpPtr = 0; + } + else + { + unpPtr = wrPtr; + } + --destUnpSize; + } + if (destUnpSize >= 0) + { + getFlagsBuf(); + FlagsCnt = 8; + } + + while (destUnpSize >= 0) + { + unpPtr &= PackDef.MAXWINMASK; + + if ( + inAddr > readTop - 30 + && !await unpReadBufAsync(cancellationToken).ConfigureAwait(false) + ) + { + break; + } + if (((wrPtr - unpPtr) & PackDef.MAXWINMASK) < 270 && wrPtr != unpPtr) + { + await oldUnpWriteBufAsync(cancellationToken).ConfigureAwait(false); + if (suspended) + { + return; + } + } + if (StMode != 0) + { + huffDecode(); + continue; + } + + if (--FlagsCnt < 0) + { + getFlagsBuf(); + FlagsCnt = 7; + } + + if ((FlagBuf & 0x80) != 0) + { + FlagBuf <<= 1; + if (Nlzb > Nhfb) + { + longLZ(); + } + else + { + huffDecode(); + } + } + else + { + FlagBuf <<= 1; + if (--FlagsCnt < 0) + { + getFlagsBuf(); + FlagsCnt = 7; + } + if ((FlagBuf & 0x80) != 0) + { + FlagBuf <<= 1; + if (Nlzb > Nhfb) + { + huffDecode(); + } + else + { + longLZ(); + } + } + else + { + FlagBuf <<= 1; + shortLZ(); + } + } + } + await oldUnpWriteBufAsync(cancellationToken).ConfigureAwait(false); + } + private bool unpReadBuf() { var dataSize = readTop - inAddr; @@ -848,4 +952,26 @@ internal partial class Unpack } wrPtr = unpPtr; } + + private async System.Threading.Tasks.Task oldUnpWriteBufAsync( + System.Threading.CancellationToken cancellationToken = default + ) + { + if (unpPtr < wrPtr) + { + await writeStream + .WriteAsync(window, wrPtr, -wrPtr & PackDef.MAXWINMASK, cancellationToken) + .ConfigureAwait(false); + await writeStream + .WriteAsync(window, 0, unpPtr, cancellationToken) + .ConfigureAwait(false); + } + else + { + await writeStream + .WriteAsync(window, wrPtr, unpPtr - wrPtr, cancellationToken) + .ConfigureAwait(false); + } + wrPtr = unpPtr; + } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs index 69a266bb..2b941eab 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs @@ -368,6 +368,163 @@ internal partial class Unpack oldUnpWriteBuf(); } + private async System.Threading.Tasks.Task unpack20Async( + bool solid, + System.Threading.CancellationToken cancellationToken = default + ) + { + int Bits; + + if (suspended) + { + unpPtr = wrPtr; + } + else + { + UnpInitData(solid); + if (!await unpReadBufAsync(cancellationToken).ConfigureAwait(false)) + { + return; + } + if (!solid) + { + if (!await ReadTables20Async(cancellationToken).ConfigureAwait(false)) + { + return; + } + } + --destUnpSize; + } + + while (destUnpSize >= 0) + { + unpPtr &= PackDef.MAXWINMASK; + + if (inAddr > readTop - 30) + { + if (!await unpReadBufAsync(cancellationToken).ConfigureAwait(false)) + { + break; + } + } + if (((wrPtr - unpPtr) & PackDef.MAXWINMASK) < 270 && wrPtr != unpPtr) + { + await oldUnpWriteBufAsync(cancellationToken).ConfigureAwait(false); + if (suspended) + { + return; + } + } + if (UnpAudioBlock != 0) + { + var AudioNumber = this.decodeNumber(MD[UnpCurChannel]); + + if (AudioNumber == 256) + { + if (!await ReadTables20Async(cancellationToken).ConfigureAwait(false)) + { + break; + } + continue; + } + window[unpPtr++] = DecodeAudio(AudioNumber); + if (++UnpCurChannel == UnpChannels) + { + UnpCurChannel = 0; + } + --destUnpSize; + continue; + } + + var Number = this.decodeNumber(LD); + if (Number < 256) + { + window[unpPtr++] = (byte)Number; + --destUnpSize; + continue; + } + if (Number > 269) + { + var Length = LDecode[Number -= 270] + 3; + if ((Bits = LBits[Number]) > 0) + { + Length += Utility.URShift(GetBits(), (16 - Bits)); + AddBits(Bits); + } + + var DistNumber = this.decodeNumber(DD); + var Distance = DDecode[DistNumber] + 1; + if ((Bits = DBits[DistNumber]) > 0) + { + Distance += Utility.URShift(GetBits(), (16 - Bits)); + AddBits(Bits); + } + + if (Distance >= 0x2000) + { + Length++; + if (Distance >= 0x40000L) + { + Length++; + } + } + + CopyString20(Length, Distance); + continue; + } + if (Number == 269) + { + if (!await ReadTables20Async(cancellationToken).ConfigureAwait(false)) + { + break; + } + continue; + } + if (Number == 256) + { + CopyString20(lastLength, lastDist); + continue; + } + if (Number < 261) + { + var Distance = oldDist[(oldDistPtr - (Number - 256)) & 3]; + var LengthNumber = this.decodeNumber(RD); + var Length = LDecode[LengthNumber] + 2; + if ((Bits = LBits[LengthNumber]) > 0) + { + Length += Utility.URShift(GetBits(), (16 - Bits)); + AddBits(Bits); + } + if (Distance >= 0x101) + { + Length++; + if (Distance >= 0x2000) + { + Length++; + if (Distance >= 0x40000) + { + Length++; + } + } + } + CopyString20(Length, Distance); + continue; + } + if (Number < 270) + { + var Distance = SDDecode[Number -= 261] + 1; + if ((Bits = SDBits[Number]) > 0) + { + Distance += Utility.URShift(GetBits(), (16 - Bits)); + AddBits(Bits); + } + CopyString20(2, Distance); + } + } + ReadLastTables(); + await oldUnpWriteBufAsync(cancellationToken).ConfigureAwait(false); + } + private void CopyString20(int Length, int Distance) { lastDist = oldDist[oldDistPtr++ & 3] = Distance; @@ -534,6 +691,120 @@ internal partial class Unpack return (true); } + private async System.Threading.Tasks.Task ReadTables20Async( + System.Threading.CancellationToken cancellationToken = default + ) + { + byte[] BitLength = new byte[PackDef.BC20]; + byte[] Table = new byte[PackDef.MC20 * 4]; + int TableSize, + N, + I; + if (inAddr > readTop - 25) + { + if (!await unpReadBufAsync(cancellationToken).ConfigureAwait(false)) + { + return (false); + } + } + var BitField = GetBits(); + UnpAudioBlock = (BitField & 0x8000); + + if (0 == (BitField & 0x4000)) + { + new Span(UnpOldTable20).Clear(); + } + AddBits(2); + + if (UnpAudioBlock != 0) + { + UnpChannels = ((Utility.URShift(BitField, 12)) & 3) + 1; + if (UnpCurChannel >= UnpChannels) + { + UnpCurChannel = 0; + } + AddBits(2); + TableSize = PackDef.MC20 * UnpChannels; + } + else + { + TableSize = PackDef.NC20 + PackDef.DC20 + PackDef.RC20; + } + for (I = 0; I < PackDef.BC20; I++) + { + BitLength[I] = (byte)(Utility.URShift(GetBits(), 12)); + AddBits(4); + } + UnpackUtility.makeDecodeTables(BitLength, 0, BD, PackDef.BC20); + I = 0; + while (I < TableSize) + { + if (inAddr > readTop - 5) + { + if (!await unpReadBufAsync(cancellationToken).ConfigureAwait(false)) + { + return (false); + } + } + var Number = this.decodeNumber(BD); + if (Number < 16) + { + Table[I] = (byte)((Number + UnpOldTable20[I]) & 0xf); + I++; + } + else if (Number == 16) + { + N = (Utility.URShift(GetBits(), 14)) + 3; + AddBits(2); + while (N-- > 0 && I < TableSize) + { + Table[I] = Table[I - 1]; + I++; + } + } + else + { + if (Number == 17) + { + N = (Utility.URShift(GetBits(), 13)) + 3; + AddBits(3); + } + else + { + N = (Utility.URShift(GetBits(), 9)) + 11; + AddBits(7); + } + while (N-- > 0 && I < TableSize) + { + Table[I++] = 0; + } + } + } + if (inAddr > readTop) + { + return (true); + } + if (UnpAudioBlock != 0) + { + for (I = 0; I < UnpChannels; I++) + { + UnpackUtility.makeDecodeTables(Table, I * PackDef.MC20, MD[I], PackDef.MC20); + } + } + else + { + UnpackUtility.makeDecodeTables(Table, 0, LD, PackDef.NC20); + UnpackUtility.makeDecodeTables(Table, PackDef.NC20, DD, PackDef.DC20); + UnpackUtility.makeDecodeTables(Table, PackDef.NC20 + PackDef.DC20, RD, PackDef.RC20); + } + + for (var i = 0; i < UnpOldTable20.Length; i++) + { + UnpOldTable20[i] = Table[i]; + } + return (true); + } + private void unpInitData20(bool Solid) { if (!Solid)