diff --git a/src/SharpCompress/Common/CompressionType.cs b/src/SharpCompress/Common/CompressionType.cs index 20f166c4..927c1e7a 100644 --- a/src/SharpCompress/Common/CompressionType.cs +++ b/src/SharpCompress/Common/CompressionType.cs @@ -16,5 +16,10 @@ public enum CompressionType Unknown, Deflate64, Shrink, - Lzw + Lzw, + Reduce1, + Reduce2, + Reduce3, + Reduce4, + Explode } diff --git a/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs b/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs index a98ed667..b4147eee 100644 --- a/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs +++ b/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs @@ -4,6 +4,11 @@ internal enum ZipCompressionMethod { None = 0, Shrink = 1, + Reduce1 = 2, + Reduce2 = 3, + Reduce3 = 4, + Reduce4 = 5, + Explode = 6, Deflate = 8, Deflate64 = 9, BZip2 = 12, diff --git a/src/SharpCompress/Common/Zip/ZipEntry.cs b/src/SharpCompress/Common/Zip/ZipEntry.cs index c2cecf39..2700e005 100644 --- a/src/SharpCompress/Common/Zip/ZipEntry.cs +++ b/src/SharpCompress/Common/Zip/ZipEntry.cs @@ -31,6 +31,11 @@ internal ZipEntry(ZipFilePart? filePart) ZipCompressionMethod.PPMd => CompressionType.PPMd, ZipCompressionMethod.None => CompressionType.None, ZipCompressionMethod.Shrink => CompressionType.Shrink, + ZipCompressionMethod.Reduce1 => CompressionType.Reduce1, + ZipCompressionMethod.Reduce2 => CompressionType.Reduce2, + ZipCompressionMethod.Reduce3 => CompressionType.Reduce3, + ZipCompressionMethod.Reduce4 => CompressionType.Reduce4, + ZipCompressionMethod.Explode => CompressionType.Explode, _ => CompressionType.Unknown }; diff --git a/src/SharpCompress/Common/Zip/ZipFilePart.cs b/src/SharpCompress/Common/Zip/ZipFilePart.cs index e8516150..5d0a69c3 100644 --- a/src/SharpCompress/Common/Zip/ZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/ZipFilePart.cs @@ -11,6 +11,8 @@ using SharpCompress.Compressors.PPMd; using SharpCompress.Compressors.Shrink; using SharpCompress.Compressors.Xz; +using SharpCompress.Compressors.Reduce; +using SharpCompress.Compressors.Explode; using SharpCompress.IO; using ZstdSharp; @@ -89,6 +91,27 @@ protected Stream CreateDecompressionStream(Stream stream, ZipCompressionMethod m Header.UncompressedSize ); } + case ZipCompressionMethod.Reduce1: + { + return new ReduceStream(stream, Header.CompressedSize, Header.UncompressedSize, 1); + } + case ZipCompressionMethod.Reduce2: + { + return new ReduceStream(stream, Header.CompressedSize, Header.UncompressedSize, 2); + } + case ZipCompressionMethod.Reduce3: + { + return new ReduceStream(stream, Header.CompressedSize, Header.UncompressedSize, 3); + } + case ZipCompressionMethod.Reduce4: + { + return new ReduceStream(stream, Header.CompressedSize, Header.UncompressedSize, 4); + } + case ZipCompressionMethod.Explode: + { + return new ExplodeStream(stream, Header.CompressedSize, Header.UncompressedSize, Header.Flags); + } + case ZipCompressionMethod.Deflate: { return new DeflateStream(stream, CompressionMode.Decompress); @@ -203,6 +226,10 @@ protected Stream GetCryptoStream(Stream plainStream) { case ZipCompressionMethod.None: case ZipCompressionMethod.Shrink: + case ZipCompressionMethod.Reduce1: + case ZipCompressionMethod.Reduce2: + case ZipCompressionMethod.Reduce3: + case ZipCompressionMethod.Reduce4: case ZipCompressionMethod.Deflate: case ZipCompressionMethod.Deflate64: case ZipCompressionMethod.BZip2: diff --git a/src/SharpCompress/Compressors/Explode/ExplodeStream.cs b/src/SharpCompress/Compressors/Explode/ExplodeStream.cs new file mode 100644 index 00000000..a68353ce --- /dev/null +++ b/src/SharpCompress/Compressors/Explode/ExplodeStream.cs @@ -0,0 +1,746 @@ +using System; +using System.IO; +using SharpCompress.Common.Zip.Headers; + +namespace SharpCompress.Compressors.Explode; + +public class ExplodeStream : Stream +{ + private const int INVALID_CODE = 99; + private const int WSIZE = 64 * 1024; + + private readonly long unCompressedSize; + private readonly int compressedSize; + private readonly HeaderFlags generalPurposeBitFlag; + private readonly Stream inStream; + + private huftNode[]? hufLiteralCodeTable; /* literal code table */ + private huftNode[] hufLengthCodeTable = []; /* length code table */ + private huftNode[] hufDistanceCodeTable = []; /* distance code table */ + + private int bitsForLiteralCodeTable; + private int bitsForLengthCodeTable; + private int bitsForDistanceCodeTable; + private int numOfUncodedLowerDistanceBits; /* number of uncoded lower distance bits */ + + private ulong bitBuffer; + private int bitBufferCount; + + private readonly byte[] windowsBuffer; + private uint maskForLiteralCodeTable; + private uint maskForLengthCodeTable; + private uint maskForDistanceCodeTable; + private uint maskForDistanceLowBits; + private long outBytesCount; + + private int windowIndex; + private int distance; + private int length; + + internal ExplodeStream( + Stream inStr, + long compressedSize, + long uncompressedSize, + HeaderFlags generalPurposeBitFlag + ) + { + inStream = inStr; + this.compressedSize = (int)compressedSize; + unCompressedSize = (long)uncompressedSize; + this.generalPurposeBitFlag = generalPurposeBitFlag; + explode_SetTables(); + + windowsBuffer = new byte[WSIZE]; + explode_var_init(); + } + + public override void Flush() + { + throw new NotImplementedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => false; + public override long Length => unCompressedSize; + public override long Position + { + get => outBytesCount; + set { } + } + + static uint[] mask_bits = new uint[] + { + 0x0000, + 0x0001, + 0x0003, + 0x0007, + 0x000f, + 0x001f, + 0x003f, + 0x007f, + 0x00ff, + 0x01ff, + 0x03ff, + 0x07ff, + 0x0fff, + 0x1fff, + 0x3fff, + 0x7fff, + 0xffff + }; + + /* Tables for length and distance */ + static int[] cplen2 = new int[] + { + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65 + }; + + static int[] cplen3 = new int[] + { + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66 + }; + + static int[] extra = new int[] + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8 + }; + + static int[] cpdist4 = new int[] + { + 1, + 65, + 129, + 193, + 257, + 321, + 385, + 449, + 513, + 577, + 641, + 705, + 769, + 833, + 897, + 961, + 1025, + 1089, + 1153, + 1217, + 1281, + 1345, + 1409, + 1473, + 1537, + 1601, + 1665, + 1729, + 1793, + 1857, + 1921, + 1985, + 2049, + 2113, + 2177, + 2241, + 2305, + 2369, + 2433, + 2497, + 2561, + 2625, + 2689, + 2753, + 2817, + 2881, + 2945, + 3009, + 3073, + 3137, + 3201, + 3265, + 3329, + 3393, + 3457, + 3521, + 3585, + 3649, + 3713, + 3777, + 3841, + 3905, + 3969, + 4033 + }; + + static int[] cpdist8 = new int[] + { + 1, + 129, + 257, + 385, + 513, + 641, + 769, + 897, + 1025, + 1153, + 1281, + 1409, + 1537, + 1665, + 1793, + 1921, + 2049, + 2177, + 2305, + 2433, + 2561, + 2689, + 2817, + 2945, + 3073, + 3201, + 3329, + 3457, + 3585, + 3713, + 3841, + 3969, + 4097, + 4225, + 4353, + 4481, + 4609, + 4737, + 4865, + 4993, + 5121, + 5249, + 5377, + 5505, + 5633, + 5761, + 5889, + 6017, + 6145, + 6273, + 6401, + 6529, + 6657, + 6785, + 6913, + 7041, + 7169, + 7297, + 7425, + 7553, + 7681, + 7809, + 7937, + 8065 + }; + + private int get_tree(int[] arrBitLengths, int numberExpected) + /* Get the bit lengths for a code representation from the compressed + stream. If get_tree() returns 4, then there is an error in the data. + Otherwise zero is returned. */ + { + /* get bit lengths */ + int inIndex = inStream.ReadByte() + 1; /* length/count pairs to read */ + int outIndex = 0; /* next code */ + do + { + int nextByte = inStream.ReadByte(); + int bitLengthOfCodes = (nextByte & 0xf) + 1; /* bits in code (1..16) */ + int numOfCodes = ((nextByte & 0xf0) >> 4) + 1; /* codes with those bits (1..16) */ + if (outIndex + numOfCodes > numberExpected) + return 4; /* don't overflow arrBitLengths[] */ + do + { + arrBitLengths[outIndex++] = bitLengthOfCodes; + } while ((--numOfCodes) != 0); + } while ((--inIndex) != 0); + + return outIndex != numberExpected ? 4 : 0; /* should have read numberExpected of them */ + } + + private int explode_SetTables() + { + int returnCode; /* return codes */ + int[] arrBitLengthsForCodes = new int[256]; /* bit lengths for codes */ + + bitsForLiteralCodeTable = 0; /* bits for tb */ + bitsForLengthCodeTable = 7; + bitsForDistanceCodeTable = (compressedSize) > 200000 ? 8 : 7; + + if ((generalPurposeBitFlag & HeaderFlags.Bit2) != 0) + /* With literal tree--minimum match length is 3 */ + { + bitsForLiteralCodeTable = 9; /* base table size for literals */ + if ((returnCode = get_tree(arrBitLengthsForCodes, 256)) != 0) + return returnCode; + + if ( + ( + returnCode = HuftTree.huftbuid( + arrBitLengthsForCodes, + 256, + 256, + [], + [], + out hufLiteralCodeTable, + ref bitsForLiteralCodeTable + ) + ) != 0 + ) + return returnCode; + + if ((returnCode = get_tree(arrBitLengthsForCodes, 64)) != 0) + return returnCode; + + if ( + ( + returnCode = HuftTree.huftbuid( + arrBitLengthsForCodes, + 64, + 0, + cplen3, + extra, + out hufLengthCodeTable, + ref bitsForLengthCodeTable + ) + ) != 0 + ) + return returnCode; + } + else + /* No literal tree--minimum match length is 2 */ + { + if ((returnCode = get_tree(arrBitLengthsForCodes, 64)) != 0) + return returnCode; + + hufLiteralCodeTable = null; + + if ( + ( + returnCode = HuftTree.huftbuid( + arrBitLengthsForCodes, + 64, + 0, + cplen2, + extra, + out hufLengthCodeTable, + ref bitsForLengthCodeTable + ) + ) != 0 + ) + return returnCode; + } + + if ((returnCode = get_tree(arrBitLengthsForCodes, 64)) != 0) + return (int)returnCode; + + if ((generalPurposeBitFlag & HeaderFlags.Bit1) != 0) /* true if 8K */ + { + numOfUncodedLowerDistanceBits = 7; + returnCode = HuftTree.huftbuid( + arrBitLengthsForCodes, + 64, + 0, + cpdist8, + extra, + out hufDistanceCodeTable, + ref bitsForDistanceCodeTable + ); + } + else /* else 4K */ + { + numOfUncodedLowerDistanceBits = 6; + returnCode = HuftTree.huftbuid( + arrBitLengthsForCodes, + 64, + 0, + cpdist4, + extra, + out hufDistanceCodeTable, + ref bitsForDistanceCodeTable + ); + } + + return returnCode; + } + + private void NeedBits(int numberOfBits) + { + while (bitBufferCount < (numberOfBits)) + { + bitBuffer |= (uint)inStream.ReadByte() << bitBufferCount; + bitBufferCount += 8; + } + } + + private void DumpBits(int numberOfBits) + { + bitBuffer >>= numberOfBits; + bitBufferCount -= numberOfBits; + } + + int DecodeHuft(huftNode[] htab, int bits, uint mask, out huftNode huftPointer, out int e) + { + NeedBits(bits); + + int tabOffset = (int)(~bitBuffer & mask); + huftPointer = htab[tabOffset]; + + while (true) + { + DumpBits(huftPointer.NumberOfBitsUsed); + e = huftPointer.NumberOfExtraBits; + if (e <= 32) + break; + if (e == INVALID_CODE) + return 1; + + e &= 31; + NeedBits(e); + + tabOffset = (int)(~bitBuffer & mask_bits[e]); + huftPointer = huftPointer.ChildNodes[tabOffset]; + } + + return 0; + } + + private void explode_var_init() + { + /* explode the coded data */ + bitBuffer = 0; + bitBufferCount = 0; + maskForLiteralCodeTable = mask_bits[bitsForLiteralCodeTable]; //only used in explode_lit + maskForLengthCodeTable = mask_bits[bitsForLengthCodeTable]; + maskForDistanceCodeTable = mask_bits[bitsForDistanceCodeTable]; + maskForDistanceLowBits = mask_bits[numOfUncodedLowerDistanceBits]; + outBytesCount = 0; + + windowIndex = 0; /* initialize bit buffer, window */ + } + + public override int Read(byte[] buffer, int offset, int count) + { + int countIndex = 0; + while (countIndex < count && outBytesCount < unCompressedSize) /* do until unCompressedSize bytes uncompressed */ + { + if (length == 0) + { + NeedBits(1); + bool literal = (bitBuffer & 1) == 1; + DumpBits(1); + + huftNode huftPointer; + if (literal) /* then literal--decode it */ + { + byte nextByte; + if (hufLiteralCodeTable != null) + { + /* get coded literal */ + if ( + DecodeHuft( + hufLiteralCodeTable, + bitsForLiteralCodeTable, + maskForLiteralCodeTable, + out huftPointer, + out _ + ) != 0 + ) + throw new Exception("Error decoding literal value"); + + nextByte = (byte)huftPointer.Value; + } + else + { + NeedBits(8); + nextByte = (byte)bitBuffer; + DumpBits(8); + } + + buffer[offset + (countIndex++)] = nextByte; + windowsBuffer[windowIndex++] = nextByte; + outBytesCount++; + + if (windowIndex == WSIZE) + windowIndex = 0; + + continue; + } + + NeedBits(numOfUncodedLowerDistanceBits); /* get distance low bits */ + distance = (int)(bitBuffer & maskForDistanceLowBits); + DumpBits(numOfUncodedLowerDistanceBits); + + /* get coded distance high bits */ + if ( + DecodeHuft( + hufDistanceCodeTable, + bitsForDistanceCodeTable, + maskForDistanceCodeTable, + out huftPointer, + out _ + ) != 0 + ) + throw new Exception("Error decoding distance high bits"); + + distance = windowIndex - (distance + huftPointer.Value); /* construct offset */ + + /* get coded length */ + if ( + DecodeHuft( + hufLengthCodeTable, + bitsForLengthCodeTable, + maskForLengthCodeTable, + out huftPointer, + out int extraBitLength + ) != 0 + ) + throw new Exception("Error decoding coded length"); + + length = huftPointer.Value; + + if (extraBitLength != 0) /* get length extra bits */ + { + NeedBits(8); + 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; + } +} diff --git a/src/SharpCompress/Compressors/Explode/HuftTree.cs b/src/SharpCompress/Compressors/Explode/HuftTree.cs new file mode 100644 index 00000000..caa1a141 --- /dev/null +++ b/src/SharpCompress/Compressors/Explode/HuftTree.cs @@ -0,0 +1,269 @@ +/* + * This code has been converted to C# based on the original huft_tree code found in + * inflate.c -- by Mark Adler version c17e, 30 Mar 2007 + */ + +namespace SharpCompress.Compressors.Explode; + +public class huftNode +{ + public int NumberOfExtraBits; /* number of extra bits or operation */ + public int NumberOfBitsUsed; /* number of bits in this code or subcode */ + public int Value; /* literal, length base, or distance base */ + public huftNode[] ChildNodes = []; /* next level of table */ +} + +public static class HuftTree +{ + private const int INVALID_CODE = 99; + + /* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ + private const int BMAX = 16; /* maximum bit length of any code (16 for explode) */ + private const int N_MAX = 288; /* maximum number of codes in any set */ + + public static int huftbuid( + int[] arrBitLengthForCodes, + int numberOfCodes, + int numberOfSimpleValueCodes, + int[] arrBaseValuesForNonSimpleCodes, + int[] arrExtraBitsForNonSimpleCodes, + out huftNode[] outHufTable, + ref int outBitsForTable + ) + /* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return zero on success, one if + the given code set is incomplete (the tables are still built in this + case), two if the input is invalid (all zero length codes or an + oversubscribed set of lengths), and three if not enough memory. + The code with value 256 is special, and the tables are constructed + so that no bits beyond that code are fetched when that code is + decoded. */ + { + outHufTable = []; + + /* Generate counts for each bit length */ + int lengthOfEOBcode = numberOfCodes > 256 ? arrBitLengthForCodes[256] : BMAX; /* set length of EOB code, if any */ + + int[] arrBitLengthCount = new int[BMAX + 1]; + for (int i = 0; i < BMAX + 1; i++) + arrBitLengthCount[i] = 0; + + int pIndex = 0; + int counterCurrentCode = numberOfCodes; + do + { + arrBitLengthCount[arrBitLengthForCodes[pIndex]]++; + pIndex++; /* assume all entries <= BMAX */ + } while ((--counterCurrentCode) != 0); + + if (arrBitLengthCount[0] == numberOfCodes) /* null input--all zero length codes */ + { + return 0; + } + + /* Find minimum and maximum length, bound *outBitsForTable by those */ + int counter; + for (counter = 1; counter <= BMAX; counter++) + if (arrBitLengthCount[counter] != 0) + break; + + int numberOfBitsInCurrentCode = counter; /* minimum code length */ + if (outBitsForTable < counter) + outBitsForTable = counter; + + for (counterCurrentCode = BMAX; counterCurrentCode != 0; counterCurrentCode--) + if (arrBitLengthCount[counterCurrentCode] != 0) + break; + + int maximumCodeLength = counterCurrentCode; /* maximum code length */ + if (outBitsForTable > counterCurrentCode) + outBitsForTable = counterCurrentCode; + + /* Adjust last length count to fill out codes, if needed */ + int numberOfDummyCodesAdded; + for ( + numberOfDummyCodesAdded = 1 << counter; + counter < counterCurrentCode; + counter++, numberOfDummyCodesAdded <<= 1 + ) + if ((numberOfDummyCodesAdded -= arrBitLengthCount[counter]) < 0) + return 2; /* bad input: more codes than bits */ + + if ((numberOfDummyCodesAdded -= arrBitLengthCount[counterCurrentCode]) < 0) + return 2; + + arrBitLengthCount[counterCurrentCode] += numberOfDummyCodesAdded; + + /* Generate starting offsets into the value table for each length */ + int[] bitOffset = new int[BMAX + 1]; + bitOffset[1] = 0; + counter = 0; + pIndex = 1; + int xIndex = 2; + while ((--counterCurrentCode) != 0) + { /* note that i == g from above */ + bitOffset[xIndex++] = (counter += arrBitLengthCount[pIndex++]); + } + + /* Make a table of values in order of bit lengths */ + int[] arrValuesInOrderOfBitLength = new int[N_MAX]; + for (int i = 0; i < N_MAX; i++) + arrValuesInOrderOfBitLength[i] = 0; + + pIndex = 0; + counterCurrentCode = 0; + do + { + if ((counter = arrBitLengthForCodes[pIndex++]) != 0) + arrValuesInOrderOfBitLength[bitOffset[counter]++] = counterCurrentCode; + } while (++counterCurrentCode < numberOfCodes); + + numberOfCodes = bitOffset[maximumCodeLength]; /* set numberOfCodes to length of v */ + + /* Generate the Huffman codes and for each, make the table entries */ + bitOffset[0] = counterCurrentCode = 0; /* first Huffman code is zero */ + pIndex = 0; /* grab values in bit order */ + int tableLevel = -1; /* no tables yet--level -1 */ + int bitsBeforeThisTable = 0; + int[] arrLX = new int[BMAX + 1]; + int stackOfBitsPerTable = 1; /* stack of bits per table */ + arrLX[stackOfBitsPerTable - 1] = 0; /* no bits decoded yet */ + + huftNode[][] arrHufTableStack = new huftNode[BMAX][]; + huftNode[] pointerToCurrentTable = []; + int numberOfEntriesInCurrentTable = 0; + + bool first = true; + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; numberOfBitsInCurrentCode <= maximumCodeLength; numberOfBitsInCurrentCode++) + { + int counterForCodes = arrBitLengthCount[numberOfBitsInCurrentCode]; + while ((counterForCodes--) != 0) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while ( + numberOfBitsInCurrentCode + > bitsBeforeThisTable + arrLX[stackOfBitsPerTable + tableLevel] + ) + { + bitsBeforeThisTable += arrLX[stackOfBitsPerTable + (tableLevel++)]; /* add bits already decoded */ + + /* compute minimum size table less than or equal to *outBitsForTable bits */ + numberOfEntriesInCurrentTable = + (numberOfEntriesInCurrentTable = maximumCodeLength - bitsBeforeThisTable) + > outBitsForTable + ? outBitsForTable + : numberOfEntriesInCurrentTable; /* upper limit */ + int fBitCounter1 = + 1 << (counter = numberOfBitsInCurrentCode - bitsBeforeThisTable); + if (fBitCounter1 > counterForCodes + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + fBitCounter1 -= counterForCodes + 1; /* deduct codes from patterns left */ + xIndex = numberOfBitsInCurrentCode; + while (++counter < numberOfEntriesInCurrentTable) /* try smaller tables up to z bits */ + { + if ((fBitCounter1 <<= 1) <= arrBitLengthCount[++xIndex]) + break; /* enough codes to use up j bits */ + fBitCounter1 -= arrBitLengthCount[xIndex]; /* else deduct codes from patterns */ + } + } + if ( + bitsBeforeThisTable + counter > lengthOfEOBcode + && bitsBeforeThisTable < lengthOfEOBcode + ) + counter = lengthOfEOBcode - bitsBeforeThisTable; /* make EOB code end at table */ + + numberOfEntriesInCurrentTable = 1 << counter; /* table entries for j-bit table */ + arrLX[stackOfBitsPerTable + tableLevel] = counter; /* set table size in stack */ + + /* allocate and link in new table */ + pointerToCurrentTable = new huftNode[numberOfEntriesInCurrentTable]; + + // set the pointer, pointed to by *outHufTable to the second huft in pointertoCurrentTable + if (first) + { + outHufTable = pointerToCurrentTable; /* link to list for huft_free() */ + first = false; + } + + arrHufTableStack[tableLevel] = pointerToCurrentTable; /* table starts after link */ + + /* connect to last table, if there is one */ + if (tableLevel != 0) + { + bitOffset[tableLevel] = counterCurrentCode; /* save pattern for backing up */ + + huftNode vHuft = new huftNode + { + NumberOfBitsUsed = arrLX[stackOfBitsPerTable + tableLevel - 1], /* bits to dump before this table */ + NumberOfExtraBits = 32 + counter, /* bits in this table */ + ChildNodes = pointerToCurrentTable /* pointer to this table */ + }; + + counter = + (counterCurrentCode & ((1 << bitsBeforeThisTable) - 1)) + >> (bitsBeforeThisTable - arrLX[stackOfBitsPerTable + tableLevel - 1]); + arrHufTableStack[tableLevel - 1][counter] = vHuft; /* connect to last table */ + } + } + + /* set up table entry in r */ + huftNode vHuft1 = new huftNode + { + NumberOfBitsUsed = numberOfBitsInCurrentCode - bitsBeforeThisTable + }; + + if (pIndex >= numberOfCodes) + vHuft1.NumberOfExtraBits = INVALID_CODE; /* out of values--invalid code */ + else if (arrValuesInOrderOfBitLength[pIndex] < numberOfSimpleValueCodes) + { + vHuft1.NumberOfExtraBits = ( + arrValuesInOrderOfBitLength[pIndex] < 256 ? 32 : 31 + ); /* 256 is end-of-block code */ + vHuft1.Value = arrValuesInOrderOfBitLength[pIndex++]; /* simple code is just the value */ + } + else + { + vHuft1.NumberOfExtraBits = arrExtraBitsForNonSimpleCodes[ + arrValuesInOrderOfBitLength[pIndex] - numberOfSimpleValueCodes + ]; /* non-simple--look up in lists */ + vHuft1.Value = arrBaseValuesForNonSimpleCodes[ + arrValuesInOrderOfBitLength[pIndex++] - numberOfSimpleValueCodes + ]; + } + + /* fill code-like entries with r */ + int fBitCounter2 = 1 << (numberOfBitsInCurrentCode - bitsBeforeThisTable); + for ( + counter = counterCurrentCode >> bitsBeforeThisTable; + counter < numberOfEntriesInCurrentTable; + counter += fBitCounter2 + ) + pointerToCurrentTable[counter] = vHuft1; + + /* backwards increment the k-bit code i */ + for ( + counter = 1 << (numberOfBitsInCurrentCode - 1); + (counterCurrentCode & counter) != 0; + counter >>= 1 + ) + counterCurrentCode ^= counter; + counterCurrentCode ^= counter; + + /* backup over finished tables */ + while ( + (counterCurrentCode & ((1 << bitsBeforeThisTable) - 1)) != bitOffset[tableLevel] + ) + bitsBeforeThisTable -= arrLX[stackOfBitsPerTable + (--tableLevel)]; + } + } + + /* return actual size of base table */ + outBitsForTable = arrLX[stackOfBitsPerTable]; + + /* Return true (1) if we were given an incomplete table */ + return (numberOfDummyCodesAdded != 0 && maximumCodeLength != 1) ? 1 : 0; + } +} diff --git a/src/SharpCompress/Compressors/Reduce/ReduceStream.cs b/src/SharpCompress/Compressors/Reduce/ReduceStream.cs new file mode 100644 index 00000000..2ebc20ab --- /dev/null +++ b/src/SharpCompress/Compressors/Reduce/ReduceStream.cs @@ -0,0 +1,249 @@ +using System; +using System.IO; + +namespace SharpCompress.Compressors.Reduce; + +public class ReduceStream : Stream +{ + private readonly long unCompressedSize; + private readonly long compressedSize; + private readonly Stream inStream; + + private long inByteCount; + private const int EOF = 1234; + + private readonly int factor; + private readonly int distanceMask; + private readonly int lengthMask; + + private long outBytesCount; + + private readonly byte[] windowsBuffer; + private int windowIndex; + private int length; + private int distance; + + public ReduceStream(Stream inStr, long compsize, long unCompSize, int factor) + { + inStream = inStr; + compressedSize = compsize; + unCompressedSize = unCompSize; + inByteCount = 0; + outBytesCount = 0; + + this.factor = factor; + distanceMask = (int)mask_bits[factor] << 8; + lengthMask = 0xff >> factor; + + windowIndex = 0; + length = 0; + distance = 0; + + windowsBuffer = new byte[WSIZE]; + + outByte = 0; + + LoadBitLengthTable(); + LoadNextByteTable(); + } + + public override void Flush() + { + throw new NotImplementedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => false; + public override long Length => unCompressedSize; + public override long Position + { + get => outBytesCount; + set { } + } + + private const int RunLengthCode = 144; + private const int WSIZE = 0x4000; + + private readonly uint[] mask_bits = new uint[] + { + 0x0000, + 0x0001, + 0x0003, + 0x0007, + 0x000f, + 0x001f, + 0x003f, + 0x007f, + 0x00ff, + 0x01ff, + 0x03ff, + 0x07ff, + 0x0fff, + 0x1fff, + 0x3fff, + 0x7fff, + 0xffff + }; + + private int bitBufferCount; + private ulong bitBuffer; + + private int NEXTBYTE() + { + if (inByteCount == compressedSize) + return EOF; + inByteCount++; + return inStream.ReadByte(); + } + + private void READBITS(int nbits, out byte zdest) + { + if (nbits > bitBufferCount) + { + int temp; + while (bitBufferCount <= 8 * (int)(4 - 1) && (temp = NEXTBYTE()) != EOF) + { + bitBuffer |= (ulong)temp << bitBufferCount; + bitBufferCount += 8; + } + } + zdest = (byte)(bitBuffer & (ulong)mask_bits[nbits]); + bitBuffer >>= nbits; + bitBufferCount -= nbits; + } + + private byte[] bitCountTable = []; + + private void LoadBitLengthTable() + { + byte[] bitPos = { 0, 2, 4, 8, 16, 32, 64, 128, 255 }; + bitCountTable = new byte[256]; + + for (byte i = 1; i <= 8; i++) + { + int vMin = bitPos[i - 1] + 1; + int vMax = bitPos[i]; + for (int j = vMin; j <= vMax; j++) + { + bitCountTable[j] = i; + } + } + } + + private byte[][] nextByteTable = []; + + private void LoadNextByteTable() + { + nextByteTable = new byte[256][]; + for (int x = 255; x >= 0; x--) + { + READBITS(6, out byte Slen); + nextByteTable[x] = new byte[Slen]; + for (int i = 0; i < Slen; i++) + { + READBITS(8, out nextByteTable[x][i]); + } + } + } + + private byte outByte; + + private byte GetNextByte() + { + if (nextByteTable[outByte].Length == 0) + { + READBITS(8, out outByte); + return outByte; + } + READBITS(1, out byte nextBit); + if (nextBit == 1) + { + READBITS(8, out outByte); + return outByte; + } + READBITS(bitCountTable[nextByteTable[outByte].Length], out byte nextByteIndex); + outByte = nextByteTable[outByte][nextByteIndex]; + return outByte; + } + + public override int Read(byte[] buffer, int offset, int count) + { + int countIndex = 0; + while (countIndex < count && outBytesCount < unCompressedSize) + { + if (length == 0) + { + byte nextByte = GetNextByte(); + if (nextByte != RunLengthCode) + { + buffer[offset + (countIndex++)] = nextByte; + windowsBuffer[windowIndex++] = nextByte; + outBytesCount++; + if (windowIndex == WSIZE) + windowIndex = 0; + + continue; + } + + nextByte = GetNextByte(); + if (nextByte == 0) + { + buffer[offset + (countIndex++)] = RunLengthCode; + windowsBuffer[windowIndex++] = RunLengthCode; + outBytesCount++; + if (windowIndex == WSIZE) + windowIndex = 0; + + continue; + } + + int lengthDistanceByte = nextByte; + length = lengthDistanceByte & lengthMask; + if (length == lengthMask) + { + length += GetNextByte(); + } + length += 3; + + int distanceHighByte = (lengthDistanceByte << factor) & distanceMask; + distance = windowIndex - (distanceHighByte + GetNextByte() + 1); + + 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; + } +}