From 959bbdcf1b83ff616280eaa17ecb08da3df4521e Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Tue, 20 Dec 2022 15:06:44 +0000 Subject: [PATCH] big clean up --- build/Program.cs | 2 +- src/SharpCompress/Algorithms/Adler32.cs | 763 ++- src/SharpCompress/Archives/AbstractArchive.cs | 303 +- .../Archives/AbstractWritableArchive.cs | 356 +- src/SharpCompress/Archives/ArchiveFactory.cs | 364 +- .../Archives/GZip/GZipArchive.cs | 393 +- .../Archives/GZip/GZipArchiveEntry.cs | 51 +- .../Archives/GZip/GZipWritableArchiveEntry.cs | 115 +- src/SharpCompress/Archives/IArchive.cs | 69 +- src/SharpCompress/Archives/IArchiveEntry.cs | 35 +- .../Archives/IArchiveEntryExtensions.cs | 127 +- .../Archives/IArchiveExtensions.cs | 29 +- .../Archives/IArchiveExtractionListener.cs | 15 +- src/SharpCompress/Archives/IArchiveFactory.cs | 57 +- .../Archives/IMultiArchiveFactory.cs | 62 +- .../Archives/IWritableArchive.cs | 35 +- .../Archives/IWritableArchiveEntry.cs | 11 +- .../Archives/IWritableArchiveExtensions.cs | 155 +- .../Archives/IWriteableArchiveFactory.cs | 37 +- .../Archives/Rar/FileInfoRarArchiveVolume.cs | 65 +- .../Archives/Rar/FileInfoRarFilePart.cs | 33 +- .../Archives/Rar/RarArchive.Extensions.cs | 33 +- src/SharpCompress/Archives/Rar/RarArchive.cs | 343 +- .../Archives/Rar/RarArchiveEntry.cs | 138 +- .../Archives/Rar/RarArchiveEntryFactory.cs | 83 +- .../Archives/Rar/SeekableFilePart.cs | 57 +- .../Archives/Rar/StreamRarArchiveVolume.cs | 37 +- .../Archives/SevenZip/SevenZipArchive.cs | 481 +- .../Archives/SevenZip/SevenZipArchiveEntry.cs | 41 +- src/SharpCompress/Archives/Tar/TarArchive.cs | 421 +- .../Archives/Tar/TarArchiveEntry.cs | 47 +- .../Archives/Tar/TarWritableArchiveEntry.cs | 115 +- src/SharpCompress/Archives/Zip/ZipArchive.cs | 576 ++- .../Archives/Zip/ZipArchiveEntry.cs | 43 +- .../Archives/Zip/ZipWritableArchiveEntry.cs | 119 +- src/SharpCompress/AssemblyInfo.cs | 2 +- src/SharpCompress/Common/ArchiveEncoding.cs | 119 +- src/SharpCompress/Common/ArchiveException.cs | 11 +- .../Common/ArchiveExtractionEventArgs.cs | 17 +- src/SharpCompress/Common/ArchiveType.cs | 17 +- .../Common/CompressedBytesReadEventArgs.cs | 41 +- src/SharpCompress/Common/CompressionType.cs | 33 +- .../Common/CryptographicException.cs | 11 +- src/SharpCompress/Common/Entry.cs | 133 +- src/SharpCompress/Common/EntryStream.cs | 151 +- .../Common/ExtractionException.cs | 13 +- src/SharpCompress/Common/ExtractionMethods.cs | 2 +- src/SharpCompress/Common/ExtractionOptions.cs | 73 +- src/SharpCompress/Common/FilePart.cs | 29 +- .../FilePartExtractionBeginEventArgs.cs | 47 +- src/SharpCompress/Common/FlagUtility.cs | 179 +- src/SharpCompress/Common/GZip/GZipEntry.cs | 53 +- src/SharpCompress/Common/GZip/GZipFilePart.cs | 217 +- src/SharpCompress/Common/GZip/GZipVolume.cs | 27 +- src/SharpCompress/Common/IEntry.Extensions.cs | 67 +- src/SharpCompress/Common/IEntry.cs | 42 +- .../Common/IExtractionListener.cs | 11 +- src/SharpCompress/Common/IVolume.cs | 13 +- .../Common/IncompleteArchiveException.cs | 9 +- .../Common/InvalidFormatException.cs | 13 +- .../Common/MultiVolumeExtractionException.cs | 15 +- .../MultipartStreamRequiredException.cs | 9 +- src/SharpCompress/Common/OptionsBase.cs | 19 +- .../Common/PasswordProtectedException.cs | 15 +- .../Common/Rar/Headers/AVHeader.cs | 49 +- .../Common/Rar/Headers/ArchiveCryptHeader.cs | 88 +- .../Common/Rar/Headers/ArchiveHeader.cs | 125 +- .../Common/Rar/Headers/CommentHeader.cs | 47 +- .../Common/Rar/Headers/EndArchiveHeader.cs | 59 +- .../Common/Rar/Headers/FileHeader.cs | 895 ++-- .../Common/Rar/Headers/FileNameDecoder.cs | 149 +- src/SharpCompress/Common/Rar/Headers/Flags.cs | 293 +- .../Common/Rar/Headers/IRarHeader.cs | 9 +- .../Common/Rar/Headers/MarkHeader.cs | 195 +- .../Common/Rar/Headers/NewSubHeaderType.cs | 83 +- .../Common/Rar/Headers/ProtectHeader.cs | 47 +- .../Common/Rar/Headers/RarHeader.cs | 253 +- .../Common/Rar/Headers/RarHeaderFactory.cs | 337 +- .../Common/Rar/Headers/SignHeader.cs | 43 +- .../Common/Rar/RarCrcBinaryReader.cs | 67 +- .../Common/Rar/RarCryptoBinaryReader.cs | 201 +- .../Common/Rar/RarCryptoWrapper.cs | 151 +- src/SharpCompress/Common/Rar/RarEntry.cs | 135 +- src/SharpCompress/Common/Rar/RarFilePart.cs | 33 +- src/SharpCompress/Common/Rar/RarRijndael.cs | 209 +- src/SharpCompress/Common/Rar/RarVolume.cs | 308 +- .../Common/ReaderExtractionEventArgs.cs | 23 +- .../Common/SevenZip/ArchiveDatabase.cs | 329 +- .../Common/SevenZip/ArchiveReader.cs | 3092 ++++++------ .../Common/SevenZip/CBindPair.cs | 11 +- .../Common/SevenZip/CCoderInfo.cs | 17 +- .../Common/SevenZip/CFileItem.cs | 55 +- src/SharpCompress/Common/SevenZip/CFolder.cs | 301 +- .../Common/SevenZip/CMethodId.cs | 95 +- .../Common/SevenZip/CStreamSwitch.cs | 95 +- .../Common/SevenZip/DataReader.cs | 355 +- .../Common/SevenZip/SevenZipEntry.cs | 79 +- .../Common/SevenZip/SevenZipFilePart.cs | 206 +- .../Common/SevenZip/SevenZipVolume.cs | 11 +- .../Common/Tar/Headers/EntryType.cs | 33 +- .../Common/Tar/Headers/TarHeader.cs | 593 +-- src/SharpCompress/Common/Tar/TarEntry.cs | 119 +- src/SharpCompress/Common/Tar/TarFilePart.cs | 50 +- .../Common/Tar/TarHeaderFactory.cs | 95 +- .../Common/Tar/TarReadOnlySubStream.cs | 2 +- src/SharpCompress/Common/Tar/TarVolume.cs | 11 +- src/SharpCompress/Common/Volume.cs | 81 +- .../Common/Zip/Headers/DirectoryEndHeader.cs | 73 +- .../Zip/Headers/DirectoryEntryHeader.cs | 181 +- .../Common/Zip/Headers/HeaderFlags.cs | 27 +- .../Common/Zip/Headers/IgnoreHeader.cs | 13 +- .../Common/Zip/Headers/LocalEntryHeader.cs | 125 +- .../Headers/LocalEntryHeaderExtraFactory.cs | 303 +- .../Common/Zip/Headers/SplitHeader.cs | 19 +- .../Zip/Headers/Zip64DirectoryEndHeader.cs | 89 +- .../Headers/Zip64DirectoryEndLocatorHeader.cs | 31 +- .../Common/Zip/Headers/ZipFileEntry.cs | 197 +- .../Common/Zip/Headers/ZipHeader.cs | 27 +- .../Common/Zip/Headers/ZipHeaderType.cs | 21 +- .../Zip/PkwareTraditionalCryptoStream.cs | 173 +- .../Zip/PkwareTraditionalEncryptionData.cs | 215 +- .../Common/Zip/SeekableZipFilePart.cs | 95 +- .../Common/Zip/SeekableZipHeaderFactory.cs | 250 +- .../Common/Zip/StreamingZipFilePart.cs | 161 +- .../Common/Zip/StreamingZipHeaderFactory.cs | 189 +- .../Common/Zip/WinzipAesCryptoStream.cs | 335 +- .../Common/Zip/WinzipAesEncryptionData.cs | 117 +- .../Common/Zip/WinzipAesKeySize.cs | 13 +- .../Common/Zip/ZipCompressionMethod.cs | 21 +- src/SharpCompress/Common/Zip/ZipEntry.cs | 143 +- src/SharpCompress/Common/Zip/ZipFilePart.cs | 351 +- .../Common/Zip/ZipHeaderFactory.cs | 357 +- src/SharpCompress/Common/Zip/ZipVolume.cs | 15 +- src/SharpCompress/Compressors/ADC/ADCBase.cs | 319 +- .../Compressors/ADC/ADCStream.cs | 271 +- .../Compressors/BZip2/BZip2Constants.cs | 1071 +++-- .../Compressors/BZip2/BZip2Stream.cs | 201 +- .../Compressors/BZip2/CBZip2InputStream.cs | 1962 ++++---- .../Compressors/BZip2/CBZip2OutputStream.cs | 3553 +++++++------- src/SharpCompress/Compressors/BZip2/CRC.cs | 609 ++- .../Compressors/CompressionMode.cs | 11 +- .../Compressors/Deflate/CRC32.cs | 429 +- .../Compressors/Deflate/DeflateManager.cs | 3824 +++++++-------- .../Compressors/Deflate/DeflateStream.cs | 697 ++- .../Compressors/Deflate/FlushType.cs | 75 +- .../Compressors/Deflate/GZipStream.cs | 888 ++-- .../Compressors/Deflate/InfTree.cs | 4211 ++++++++--------- .../Compressors/Deflate/Inflate.cs | 3538 +++++++------- src/SharpCompress/Compressors/Deflate/Tree.cs | 2265 +++++---- src/SharpCompress/Compressors/Deflate/Zlib.cs | 1837 ++++--- .../Compressors/Deflate/ZlibCodec.cs | 1319 +++--- .../Compressors/Deflate/ZlibConstants.cs | 91 +- .../Compressors/Deflate/ZlibStream.cs | 625 ++- .../Compressors/Deflate64/BlockType.cs | 13 +- .../Compressors/Deflate64/DeflateInput.cs | 73 +- .../Deflate64/FastEncoderStatus.cs | 1917 ++++---- .../Compressors/Deflate64/HuffmanTree.cs | 587 ++- .../Compressors/Deflate64/InflaterManaged.cs | 1523 +++--- .../Compressors/Deflate64/InflaterState.cs | 59 +- .../Compressors/Deflate64/InputBuffer.cs | 311 +- .../Compressors/Deflate64/Match.cs | 21 +- .../Compressors/Deflate64/MatchState.cs | 13 +- .../Compressors/Deflate64/OutputWindow.cs | 249 +- .../Compressors/Filters/BCJ2Filter.cs | 379 +- .../Compressors/Filters/BCJFilter.cs | 225 +- .../Compressors/Filters/BranchExecFilter.cs | 577 +-- .../Compressors/Filters/Filter.cs | 261 +- .../Compressors/LZMA/Bcj2DecoderStream.cs | 443 +- .../Compressors/LZMA/BitVector.cs | 175 +- src/SharpCompress/Compressors/LZMA/CRC.cs | 147 +- .../Compressors/LZMA/DecoderStream.cs | 429 +- src/SharpCompress/Compressors/LZMA/ICoder.cs | 327 +- .../Compressors/LZMA/LZ/LzBinTree.cs | 705 ++- .../Compressors/LZMA/LZ/LzInWindow.cs | 324 +- .../Compressors/LZMA/LZ/LzOutWindow.cs | 361 +- .../Compressors/LZMA/LZipStream.cs | 413 +- src/SharpCompress/Compressors/LZMA/Log.cs | 143 +- .../Compressors/LZMA/LzmaBase.cs | 175 +- .../Compressors/LZMA/LzmaDecoder.cs | 923 ++-- .../Compressors/LZMA/LzmaEncoder.cs | 3447 +++++++------- .../Compressors/LZMA/LzmaEncoderProperties.cs | 79 +- .../Compressors/LZMA/LzmaStream.cs | 587 ++- .../Compressors/LZMA/RangeCoder/RangeCoder.cs | 438 +- .../LZMA/RangeCoder/RangeCoderBit.cs | 243 +- .../LZMA/RangeCoder/RangeCoderBitTree.cs | 299 +- .../Compressors/LZMA/Registry.cs | 90 +- .../LZMA/Utilites/CrcBuilderStream.cs | 145 +- .../LZMA/Utilites/CrcCheckStream.cs | 185 +- .../LZMA/Utilites/IPasswordProvider.cs | 9 +- .../Compressors/LZMA/Utilites/Utils.cs | 145 +- .../Compressors/PPMd/H/FreqData.cs | 103 +- .../Compressors/PPMd/H/ModelPPM.cs | 1786 ++++--- .../Compressors/PPMd/H/PPMContext.cs | 1148 +++-- .../Compressors/PPMd/H/Pointer.cs | 33 +- .../Compressors/PPMd/H/RangeCoder.cs | 290 +- .../Compressors/PPMd/H/RarMemBlock.cs | 217 +- .../Compressors/PPMd/H/RarNode.cs | 75 +- .../Compressors/PPMd/H/SEE2Context.cs | 156 +- src/SharpCompress/Compressors/PPMd/H/State.cs | 147 +- .../Compressors/PPMd/H/StateRef.cs | 128 +- .../Compressors/PPMd/H/SubAllocator.cs | 853 ++-- .../Compressors/PPMd/I1/Allocator.cs | 935 ++-- .../Compressors/PPMd/I1/Coder.cs | 193 +- .../Compressors/PPMd/I1/MemoryNode.cs | 484 +- .../Compressors/PPMd/I1/Model.cs | 1749 ++++--- .../PPMd/I1/ModelRestorationMethod.cs | 33 +- .../Compressors/PPMd/I1/Pointer.cs | 706 ++- .../Compressors/PPMd/I1/PpmContext.cs | 1572 +++--- .../Compressors/PPMd/I1/PpmState.cs | 388 +- .../Compressors/PPMd/I1/See2Context.cs | 81 +- .../Compressors/PPMd/PpmdProperties.cs | 125 +- .../Compressors/PPMd/PpmdStream.cs | 213 +- .../Compressors/PPMd/PpmdVersion.cs | 13 +- .../Compressors/Rar/IRarUnpack.cs | 23 +- .../Rar/MultiVolumeReadOnlyStream.cs | 285 +- src/SharpCompress/Compressors/Rar/RarCRC.cs | 61 +- .../Compressors/Rar/RarCrcStream.cs | 71 +- .../Compressors/Rar/RarStream.cs | 227 +- .../Rar/UnpackV1/Decode/AudioVariables.cs | 43 +- .../Rar/UnpackV1/Decode/BitDecode.cs | 9 +- .../Rar/UnpackV1/Decode/CodeType.cs | 25 +- .../Compressors/Rar/UnpackV1/Decode/Decode.cs | 53 +- .../Rar/UnpackV1/Decode/DistDecode.cs | 9 +- .../Rar/UnpackV1/Decode/FilterType.cs | 29 +- .../Rar/UnpackV1/Decode/LitDecode.cs | 9 +- .../Rar/UnpackV1/Decode/LowDistDecode.cs | 9 +- .../Rar/UnpackV1/Decode/MultDecode.cs | 9 +- .../Rar/UnpackV1/Decode/PackDef.cs | 79 +- .../Rar/UnpackV1/Decode/RepDecode.cs | 9 +- .../Rar/UnpackV1/PPM/BlockTypes.cs | 11 +- .../Compressors/Rar/UnpackV1/Unpack.cs | 2252 +++++---- .../Compressors/Rar/UnpackV1/Unpack15.cs | 1451 +++--- .../Compressors/Rar/UnpackV1/Unpack20.cs | 1167 +++-- .../Compressors/Rar/UnpackV1/Unpack50.cs | 1781 ++++--- .../Compressors/Rar/UnpackV1/UnpackFilter.cs | 69 +- .../Compressors/Rar/UnpackV1/UnpackInline.cs | 51 +- .../Compressors/Rar/UnpackV1/UnpackUtility.cs | 363 +- .../Rar/UnpackV2017/BitInput.getbits_cpp.cs | 85 +- .../Rar/UnpackV2017/BitInput.getbits_hpp.cs | 115 +- .../FragmentedWindow.unpack50frag_cpp.cs | 280 +- .../Rar/UnpackV2017/PackDef.compress_hpp.cs | 89 +- .../Compressors/Rar/UnpackV2017/Unpack.cs | 207 +- .../Rar/UnpackV2017/Unpack.rawint_hpp.cs | 219 +- .../Rar/UnpackV2017/Unpack.unpack15_cpp.cs | 1267 ++--- .../Rar/UnpackV2017/Unpack.unpack20_cpp.cs | 1142 +++-- .../Rar/UnpackV2017/Unpack.unpack30_cpp.cs | 6 +- .../Rar/UnpackV2017/Unpack.unpack50_cpp.cs | 1633 ++++--- .../Rar/UnpackV2017/Unpack.unpack_cpp.cs | 699 ++- .../UnpackV2017/Unpack.unpackinline_cpp.cs | 318 +- .../Compressors/Rar/UnpackV2017/unpack_hpp.cs | 660 ++- .../Compressors/Rar/VM/BitInput.cs | 183 +- src/SharpCompress/Compressors/Rar/VM/RarVM.cs | 3104 ++++++------ .../Compressors/Rar/VM/VMCmdFlags.cs | 113 +- .../Compressors/Rar/VM/VMCommands.cs | 117 +- .../Compressors/Rar/VM/VMFlags.cs | 15 +- .../Compressors/Rar/VM/VMOpType.cs | 15 +- .../Compressors/Rar/VM/VMPreparedCommand.cs | 25 +- .../Compressors/Rar/VM/VMPreparedOperand.cs | 15 +- .../Compressors/Rar/VM/VMPreparedProgram.cs | 25 +- .../Rar/VM/VMStandardFilterSignature.cs | 27 +- .../Compressors/Rar/VM/VMStandardFilters.cs | 23 +- .../Compressors/Xz/BinaryUtils.cs | 95 +- src/SharpCompress/Compressors/Xz/CheckType.cs | 15 +- src/SharpCompress/Compressors/Xz/Crc32.cs | 107 +- src/SharpCompress/Compressors/Xz/Crc64.cs | 98 +- .../Compressors/Xz/Filters/ArmFilter.cs | 86 +- .../Compressors/Xz/Filters/ArmThumbFilter.cs | 86 +- .../Compressors/Xz/Filters/BlockFilter.cs | 119 +- .../Compressors/Xz/Filters/IA64Filter.cs | 86 +- .../Compressors/Xz/Filters/Lzma2Filter.cs | 103 +- .../Compressors/Xz/Filters/PowerPCFilter.cs | 86 +- .../Compressors/Xz/Filters/SparcFilter.cs | 86 +- .../Compressors/Xz/Filters/X86Filter.cs | 90 +- .../Compressors/Xz/MultiByteIntegers.cs | 67 +- .../Compressors/Xz/ReadOnlyStream.cs | 67 +- src/SharpCompress/Compressors/Xz/XZBlock.cs | 331 +- src/SharpCompress/Compressors/Xz/XZFooter.cs | 73 +- src/SharpCompress/Compressors/Xz/XZHeader.cs | 85 +- src/SharpCompress/Compressors/Xz/XZIndex.cs | 143 +- .../Xz/XZIndexMarkerReachedException.cs | 9 +- .../Compressors/Xz/XZIndexRecord.cs | 31 +- .../Compressors/Xz/XZReadOnlyStream.cs | 17 +- src/SharpCompress/Compressors/Xz/XZStream.cs | 235 +- src/SharpCompress/Crypto/Crc32Stream.cs | 223 +- src/SharpCompress/Crypto/CryptoException.cs | 15 +- .../Crypto/DataLengthException.cs | 33 +- src/SharpCompress/Crypto/IBlockCipher.cs | 51 +- src/SharpCompress/Crypto/ICipherParameters.cs | 7 +- src/SharpCompress/Crypto/KeyParameter.cs | 63 +- src/SharpCompress/Crypto/RijndaelEngine.cs | 3706 ++++++++------- src/SharpCompress/Factories/Factory.cs | 156 +- src/SharpCompress/Factories/GZipFactory.cs | 235 +- src/SharpCompress/Factories/IFactory.cs | 81 +- src/SharpCompress/Factories/RarFactory.cs | 152 +- .../Factories/SevenZipFactory.cs | 146 +- src/SharpCompress/Factories/TarFactory.cs | 328 +- src/SharpCompress/Factories/ZipFactory.cs | 223 +- src/SharpCompress/IO/BufferedSubStream.cs | 133 +- .../IO/CountingWritableSubStream.cs | 87 +- src/SharpCompress/IO/ListeningStream.cs | 151 +- src/SharpCompress/IO/MarkingBinaryReader.cs | 375 +- src/SharpCompress/IO/NonDisposingStream.cs | 129 +- src/SharpCompress/IO/ReadOnlySubStream.cs | 133 +- src/SharpCompress/IO/RewindableStream.cs | 255 +- src/SharpCompress/IO/SourceStream.cs | 431 +- src/SharpCompress/IO/StreamingMode.cs | 11 +- src/SharpCompress/Lazy.cs | 35 +- src/SharpCompress/LazyReadOnlyCollection.cs | 249 +- .../Polyfills/StreamExtensions.cs | 55 +- .../Polyfills/StringExtensions.cs | 21 +- src/SharpCompress/ReadOnlyCollection.cs | 77 +- src/SharpCompress/Readers/AbstractReader.cs | 497 +- src/SharpCompress/Readers/GZip/GZipReader.cs | 49 +- src/SharpCompress/Readers/IReader.cs | 53 +- .../Readers/IReaderExtensions.cs | 131 +- .../Readers/IReaderExtractionListener.cs | 11 +- src/SharpCompress/Readers/IReaderFactory.cs | 26 +- .../Readers/Rar/MultiVolumeRarReader.cs | 185 +- .../Readers/Rar/NonSeekableStreamFilePart.cs | 23 +- src/SharpCompress/Readers/Rar/RarReader.cs | 127 +- .../Readers/Rar/RarReaderEntry.cs | 51 +- .../Readers/Rar/RarReaderVolume.cs | 25 +- .../Readers/Rar/SingleVolumeRarReader.cs | 41 +- src/SharpCompress/Readers/ReaderFactory.cs | 53 +- src/SharpCompress/Readers/ReaderOptions.cs | 21 +- src/SharpCompress/Readers/ReaderProgress.cs | 31 +- src/SharpCompress/Readers/Tar/TarReader.cs | 2 +- src/SharpCompress/Readers/Zip/ZipReader.cs | 93 +- src/SharpCompress/Utility.cs | 772 +-- src/SharpCompress/Writers/AbstractWriter.cs | 81 +- src/SharpCompress/Writers/GZip/GZipWriter.cs | 69 +- .../Writers/GZip/GZipWriterOptions.cs | 28 +- src/SharpCompress/Writers/IWriter.cs | 13 +- .../Writers/IWriterExtensions.cs | 105 +- src/SharpCompress/Writers/IWriterFactory.cs | 14 +- src/SharpCompress/Writers/Tar/TarWriter.cs | 205 +- .../Writers/Tar/TarWriterOptions.cs | 31 +- src/SharpCompress/Writers/WriterFactory.cs | 31 +- src/SharpCompress/Writers/WriterOptions.cs | 23 +- .../Writers/Zip/ZipCentralDirectoryEntry.cs | 297 +- src/SharpCompress/Writers/Zip/ZipWriter.cs | 1033 ++-- .../Writers/Zip/ZipWriterEntryOptions.cs | 37 +- .../Writers/Zip/ZipWriterOptions.cs | 60 +- tests/SharpCompress.Test/ADCTest.cs | 239 +- tests/SharpCompress.Test/ArchiveTests.cs | 451 +- .../Filters/BranchExecTests.cs | 2453 +++++----- .../GZip/GZipArchiveTests.cs | 197 +- .../GZip/GZipReaderTests.cs | 51 +- .../GZip/GZipWriterTests.cs | 147 +- .../Mocks/FlushOnDisposeStream.cs | 115 +- .../Mocks/ForwardOnlyStream.cs | 95 +- tests/SharpCompress.Test/Mocks/TestStream.cs | 103 +- .../SharpCompress.Test/Rar/RarArchiveTests.cs | 1321 +++--- .../Rar/RarHeaderFactoryTest.cs | 107 +- .../SharpCompress.Test/Rar/RarReaderTests.cs | 719 ++- tests/SharpCompress.Test/ReaderTests.cs | 117 +- .../SevenZip/SevenZipArchiveTests.cs | 273 +- .../Streams/LzmaStreamTests.cs | 1035 ++-- .../Streams/RewindableStreamTest.cs | 153 +- .../Streams/ZlibBaseStreamTests.cs | 181 +- .../SharpCompress.Test/Tar/TarArchiveTests.cs | 2 +- .../SharpCompress.Test/Tar/TarReaderTests.cs | 2 +- .../SharpCompress.Test/Tar/TarWriterTests.cs | 143 +- tests/SharpCompress.Test/TestBase.cs | 405 +- tests/SharpCompress.Test/WriterTests.cs | 91 +- tests/SharpCompress.Test/Xz/Crc32Tests.cs | 41 +- tests/SharpCompress.Test/Xz/Crc64Tests.cs | 41 +- .../SharpCompress.Test/Xz/Filters/BCJTests.cs | 125 +- .../Xz/Filters/Lzma2Tests.cs | 2 +- tests/SharpCompress.Test/Xz/XZBlockTests.cs | 169 +- tests/SharpCompress.Test/Xz/XZHeaderTests.cs | 2 +- tests/SharpCompress.Test/Xz/XZIndexTests.cs | 2 +- tests/SharpCompress.Test/Xz/XZStreamTests.cs | 2 +- tests/SharpCompress.Test/Xz/XZTestsBase.cs | 2287 +++++---- tests/SharpCompress.Test/Zip/Zip64Tests.cs | 378 +- .../SharpCompress.Test/Zip/ZipArchiveTests.cs | 2 +- .../SharpCompress.Test/Zip/ZipReaderTests.cs | 720 ++- .../SharpCompress.Test/Zip/ZipWriterTests.cs | 131 +- 378 files changed, 59744 insertions(+), 60562 deletions(-) diff --git a/build/Program.cs b/build/Program.cs index 358082aa..000051dd 100644 --- a/build/Program.cs +++ b/build/Program.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; diff --git a/src/SharpCompress/Algorithms/Adler32.cs b/src/SharpCompress/Algorithms/Adler32.cs index 0d4de7fa..e32893ac 100644 --- a/src/SharpCompress/Algorithms/Adler32.cs +++ b/src/SharpCompress/Algorithms/Adler32.cs @@ -15,101 +15,289 @@ using System.Runtime.Intrinsics.X86; #endif #pragma warning disable IDE0007 // Use implicit type -namespace SharpCompress.Algorithms +namespace SharpCompress.Algorithms; + +/// +/// Calculates the 32 bit Adler checksum of a given buffer according to +/// RFC 1950. ZLIB Compressed Data Format Specification version 3.3) +/// +internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Compression/Zlib/Adler32.cs { /// - /// Calculates the 32 bit Adler checksum of a given buffer according to - /// RFC 1950. ZLIB Compressed Data Format Specification version 3.3) + /// Global inlining options. Helps temporarily disable inlining for better profiler output. /// - internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Compression/Zlib/Adler32.cs + private static class InliningOptions // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Common/Helpers/InliningOptions.cs { /// - /// Global inlining options. Helps temporarily disable inlining for better profiler output. + /// regardless of the build conditions. /// - private static class InliningOptions // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Common/Helpers/InliningOptions.cs - { - /// - /// regardless of the build conditions. - /// - public const MethodImplOptions AlwaysInline = MethodImplOptions.AggressiveInlining; + public const MethodImplOptions AlwaysInline = MethodImplOptions.AggressiveInlining; #if PROFILING - public const MethodImplOptions HotPath = MethodImplOptions.NoInlining; - public const MethodImplOptions ShortMethod = MethodImplOptions.NoInlining; + public const MethodImplOptions HotPath = MethodImplOptions.NoInlining; + public const MethodImplOptions ShortMethod = MethodImplOptions.NoInlining; #else #if SUPPORTS_HOTPATH - public const MethodImplOptions HotPath = MethodImplOptions.AggressiveOptimization; + public const MethodImplOptions HotPath = MethodImplOptions.AggressiveOptimization; #else - public const MethodImplOptions HotPath = MethodImplOptions.AggressiveInlining; + public const MethodImplOptions HotPath = MethodImplOptions.AggressiveInlining; #endif - public const MethodImplOptions ShortMethod = MethodImplOptions.AggressiveInlining; + public const MethodImplOptions ShortMethod = MethodImplOptions.AggressiveInlining; #endif - public const MethodImplOptions ColdPath = MethodImplOptions.NoInlining; - } + public const MethodImplOptions ColdPath = MethodImplOptions.NoInlining; + } #if SUPPORTS_RUNTIME_INTRINSICS + /// + /// Provides optimized static methods for trigonometric, logarithmic, + /// and other common mathematical functions. + /// + private static class Numerics // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Common/Helpers/Numerics.cs + { /// - /// Provides optimized static methods for trigonometric, logarithmic, - /// and other common mathematical functions. + /// Reduces elements of the vector into one sum. /// - private static class Numerics // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Common/Helpers/Numerics.cs + /// The accumulator to reduce. + /// The sum of all elements. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ReduceSum(Vector256 accumulator) { - /// - /// Reduces elements of the vector into one sum. - /// - /// The accumulator to reduce. - /// The sum of all elements. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int ReduceSum(Vector256 accumulator) - { - // Add upper lane to lower lane. - Vector128 vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); + // Add upper lane to lower lane. + Vector128 vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); - // Add odd to even. - vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_11_01_01)); + // Add odd to even. + vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_11_01_01)); - // Add high to low. - vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_10_11_10)); + // Add high to low. + vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_10_11_10)); - return Sse2.ConvertToInt32(vsum); - } - - /// - /// Reduces even elements of the vector into one sum. - /// - /// The accumulator to reduce. - /// The sum of even elements. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int EvenReduceSum(Vector256 accumulator) - { - Vector128 vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); // add upper lane to lower lane - vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_10_11_10)); // add high to low - - // Vector128.ToScalar() isn't optimized pre-net5.0 https://github.com/dotnet/runtime/pull/37882 - return Sse2.ConvertToInt32(vsum); - } + return Sse2.ConvertToInt32(vsum); } -#endif /// - /// The default initial seed value of a Adler32 checksum calculation. + /// Reduces even elements of the vector into one sum. /// - public const uint SeedValue = 1U; + /// The accumulator to reduce. + /// The sum of even elements. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int EvenReduceSum(Vector256 accumulator) + { + Vector128 vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); // add upper lane to lower lane + vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_10_11_10)); // add high to low - // Largest prime smaller than 65536 - private const uint BASE = 65521; + // Vector128.ToScalar() isn't optimized pre-net5.0 https://github.com/dotnet/runtime/pull/37882 + return Sse2.ConvertToInt32(vsum); + } + } +#endif - // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 - private const uint NMAX = 5552; + /// + /// The default initial seed value of a Adler32 checksum calculation. + /// + public const uint SeedValue = 1U; + + // Largest prime smaller than 65536 + private const uint BASE = 65521; + + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const uint NMAX = 5552; #if SUPPORTS_RUNTIME_INTRINSICS - private const int MinBufferSize = 64; + private const int MinBufferSize = 64; - private const int BlockSize = 1 << 5; + private const int BlockSize = 1 << 5; - // The C# compiler emits this as a compile-time constant embedded in the PE file. - private static ReadOnlySpan Tap1Tap2 => - new byte[] + // The C# compiler emits this as a compile-time constant embedded in the PE file. + private static ReadOnlySpan Tap1Tap2 => + new byte[] + { + 32, + 31, + 30, + 29, + 28, + 27, + 26, + 25, + 24, + 23, + 22, + 21, + 20, + 19, + 18, + 17, // tap1 + 16, + 15, + 14, + 13, + 12, + 11, + 10, + 9, + 8, + 7, + 6, + 5, + 4, + 3, + 2, + 1 // tap2 + }; +#endif + + /// + /// Calculates the Adler32 checksum with the bytes taken from the span. + /// + /// The readonly span of bytes. + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static uint Calculate(ReadOnlySpan buffer) => Calculate(SeedValue, buffer); + + /// + /// Calculates the Adler32 checksum with the bytes taken from the span and seed. + /// + /// The input Adler32 value. + /// The readonly span of bytes. + /// The . + [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] + public static uint Calculate(uint adler, ReadOnlySpan buffer) + { + if (buffer.IsEmpty) + { + return adler; + } + +#if SUPPORTS_RUNTIME_INTRINSICS + if (Avx2.IsSupported && buffer.Length >= MinBufferSize) + { + return CalculateAvx2(adler, buffer); + } + + if (Ssse3.IsSupported && buffer.Length >= MinBufferSize) + { + return CalculateSse(adler, buffer); + } + + return CalculateScalar(adler, buffer); +#else + return CalculateScalar(adler, buffer); +#endif + } + + // Based on https://github.com/chromium/chromium/blob/master/third_party/zlib/adler32_simd.c +#if SUPPORTS_RUNTIME_INTRINSICS + [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] + private static unsafe uint CalculateSse(uint adler, ReadOnlySpan buffer) + { + uint s1 = adler & 0xFFFF; + uint s2 = (adler >> 16) & 0xFFFF; + + // Process the data in blocks. + uint length = (uint)buffer.Length; + uint blocks = length / BlockSize; + length -= blocks * BlockSize; + + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) + { + fixed (byte* tapPtr = &MemoryMarshal.GetReference(Tap1Tap2)) { + byte* localBufferPtr = bufferPtr; + + // _mm_setr_epi8 on x86 + Vector128 tap1 = Sse2.LoadVector128((sbyte*)tapPtr); + Vector128 tap2 = Sse2.LoadVector128((sbyte*)(tapPtr + 0x10)); + Vector128 zero = Vector128.Zero; + var ones = Vector128.Create((short)1); + + while (blocks > 0) + { + uint n = NMAX / BlockSize; /* The NMAX constraint. */ + if (n > blocks) + { + n = blocks; + } + + blocks -= n; + + // Process n blocks of data. At most NMAX data bytes can be + // processed before s2 must be reduced modulo BASE. + Vector128 v_ps = Vector128.CreateScalar(s1 * n); + Vector128 v_s2 = Vector128.CreateScalar(s2); + Vector128 v_s1 = Vector128.Zero; + + do + { + // Load 32 input bytes. + Vector128 bytes1 = Sse3.LoadDquVector128(localBufferPtr); + Vector128 bytes2 = Sse3.LoadDquVector128(localBufferPtr + 0x10); + + // Add previous block byte sum to v_ps. + v_ps = Sse2.Add(v_ps, v_s1); + + // Horizontally add the bytes for s1, multiply-adds the + // bytes by [ 32, 31, 30, ... ] for s2. + v_s1 = Sse2.Add( + v_s1, + Sse2.SumAbsoluteDifferences(bytes1, zero).AsUInt32() + ); + Vector128 mad1 = Ssse3.MultiplyAddAdjacent(bytes1, tap1); + v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad1, ones).AsUInt32()); + + v_s1 = Sse2.Add( + v_s1, + Sse2.SumAbsoluteDifferences(bytes2, zero).AsUInt32() + ); + Vector128 mad2 = Ssse3.MultiplyAddAdjacent(bytes2, tap2); + v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad2, ones).AsUInt32()); + + localBufferPtr += BlockSize; + } while (--n > 0); + + v_s2 = Sse2.Add(v_s2, Sse2.ShiftLeftLogical(v_ps, 5)); + + // Sum epi32 ints v_s1(s2) and accumulate in s1(s2). + const byte S2301 = 0b1011_0001; // A B C D -> B A D C + const byte S1032 = 0b0100_1110; // A B C D -> C D A B + + v_s1 = Sse2.Add(v_s1, Sse2.Shuffle(v_s1, S1032)); + + s1 += v_s1.ToScalar(); + + v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S2301)); + v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S1032)); + + s2 = v_s2.ToScalar(); + + // Reduce. + s1 %= BASE; + s2 %= BASE; + } + + if (length > 0) + { + HandleLeftOver(localBufferPtr, length, ref s1, ref s2); + } + + return s1 | (s2 << 16); + } + } + } + + // Based on: https://github.com/zlib-ng/zlib-ng/blob/develop/arch/x86/adler32_avx2.c + [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] + public static unsafe uint CalculateAvx2(uint adler, ReadOnlySpan buffer) + { + uint s1 = adler & 0xFFFF; + uint s2 = (adler >> 16) & 0xFFFF; + uint length = (uint)buffer.Length; + + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) + { + byte* localBufferPtr = bufferPtr; + + Vector256 zero = Vector256.Zero; + var dot3v = Vector256.Create((short)1); + var dot2v = Vector256.Create( 32, 31, 30, @@ -125,7 +313,7 @@ namespace SharpCompress.Algorithms 20, 19, 18, - 17, // tap1 + 17, 16, 15, 14, @@ -141,353 +329,164 @@ namespace SharpCompress.Algorithms 4, 3, 2, - 1 // tap2 - }; -#endif + 1 + ); - /// - /// Calculates the Adler32 checksum with the bytes taken from the span. - /// - /// The readonly span of bytes. - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static uint Calculate(ReadOnlySpan buffer) => Calculate(SeedValue, buffer); + // Process n blocks of data. At most NMAX data bytes can be + // processed before s2 must be reduced modulo BASE. + var vs1 = Vector256.CreateScalar(s1); + var vs2 = Vector256.CreateScalar(s2); - /// - /// Calculates the Adler32 checksum with the bytes taken from the span and seed. - /// - /// The input Adler32 value. - /// The readonly span of bytes. - /// The . - [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] - public static uint Calculate(uint adler, ReadOnlySpan buffer) - { - if (buffer.IsEmpty) + while (length >= 32) { - return adler; - } + int k = length < NMAX ? (int)length : (int)NMAX; + k -= k % 32; + length -= (uint)k; -#if SUPPORTS_RUNTIME_INTRINSICS - if (Avx2.IsSupported && buffer.Length >= MinBufferSize) - { - return CalculateAvx2(adler, buffer); - } + Vector256 vs10 = vs1; + Vector256 vs3 = Vector256.Zero; - if (Ssse3.IsSupported && buffer.Length >= MinBufferSize) - { - return CalculateSse(adler, buffer); - } - - return CalculateScalar(adler, buffer); -#else - return CalculateScalar(adler, buffer); -#endif - } - - // Based on https://github.com/chromium/chromium/blob/master/third_party/zlib/adler32_simd.c -#if SUPPORTS_RUNTIME_INTRINSICS - [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] - private static unsafe uint CalculateSse(uint adler, ReadOnlySpan buffer) - { - uint s1 = adler & 0xFFFF; - uint s2 = (adler >> 16) & 0xFFFF; - - // Process the data in blocks. - uint length = (uint)buffer.Length; - uint blocks = length / BlockSize; - length -= blocks * BlockSize; - - fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) - { - fixed (byte* tapPtr = &MemoryMarshal.GetReference(Tap1Tap2)) + while (k >= 32) { - byte* localBufferPtr = bufferPtr; + // Load 32 input bytes. + Vector256 block = Avx.LoadVector256(localBufferPtr); - // _mm_setr_epi8 on x86 - Vector128 tap1 = Sse2.LoadVector128((sbyte*)tapPtr); - Vector128 tap2 = Sse2.LoadVector128((sbyte*)(tapPtr + 0x10)); - Vector128 zero = Vector128.Zero; - var ones = Vector128.Create((short)1); + // Sum of abs diff, resulting in 2 x int32's + Vector256 vs1sad = Avx2.SumAbsoluteDifferences(block, zero); - while (blocks > 0) - { - uint n = NMAX / BlockSize; /* The NMAX constraint. */ - if (n > blocks) - { - n = blocks; - } + vs1 = Avx2.Add(vs1, vs1sad.AsUInt32()); + vs3 = Avx2.Add(vs3, vs10); - blocks -= n; + // sum 32 uint8s to 16 shorts. + Vector256 vshortsum2 = Avx2.MultiplyAddAdjacent(block, dot2v); - // Process n blocks of data. At most NMAX data bytes can be - // processed before s2 must be reduced modulo BASE. - Vector128 v_ps = Vector128.CreateScalar(s1 * n); - Vector128 v_s2 = Vector128.CreateScalar(s2); - Vector128 v_s1 = Vector128.Zero; + // sum 16 shorts to 8 uint32s. + Vector256 vsum2 = Avx2.MultiplyAddAdjacent(vshortsum2, dot3v); - do - { - // Load 32 input bytes. - Vector128 bytes1 = Sse3.LoadDquVector128(localBufferPtr); - Vector128 bytes2 = Sse3.LoadDquVector128(localBufferPtr + 0x10); + vs2 = Avx2.Add(vsum2.AsUInt32(), vs2); + vs10 = vs1; - // Add previous block byte sum to v_ps. - v_ps = Sse2.Add(v_ps, v_s1); - - // Horizontally add the bytes for s1, multiply-adds the - // bytes by [ 32, 31, 30, ... ] for s2. - v_s1 = Sse2.Add( - v_s1, - Sse2.SumAbsoluteDifferences(bytes1, zero).AsUInt32() - ); - Vector128 mad1 = Ssse3.MultiplyAddAdjacent(bytes1, tap1); - v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad1, ones).AsUInt32()); - - v_s1 = Sse2.Add( - v_s1, - Sse2.SumAbsoluteDifferences(bytes2, zero).AsUInt32() - ); - Vector128 mad2 = Ssse3.MultiplyAddAdjacent(bytes2, tap2); - v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad2, ones).AsUInt32()); - - localBufferPtr += BlockSize; - } while (--n > 0); - - v_s2 = Sse2.Add(v_s2, Sse2.ShiftLeftLogical(v_ps, 5)); - - // Sum epi32 ints v_s1(s2) and accumulate in s1(s2). - const byte S2301 = 0b1011_0001; // A B C D -> B A D C - const byte S1032 = 0b0100_1110; // A B C D -> C D A B - - v_s1 = Sse2.Add(v_s1, Sse2.Shuffle(v_s1, S1032)); - - s1 += v_s1.ToScalar(); - - v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S2301)); - v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S1032)); - - s2 = v_s2.ToScalar(); - - // Reduce. - s1 %= BASE; - s2 %= BASE; - } - - if (length > 0) - { - HandleLeftOver(localBufferPtr, length, ref s1, ref s2); - } - - return s1 | (s2 << 16); + localBufferPtr += BlockSize; + k -= 32; } + + // Defer the multiplication with 32 to outside of the loop. + vs3 = Avx2.ShiftLeftLogical(vs3, 5); + vs2 = Avx2.Add(vs2, vs3); + + s1 = (uint)Numerics.EvenReduceSum(vs1.AsInt32()); + s2 = (uint)Numerics.ReduceSum(vs2.AsInt32()); + + s1 %= BASE; + s2 %= BASE; + + vs1 = Vector256.CreateScalar(s1); + vs2 = Vector256.CreateScalar(s2); } + + if (length > 0) + { + HandleLeftOver(localBufferPtr, length, ref s1, ref s2); + } + + return s1 | (s2 << 16); + } + } + + private static unsafe void HandleLeftOver( + byte* localBufferPtr, + uint length, + ref uint s1, + ref uint s2 + ) + { + if (length >= 16) + { + s2 += s1 += localBufferPtr[0]; + s2 += s1 += localBufferPtr[1]; + s2 += s1 += localBufferPtr[2]; + s2 += s1 += localBufferPtr[3]; + s2 += s1 += localBufferPtr[4]; + s2 += s1 += localBufferPtr[5]; + s2 += s1 += localBufferPtr[6]; + s2 += s1 += localBufferPtr[7]; + s2 += s1 += localBufferPtr[8]; + s2 += s1 += localBufferPtr[9]; + s2 += s1 += localBufferPtr[10]; + s2 += s1 += localBufferPtr[11]; + s2 += s1 += localBufferPtr[12]; + s2 += s1 += localBufferPtr[13]; + s2 += s1 += localBufferPtr[14]; + s2 += s1 += localBufferPtr[15]; + + localBufferPtr += 16; + length -= 16; } - // Based on: https://github.com/zlib-ng/zlib-ng/blob/develop/arch/x86/adler32_avx2.c - [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] - public static unsafe uint CalculateAvx2(uint adler, ReadOnlySpan buffer) + while (length-- > 0) { - uint s1 = adler & 0xFFFF; - uint s2 = (adler >> 16) & 0xFFFF; + s2 += s1 += *localBufferPtr++; + } + + if (s1 >= BASE) + { + s1 -= BASE; + } + + s2 %= BASE; + } +#endif + + [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] + private static unsafe uint CalculateScalar(uint adler, ReadOnlySpan buffer) + { + uint s1 = adler & 0xFFFF; + uint s2 = (adler >> 16) & 0xFFFF; + uint k; + + fixed (byte* bufferPtr = buffer) + { + var localBufferPtr = bufferPtr; uint length = (uint)buffer.Length; - fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) + while (length > 0) { - byte* localBufferPtr = bufferPtr; + k = length < NMAX ? length : NMAX; + length -= k; - Vector256 zero = Vector256.Zero; - var dot3v = Vector256.Create((short)1); - var dot2v = Vector256.Create( - 32, - 31, - 30, - 29, - 28, - 27, - 26, - 25, - 24, - 23, - 22, - 21, - 20, - 19, - 18, - 17, - 16, - 15, - 14, - 13, - 12, - 11, - 10, - 9, - 8, - 7, - 6, - 5, - 4, - 3, - 2, - 1 - ); - - // Process n blocks of data. At most NMAX data bytes can be - // processed before s2 must be reduced modulo BASE. - var vs1 = Vector256.CreateScalar(s1); - var vs2 = Vector256.CreateScalar(s2); - - while (length >= 32) + while (k >= 16) { - int k = length < NMAX ? (int)length : (int)NMAX; - k -= k % 32; - length -= (uint)k; + s2 += s1 += localBufferPtr[0]; + s2 += s1 += localBufferPtr[1]; + s2 += s1 += localBufferPtr[2]; + s2 += s1 += localBufferPtr[3]; + s2 += s1 += localBufferPtr[4]; + s2 += s1 += localBufferPtr[5]; + s2 += s1 += localBufferPtr[6]; + s2 += s1 += localBufferPtr[7]; + s2 += s1 += localBufferPtr[8]; + s2 += s1 += localBufferPtr[9]; + s2 += s1 += localBufferPtr[10]; + s2 += s1 += localBufferPtr[11]; + s2 += s1 += localBufferPtr[12]; + s2 += s1 += localBufferPtr[13]; + s2 += s1 += localBufferPtr[14]; + s2 += s1 += localBufferPtr[15]; - Vector256 vs10 = vs1; - Vector256 vs3 = Vector256.Zero; - - while (k >= 32) - { - // Load 32 input bytes. - Vector256 block = Avx.LoadVector256(localBufferPtr); - - // Sum of abs diff, resulting in 2 x int32's - Vector256 vs1sad = Avx2.SumAbsoluteDifferences(block, zero); - - vs1 = Avx2.Add(vs1, vs1sad.AsUInt32()); - vs3 = Avx2.Add(vs3, vs10); - - // sum 32 uint8s to 16 shorts. - Vector256 vshortsum2 = Avx2.MultiplyAddAdjacent(block, dot2v); - - // sum 16 shorts to 8 uint32s. - Vector256 vsum2 = Avx2.MultiplyAddAdjacent(vshortsum2, dot3v); - - vs2 = Avx2.Add(vsum2.AsUInt32(), vs2); - vs10 = vs1; - - localBufferPtr += BlockSize; - k -= 32; - } - - // Defer the multiplication with 32 to outside of the loop. - vs3 = Avx2.ShiftLeftLogical(vs3, 5); - vs2 = Avx2.Add(vs2, vs3); - - s1 = (uint)Numerics.EvenReduceSum(vs1.AsInt32()); - s2 = (uint)Numerics.ReduceSum(vs2.AsInt32()); - - s1 %= BASE; - s2 %= BASE; - - vs1 = Vector256.CreateScalar(s1); - vs2 = Vector256.CreateScalar(s2); + localBufferPtr += 16; + k -= 16; } - if (length > 0) + while (k-- > 0) { - HandleLeftOver(localBufferPtr, length, ref s1, ref s2); + s2 += s1 += *localBufferPtr++; } - return s1 | (s2 << 16); - } - } - - private static unsafe void HandleLeftOver( - byte* localBufferPtr, - uint length, - ref uint s1, - ref uint s2 - ) - { - if (length >= 16) - { - s2 += s1 += localBufferPtr[0]; - s2 += s1 += localBufferPtr[1]; - s2 += s1 += localBufferPtr[2]; - s2 += s1 += localBufferPtr[3]; - s2 += s1 += localBufferPtr[4]; - s2 += s1 += localBufferPtr[5]; - s2 += s1 += localBufferPtr[6]; - s2 += s1 += localBufferPtr[7]; - s2 += s1 += localBufferPtr[8]; - s2 += s1 += localBufferPtr[9]; - s2 += s1 += localBufferPtr[10]; - s2 += s1 += localBufferPtr[11]; - s2 += s1 += localBufferPtr[12]; - s2 += s1 += localBufferPtr[13]; - s2 += s1 += localBufferPtr[14]; - s2 += s1 += localBufferPtr[15]; - - localBufferPtr += 16; - length -= 16; + s1 %= BASE; + s2 %= BASE; } - while (length-- > 0) - { - s2 += s1 += *localBufferPtr++; - } - - if (s1 >= BASE) - { - s1 -= BASE; - } - - s2 %= BASE; - } -#endif - - [MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)] - private static unsafe uint CalculateScalar(uint adler, ReadOnlySpan buffer) - { - uint s1 = adler & 0xFFFF; - uint s2 = (adler >> 16) & 0xFFFF; - uint k; - - fixed (byte* bufferPtr = buffer) - { - var localBufferPtr = bufferPtr; - uint length = (uint)buffer.Length; - - while (length > 0) - { - k = length < NMAX ? length : NMAX; - length -= k; - - while (k >= 16) - { - s2 += s1 += localBufferPtr[0]; - s2 += s1 += localBufferPtr[1]; - s2 += s1 += localBufferPtr[2]; - s2 += s1 += localBufferPtr[3]; - s2 += s1 += localBufferPtr[4]; - s2 += s1 += localBufferPtr[5]; - s2 += s1 += localBufferPtr[6]; - s2 += s1 += localBufferPtr[7]; - s2 += s1 += localBufferPtr[8]; - s2 += s1 += localBufferPtr[9]; - s2 += s1 += localBufferPtr[10]; - s2 += s1 += localBufferPtr[11]; - s2 += s1 += localBufferPtr[12]; - s2 += s1 += localBufferPtr[13]; - s2 += s1 += localBufferPtr[14]; - s2 += s1 += localBufferPtr[15]; - - localBufferPtr += 16; - k -= 16; - } - - while (k-- > 0) - { - s2 += s1 += *localBufferPtr++; - } - - s1 %= BASE; - s2 %= BASE; - } - - return (s2 << 16) | s1; - } + return (s2 << 16) | s1; } } } diff --git a/src/SharpCompress/Archives/AbstractArchive.cs b/src/SharpCompress/Archives/AbstractArchive.cs index 16b07864..d747189e 100644 --- a/src/SharpCompress/Archives/AbstractArchive.cs +++ b/src/SharpCompress/Archives/AbstractArchive.cs @@ -6,180 +6,179 @@ using SharpCompress.Common; using SharpCompress.IO; using SharpCompress.Readers; -namespace SharpCompress.Archives +namespace SharpCompress.Archives; + +public abstract class AbstractArchive : IArchive, IArchiveExtractionListener + where TEntry : IArchiveEntry + where TVolume : IVolume { - public abstract class AbstractArchive : IArchive, IArchiveExtractionListener - where TEntry : IArchiveEntry - where TVolume : IVolume + private readonly LazyReadOnlyCollection lazyVolumes; + private readonly LazyReadOnlyCollection lazyEntries; + + public event EventHandler>? EntryExtractionBegin; + public event EventHandler>? EntryExtractionEnd; + + public event EventHandler? CompressedBytesRead; + public event EventHandler? FilePartExtractionBegin; + + protected ReaderOptions ReaderOptions { get; } + + private bool disposed; + protected SourceStream SrcStream; + + internal AbstractArchive(ArchiveType type, SourceStream srcStream) { - private readonly LazyReadOnlyCollection lazyVolumes; - private readonly LazyReadOnlyCollection lazyEntries; - - public event EventHandler>? EntryExtractionBegin; - public event EventHandler>? EntryExtractionEnd; - - public event EventHandler? CompressedBytesRead; - public event EventHandler? FilePartExtractionBegin; - - protected ReaderOptions ReaderOptions { get; } - - private bool disposed; - protected SourceStream SrcStream; - - internal AbstractArchive(ArchiveType type, SourceStream srcStream) - { - Type = type; - ReaderOptions = srcStream.ReaderOptions; - SrcStream = srcStream; - lazyVolumes = new LazyReadOnlyCollection(LoadVolumes(SrcStream)); - lazyEntries = new LazyReadOnlyCollection(LoadEntries(Volumes)); - } + Type = type; + ReaderOptions = srcStream.ReaderOptions; + SrcStream = srcStream; + lazyVolumes = new LazyReadOnlyCollection(LoadVolumes(SrcStream)); + lazyEntries = new LazyReadOnlyCollection(LoadEntries(Volumes)); + } #nullable disable - internal AbstractArchive(ArchiveType type) - { - Type = type; - lazyVolumes = new LazyReadOnlyCollection(Enumerable.Empty()); - lazyEntries = new LazyReadOnlyCollection(Enumerable.Empty()); - } + internal AbstractArchive(ArchiveType type) + { + Type = type; + lazyVolumes = new LazyReadOnlyCollection(Enumerable.Empty()); + lazyEntries = new LazyReadOnlyCollection(Enumerable.Empty()); + } #nullable enable - public ArchiveType Type { get; } + public ArchiveType Type { get; } - void IArchiveExtractionListener.FireEntryExtractionBegin(IArchiveEntry entry) + void IArchiveExtractionListener.FireEntryExtractionBegin(IArchiveEntry entry) + { + EntryExtractionBegin?.Invoke( + this, + new ArchiveExtractionEventArgs(entry) + ); + } + + void IArchiveExtractionListener.FireEntryExtractionEnd(IArchiveEntry entry) + { + EntryExtractionEnd?.Invoke(this, new ArchiveExtractionEventArgs(entry)); + } + + private static Stream CheckStreams(Stream stream) + { + if (!stream.CanSeek || !stream.CanRead) { - EntryExtractionBegin?.Invoke( - this, - new ArchiveExtractionEventArgs(entry) - ); + throw new ArgumentException("Archive streams must be Readable and Seekable"); } + return stream; + } - void IArchiveExtractionListener.FireEntryExtractionEnd(IArchiveEntry entry) + /// + /// Returns an ReadOnlyCollection of all the RarArchiveEntries across the one or many parts of the RarArchive. + /// + public virtual ICollection Entries => lazyEntries; + + /// + /// Returns an ReadOnlyCollection of all the RarArchiveVolumes across the one or many parts of the RarArchive. + /// + public ICollection Volumes => lazyVolumes; + + /// + /// The total size of the files compressed in the archive. + /// + public virtual long TotalSize => + Entries.Aggregate(0L, (total, cf) => total + cf.CompressedSize); + + /// + /// The total size of the files as uncompressed in the archive. + /// + public virtual long TotalUncompressSize => + Entries.Aggregate(0L, (total, cf) => total + cf.Size); + + protected abstract IEnumerable LoadVolumes(SourceStream srcStream); + protected abstract IEnumerable LoadEntries(IEnumerable volumes); + + IEnumerable IArchive.Entries => Entries.Cast(); + + IEnumerable IArchive.Volumes => lazyVolumes.Cast(); + + public virtual void Dispose() + { + if (!disposed) { - EntryExtractionEnd?.Invoke(this, new ArchiveExtractionEventArgs(entry)); + lazyVolumes.ForEach(v => v.Dispose()); + lazyEntries.GetLoaded().Cast().ForEach(x => x.Close()); + SrcStream?.Dispose(); + + disposed = true; } + } - private static Stream CheckStreams(Stream stream) - { - if (!stream.CanSeek || !stream.CanRead) - { - throw new ArgumentException("Archive streams must be Readable and Seekable"); - } - return stream; - } + void IArchiveExtractionListener.EnsureEntriesLoaded() + { + lazyEntries.EnsureFullyLoaded(); + lazyVolumes.EnsureFullyLoaded(); + } - /// - /// Returns an ReadOnlyCollection of all the RarArchiveEntries across the one or many parts of the RarArchive. - /// - public virtual ICollection Entries => lazyEntries; + void IExtractionListener.FireCompressedBytesRead( + long currentPartCompressedBytes, + long compressedReadBytes + ) + { + CompressedBytesRead?.Invoke( + this, + new CompressedBytesReadEventArgs( + currentFilePartCompressedBytesRead: currentPartCompressedBytes, + compressedBytesRead: compressedReadBytes + ) + ); + } - /// - /// Returns an ReadOnlyCollection of all the RarArchiveVolumes across the one or many parts of the RarArchive. - /// - public ICollection Volumes => lazyVolumes; + void IExtractionListener.FireFilePartExtractionBegin( + string name, + long size, + long compressedSize + ) + { + FilePartExtractionBegin?.Invoke( + this, + new FilePartExtractionBeginEventArgs( + compressedSize: compressedSize, + size: size, + name: name + ) + ); + } - /// - /// The total size of the files compressed in the archive. - /// - public virtual long TotalSize => - Entries.Aggregate(0L, (total, cf) => total + cf.CompressedSize); + /// + /// Use this method to extract all entries in an archive in order. + /// This is primarily for SOLID Rar Archives or 7Zip Archives as they need to be + /// extracted sequentially for the best performance. + /// + /// This method will load all entry information from the archive. + /// + /// WARNING: this will reuse the underlying stream for the archive. Errors may + /// occur if this is used at the same time as other extraction methods on this instance. + /// + /// + public IReader ExtractAllEntries() + { + ((IArchiveExtractionListener)this).EnsureEntriesLoaded(); + return CreateReaderForSolidExtraction(); + } - /// - /// The total size of the files as uncompressed in the archive. - /// - public virtual long TotalUncompressSize => - Entries.Aggregate(0L, (total, cf) => total + cf.Size); + protected abstract IReader CreateReaderForSolidExtraction(); - protected abstract IEnumerable LoadVolumes(SourceStream srcStream); - protected abstract IEnumerable LoadEntries(IEnumerable volumes); + /// + /// Archive is SOLID (this means the Archive saved bytes by reusing information which helps for archives containing many small files). + /// + public virtual bool IsSolid => false; - IEnumerable IArchive.Entries => Entries.Cast(); - - IEnumerable IArchive.Volumes => lazyVolumes.Cast(); - - public virtual void Dispose() - { - if (!disposed) - { - lazyVolumes.ForEach(v => v.Dispose()); - lazyEntries.GetLoaded().Cast().ForEach(x => x.Close()); - if (SrcStream != null) - SrcStream.Dispose(); - disposed = true; - } - } - - void IArchiveExtractionListener.EnsureEntriesLoaded() - { - lazyEntries.EnsureFullyLoaded(); - lazyVolumes.EnsureFullyLoaded(); - } - - void IExtractionListener.FireCompressedBytesRead( - long currentPartCompressedBytes, - long compressedReadBytes - ) - { - CompressedBytesRead?.Invoke( - this, - new CompressedBytesReadEventArgs( - currentFilePartCompressedBytesRead: currentPartCompressedBytes, - compressedBytesRead: compressedReadBytes - ) - ); - } - - void IExtractionListener.FireFilePartExtractionBegin( - string name, - long size, - long compressedSize - ) - { - FilePartExtractionBegin?.Invoke( - this, - new FilePartExtractionBeginEventArgs( - compressedSize: compressedSize, - size: size, - name: name - ) - ); - } - - /// - /// Use this method to extract all entries in an archive in order. - /// This is primarily for SOLID Rar Archives or 7Zip Archives as they need to be - /// extracted sequentially for the best performance. - /// - /// This method will load all entry information from the archive. - /// - /// WARNING: this will reuse the underlying stream for the archive. Errors may - /// occur if this is used at the same time as other extraction methods on this instance. - /// - /// - public IReader ExtractAllEntries() + /// + /// The archive can find all the parts of the archive needed to fully extract the archive. This forces the parsing of the entire archive. + /// + public bool IsComplete + { + get { ((IArchiveExtractionListener)this).EnsureEntriesLoaded(); - return CreateReaderForSolidExtraction(); - } - - protected abstract IReader CreateReaderForSolidExtraction(); - - /// - /// Archive is SOLID (this means the Archive saved bytes by reusing information which helps for archives containing many small files). - /// - public virtual bool IsSolid => false; - - /// - /// The archive can find all the parts of the archive needed to fully extract the archive. This forces the parsing of the entire archive. - /// - public bool IsComplete - { - get - { - ((IArchiveExtractionListener)this).EnsureEntriesLoaded(); - return Entries.All(x => x.IsComplete); - } + return Entries.All(x => x.IsComplete); } } } diff --git a/src/SharpCompress/Archives/AbstractWritableArchive.cs b/src/SharpCompress/Archives/AbstractWritableArchive.cs index fc877cbd..146c8338 100644 --- a/src/SharpCompress/Archives/AbstractWritableArchive.cs +++ b/src/SharpCompress/Archives/AbstractWritableArchive.cs @@ -4,193 +4,191 @@ using System.IO; using System.Linq; using SharpCompress.Common; using SharpCompress.IO; -using SharpCompress.Readers; using SharpCompress.Writers; -namespace SharpCompress.Archives +namespace SharpCompress.Archives; + +public abstract class AbstractWritableArchive + : AbstractArchive, + IWritableArchive + where TEntry : IArchiveEntry + where TVolume : IVolume { - public abstract class AbstractWritableArchive - : AbstractArchive, - IWritableArchive - where TEntry : IArchiveEntry - where TVolume : IVolume + private class RebuildPauseDisposable : IDisposable { - private class RebuildPauseDisposable : IDisposable + private readonly AbstractWritableArchive archive; + + public RebuildPauseDisposable(AbstractWritableArchive archive) { - private readonly AbstractWritableArchive archive; - - public RebuildPauseDisposable(AbstractWritableArchive archive) - { - this.archive = archive; - archive.pauseRebuilding = true; - } - - public void Dispose() - { - archive.pauseRebuilding = false; - archive.RebuildModifiedCollection(); - } + this.archive = archive; + archive.pauseRebuilding = true; } - private readonly List newEntries = new List(); - private readonly List removedEntries = new List(); - - private readonly List modifiedEntries = new List(); - private bool hasModifications; - private bool pauseRebuilding; - - internal AbstractWritableArchive(ArchiveType type) : base(type) { } - - internal AbstractWritableArchive(ArchiveType type, SourceStream srcStream) - : base(type, srcStream) { } - - public override ICollection Entries + public void Dispose() { - get - { - if (hasModifications) - { - return modifiedEntries; - } - return base.Entries; - } - } - - public IDisposable PauseEntryRebuilding() - { - return new RebuildPauseDisposable(this); - } - - private void RebuildModifiedCollection() - { - if (pauseRebuilding) - { - return; - } - hasModifications = true; - newEntries.RemoveAll(v => removedEntries.Contains(v)); - modifiedEntries.Clear(); - modifiedEntries.AddRange(OldEntries.Concat(newEntries)); - } - - private IEnumerable OldEntries - { - get { return base.Entries.Where(x => !removedEntries.Contains(x)); } - } - - public void RemoveEntry(TEntry entry) - { - if (!removedEntries.Contains(entry)) - { - removedEntries.Add(entry); - RebuildModifiedCollection(); - } - } - - void IWritableArchive.RemoveEntry(IArchiveEntry entry) - { - RemoveEntry((TEntry)entry); - } - - public TEntry AddEntry(string key, Stream source, long size = 0, DateTime? modified = null) - { - return AddEntry(key, source, false, size, modified); - } - - IArchiveEntry IWritableArchive.AddEntry( - string key, - Stream source, - bool closeStream, - long size, - DateTime? modified - ) - { - return AddEntry(key, source, closeStream, size, modified); - } - - public TEntry AddEntry( - string key, - Stream source, - bool closeStream, - long size = 0, - DateTime? modified = null - ) - { - if (key.Length > 0 && key[0] is '/' or '\\') - { - key = key.Substring(1); - } - if (DoesKeyMatchExisting(key)) - { - throw new ArchiveException("Cannot add entry with duplicate key: " + key); - } - var entry = CreateEntry(key, source, size, modified, closeStream); - newEntries.Add(entry); - RebuildModifiedCollection(); - return entry; - } - - private bool DoesKeyMatchExisting(string key) - { - foreach (var path in Entries.Select(x => x.Key)) - { - var p = path.Replace('/', '\\'); - if (p.Length > 0 && p[0] == '\\') - { - p = p.Substring(1); - } - return string.Equals(p, key, StringComparison.OrdinalIgnoreCase); - } - return false; - } - - public void SaveTo(Stream stream, WriterOptions options) - { - //reset streams of new entries - newEntries - .Cast() - .ForEach(x => x.Stream.Seek(0, SeekOrigin.Begin)); - SaveTo(stream, options, OldEntries, newEntries); - } - - protected TEntry CreateEntry( - string key, - Stream source, - long size, - DateTime? modified, - bool closeStream - ) - { - if (!source.CanRead || !source.CanSeek) - { - throw new ArgumentException( - "Streams must be readable and seekable to use the Writing Archive API" - ); - } - return CreateEntryInternal(key, source, size, modified, closeStream); - } - - protected abstract TEntry CreateEntryInternal( - string key, - Stream source, - long size, - DateTime? modified, - bool closeStream - ); - - protected abstract void SaveTo( - Stream stream, - WriterOptions options, - IEnumerable oldEntries, - IEnumerable newEntries - ); - - public override void Dispose() - { - base.Dispose(); - newEntries.Cast().ForEach(x => x.Close()); - removedEntries.Cast().ForEach(x => x.Close()); - modifiedEntries.Cast().ForEach(x => x.Close()); + archive.pauseRebuilding = false; + archive.RebuildModifiedCollection(); } } + + private readonly List newEntries = new List(); + private readonly List removedEntries = new List(); + + private readonly List modifiedEntries = new List(); + private bool hasModifications; + private bool pauseRebuilding; + + internal AbstractWritableArchive(ArchiveType type) : base(type) { } + + internal AbstractWritableArchive(ArchiveType type, SourceStream srcStream) + : base(type, srcStream) { } + + public override ICollection Entries + { + get + { + if (hasModifications) + { + return modifiedEntries; + } + return base.Entries; + } + } + + public IDisposable PauseEntryRebuilding() + { + return new RebuildPauseDisposable(this); + } + + private void RebuildModifiedCollection() + { + if (pauseRebuilding) + { + return; + } + hasModifications = true; + newEntries.RemoveAll(v => removedEntries.Contains(v)); + modifiedEntries.Clear(); + modifiedEntries.AddRange(OldEntries.Concat(newEntries)); + } + + private IEnumerable OldEntries + { + get { return base.Entries.Where(x => !removedEntries.Contains(x)); } + } + + public void RemoveEntry(TEntry entry) + { + if (!removedEntries.Contains(entry)) + { + removedEntries.Add(entry); + RebuildModifiedCollection(); + } + } + + void IWritableArchive.RemoveEntry(IArchiveEntry entry) + { + RemoveEntry((TEntry)entry); + } + + public TEntry AddEntry(string key, Stream source, long size = 0, DateTime? modified = null) + { + return AddEntry(key, source, false, size, modified); + } + + IArchiveEntry IWritableArchive.AddEntry( + string key, + Stream source, + bool closeStream, + long size, + DateTime? modified + ) + { + return AddEntry(key, source, closeStream, size, modified); + } + + public TEntry AddEntry( + string key, + Stream source, + bool closeStream, + long size = 0, + DateTime? modified = null + ) + { + if (key.Length > 0 && key[0] is '/' or '\\') + { + key = key.Substring(1); + } + if (DoesKeyMatchExisting(key)) + { + throw new ArchiveException("Cannot add entry with duplicate key: " + key); + } + var entry = CreateEntry(key, source, size, modified, closeStream); + newEntries.Add(entry); + RebuildModifiedCollection(); + return entry; + } + + private bool DoesKeyMatchExisting(string key) + { + foreach (var path in Entries.Select(x => x.Key)) + { + var p = path.Replace('/', '\\'); + if (p.Length > 0 && p[0] == '\\') + { + p = p.Substring(1); + } + return string.Equals(p, key, StringComparison.OrdinalIgnoreCase); + } + return false; + } + + public void SaveTo(Stream stream, WriterOptions options) + { + //reset streams of new entries + newEntries + .Cast() + .ForEach(x => x.Stream.Seek(0, SeekOrigin.Begin)); + SaveTo(stream, options, OldEntries, newEntries); + } + + protected TEntry CreateEntry( + string key, + Stream source, + long size, + DateTime? modified, + bool closeStream + ) + { + if (!source.CanRead || !source.CanSeek) + { + throw new ArgumentException( + "Streams must be readable and seekable to use the Writing Archive API" + ); + } + return CreateEntryInternal(key, source, size, modified, closeStream); + } + + protected abstract TEntry CreateEntryInternal( + string key, + Stream source, + long size, + DateTime? modified, + bool closeStream + ); + + protected abstract void SaveTo( + Stream stream, + WriterOptions options, + IEnumerable oldEntries, + IEnumerable newEntries + ); + + public override void Dispose() + { + base.Dispose(); + newEntries.Cast().ForEach(x => x.Close()); + removedEntries.Cast().ForEach(x => x.Close()); + modifiedEntries.Cast().ForEach(x => x.Close()); + } } diff --git a/src/SharpCompress/Archives/ArchiveFactory.cs b/src/SharpCompress/Archives/ArchiveFactory.cs index f5c5cdee..a246bad9 100644 --- a/src/SharpCompress/Archives/ArchiveFactory.cs +++ b/src/SharpCompress/Archives/ArchiveFactory.cs @@ -2,234 +2,238 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using SharpCompress.Archives.GZip; -using SharpCompress.Archives.Rar; -using SharpCompress.Archives.SevenZip; -using SharpCompress.Archives.Tar; -using SharpCompress.Archives.Zip; using SharpCompress.Common; using SharpCompress.Factories; -using SharpCompress.IO; using SharpCompress.Readers; -namespace SharpCompress.Archives +namespace SharpCompress.Archives; + +public static class ArchiveFactory { - public static class ArchiveFactory + /// + /// Opens an Archive for random access + /// + /// + /// + /// + public static IArchive Open(Stream stream, ReaderOptions? readerOptions = null) { - /// - /// Opens an Archive for random access - /// - /// - /// - /// - public static IArchive Open(Stream stream, ReaderOptions? readerOptions = null) - { - readerOptions ??= new ReaderOptions(); + readerOptions ??= new ReaderOptions(); - return FindFactory(stream).Open(stream, readerOptions); + return FindFactory(stream).Open(stream, readerOptions); + } + + public static IWritableArchive Create(ArchiveType type) + { + var factory = Factory.Factories + .OfType() +.FirstOrDefault(item => item.KnownArchiveType == type); + + if (factory != null) + { + return factory.CreateWriteableArchive(); } - public static IWritableArchive Create(ArchiveType type) + throw new NotSupportedException("Cannot create Archives of type: " + type); + } + + /// + /// Constructor expects a filepath to an existing file. + /// + /// + /// + public static IArchive Open(string filePath, ReaderOptions? options = null) + { + filePath.CheckNotNullOrEmpty(nameof(filePath)); + return Open(new FileInfo(filePath), options); + } + + /// + /// Constructor with a FileInfo object to an existing file. + /// + /// + /// + public static IArchive Open(FileInfo fileInfo, ReaderOptions? options = null) + { + options ??= new ReaderOptions { LeaveStreamOpen = false }; + + return FindFactory(fileInfo).Open(fileInfo, options); + } + + /// + /// Constructor with IEnumerable FileInfo objects, multi and split support. + /// + /// + /// + public static IArchive Open(IEnumerable fileInfos, ReaderOptions? options = null) + { + fileInfos.CheckNotNull(nameof(fileInfos)); + var filesArray = fileInfos.ToArray(); + if (filesArray.Length == 0) { - var factory = Factories.Factory.Factories - .OfType() - .Where(item => item.KnownArchiveType == type) - .FirstOrDefault(); - - if (factory != null) - return factory.CreateWriteableArchive(); - - throw new NotSupportedException("Cannot create Archives of type: " + type); + throw new InvalidOperationException("No files to open"); } - /// - /// Constructor expects a filepath to an existing file. - /// - /// - /// - public static IArchive Open(string filePath, ReaderOptions? options = null) + var fileInfo = filesArray[0]; + if (filesArray.Length == 1) { - filePath.CheckNotNullOrEmpty(nameof(filePath)); - return Open(new FileInfo(filePath), options); + return Open(fileInfo, options); } - /// - /// Constructor with a FileInfo object to an existing file. - /// - /// - /// - public static IArchive Open(FileInfo fileInfo, ReaderOptions? options = null) - { - options ??= new ReaderOptions { LeaveStreamOpen = false }; + fileInfo.CheckNotNull(nameof(fileInfo)); + options ??= new ReaderOptions { LeaveStreamOpen = false }; - return FindFactory(fileInfo).Open(fileInfo, options); + return FindFactory(fileInfo).Open(filesArray, options); + } + + /// + /// Constructor with IEnumerable FileInfo objects, multi and split support. + /// + /// + /// + public static IArchive Open(IEnumerable streams, ReaderOptions? options = null) + { + streams.CheckNotNull(nameof(streams)); + var streamsArray = streams.ToArray(); + if (streamsArray.Length == 0) + { + throw new InvalidOperationException("No streams"); } - /// - /// Constructor with IEnumerable FileInfo objects, multi and split support. - /// - /// - /// - public static IArchive Open(IEnumerable fileInfos, ReaderOptions? options = null) + var firstStream = streamsArray[0]; + if (streamsArray.Length == 1) { - fileInfos.CheckNotNull(nameof(fileInfos)); - FileInfo[] filesArray = fileInfos.ToArray(); - if (filesArray.Length == 0) - throw new InvalidOperationException("No files to open"); - FileInfo fileInfo = filesArray[0]; - if (filesArray.Length == 1) - return Open(fileInfo, options); - - fileInfo.CheckNotNull(nameof(fileInfo)); - options ??= new ReaderOptions { LeaveStreamOpen = false }; - - return FindFactory(fileInfo).Open(filesArray, options); + return Open(firstStream, options); } - /// - /// Constructor with IEnumerable FileInfo objects, multi and split support. - /// - /// - /// - public static IArchive Open(IEnumerable streams, ReaderOptions? options = null) + firstStream.CheckNotNull(nameof(firstStream)); + options ??= new ReaderOptions(); + + return FindFactory(firstStream).Open(streamsArray, options); + } + + /// + /// Extract to specific directory, retaining filename + /// + public static void WriteToDirectory( + string sourceArchive, + string destinationDirectory, + ExtractionOptions? options = null + ) + { + using var archive = Open(sourceArchive); + foreach (var entry in archive.Entries) { - streams.CheckNotNull(nameof(streams)); - Stream[] streamsArray = streams.ToArray(); - if (streamsArray.Length == 0) - throw new InvalidOperationException("No streams"); - Stream firstStream = streamsArray[0]; - if (streamsArray.Length == 1) - return Open(firstStream, options); + entry.WriteToDirectory(destinationDirectory, options); + } + } - firstStream.CheckNotNull(nameof(firstStream)); - options ??= new ReaderOptions(); + private static T FindFactory(FileInfo finfo) where T : IFactory + { + finfo.CheckNotNull(nameof(finfo)); + using Stream stream = finfo.OpenRead(); + return FindFactory(stream); + } - return FindFactory(firstStream).Open(streamsArray, options); + private static T FindFactory(Stream stream) where T : IFactory + { + stream.CheckNotNull(nameof(stream)); + if (!stream.CanRead || !stream.CanSeek) + { + throw new ArgumentException("Stream should be readable and seekable"); } - /// - /// Extract to specific directory, retaining filename - /// - public static void WriteToDirectory( - string sourceArchive, - string destinationDirectory, - ExtractionOptions? options = null - ) + var factories = Factory.Factories.OfType(); + + var startPosition = stream.Position; + + foreach (var factory in factories) { - using IArchive archive = Open(sourceArchive); - foreach (IArchiveEntry entry in archive.Entries) - { - entry.WriteToDirectory(destinationDirectory, options); - } - } + stream.Seek(startPosition, SeekOrigin.Begin); - private static T FindFactory(FileInfo finfo) where T : IFactory - { - finfo.CheckNotNull(nameof(finfo)); - using (Stream stream = finfo.OpenRead()) - { - return FindFactory(stream); - } - } - - private static T FindFactory(Stream stream) where T : IFactory - { - stream.CheckNotNull(nameof(stream)); - if (!stream.CanRead || !stream.CanSeek) - { - throw new ArgumentException("Stream should be readable and seekable"); - } - - var factories = Factories.Factory.Factories.OfType(); - - long startPosition = stream.Position; - - foreach (var factory in factories) + if (factory.IsArchive(stream)) { stream.Seek(startPosition, SeekOrigin.Begin); - if (factory.IsArchive(stream)) - { - stream.Seek(startPosition, SeekOrigin.Begin); - - return factory; - } + return factory; } - - var extensions = string.Join(", ", factories.Select(item => item.Name)); - - throw new InvalidOperationException( - $"Cannot determine compressed stream type. Supported Archive Formats: {extensions}" - ); } - public static bool IsArchive(string filePath, out ArchiveType? type) + var extensions = string.Join(", ", factories.Select(item => item.Name)); + + throw new InvalidOperationException( + $"Cannot determine compressed stream type. Supported Archive Formats: {extensions}" + ); + } + + public static bool IsArchive(string filePath, out ArchiveType? type) + { + filePath.CheckNotNullOrEmpty(nameof(filePath)); + using Stream s = File.OpenRead(filePath); + return IsArchive(s, out type); + } + + private static bool IsArchive(Stream stream, out ArchiveType? type) + { + type = null; + stream.CheckNotNull(nameof(stream)); + + if (!stream.CanRead || !stream.CanSeek) { - filePath.CheckNotNullOrEmpty(nameof(filePath)); - using (Stream s = File.OpenRead(filePath)) - return IsArchive(s, out type); + throw new ArgumentException("Stream should be readable and seekable"); } - private static bool IsArchive(Stream stream, out ArchiveType? type) - { - type = null; - stream.CheckNotNull(nameof(stream)); + var startPosition = stream.Position; - if (!stream.CanRead || !stream.CanSeek) + foreach (var factory in Factory.Factories) + { + stream.Position = startPosition; + + if (factory.IsArchive(stream, null)) { - throw new ArgumentException("Stream should be readable and seekable"); + type = factory.KnownArchiveType; + return true; } - - var startPosition = stream.Position; - - foreach (var factory in Factories.Factory.Factories) - { - stream.Position = startPosition; - - if (factory.IsArchive(stream, null)) - { - type = factory.KnownArchiveType; - return true; - } - } - - return false; } - /// - /// From a passed in archive (zip, rar, 7z, 001), return all parts. - /// - /// - /// - public static IEnumerable GetFileParts(string part1) - { - part1.CheckNotNullOrEmpty(nameof(part1)); - return GetFileParts(new FileInfo(part1)).Select(a => a.FullName); - } + return false; + } - /// - /// From a passed in archive (zip, rar, 7z, 001), return all parts. - /// - /// - /// - public static IEnumerable GetFileParts(FileInfo part1) - { - part1.CheckNotNull(nameof(part1)); - yield return part1; + /// + /// From a passed in archive (zip, rar, 7z, 001), return all parts. + /// + /// + /// + public static IEnumerable GetFileParts(string part1) + { + part1.CheckNotNullOrEmpty(nameof(part1)); + return GetFileParts(new FileInfo(part1)).Select(a => a.FullName); + } - foreach (var factory in Factory.Factories.OfType()) + /// + /// From a passed in archive (zip, rar, 7z, 001), return all parts. + /// + /// + /// + public static IEnumerable GetFileParts(FileInfo part1) + { + part1.CheckNotNull(nameof(part1)); + yield return part1; + + foreach (var factory in Factory.Factories.OfType()) + { + var i = 1; + var part = factory.GetFilePart(i++, part1); + + if (part != null) { - int i = 1; - FileInfo? part = factory.GetFilePart(i++, part1); - - if (part != null) + yield return part; + while ((part = factory.GetFilePart(i++, part1)) != null) //tests split too { yield return part; - while ((part = factory.GetFilePart(i++, part1)) != null) //tests split too - yield return part; - - yield break; } + + yield break; } } } diff --git a/src/SharpCompress/Archives/GZip/GZipArchive.cs b/src/SharpCompress/Archives/GZip/GZipArchive.cs index ed1b6a6b..4100f2f2 100644 --- a/src/SharpCompress/Archives/GZip/GZipArchive.cs +++ b/src/SharpCompress/Archives/GZip/GZipArchive.cs @@ -10,223 +10,216 @@ using SharpCompress.Readers.GZip; using SharpCompress.Writers; using SharpCompress.Writers.GZip; -namespace SharpCompress.Archives.GZip +namespace SharpCompress.Archives.GZip; + +public class GZipArchive : AbstractWritableArchive { - public class GZipArchive : AbstractWritableArchive + /// + /// Constructor expects a filepath to an existing file. + /// + /// + /// + public static GZipArchive Open(string filePath, ReaderOptions? readerOptions = null) { - /// - /// Constructor expects a filepath to an existing file. - /// - /// - /// - public static GZipArchive Open(string filePath, ReaderOptions? readerOptions = null) + filePath.CheckNotNullOrEmpty(nameof(filePath)); + return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); + } + + /// + /// Constructor with a FileInfo object to an existing file. + /// + /// + /// + public static GZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + { + fileInfo.CheckNotNull(nameof(fileInfo)); + return new GZipArchive( + new SourceStream( + fileInfo, + i => ArchiveVolumeFactory.GetFilePart(i, fileInfo), + readerOptions ?? new ReaderOptions() + ) + ); + } + + /// + /// Constructor with all file parts passed in + /// + /// + /// + public static GZipArchive Open( + IEnumerable fileInfos, + ReaderOptions? readerOptions = null + ) + { + fileInfos.CheckNotNull(nameof(fileInfos)); + var files = fileInfos.ToArray(); + return new GZipArchive( + new SourceStream( + files[0], + i => i < files.Length ? files[i] : null, + readerOptions ?? new ReaderOptions() + ) + ); + } + + /// + /// Constructor with all stream parts passed in + /// + /// + /// + public static GZipArchive Open( + IEnumerable streams, + ReaderOptions? readerOptions = null + ) + { + streams.CheckNotNull(nameof(streams)); + var strms = streams.ToArray(); + return new GZipArchive( + new SourceStream( + strms[0], + i => i < strms.Length ? strms[i] : null, + readerOptions ?? new ReaderOptions() + ) + ); + } + + /// + /// Takes a seekable Stream as a source + /// + /// + /// + public static GZipArchive Open(Stream stream, ReaderOptions? readerOptions = null) + { + stream.CheckNotNull(nameof(stream)); + return new GZipArchive( + new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()) + ); + } + + public static GZipArchive Create() + { + return new GZipArchive(); + } + + /// + /// Constructor with a SourceStream able to handle FileInfo and Streams. + /// + /// + /// + internal GZipArchive(SourceStream srcStream) : base(ArchiveType.Tar, srcStream) { } + + protected override IEnumerable LoadVolumes(SourceStream srcStream) + { + srcStream.LoadAllParts(); + var idx = 0; + return srcStream.Streams.Select(a => new GZipVolume(a, ReaderOptions, idx++)); + } + + public static bool IsGZipFile(string filePath) + { + return IsGZipFile(new FileInfo(filePath)); + } + + public static bool IsGZipFile(FileInfo fileInfo) + { + if (!fileInfo.Exists) { - filePath.CheckNotNullOrEmpty(nameof(filePath)); - return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); + return false; } - /// - /// Constructor with a FileInfo object to an existing file. - /// - /// - /// - public static GZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + using Stream stream = fileInfo.OpenRead(); + return IsGZipFile(stream); + } + + public void SaveTo(string filePath) + { + SaveTo(new FileInfo(filePath)); + } + + public void SaveTo(FileInfo fileInfo) + { + using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write); + SaveTo(stream, new WriterOptions(CompressionType.GZip)); + } + + public static bool IsGZipFile(Stream stream) + { + // read the header on the first read + Span header = stackalloc byte[10]; + + // workitem 8501: handle edge case (decompress empty stream) + if (!stream.ReadFully(header)) { - fileInfo.CheckNotNull(nameof(fileInfo)); - return new GZipArchive( - new SourceStream( - fileInfo, - i => ArchiveVolumeFactory.GetFilePart(i, fileInfo), - readerOptions ?? new ReaderOptions() - ) - ); + return false; } - /// - /// Constructor with all file parts passed in - /// - /// - /// - public static GZipArchive Open( - IEnumerable fileInfos, - ReaderOptions? readerOptions = null - ) + if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8) { - fileInfos.CheckNotNull(nameof(fileInfos)); - FileInfo[] files = fileInfos.ToArray(); - return new GZipArchive( - new SourceStream( - files[0], - i => i < files.Length ? files[i] : null, - readerOptions ?? new ReaderOptions() - ) - ); + return false; } - /// - /// Constructor with all stream parts passed in - /// - /// - /// - public static GZipArchive Open( - IEnumerable streams, - ReaderOptions? readerOptions = null - ) + return true; + } + + internal GZipArchive() : base(ArchiveType.GZip) { } + + protected override GZipArchiveEntry CreateEntryInternal( + string filePath, + Stream source, + long size, + DateTime? modified, + bool closeStream + ) + { + if (Entries.Any()) { - streams.CheckNotNull(nameof(streams)); - Stream[] strms = streams.ToArray(); - return new GZipArchive( - new SourceStream( - strms[0], - i => i < strms.Length ? strms[i] : null, - readerOptions ?? new ReaderOptions() - ) - ); + throw new InvalidOperationException("Only one entry is allowed in a GZip Archive"); } + return new GZipWritableArchiveEntry( + this, + source, + filePath, + size, + modified, + closeStream + ); + } - /// - /// Takes a seekable Stream as a source - /// - /// - /// - public static GZipArchive Open(Stream stream, ReaderOptions? readerOptions = null) + protected override void SaveTo( + Stream stream, + WriterOptions options, + IEnumerable oldEntries, + IEnumerable newEntries + ) + { + if (Entries.Count > 1) { - stream.CheckNotNull(nameof(stream)); - return new GZipArchive( - new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()) - ); + throw new InvalidOperationException("Only one entry is allowed in a GZip Archive"); } - - public static GZipArchive Create() + using var writer = new GZipWriter(stream, new GZipWriterOptions(options)); + foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory)) { - return new GZipArchive(); - } - - /// - /// Constructor with a SourceStream able to handle FileInfo and Streams. - /// - /// - /// - internal GZipArchive(SourceStream srcStream) : base(ArchiveType.Tar, srcStream) { } - - protected override IEnumerable LoadVolumes(SourceStream srcStream) - { - srcStream.LoadAllParts(); - int idx = 0; - return srcStream.Streams.Select(a => new GZipVolume(a, ReaderOptions, idx++)); - } - - public static bool IsGZipFile(string filePath) - { - return IsGZipFile(new FileInfo(filePath)); - } - - public static bool IsGZipFile(FileInfo fileInfo) - { - if (!fileInfo.Exists) - { - return false; - } - - using Stream stream = fileInfo.OpenRead(); - return IsGZipFile(stream); - } - - public void SaveTo(string filePath) - { - SaveTo(new FileInfo(filePath)); - } - - public void SaveTo(FileInfo fileInfo) - { - using (var stream = fileInfo.Open(FileMode.Create, FileAccess.Write)) - { - SaveTo(stream, new WriterOptions(CompressionType.GZip)); - } - } - - public static bool IsGZipFile(Stream stream) - { - // read the header on the first read - Span header = stackalloc byte[10]; - - // workitem 8501: handle edge case (decompress empty stream) - if (!stream.ReadFully(header)) - { - return false; - } - - if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8) - { - return false; - } - - return true; - } - - internal GZipArchive() : base(ArchiveType.GZip) { } - - protected override GZipArchiveEntry CreateEntryInternal( - string filePath, - Stream source, - long size, - DateTime? modified, - bool closeStream - ) - { - if (Entries.Any()) - { - throw new InvalidOperationException("Only one entry is allowed in a GZip Archive"); - } - return new GZipWritableArchiveEntry( - this, - source, - filePath, - size, - modified, - closeStream - ); - } - - protected override void SaveTo( - Stream stream, - WriterOptions options, - IEnumerable oldEntries, - IEnumerable newEntries - ) - { - if (Entries.Count > 1) - { - throw new InvalidOperationException("Only one entry is allowed in a GZip Archive"); - } - using (var writer = new GZipWriter(stream, new GZipWriterOptions(options))) - { - foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory)) - { - using (var entryStream = entry.OpenEntryStream()) - { - writer.Write(entry.Key, entryStream, entry.LastModifiedTime); - } - } - } - } - - protected override IEnumerable LoadEntries( - IEnumerable volumes - ) - { - Stream stream = volumes.Single().Stream; - yield return new GZipArchiveEntry( - this, - new GZipFilePart(stream, ReaderOptions.ArchiveEncoding) - ); - } - - protected override IReader CreateReaderForSolidExtraction() - { - var stream = Volumes.Single().Stream; - stream.Position = 0; - return GZipReader.Open(stream); + using var entryStream = entry.OpenEntryStream(); + writer.Write(entry.Key, entryStream, entry.LastModifiedTime); } } + + protected override IEnumerable LoadEntries( + IEnumerable volumes + ) + { + var stream = volumes.Single().Stream; + yield return new GZipArchiveEntry( + this, + new GZipFilePart(stream, ReaderOptions.ArchiveEncoding) + ); + } + + protected override IReader CreateReaderForSolidExtraction() + { + var stream = Volumes.Single().Stream; + stream.Position = 0; + return GZipReader.Open(stream); + } } diff --git a/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs b/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs index 23316046..68032131 100644 --- a/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs +++ b/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs @@ -1,33 +1,32 @@ -using System.IO; +using System.IO; using System.Linq; using SharpCompress.Common.GZip; -namespace SharpCompress.Archives.GZip +namespace SharpCompress.Archives.GZip; + +public class GZipArchiveEntry : GZipEntry, IArchiveEntry { - public class GZipArchiveEntry : GZipEntry, IArchiveEntry + internal GZipArchiveEntry(GZipArchive archive, GZipFilePart part) : base(part) { - internal GZipArchiveEntry(GZipArchive archive, GZipFilePart part) : base(part) - { - Archive = archive; - } - - public virtual Stream OpenEntryStream() - { - //this is to reset the stream to be read multiple times - var part = (GZipFilePart)Parts.Single(); - if (part.GetRawStream().Position != part.EntryStartPosition) - { - part.GetRawStream().Position = part.EntryStartPosition; - } - return Parts.Single().GetCompressedStream(); - } - - #region IArchiveEntry Members - - public IArchive Archive { get; } - - public bool IsComplete => true; - - #endregion + Archive = archive; } + + public virtual Stream OpenEntryStream() + { + //this is to reset the stream to be read multiple times + var part = (GZipFilePart)Parts.Single(); + if (part.GetRawStream().Position != part.EntryStartPosition) + { + part.GetRawStream().Position = part.EntryStartPosition; + } + return Parts.Single().GetCompressedStream(); + } + + #region IArchiveEntry Members + + public IArchive Archive { get; } + + public bool IsComplete => true; + + #endregion } diff --git a/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs b/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs index 4f35f24d..7685ceb1 100644 --- a/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs +++ b/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs @@ -6,68 +6,67 @@ using System.IO; using SharpCompress.Common; using SharpCompress.IO; -namespace SharpCompress.Archives.GZip +namespace SharpCompress.Archives.GZip; + +internal sealed class GZipWritableArchiveEntry : GZipArchiveEntry, IWritableArchiveEntry { - internal sealed class GZipWritableArchiveEntry : GZipArchiveEntry, IWritableArchiveEntry + private readonly bool closeStream; + private readonly Stream stream; + + internal GZipWritableArchiveEntry( + GZipArchive archive, + Stream stream, + string path, + long size, + DateTime? lastModified, + bool closeStream + ) : base(archive, null) { - private readonly bool closeStream; - private readonly Stream stream; + this.stream = stream; + Key = path; + Size = size; + LastModifiedTime = lastModified; + this.closeStream = closeStream; + } - internal GZipWritableArchiveEntry( - GZipArchive archive, - Stream stream, - string path, - long size, - DateTime? lastModified, - bool closeStream - ) : base(archive, null) + public override long Crc => 0; + + public override string Key { get; } + + public override long CompressedSize => 0; + + public override long Size { get; } + + public override DateTime? LastModifiedTime { get; } + + public override DateTime? CreatedTime => null; + + public override DateTime? LastAccessedTime => null; + + public override DateTime? ArchivedTime => null; + + public override bool IsEncrypted => false; + + public override bool IsDirectory => false; + + public override bool IsSplitAfter => false; + + internal override IEnumerable Parts => throw new NotImplementedException(); + + Stream IWritableArchiveEntry.Stream => stream; + + public override Stream OpenEntryStream() + { + //ensure new stream is at the start, this could be reset + stream.Seek(0, SeekOrigin.Begin); + return NonDisposingStream.Create(stream); + } + + internal override void Close() + { + if (closeStream) { - this.stream = stream; - Key = path; - Size = size; - LastModifiedTime = lastModified; - this.closeStream = closeStream; - } - - public override long Crc => 0; - - public override string Key { get; } - - public override long CompressedSize => 0; - - public override long Size { get; } - - public override DateTime? LastModifiedTime { get; } - - public override DateTime? CreatedTime => null; - - public override DateTime? LastAccessedTime => null; - - public override DateTime? ArchivedTime => null; - - public override bool IsEncrypted => false; - - public override bool IsDirectory => false; - - public override bool IsSplitAfter => false; - - internal override IEnumerable Parts => throw new NotImplementedException(); - - Stream IWritableArchiveEntry.Stream => stream; - - public override Stream OpenEntryStream() - { - //ensure new stream is at the start, this could be reset - stream.Seek(0, SeekOrigin.Begin); - return NonDisposingStream.Create(stream); - } - - internal override void Close() - { - if (closeStream) - { - stream.Dispose(); - } + stream.Dispose(); } } } diff --git a/src/SharpCompress/Archives/IArchive.cs b/src/SharpCompress/Archives/IArchive.cs index acb2bf11..154529bf 100644 --- a/src/SharpCompress/Archives/IArchive.cs +++ b/src/SharpCompress/Archives/IArchive.cs @@ -1,49 +1,48 @@ -using System; +using System; using System.Collections.Generic; using SharpCompress.Common; using SharpCompress.Readers; -namespace SharpCompress.Archives +namespace SharpCompress.Archives; + +public interface IArchive : IDisposable { - public interface IArchive : IDisposable - { - event EventHandler> EntryExtractionBegin; - event EventHandler> EntryExtractionEnd; + event EventHandler> EntryExtractionBegin; + event EventHandler> EntryExtractionEnd; - event EventHandler CompressedBytesRead; - event EventHandler FilePartExtractionBegin; + event EventHandler CompressedBytesRead; + event EventHandler FilePartExtractionBegin; - IEnumerable Entries { get; } - IEnumerable Volumes { get; } + IEnumerable Entries { get; } + IEnumerable Volumes { get; } - ArchiveType Type { get; } + ArchiveType Type { get; } - /// - /// Use this method to extract all entries in an archive in order. - /// This is primarily for SOLID Rar Archives or 7Zip Archives as they need to be - /// extracted sequentially for the best performance. - /// - IReader ExtractAllEntries(); + /// + /// Use this method to extract all entries in an archive in order. + /// This is primarily for SOLID Rar Archives or 7Zip Archives as they need to be + /// extracted sequentially for the best performance. + /// + IReader ExtractAllEntries(); - /// - /// Archive is SOLID (this means the Archive saved bytes by reusing information which helps for archives containing many small files). - /// Rar Archives can be SOLID while all 7Zip archives are considered SOLID. - /// - bool IsSolid { get; } + /// + /// Archive is SOLID (this means the Archive saved bytes by reusing information which helps for archives containing many small files). + /// Rar Archives can be SOLID while all 7Zip archives are considered SOLID. + /// + bool IsSolid { get; } - /// - /// This checks to see if all the known entries have IsComplete = true - /// - bool IsComplete { get; } + /// + /// This checks to see if all the known entries have IsComplete = true + /// + bool IsComplete { get; } - /// - /// The total size of the files compressed in the archive. - /// - long TotalSize { get; } + /// + /// The total size of the files compressed in the archive. + /// + long TotalSize { get; } - /// - /// The total size of the files as uncompressed in the archive. - /// - long TotalUncompressSize { get; } - } + /// + /// The total size of the files as uncompressed in the archive. + /// + long TotalUncompressSize { get; } } diff --git a/src/SharpCompress/Archives/IArchiveEntry.cs b/src/SharpCompress/Archives/IArchiveEntry.cs index e3351582..708753cb 100644 --- a/src/SharpCompress/Archives/IArchiveEntry.cs +++ b/src/SharpCompress/Archives/IArchiveEntry.cs @@ -1,24 +1,23 @@ -using System.IO; +using System.IO; using SharpCompress.Common; -namespace SharpCompress.Archives +namespace SharpCompress.Archives; + +public interface IArchiveEntry : IEntry { - public interface IArchiveEntry : IEntry - { - /// - /// Opens the current entry as a stream that will decompress as it is read. - /// Read the entire stream or use SkipEntry on EntryStream. - /// - Stream OpenEntryStream(); + /// + /// Opens the current entry as a stream that will decompress as it is read. + /// Read the entire stream or use SkipEntry on EntryStream. + /// + Stream OpenEntryStream(); - /// - /// The archive can find all the parts of the archive needed to extract this entry. - /// - bool IsComplete { get; } + /// + /// The archive can find all the parts of the archive needed to extract this entry. + /// + bool IsComplete { get; } - /// - /// The archive instance this entry belongs to - /// - IArchive Archive { get; } - } + /// + /// The archive instance this entry belongs to + /// + IArchive Archive { get; } } diff --git a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs index d5eac3f9..cd49cef0 100644 --- a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs @@ -1,79 +1,74 @@ -using System.IO; +using System.IO; using SharpCompress.Common; using SharpCompress.IO; -namespace SharpCompress.Archives +namespace SharpCompress.Archives; + +public static class IArchiveEntryExtensions { - public static class IArchiveEntryExtensions + public static void WriteTo(this IArchiveEntry archiveEntry, Stream streamToWriteTo) { - public static void WriteTo(this IArchiveEntry archiveEntry, Stream streamToWriteTo) + if (archiveEntry.IsDirectory) { - if (archiveEntry.IsDirectory) - { - throw new ExtractionException("Entry is a file directory and cannot be extracted."); - } - - var streamListener = (IArchiveExtractionListener)archiveEntry.Archive; - streamListener.EnsureEntriesLoaded(); - streamListener.FireEntryExtractionBegin(archiveEntry); - streamListener.FireFilePartExtractionBegin( - archiveEntry.Key, - archiveEntry.Size, - archiveEntry.CompressedSize - ); - var entryStream = archiveEntry.OpenEntryStream(); - if (entryStream is null) - { - return; - } - using (entryStream) - { - using (Stream s = new ListeningStream(streamListener, entryStream)) - { - s.TransferTo(streamToWriteTo); - } - } - streamListener.FireEntryExtractionEnd(archiveEntry); + throw new ExtractionException("Entry is a file directory and cannot be extracted."); } - /// - /// Extract to specific directory, retaining filename - /// - public static void WriteToDirectory( - this IArchiveEntry entry, - string destinationDirectory, - ExtractionOptions? options = null - ) + var streamListener = (IArchiveExtractionListener)archiveEntry.Archive; + streamListener.EnsureEntriesLoaded(); + streamListener.FireEntryExtractionBegin(archiveEntry); + streamListener.FireFilePartExtractionBegin( + archiveEntry.Key, + archiveEntry.Size, + archiveEntry.CompressedSize + ); + var entryStream = archiveEntry.OpenEntryStream(); + if (entryStream is null) { - ExtractionMethods.WriteEntryToDirectory( - entry, - destinationDirectory, - options, - entry.WriteToFile - ); + return; } + using (entryStream) + { + using Stream s = new ListeningStream(streamListener, entryStream); + s.TransferTo(streamToWriteTo); + } + streamListener.FireEntryExtractionEnd(archiveEntry); + } - /// - /// Extract to specific file - /// - public static void WriteToFile( - this IArchiveEntry entry, - string destinationFileName, - ExtractionOptions? options = null - ) - { - ExtractionMethods.WriteEntryToFile( - entry, - destinationFileName, - options, - (x, fm) => - { - using (FileStream fs = File.Open(destinationFileName, fm)) - { - entry.WriteTo(fs); - } - } - ); - } + /// + /// Extract to specific directory, retaining filename + /// + public static void WriteToDirectory( + this IArchiveEntry entry, + string destinationDirectory, + ExtractionOptions? options = null + ) + { + ExtractionMethods.WriteEntryToDirectory( + entry, + destinationDirectory, + options, + entry.WriteToFile + ); + } + + /// + /// Extract to specific file + /// + public static void WriteToFile( + this IArchiveEntry entry, + string destinationFileName, + ExtractionOptions? options = null + ) + { + ExtractionMethods.WriteEntryToFile( + entry, + destinationFileName, + options, + (x, fm) => + { + using var fs = File.Open(destinationFileName, fm); + entry.WriteTo(fs); + } + ); } } diff --git a/src/SharpCompress/Archives/IArchiveExtensions.cs b/src/SharpCompress/Archives/IArchiveExtensions.cs index c0a9e1dd..14a48dbd 100644 --- a/src/SharpCompress/Archives/IArchiveExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveExtensions.cs @@ -1,23 +1,22 @@ -using System.Linq; +using System.Linq; using SharpCompress.Common; -namespace SharpCompress.Archives +namespace SharpCompress.Archives; + +public static class IArchiveExtensions { - public static class IArchiveExtensions + /// + /// Extract to specific directory, retaining filename + /// + public static void WriteToDirectory( + this IArchive archive, + string destinationDirectory, + ExtractionOptions? options = null + ) { - /// - /// Extract to specific directory, retaining filename - /// - public static void WriteToDirectory( - this IArchive archive, - string destinationDirectory, - ExtractionOptions? options = null - ) + foreach (var entry in archive.Entries.Where(x => !x.IsDirectory)) { - foreach (IArchiveEntry entry in archive.Entries.Where(x => !x.IsDirectory)) - { - entry.WriteToDirectory(destinationDirectory, options); - } + entry.WriteToDirectory(destinationDirectory, options); } } } diff --git a/src/SharpCompress/Archives/IArchiveExtractionListener.cs b/src/SharpCompress/Archives/IArchiveExtractionListener.cs index b352691e..7bc2ef34 100644 --- a/src/SharpCompress/Archives/IArchiveExtractionListener.cs +++ b/src/SharpCompress/Archives/IArchiveExtractionListener.cs @@ -1,11 +1,10 @@ -using SharpCompress.Common; +using SharpCompress.Common; -namespace SharpCompress.Archives +namespace SharpCompress.Archives; + +internal interface IArchiveExtractionListener : IExtractionListener { - internal interface IArchiveExtractionListener : IExtractionListener - { - void EnsureEntriesLoaded(); - void FireEntryExtractionBegin(IArchiveEntry entry); - void FireEntryExtractionEnd(IArchiveEntry entry); - } + void EnsureEntriesLoaded(); + void FireEntryExtractionBegin(IArchiveEntry entry); + void FireEntryExtractionEnd(IArchiveEntry entry); } diff --git a/src/SharpCompress/Archives/IArchiveFactory.cs b/src/SharpCompress/Archives/IArchiveFactory.cs index 6ba64079..370e5c9f 100644 --- a/src/SharpCompress/Archives/IArchiveFactory.cs +++ b/src/SharpCompress/Archives/IArchiveFactory.cs @@ -1,40 +1,35 @@ -using System; -using System.Collections.Generic; using System.IO; - -using SharpCompress.Common; using SharpCompress.Factories; using SharpCompress.Readers; -namespace SharpCompress.Archives +namespace SharpCompress.Archives; + +/// +/// Represents a factory used to identify and open archives. +/// +/// +/// Currently implemented by:
+/// +/// +/// +/// +/// +/// +/// +///
+public interface IArchiveFactory : IFactory { /// - /// Represents a factory used to identify and open archives. + /// Opens an Archive for random access. /// - /// - /// Currently implemented by:
- /// - /// - /// - /// - /// - /// - /// - ///
- public interface IArchiveFactory : IFactory - { - /// - /// Opens an Archive for random access. - /// - /// An open, readable and seekable stream. - /// reading options. - IArchive Open(Stream stream, ReaderOptions? readerOptions = null); + /// An open, readable and seekable stream. + /// reading options. + IArchive Open(Stream stream, ReaderOptions? readerOptions = null); - /// - /// Constructor with a FileInfo object to an existing file. - /// - /// the file to open. - /// reading options. - IArchive Open(System.IO.FileInfo fileInfo, ReaderOptions? readerOptions = null); - } + /// + /// Constructor with a FileInfo object to an existing file. + /// + /// the file to open. + /// reading options. + IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null); } diff --git a/src/SharpCompress/Archives/IMultiArchiveFactory.cs b/src/SharpCompress/Archives/IMultiArchiveFactory.cs index 7610b146..be2eefb8 100644 --- a/src/SharpCompress/Archives/IMultiArchiveFactory.cs +++ b/src/SharpCompress/Archives/IMultiArchiveFactory.cs @@ -1,43 +1,39 @@ -using System; using System.Collections.Generic; using System.IO; - -using SharpCompress.Common; using SharpCompress.Factories; using SharpCompress.Readers; -namespace SharpCompress.Archives +namespace SharpCompress.Archives; + +/// +/// Represents a factory used to identify and open archives. +/// +/// +/// Implemented by:
+/// +/// +/// +/// +/// +/// +/// +///
+public interface IMultiArchiveFactory : IFactory { /// - /// Represents a factory used to identify and open archives. + /// Constructor with IEnumerable FileInfo objects, multi and split support. /// - /// - /// Implemented by:
- /// - /// - /// - /// - /// - /// - /// - ///
- public interface IMultiArchiveFactory : IFactory - { - /// - /// Constructor with IEnumerable FileInfo objects, multi and split support. - /// - /// - /// reading options. - IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null); + /// + /// reading options. + IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null); - /// - /// Constructor with IEnumerable Stream objects, multi and split support. - /// - /// - /// reading options. - IArchive Open( - IReadOnlyList fileInfos, - ReaderOptions? readerOptions = null - ); - } + /// + /// Constructor with IEnumerable Stream objects, multi and split support. + /// + /// + /// reading options. + IArchive Open( + IReadOnlyList fileInfos, + ReaderOptions? readerOptions = null + ); } diff --git a/src/SharpCompress/Archives/IWritableArchive.cs b/src/SharpCompress/Archives/IWritableArchive.cs index 02b76f66..37b84aa0 100644 --- a/src/SharpCompress/Archives/IWritableArchive.cs +++ b/src/SharpCompress/Archives/IWritableArchive.cs @@ -2,26 +2,25 @@ using System; using System.IO; using SharpCompress.Writers; -namespace SharpCompress.Archives +namespace SharpCompress.Archives; + +public interface IWritableArchive : IArchive { - public interface IWritableArchive : IArchive - { - void RemoveEntry(IArchiveEntry entry); + void RemoveEntry(IArchiveEntry entry); - IArchiveEntry AddEntry( - string key, - Stream source, - bool closeStream, - long size = 0, - DateTime? modified = null - ); + IArchiveEntry AddEntry( + string key, + Stream source, + bool closeStream, + long size = 0, + DateTime? modified = null + ); - void SaveTo(Stream stream, WriterOptions options); + void SaveTo(Stream stream, WriterOptions options); - /// - /// Use this to pause entry rebuilding when adding large collections of entries. Dispose when complete. A using statement is recommended. - /// - /// IDisposeable to resume entry rebuilding - IDisposable PauseEntryRebuilding(); - } + /// + /// Use this to pause entry rebuilding when adding large collections of entries. Dispose when complete. A using statement is recommended. + /// + /// IDisposeable to resume entry rebuilding + IDisposable PauseEntryRebuilding(); } diff --git a/src/SharpCompress/Archives/IWritableArchiveEntry.cs b/src/SharpCompress/Archives/IWritableArchiveEntry.cs index f3d5eb26..b4ed22c1 100644 --- a/src/SharpCompress/Archives/IWritableArchiveEntry.cs +++ b/src/SharpCompress/Archives/IWritableArchiveEntry.cs @@ -1,9 +1,8 @@ -using System.IO; +using System.IO; -namespace SharpCompress.Archives +namespace SharpCompress.Archives; + +internal interface IWritableArchiveEntry { - internal interface IWritableArchiveEntry - { - Stream Stream { get; } - } + Stream Stream { get; } } diff --git a/src/SharpCompress/Archives/IWritableArchiveExtensions.cs b/src/SharpCompress/Archives/IWritableArchiveExtensions.cs index 1827e9e8..e534b306 100644 --- a/src/SharpCompress/Archives/IWritableArchiveExtensions.cs +++ b/src/SharpCompress/Archives/IWritableArchiveExtensions.cs @@ -1,94 +1,91 @@ -using System; +using System; using System.IO; using SharpCompress.Writers; -namespace SharpCompress.Archives +namespace SharpCompress.Archives; + +public static class IWritableArchiveExtensions { - public static class IWritableArchiveExtensions + public static void AddEntry( + this IWritableArchive writableArchive, + string entryPath, + string filePath + ) { - public static void AddEntry( - this IWritableArchive writableArchive, - string entryPath, - string filePath - ) + var fileInfo = new FileInfo(filePath); + if (!fileInfo.Exists) { - var fileInfo = new FileInfo(filePath); - if (!fileInfo.Exists) - { - throw new FileNotFoundException("Could not AddEntry: " + filePath); - } - writableArchive.AddEntry( - entryPath, - new FileInfo(filePath).OpenRead(), - true, - fileInfo.Length, - fileInfo.LastWriteTime - ); + throw new FileNotFoundException("Could not AddEntry: " + filePath); } + writableArchive.AddEntry( + entryPath, + new FileInfo(filePath).OpenRead(), + true, + fileInfo.Length, + fileInfo.LastWriteTime + ); + } - public static void SaveTo( - this IWritableArchive writableArchive, - string filePath, - WriterOptions options - ) - { - writableArchive.SaveTo(new FileInfo(filePath), options); - } + public static void SaveTo( + this IWritableArchive writableArchive, + string filePath, + WriterOptions options + ) + { + writableArchive.SaveTo(new FileInfo(filePath), options); + } - public static void SaveTo( - this IWritableArchive writableArchive, - FileInfo fileInfo, - WriterOptions options - ) + public static void SaveTo( + this IWritableArchive writableArchive, + FileInfo fileInfo, + WriterOptions options + ) + { + using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write); + writableArchive.SaveTo(stream, options); + } + + public static void AddAllFromDirectory( + this IWritableArchive writableArchive, + string filePath, + string searchPattern = "*.*", + SearchOption searchOption = SearchOption.AllDirectories + ) + { + using (writableArchive.PauseEntryRebuilding()) { - using (var stream = fileInfo.Open(FileMode.Create, FileAccess.Write)) + foreach ( + var path in Directory.EnumerateFiles(filePath, searchPattern, searchOption) + ) { - writableArchive.SaveTo(stream, options); + var fileInfo = new FileInfo(path); + writableArchive.AddEntry( + path.Substring(filePath.Length), + fileInfo.OpenRead(), + true, + fileInfo.Length, + fileInfo.LastWriteTime + ); } } - - public static void AddAllFromDirectory( - this IWritableArchive writableArchive, - string filePath, - string searchPattern = "*.*", - SearchOption searchOption = SearchOption.AllDirectories - ) - { - using (writableArchive.PauseEntryRebuilding()) - { - foreach ( - var path in Directory.EnumerateFiles(filePath, searchPattern, searchOption) - ) - { - var fileInfo = new FileInfo(path); - writableArchive.AddEntry( - path.Substring(filePath.Length), - fileInfo.OpenRead(), - true, - fileInfo.Length, - fileInfo.LastWriteTime - ); - } - } - } - - public static IArchiveEntry AddEntry( - this IWritableArchive writableArchive, - string key, - FileInfo fileInfo - ) - { - if (!fileInfo.Exists) - { - throw new ArgumentException("FileInfo does not exist."); - } - return writableArchive.AddEntry( - key, - fileInfo.OpenRead(), - true, - fileInfo.Length, - fileInfo.LastWriteTime - ); - } + } + + public static IArchiveEntry AddEntry( + this IWritableArchive writableArchive, + string key, + FileInfo fileInfo + ) + { + if (!fileInfo.Exists) + { + throw new ArgumentException("FileInfo does not exist."); + } + return writableArchive.AddEntry( + key, + fileInfo.OpenRead(), + true, + fileInfo.Length, + fileInfo.LastWriteTime + ); } } diff --git a/src/SharpCompress/Archives/IWriteableArchiveFactory.cs b/src/SharpCompress/Archives/IWriteableArchiveFactory.cs index 59c58134..4fae9f55 100644 --- a/src/SharpCompress/Archives/IWriteableArchiveFactory.cs +++ b/src/SharpCompress/Archives/IWriteableArchiveFactory.cs @@ -1,27 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace SharpCompress.Archives; -namespace SharpCompress.Archives +/// +/// Decorator for used to declare an archive format as able to create writeable archives +/// +/// +/// Implemented by:
+/// +/// +/// +/// +/// +public interface IWriteableArchiveFactory : Factories.IFactory { /// - /// Decorator for used to declare an archive format as able to create writeable archives + /// Creates a new, empty archive, ready to be written. /// - /// - /// Implemented by:
- /// - /// - /// - /// - /// - public interface IWriteableArchiveFactory : Factories.IFactory - { - /// - /// Creates a new, empty archive, ready to be written. - /// - /// - IWritableArchive CreateWriteableArchive(); - } + /// + IWritableArchive CreateWriteableArchive(); } diff --git a/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs b/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs index 78ec29e8..0f1acd3f 100644 --- a/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs +++ b/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs @@ -6,45 +6,44 @@ using SharpCompress.Common.Rar.Headers; using SharpCompress.IO; using SharpCompress.Readers; -namespace SharpCompress.Archives.Rar +namespace SharpCompress.Archives.Rar; + +/// +/// A rar part based on a FileInfo object +/// +internal class FileInfoRarArchiveVolume : RarVolume { - /// - /// A rar part based on a FileInfo object - /// - internal class FileInfoRarArchiveVolume : RarVolume + internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options, int index = 0) + : base(StreamingMode.Seekable, fileInfo.OpenRead(), FixOptions(options), index) { - internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options, int index = 0) - : base(StreamingMode.Seekable, fileInfo.OpenRead(), FixOptions(options), index) - { - FileInfo = fileInfo; - FileParts = GetVolumeFileParts().ToArray().ToReadOnly(); - } + FileInfo = fileInfo; + FileParts = GetVolumeFileParts().ToArray().ToReadOnly(); + } - private static ReaderOptions FixOptions(ReaderOptions options) - { - //make sure we're closing streams with fileinfo - options.LeaveStreamOpen = false; - return options; - } + private static ReaderOptions FixOptions(ReaderOptions options) + { + //make sure we're closing streams with fileinfo + options.LeaveStreamOpen = false; + return options; + } - internal ReadOnlyCollection FileParts { get; } + internal ReadOnlyCollection FileParts { get; } - internal FileInfo FileInfo { get; } + internal FileInfo FileInfo { get; } - internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) - { - return new FileInfoRarFilePart( - this, - ReaderOptions.Password, - markHeader, - fileHeader, - FileInfo - ); - } + internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) + { + return new FileInfoRarFilePart( + this, + ReaderOptions.Password, + markHeader, + fileHeader, + FileInfo + ); + } - internal override IEnumerable ReadFileParts() - { - return FileParts; - } + internal override IEnumerable ReadFileParts() + { + return FileParts; } } diff --git a/src/SharpCompress/Archives/Rar/FileInfoRarFilePart.cs b/src/SharpCompress/Archives/Rar/FileInfoRarFilePart.cs index 1908c8af..59a21334 100644 --- a/src/SharpCompress/Archives/Rar/FileInfoRarFilePart.cs +++ b/src/SharpCompress/Archives/Rar/FileInfoRarFilePart.cs @@ -1,26 +1,25 @@ using System.IO; using SharpCompress.Common.Rar.Headers; -namespace SharpCompress.Archives.Rar +namespace SharpCompress.Archives.Rar; + +internal sealed class FileInfoRarFilePart : SeekableFilePart { - internal sealed class FileInfoRarFilePart : SeekableFilePart + internal FileInfoRarFilePart( + FileInfoRarArchiveVolume volume, + string? password, + MarkHeader mh, + FileHeader fh, + FileInfo fi + ) : base(mh, fh, volume.Index, volume.Stream, password) { - internal FileInfoRarFilePart( - FileInfoRarArchiveVolume volume, - string? password, - MarkHeader mh, - FileHeader fh, - FileInfo fi - ) : base(mh, fh, volume.Index, volume.Stream, password) - { - FileInfo = fi; - } + FileInfo = fi; + } - internal FileInfo FileInfo { get; } + internal FileInfo FileInfo { get; } - internal override string FilePartName - { - get { return "Rar File: " + FileInfo.FullName + " File Entry: " + FileHeader.FileName; } - } + internal override string FilePartName + { + get { return "Rar File: " + FileInfo.FullName + " File Entry: " + FileHeader.FileName; } } } diff --git a/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs b/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs index 41abe3f4..b0edf3f5 100644 --- a/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs +++ b/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs @@ -1,23 +1,22 @@ -using System.Linq; +using System.Linq; -namespace SharpCompress.Archives.Rar +namespace SharpCompress.Archives.Rar; + +public static class RarArchiveExtensions { - public static class RarArchiveExtensions + /// + /// RarArchive is the first volume of a multi-part archive. If MultipartVolume is true and IsFirstVolume is false then the first volume file must be missing. + /// + public static bool IsFirstVolume(this RarArchive archive) { - /// - /// RarArchive is the first volume of a multi-part archive. If MultipartVolume is true and IsFirstVolume is false then the first volume file must be missing. - /// - public static bool IsFirstVolume(this RarArchive archive) - { - return archive.Volumes.First().IsFirstVolume; - } + return archive.Volumes.First().IsFirstVolume; + } - /// - /// RarArchive is part of a multi-part archive. - /// - public static bool IsMultipartVolume(this RarArchive archive) - { - return archive.Volumes.First().IsMultiVolume; - } + /// + /// RarArchive is part of a multi-part archive. + /// + public static bool IsMultipartVolume(this RarArchive archive) + { + return archive.Volumes.First().IsMultiVolume; } } diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs index 67a4d070..dad32f3c 100644 --- a/src/SharpCompress/Archives/Rar/RarArchive.cs +++ b/src/SharpCompress/Archives/Rar/RarArchive.cs @@ -9,179 +9,178 @@ using SharpCompress.IO; using SharpCompress.Readers; using SharpCompress.Readers.Rar; -namespace SharpCompress.Archives.Rar +namespace SharpCompress.Archives.Rar; + +public class RarArchive : AbstractArchive { - public class RarArchive : AbstractArchive + internal Lazy UnpackV2017 { get; } = + new Lazy(() => new Compressors.Rar.UnpackV2017.Unpack()); + internal Lazy UnpackV1 { get; } = + new Lazy(() => new Compressors.Rar.UnpackV1.Unpack()); + + /// + /// Constructor with a SourceStream able to handle FileInfo and Streams. + /// + /// + /// + internal RarArchive(SourceStream srcStream) : base(ArchiveType.Rar, srcStream) { } + + protected override IEnumerable LoadEntries(IEnumerable volumes) { - internal Lazy UnpackV2017 { get; } = - new Lazy(() => new SharpCompress.Compressors.Rar.UnpackV2017.Unpack()); - internal Lazy UnpackV1 { get; } = - new Lazy(() => new SharpCompress.Compressors.Rar.UnpackV1.Unpack()); - - /// - /// Constructor with a SourceStream able to handle FileInfo and Streams. - /// - /// - /// - internal RarArchive(SourceStream srcStream) : base(ArchiveType.Rar, srcStream) { } - - protected override IEnumerable LoadEntries(IEnumerable volumes) - { - return RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions); - } - - protected override IEnumerable LoadVolumes(SourceStream srcStream) - { - base.SrcStream.LoadAllParts(); //request all streams - Stream[] streams = base.SrcStream.Streams.ToArray(); - int idx = 0; - if (streams.Length > 1 && IsRarFile(streams[1], ReaderOptions)) //test part 2 - true = multipart not split - { - base.SrcStream.IsVolumes = true; - streams[1].Position = 0; - base.SrcStream.Position = 0; - - return srcStream.Streams.Select( - a => new StreamRarArchiveVolume(a, ReaderOptions, idx++) - ); - } - else //split mode or single file - return new StreamRarArchiveVolume(base.SrcStream, ReaderOptions, idx++).AsEnumerable(); - } - - protected override IReader CreateReaderForSolidExtraction() - { - var stream = Volumes.First().Stream; - stream.Position = 0; - return RarReader.Open(stream, ReaderOptions); - } - - public override bool IsSolid => Volumes.First().IsSolidArchive; - - public virtual int MinVersion => Volumes.First().MinVersion; - public virtual int MaxVersion => Volumes.First().MaxVersion; - - #region Creation - /// - /// Constructor with a FileInfo object to an existing file. - /// - /// - /// - public static RarArchive Open(string filePath, ReaderOptions? options = null) - { - filePath.CheckNotNullOrEmpty(nameof(filePath)); - FileInfo fileInfo = new FileInfo(filePath); - return new RarArchive( - new SourceStream( - fileInfo, - i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo), - options ?? new ReaderOptions() - ) - ); - } - - /// - /// Constructor with a FileInfo object to an existing file. - /// - /// - /// - public static RarArchive Open(FileInfo fileInfo, ReaderOptions? options = null) - { - fileInfo.CheckNotNull(nameof(fileInfo)); - return new RarArchive( - new SourceStream( - fileInfo, - i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo), - options ?? new ReaderOptions() - ) - ); - } - - /// - /// Takes a seekable Stream as a source - /// - /// - /// - public static RarArchive Open(Stream stream, ReaderOptions? options = null) - { - stream.CheckNotNull(nameof(stream)); - return new RarArchive( - new SourceStream(stream, i => null, options ?? new ReaderOptions()) - ); - } - - /// - /// Constructor with all file parts passed in - /// - /// - /// - public static RarArchive Open( - IEnumerable fileInfos, - ReaderOptions? readerOptions = null - ) - { - fileInfos.CheckNotNull(nameof(fileInfos)); - FileInfo[] files = fileInfos.ToArray(); - return new RarArchive( - new SourceStream( - files[0], - i => i < files.Length ? files[i] : null, - readerOptions ?? new ReaderOptions() - ) - ); - } - - /// - /// Constructor with all stream parts passed in - /// - /// - /// - public static RarArchive Open( - IEnumerable streams, - ReaderOptions? readerOptions = null - ) - { - streams.CheckNotNull(nameof(streams)); - Stream[] strms = streams.ToArray(); - return new RarArchive( - new SourceStream( - strms[0], - i => i < strms.Length ? strms[i] : null, - readerOptions ?? new ReaderOptions() - ) - ); - } - - public static bool IsRarFile(string filePath) - { - return IsRarFile(new FileInfo(filePath)); - } - - public static bool IsRarFile(FileInfo fileInfo) - { - if (!fileInfo.Exists) - { - return false; - } - using (Stream stream = fileInfo.OpenRead()) - { - return IsRarFile(stream); - } - } - - public static bool IsRarFile(Stream stream, ReaderOptions? options = null) - { - try - { - MarkHeader.Read(stream, true, false); - return true; - } - catch - { - return false; - } - } - - #endregion + return RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions); } + + protected override IEnumerable LoadVolumes(SourceStream srcStream) + { + SrcStream.LoadAllParts(); //request all streams + var streams = SrcStream.Streams.ToArray(); + var idx = 0; + if (streams.Length > 1 && IsRarFile(streams[1], ReaderOptions)) //test part 2 - true = multipart not split + { + SrcStream.IsVolumes = true; + streams[1].Position = 0; + SrcStream.Position = 0; + + return srcStream.Streams.Select( + a => new StreamRarArchiveVolume(a, ReaderOptions, idx++) + ); + } + else //split mode or single file + { + return new StreamRarArchiveVolume(SrcStream, ReaderOptions, idx++).AsEnumerable(); + } + } + + protected override IReader CreateReaderForSolidExtraction() + { + var stream = Volumes.First().Stream; + stream.Position = 0; + return RarReader.Open(stream, ReaderOptions); + } + + public override bool IsSolid => Volumes.First().IsSolidArchive; + + public virtual int MinVersion => Volumes.First().MinVersion; + public virtual int MaxVersion => Volumes.First().MaxVersion; + + #region Creation + /// + /// Constructor with a FileInfo object to an existing file. + /// + /// + /// + public static RarArchive Open(string filePath, ReaderOptions? options = null) + { + filePath.CheckNotNullOrEmpty(nameof(filePath)); + var fileInfo = new FileInfo(filePath); + return new RarArchive( + new SourceStream( + fileInfo, + i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo), + options ?? new ReaderOptions() + ) + ); + } + + /// + /// Constructor with a FileInfo object to an existing file. + /// + /// + /// + public static RarArchive Open(FileInfo fileInfo, ReaderOptions? options = null) + { + fileInfo.CheckNotNull(nameof(fileInfo)); + return new RarArchive( + new SourceStream( + fileInfo, + i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo), + options ?? new ReaderOptions() + ) + ); + } + + /// + /// Takes a seekable Stream as a source + /// + /// + /// + public static RarArchive Open(Stream stream, ReaderOptions? options = null) + { + stream.CheckNotNull(nameof(stream)); + return new RarArchive( + new SourceStream(stream, i => null, options ?? new ReaderOptions()) + ); + } + + /// + /// Constructor with all file parts passed in + /// + /// + /// + public static RarArchive Open( + IEnumerable fileInfos, + ReaderOptions? readerOptions = null + ) + { + fileInfos.CheckNotNull(nameof(fileInfos)); + var files = fileInfos.ToArray(); + return new RarArchive( + new SourceStream( + files[0], + i => i < files.Length ? files[i] : null, + readerOptions ?? new ReaderOptions() + ) + ); + } + + /// + /// Constructor with all stream parts passed in + /// + /// + /// + public static RarArchive Open( + IEnumerable streams, + ReaderOptions? readerOptions = null + ) + { + streams.CheckNotNull(nameof(streams)); + var strms = streams.ToArray(); + return new RarArchive( + new SourceStream( + strms[0], + i => i < strms.Length ? strms[i] : null, + readerOptions ?? new ReaderOptions() + ) + ); + } + + public static bool IsRarFile(string filePath) + { + return IsRarFile(new FileInfo(filePath)); + } + + public static bool IsRarFile(FileInfo fileInfo) + { + if (!fileInfo.Exists) + { + return false; + } + using Stream stream = fileInfo.OpenRead(); + return IsRarFile(stream); + } + + public static bool IsRarFile(Stream stream, ReaderOptions? options = null) + { + try + { + MarkHeader.Read(stream, true, false); + return true; + } + catch + { + return false; + } + } + + #endregion } diff --git a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs index a5f4d038..5885210a 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs +++ b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -8,96 +7,95 @@ using SharpCompress.Common.Rar.Headers; using SharpCompress.Compressors.Rar; using SharpCompress.Readers; -namespace SharpCompress.Archives.Rar +namespace SharpCompress.Archives.Rar; + +public class RarArchiveEntry : RarEntry, IArchiveEntry { - public class RarArchiveEntry : RarEntry, IArchiveEntry + private readonly ICollection parts; + private readonly RarArchive archive; + private readonly ReaderOptions readerOptions; + + internal RarArchiveEntry( + RarArchive archive, + IEnumerable parts, + ReaderOptions readerOptions + ) { - private readonly ICollection parts; - private readonly RarArchive archive; - private readonly ReaderOptions readerOptions; + this.parts = parts.ToList(); + this.archive = archive; + this.readerOptions = readerOptions; + IsSolid = FileHeader.IsSolid; + } - internal RarArchiveEntry( - RarArchive archive, - IEnumerable parts, - ReaderOptions readerOptions - ) + public override CompressionType CompressionType => CompressionType.Rar; + + public IArchive Archive => archive; + + internal override IEnumerable Parts => parts.Cast(); + + internal override FileHeader FileHeader => parts.First().FileHeader; + + public override long Crc + { + get { - this.parts = parts.ToList(); - this.archive = archive; - this.readerOptions = readerOptions; - this.IsSolid = this.FileHeader.IsSolid; + CheckIncomplete(); + return parts.Select(fp => fp.FileHeader).Single(fh => !fh.IsSplitAfter).FileCrc; } + } - public override CompressionType CompressionType => CompressionType.Rar; - - public IArchive Archive => archive; - - internal override IEnumerable Parts => parts.Cast(); - - internal override FileHeader FileHeader => parts.First().FileHeader; - - public override long Crc + public override long Size + { + get { - get - { - CheckIncomplete(); - return parts.Select(fp => fp.FileHeader).Single(fh => !fh.IsSplitAfter).FileCrc; - } + CheckIncomplete(); + return parts.First().FileHeader.UncompressedSize; } + } - public override long Size + public override long CompressedSize + { + get { - get - { - CheckIncomplete(); - return parts.First().FileHeader.UncompressedSize; - } + CheckIncomplete(); + return parts.Aggregate(0L, (total, fp) => total + fp.FileHeader.CompressedSize); } + } - public override long CompressedSize + public Stream OpenEntryStream() + { + if (IsRarV3) { - get - { - CheckIncomplete(); - return parts.Aggregate(0L, (total, fp) => total + fp.FileHeader.CompressedSize); - } - } - - public Stream OpenEntryStream() - { - if (IsRarV3) - { - return new RarStream( - archive.UnpackV1.Value, - FileHeader, - new MultiVolumeReadOnlyStream(Parts.Cast(), archive) - ); - } - return new RarStream( - archive.UnpackV2017.Value, + archive.UnpackV1.Value, FileHeader, new MultiVolumeReadOnlyStream(Parts.Cast(), archive) ); } - public bool IsComplete - { - get - { - var headers = parts.Select(x => x.FileHeader); - return !headers.First().IsSplitBefore && !headers.Last().IsSplitAfter; - } - } + return new RarStream( + archive.UnpackV2017.Value, + FileHeader, + new MultiVolumeReadOnlyStream(Parts.Cast(), archive) + ); + } - private void CheckIncomplete() + public bool IsComplete + { + get { - if (!readerOptions.DisableCheckIncomplete && !IsComplete) - { - throw new IncompleteArchiveException( - "ArchiveEntry is incomplete and cannot perform this operation." - ); - } + var headers = parts.Select(x => x.FileHeader); + return !headers.First().IsSplitBefore && !headers.Last().IsSplitAfter; + } + } + + private void CheckIncomplete() + { + if (!readerOptions.DisableCheckIncomplete && !IsComplete) + { + throw new IncompleteArchiveException( + "ArchiveEntry is incomplete and cannot perform this operation." + ); } } } diff --git a/src/SharpCompress/Archives/Rar/RarArchiveEntryFactory.cs b/src/SharpCompress/Archives/Rar/RarArchiveEntryFactory.cs index e345e8d3..ec4ace7c 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveEntryFactory.cs +++ b/src/SharpCompress/Archives/Rar/RarArchiveEntryFactory.cs @@ -1,53 +1,52 @@ -using System.Collections.Generic; +using System.Collections.Generic; using SharpCompress.Common.Rar; using SharpCompress.Readers; -namespace SharpCompress.Archives.Rar +namespace SharpCompress.Archives.Rar; + +internal static class RarArchiveEntryFactory { - internal static class RarArchiveEntryFactory + private static IEnumerable GetFileParts(IEnumerable parts) { - private static IEnumerable GetFileParts(IEnumerable parts) + foreach (var rarPart in parts) { - foreach (RarVolume rarPart in parts) + foreach (var fp in rarPart.ReadFileParts()) { - foreach (RarFilePart fp in rarPart.ReadFileParts()) - { - yield return fp; - } - } - } - - private static IEnumerable> GetMatchedFileParts( - IEnumerable parts - ) - { - var groupedParts = new List(); - foreach (RarFilePart fp in GetFileParts(parts)) - { - groupedParts.Add(fp); - - if (!fp.FileHeader.IsSplitAfter) - { - yield return groupedParts; - groupedParts = new List(); - } - } - if (groupedParts.Count > 0) - { - yield return groupedParts; - } - } - - internal static IEnumerable GetEntries( - RarArchive archive, - IEnumerable rarParts, - ReaderOptions readerOptions - ) - { - foreach (var groupedParts in GetMatchedFileParts(rarParts)) - { - yield return new RarArchiveEntry(archive, groupedParts, readerOptions); + yield return fp; } } } + + private static IEnumerable> GetMatchedFileParts( + IEnumerable parts + ) + { + var groupedParts = new List(); + foreach (var fp in GetFileParts(parts)) + { + groupedParts.Add(fp); + + if (!fp.FileHeader.IsSplitAfter) + { + yield return groupedParts; + groupedParts = new List(); + } + } + if (groupedParts.Count > 0) + { + yield return groupedParts; + } + } + + internal static IEnumerable GetEntries( + RarArchive archive, + IEnumerable rarParts, + ReaderOptions readerOptions + ) + { + foreach (var groupedParts in GetMatchedFileParts(rarParts)) + { + yield return new RarArchiveEntry(archive, groupedParts, readerOptions); + } + } } diff --git a/src/SharpCompress/Archives/Rar/SeekableFilePart.cs b/src/SharpCompress/Archives/Rar/SeekableFilePart.cs index 601c9788..9f27b674 100644 --- a/src/SharpCompress/Archives/Rar/SeekableFilePart.cs +++ b/src/SharpCompress/Archives/Rar/SeekableFilePart.cs @@ -2,36 +2,35 @@ using System.IO; using SharpCompress.Common.Rar; using SharpCompress.Common.Rar.Headers; -namespace SharpCompress.Archives.Rar +namespace SharpCompress.Archives.Rar; + +internal class SeekableFilePart : RarFilePart { - internal class SeekableFilePart : RarFilePart + private readonly Stream stream; + private readonly string? password; + + internal SeekableFilePart( + MarkHeader mh, + FileHeader fh, + int index, + Stream stream, + string? password + ) : base(mh, fh, index) { - private readonly Stream stream; - private readonly string? password; - - internal SeekableFilePart( - MarkHeader mh, - FileHeader fh, - int index, - Stream stream, - string? password - ) : base(mh, fh, index) - { - this.stream = stream; - this.password = password; - } - - internal override Stream GetCompressedStream() - { - stream.Position = FileHeader.DataStartPosition; - if (FileHeader.R4Salt != null) - { - return new RarCryptoWrapper(stream, password!, FileHeader.R4Salt); - } - return stream; - } - - internal override string FilePartName => - "Unknown Stream - File Entry: " + FileHeader.FileName; + this.stream = stream; + this.password = password; } + + internal override Stream GetCompressedStream() + { + stream.Position = FileHeader.DataStartPosition; + if (FileHeader.R4Salt != null) + { + return new RarCryptoWrapper(stream, password!, FileHeader.R4Salt); + } + return stream; + } + + internal override string FilePartName => + "Unknown Stream - File Entry: " + FileHeader.FileName; } diff --git a/src/SharpCompress/Archives/Rar/StreamRarArchiveVolume.cs b/src/SharpCompress/Archives/Rar/StreamRarArchiveVolume.cs index ac5dc5de..6d388746 100644 --- a/src/SharpCompress/Archives/Rar/StreamRarArchiveVolume.cs +++ b/src/SharpCompress/Archives/Rar/StreamRarArchiveVolume.cs @@ -5,27 +5,26 @@ using SharpCompress.Common.Rar.Headers; using SharpCompress.IO; using SharpCompress.Readers; -namespace SharpCompress.Archives.Rar +namespace SharpCompress.Archives.Rar; + +internal class StreamRarArchiveVolume : RarVolume { - internal class StreamRarArchiveVolume : RarVolume + internal StreamRarArchiveVolume(Stream stream, ReaderOptions options, int index = 0) + : base(StreamingMode.Seekable, stream, options, index) { } + + internal override IEnumerable ReadFileParts() { - internal StreamRarArchiveVolume(Stream stream, ReaderOptions options, int index = 0) - : base(StreamingMode.Seekable, stream, options, index) { } + return GetVolumeFileParts(); + } - internal override IEnumerable ReadFileParts() - { - return GetVolumeFileParts(); - } - - internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) - { - return new SeekableFilePart( - markHeader, - fileHeader, - this.Index, - Stream, - ReaderOptions.Password - ); - } + internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) + { + return new SeekableFilePart( + markHeader, + fileHeader, + Index, + Stream, + ReaderOptions.Password + ); } } diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs index 99c455d7..870de6b2 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs @@ -10,281 +10,278 @@ using SharpCompress.Compressors.LZMA.Utilites; using SharpCompress.IO; using SharpCompress.Readers; -namespace SharpCompress.Archives.SevenZip +namespace SharpCompress.Archives.SevenZip; + +public class SevenZipArchive : AbstractArchive { - public class SevenZipArchive : AbstractArchive + private ArchiveDatabase database; + + /// + /// Constructor expects a filepath to an existing file. + /// + /// + /// + public static SevenZipArchive Open(string filePath, ReaderOptions readerOptions = null) { - private ArchiveDatabase database; + filePath.CheckNotNullOrEmpty("filePath"); + return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); + } - /// - /// Constructor expects a filepath to an existing file. - /// - /// - /// - public static SevenZipArchive Open(string filePath, ReaderOptions readerOptions = null) + /// + /// Constructor with a FileInfo object to an existing file. + /// + /// + /// + public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null) + { + fileInfo.CheckNotNull("fileInfo"); + return new SevenZipArchive( + new SourceStream( + fileInfo, + i => ArchiveVolumeFactory.GetFilePart(i, fileInfo), + readerOptions ?? new ReaderOptions() + ) + ); + } + + /// + /// Constructor with all file parts passed in + /// + /// + /// + public static SevenZipArchive Open( + IEnumerable fileInfos, + ReaderOptions readerOptions = null + ) + { + fileInfos.CheckNotNull(nameof(fileInfos)); + var files = fileInfos.ToArray(); + return new SevenZipArchive( + new SourceStream( + files[0], + i => i < files.Length ? files[i] : null, + readerOptions ?? new ReaderOptions() + ) + ); + } + + /// + /// Constructor with all stream parts passed in + /// + /// + /// + public static SevenZipArchive Open( + IEnumerable streams, + ReaderOptions readerOptions = null + ) + { + streams.CheckNotNull(nameof(streams)); + var strms = streams.ToArray(); + return new SevenZipArchive( + new SourceStream( + strms[0], + i => i < strms.Length ? strms[i] : null, + readerOptions ?? new ReaderOptions() + ) + ); + } + + /// + /// Takes a seekable Stream as a source + /// + /// + /// + public static SevenZipArchive Open(Stream stream, ReaderOptions readerOptions = null) + { + stream.CheckNotNull("stream"); + return new SevenZipArchive( + new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()) + ); + } + + /// + /// Constructor with a SourceStream able to handle FileInfo and Streams. + /// + /// + /// + internal SevenZipArchive(SourceStream srcStream) : base(ArchiveType.SevenZip, srcStream) { } + + protected override IEnumerable LoadVolumes(SourceStream srcStream) + { + SrcStream.LoadAllParts(); //request all streams + var idx = 0; + return new SevenZipVolume(srcStream, ReaderOptions, idx++).AsEnumerable(); //simple single volume or split, multivolume not supported + } + + public static bool IsSevenZipFile(string filePath) + { + return IsSevenZipFile(new FileInfo(filePath)); + } + + public static bool IsSevenZipFile(FileInfo fileInfo) + { + if (!fileInfo.Exists) { - filePath.CheckNotNullOrEmpty("filePath"); - return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); + return false; } + using Stream stream = fileInfo.OpenRead(); + return IsSevenZipFile(stream); + } - /// - /// Constructor with a FileInfo object to an existing file. - /// - /// - /// - public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null) + internal SevenZipArchive() : base(ArchiveType.SevenZip) { } + + protected override IEnumerable LoadEntries( + IEnumerable volumes + ) + { + var stream = volumes.Single().Stream; + LoadFactory(stream); + var entries = new SevenZipArchiveEntry[database._files.Count]; + for (var i = 0; i < database._files.Count; i++) { - fileInfo.CheckNotNull("fileInfo"); - return new SevenZipArchive( - new SourceStream( - fileInfo, - i => ArchiveVolumeFactory.GetFilePart(i, fileInfo), - readerOptions ?? new ReaderOptions() - ) + var file = database._files[i]; + entries[i] = new SevenZipArchiveEntry( + this, + new SevenZipFilePart(stream, database, i, file, ReaderOptions.ArchiveEncoding) ); } - - /// - /// Constructor with all file parts passed in - /// - /// - /// - public static SevenZipArchive Open( - IEnumerable fileInfos, - ReaderOptions readerOptions = null + foreach ( + var group in entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder) ) { - fileInfos.CheckNotNull(nameof(fileInfos)); - FileInfo[] files = fileInfos.ToArray(); - return new SevenZipArchive( - new SourceStream( - files[0], - i => i < files.Length ? files[i] : null, - readerOptions ?? new ReaderOptions() - ) - ); - } - - /// - /// Constructor with all stream parts passed in - /// - /// - /// - public static SevenZipArchive Open( - IEnumerable streams, - ReaderOptions readerOptions = null - ) - { - streams.CheckNotNull(nameof(streams)); - Stream[] strms = streams.ToArray(); - return new SevenZipArchive( - new SourceStream( - strms[0], - i => i < strms.Length ? strms[i] : null, - readerOptions ?? new ReaderOptions() - ) - ); - } - - /// - /// Takes a seekable Stream as a source - /// - /// - /// - public static SevenZipArchive Open(Stream stream, ReaderOptions readerOptions = null) - { - stream.CheckNotNull("stream"); - return new SevenZipArchive( - new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()) - ); - } - - /// - /// Constructor with a SourceStream able to handle FileInfo and Streams. - /// - /// - /// - internal SevenZipArchive(SourceStream srcStream) : base(ArchiveType.SevenZip, srcStream) { } - - protected override IEnumerable LoadVolumes(SourceStream srcStream) - { - base.SrcStream.LoadAllParts(); //request all streams - int idx = 0; - return new SevenZipVolume(srcStream, ReaderOptions, idx++).AsEnumerable(); //simple single volume or split, multivolume not supported - } - - public static bool IsSevenZipFile(string filePath) - { - return IsSevenZipFile(new FileInfo(filePath)); - } - - public static bool IsSevenZipFile(FileInfo fileInfo) - { - if (!fileInfo.Exists) + var isSolid = false; + foreach (var entry in group) { - return false; - } - using (Stream stream = fileInfo.OpenRead()) - { - return IsSevenZipFile(stream); + entry.IsSolid = isSolid; + isSolid = true; //mark others in this group as solid - same as rar behaviour. } } - internal SevenZipArchive() : base(ArchiveType.SevenZip) { } + return entries; + } - protected override IEnumerable LoadEntries( - IEnumerable volumes - ) + private void LoadFactory(Stream stream) + { + if (database is null) { - var stream = volumes.Single().Stream; - LoadFactory(stream); - var entries = new SevenZipArchiveEntry[database._files.Count]; - for (int i = 0; i < database._files.Count; i++) + stream.Position = 0; + var reader = new ArchiveReader(); + reader.Open(stream); + database = reader.ReadDatabase(new PasswordProvider(ReaderOptions.Password)); + } + } + + public static bool IsSevenZipFile(Stream stream) + { + try + { + return SignatureMatch(stream); + } + catch + { + return false; + } + } + + private static ReadOnlySpan SIGNATURE => + new byte[] { (byte)'7', (byte)'z', 0xBC, 0xAF, 0x27, 0x1C }; + + private static bool SignatureMatch(Stream stream) + { + var reader = new BinaryReader(stream); + ReadOnlySpan signatureBytes = reader.ReadBytes(6); + return signatureBytes.SequenceEqual(SIGNATURE); + } + + protected override IReader CreateReaderForSolidExtraction() + { + return new SevenZipReader(ReaderOptions, this); + } + + public override bool IsSolid + { + get + { + return Entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder).Count() + > 1; + } + } + + public override long TotalSize + { + get + { + var i = Entries.Count; + return database._packSizes.Aggregate(0L, (total, packSize) => total + packSize); + } + } + + private sealed class SevenZipReader : AbstractReader + { + private readonly SevenZipArchive archive; + private CFolder currentFolder; + private Stream currentStream; + private CFileItem currentItem; + + internal SevenZipReader(ReaderOptions readerOptions, SevenZipArchive archive) + : base(readerOptions, ArchiveType.SevenZip) + { + this.archive = archive; + } + + public override SevenZipVolume Volume => archive.Volumes.Single(); + + protected override IEnumerable GetEntries(Stream stream) + { + var entries = archive.Entries.ToList(); + stream.Position = 0; + foreach (var dir in entries.Where(x => x.IsDirectory)) { - var file = database._files[i]; - entries[i] = new SevenZipArchiveEntry( - this, - new SevenZipFilePart(stream, database, i, file, ReaderOptions.ArchiveEncoding) - ); + yield return dir; } foreach ( var group in entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder) ) { - var isSolid = false; + currentFolder = group.Key; + if (group.Key is null) + { + currentStream = Stream.Null; + } + else + { + currentStream = archive.database.GetFolderStream( + stream, + currentFolder, + new PasswordProvider(Options.Password) + ); + } foreach (var entry in group) { - entry.IsSolid = isSolid; - isSolid = true; //mark others in this group as solid - same as rar behaviour. + currentItem = entry.FilePart.Header; + yield return entry; } } - - return entries; } - private void LoadFactory(Stream stream) + protected override EntryStream GetEntryStream() { - if (database is null) - { - stream.Position = 0; - var reader = new ArchiveReader(); - reader.Open(stream); - database = reader.ReadDatabase(new PasswordProvider(ReaderOptions.Password)); - } + return CreateEntryStream(new ReadOnlySubStream(currentStream, currentItem.Size)); + } + } + + private class PasswordProvider : IPasswordProvider + { + private readonly string _password; + + public PasswordProvider(string password) + { + _password = password; } - public static bool IsSevenZipFile(Stream stream) + public string CryptoGetTextPassword() { - try - { - return SignatureMatch(stream); - } - catch - { - return false; - } - } - - private static ReadOnlySpan SIGNATURE => - new byte[] { (byte)'7', (byte)'z', 0xBC, 0xAF, 0x27, 0x1C }; - - private static bool SignatureMatch(Stream stream) - { - BinaryReader reader = new BinaryReader(stream); - ReadOnlySpan signatureBytes = reader.ReadBytes(6); - return signatureBytes.SequenceEqual(SIGNATURE); - } - - protected override IReader CreateReaderForSolidExtraction() - { - return new SevenZipReader(ReaderOptions, this); - } - - public override bool IsSolid - { - get - { - return Entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder).Count() - > 1; - } - } - - public override long TotalSize - { - get - { - int i = Entries.Count; - return database._packSizes.Aggregate(0L, (total, packSize) => total + packSize); - } - } - - private sealed class SevenZipReader : AbstractReader - { - private readonly SevenZipArchive archive; - private CFolder currentFolder; - private Stream currentStream; - private CFileItem currentItem; - - internal SevenZipReader(ReaderOptions readerOptions, SevenZipArchive archive) - : base(readerOptions, ArchiveType.SevenZip) - { - this.archive = archive; - } - - public override SevenZipVolume Volume => archive.Volumes.Single(); - - protected override IEnumerable GetEntries(Stream stream) - { - List entries = archive.Entries.ToList(); - stream.Position = 0; - foreach (var dir in entries.Where(x => x.IsDirectory)) - { - yield return dir; - } - foreach ( - var group in entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder) - ) - { - currentFolder = group.Key; - if (group.Key is null) - { - currentStream = Stream.Null; - } - else - { - currentStream = archive.database.GetFolderStream( - stream, - currentFolder, - new PasswordProvider(Options.Password) - ); - } - foreach (var entry in group) - { - currentItem = entry.FilePart.Header; - yield return entry; - } - } - } - - protected override EntryStream GetEntryStream() - { - return CreateEntryStream(new ReadOnlySubStream(currentStream, currentItem.Size)); - } - } - - private class PasswordProvider : IPasswordProvider - { - private readonly string _password; - - public PasswordProvider(string password) - { - _password = password; - } - - public string CryptoGetTextPassword() - { - return _password; - } + return _password; } } } diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchiveEntry.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchiveEntry.cs index c276eade..9f8c9654 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchiveEntry.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchiveEntry.cs @@ -1,27 +1,26 @@ -using System.IO; +using System.IO; using SharpCompress.Common.SevenZip; -namespace SharpCompress.Archives.SevenZip +namespace SharpCompress.Archives.SevenZip; + +public class SevenZipArchiveEntry : SevenZipEntry, IArchiveEntry { - public class SevenZipArchiveEntry : SevenZipEntry, IArchiveEntry + internal SevenZipArchiveEntry(SevenZipArchive archive, SevenZipFilePart part) : base(part) { - internal SevenZipArchiveEntry(SevenZipArchive archive, SevenZipFilePart part) : base(part) - { - Archive = archive; - } - - public Stream OpenEntryStream() - { - return FilePart.GetCompressedStream(); - } - - public IArchive Archive { get; } - - public bool IsComplete => true; - - /// - /// This is a 7Zip Anti item - /// - public bool IsAnti => FilePart.Header.IsAnti; + Archive = archive; } + + public Stream OpenEntryStream() + { + return FilePart.GetCompressedStream(); + } + + public IArchive Archive { get; } + + public bool IsComplete => true; + + /// + /// This is a 7Zip Anti item + /// + public bool IsAnti => FilePart.Header.IsAnti; } diff --git a/src/SharpCompress/Archives/Tar/TarArchive.cs b/src/SharpCompress/Archives/Tar/TarArchive.cs index 1357660f..2870555c 100644 --- a/src/SharpCompress/Archives/Tar/TarArchive.cs +++ b/src/SharpCompress/Archives/Tar/TarArchive.cs @@ -11,248 +11,239 @@ using SharpCompress.Readers.Tar; using SharpCompress.Writers; using SharpCompress.Writers.Tar; -namespace SharpCompress.Archives.Tar +namespace SharpCompress.Archives.Tar; + +public class TarArchive : AbstractWritableArchive { - public class TarArchive : AbstractWritableArchive + /// + /// Constructor expects a filepath to an existing file. + /// + /// + /// + public static TarArchive Open(string filePath, ReaderOptions? readerOptions = null) { - /// - /// Constructor expects a filepath to an existing file. - /// - /// - /// - public static TarArchive Open(string filePath, ReaderOptions? readerOptions = null) - { - filePath.CheckNotNullOrEmpty(nameof(filePath)); - return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); - } + filePath.CheckNotNullOrEmpty(nameof(filePath)); + return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); + } - /// - /// Constructor with a FileInfo object to an existing file. - /// - /// - /// - public static TarArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) - { - fileInfo.CheckNotNull(nameof(fileInfo)); - return new TarArchive( - new SourceStream( - fileInfo, - i => ArchiveVolumeFactory.GetFilePart(i, fileInfo), - readerOptions ?? new ReaderOptions() - ) - ); - } + /// + /// Constructor with a FileInfo object to an existing file. + /// + /// + /// + public static TarArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + { + fileInfo.CheckNotNull(nameof(fileInfo)); + return new TarArchive( + new SourceStream( + fileInfo, + i => ArchiveVolumeFactory.GetFilePart(i, fileInfo), + readerOptions ?? new ReaderOptions() + ) + ); + } - /// - /// Constructor with all file parts passed in - /// - /// - /// - public static TarArchive Open( - IEnumerable fileInfos, - ReaderOptions? readerOptions = null - ) - { - fileInfos.CheckNotNull(nameof(fileInfos)); - FileInfo[] files = fileInfos.ToArray(); - return new TarArchive( - new SourceStream( - files[0], - i => i < files.Length ? files[i] : null, - readerOptions ?? new ReaderOptions() - ) - ); - } + /// + /// Constructor with all file parts passed in + /// + /// + /// + public static TarArchive Open( + IEnumerable fileInfos, + ReaderOptions? readerOptions = null + ) + { + fileInfos.CheckNotNull(nameof(fileInfos)); + var files = fileInfos.ToArray(); + return new TarArchive( + new SourceStream( + files[0], + i => i < files.Length ? files[i] : null, + readerOptions ?? new ReaderOptions() + ) + ); + } - /// - /// Constructor with all stream parts passed in - /// - /// - /// - public static TarArchive Open( - IEnumerable streams, - ReaderOptions? readerOptions = null - ) - { - streams.CheckNotNull(nameof(streams)); - Stream[] strms = streams.ToArray(); - return new TarArchive( - new SourceStream( - strms[0], - i => i < strms.Length ? strms[i] : null, - readerOptions ?? new ReaderOptions() - ) - ); - } + /// + /// Constructor with all stream parts passed in + /// + /// + /// + public static TarArchive Open( + IEnumerable streams, + ReaderOptions? readerOptions = null + ) + { + streams.CheckNotNull(nameof(streams)); + var strms = streams.ToArray(); + return new TarArchive( + new SourceStream( + strms[0], + i => i < strms.Length ? strms[i] : null, + readerOptions ?? new ReaderOptions() + ) + ); + } - /// - /// Takes a seekable Stream as a source - /// - /// - /// - public static TarArchive Open(Stream stream, ReaderOptions? readerOptions = null) - { - stream.CheckNotNull(nameof(stream)); - return new TarArchive( - new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()) - ); - } + /// + /// Takes a seekable Stream as a source + /// + /// + /// + public static TarArchive Open(Stream stream, ReaderOptions? readerOptions = null) + { + stream.CheckNotNull(nameof(stream)); + return new TarArchive( + new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()) + ); + } - public static bool IsTarFile(string filePath) - { - return IsTarFile(new FileInfo(filePath)); - } + public static bool IsTarFile(string filePath) + { + return IsTarFile(new FileInfo(filePath)); + } - public static bool IsTarFile(FileInfo fileInfo) + public static bool IsTarFile(FileInfo fileInfo) + { + if (!fileInfo.Exists) { - if (!fileInfo.Exists) - { - return false; - } - using (Stream stream = fileInfo.OpenRead()) - { - return IsTarFile(stream); - } - } - - public static bool IsTarFile(Stream stream) - { - try - { - TarHeader tarHeader = new TarHeader(new ArchiveEncoding()); - bool readSucceeded = tarHeader.Read(new BinaryReader(stream)); - bool isEmptyArchive = - tarHeader.Name.Length == 0 - && tarHeader.Size == 0 - && Enum.IsDefined(typeof(EntryType), tarHeader.EntryType); - return readSucceeded || isEmptyArchive; - } - catch { } return false; } + using Stream stream = fileInfo.OpenRead(); + return IsTarFile(stream); + } - protected override IEnumerable LoadVolumes(SourceStream srcStream) + public static bool IsTarFile(Stream stream) + { + try { - base.SrcStream.LoadAllParts(); //request all streams - int idx = 0; - return new TarVolume(srcStream, ReaderOptions, idx++).AsEnumerable(); //simple single volume or split, multivolume not supported + var tarHeader = new TarHeader(new ArchiveEncoding()); + var readSucceeded = tarHeader.Read(new BinaryReader(stream)); + var isEmptyArchive = + tarHeader.Name.Length == 0 + && tarHeader.Size == 0 + && Enum.IsDefined(typeof(EntryType), tarHeader.EntryType); + return readSucceeded || isEmptyArchive; } + catch { } + return false; + } - /// - /// Constructor with a SourceStream able to handle FileInfo and Streams. - /// - /// - /// - internal TarArchive(SourceStream srcStream) : base(ArchiveType.Tar, srcStream) { } + protected override IEnumerable LoadVolumes(SourceStream srcStream) + { + SrcStream.LoadAllParts(); //request all streams + var idx = 0; + return new TarVolume(srcStream, ReaderOptions, idx++).AsEnumerable(); //simple single volume or split, multivolume not supported + } - internal TarArchive() : base(ArchiveType.Tar) { } + /// + /// Constructor with a SourceStream able to handle FileInfo and Streams. + /// + /// + /// + internal TarArchive(SourceStream srcStream) : base(ArchiveType.Tar, srcStream) { } - protected override IEnumerable LoadEntries(IEnumerable volumes) - { - Stream stream = volumes.Single().Stream; - TarHeader? previousHeader = null; - foreach ( - TarHeader? header in TarHeaderFactory.ReadHeader( - StreamingMode.Seekable, - stream, - ReaderOptions.ArchiveEncoding - ) + internal TarArchive() : base(ArchiveType.Tar) { } + + protected override IEnumerable LoadEntries(IEnumerable volumes) + { + var stream = volumes.Single().Stream; + TarHeader? previousHeader = null; + foreach ( + var header in TarHeaderFactory.ReadHeader( + StreamingMode.Seekable, + stream, + ReaderOptions.ArchiveEncoding ) + ) + { + if (header != null) { - if (header != null) + if (header.EntryType == EntryType.LongName) { - if (header.EntryType == EntryType.LongName) + previousHeader = header; + } + else + { + if (previousHeader != null) { - previousHeader = header; - } - else - { - if (previousHeader != null) - { - var entry = new TarArchiveEntry( - this, - new TarFilePart(previousHeader, stream), - CompressionType.None - ); - - var oldStreamPos = stream.Position; - - using (var entryStream = entry.OpenEntryStream()) - { - using (var memoryStream = new MemoryStream()) - { - entryStream.TransferTo(memoryStream); - memoryStream.Position = 0; - var bytes = memoryStream.ToArray(); - - header.Name = ReaderOptions.ArchiveEncoding - .Decode(bytes) - .TrimNulls(); - } - } - - stream.Position = oldStreamPos; - - previousHeader = null; - } - yield return new TarArchiveEntry( + var entry = new TarArchiveEntry( this, - new TarFilePart(header, stream), + new TarFilePart(previousHeader, stream), CompressionType.None ); + + var oldStreamPos = stream.Position; + + using (var entryStream = entry.OpenEntryStream()) + { + using var memoryStream = new MemoryStream(); + entryStream.TransferTo(memoryStream); + memoryStream.Position = 0; + var bytes = memoryStream.ToArray(); + + header.Name = ReaderOptions.ArchiveEncoding + .Decode(bytes) + .TrimNulls(); + } + + stream.Position = oldStreamPos; + + previousHeader = null; } + yield return new TarArchiveEntry( + this, + new TarFilePart(header, stream), + CompressionType.None + ); } } } - - public static TarArchive Create() - { - return new TarArchive(); - } - - protected override TarArchiveEntry CreateEntryInternal( - string filePath, - Stream source, - long size, - DateTime? modified, - bool closeStream - ) - { - return new TarWritableArchiveEntry( - this, - source, - CompressionType.Unknown, - filePath, - size, - modified, - closeStream - ); - } - - protected override void SaveTo( - Stream stream, - WriterOptions options, - IEnumerable oldEntries, - IEnumerable newEntries - ) - { - using (var writer = new TarWriter(stream, new TarWriterOptions(options))) - { - foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory)) - { - using (var entryStream = entry.OpenEntryStream()) - { - writer.Write(entry.Key, entryStream, entry.LastModifiedTime, entry.Size); - } - } - } - } - - protected override IReader CreateReaderForSolidExtraction() - { - var stream = Volumes.Single().Stream; - stream.Position = 0; - return TarReader.Open(stream); - } + } + + public static TarArchive Create() + { + return new TarArchive(); + } + + protected override TarArchiveEntry CreateEntryInternal( + string filePath, + Stream source, + long size, + DateTime? modified, + bool closeStream + ) + { + return new TarWritableArchiveEntry( + this, + source, + CompressionType.Unknown, + filePath, + size, + modified, + closeStream + ); + } + + protected override void SaveTo( + Stream stream, + WriterOptions options, + IEnumerable oldEntries, + IEnumerable newEntries + ) + { + using var writer = new TarWriter(stream, new TarWriterOptions(options)); + foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory)) + { + using var entryStream = entry.OpenEntryStream(); + writer.Write(entry.Key, entryStream, entry.LastModifiedTime, entry.Size); + } + } + + protected override IReader CreateReaderForSolidExtraction() + { + var stream = Volumes.Single().Stream; + stream.Position = 0; + return TarReader.Open(stream); } } diff --git a/src/SharpCompress/Archives/Tar/TarArchiveEntry.cs b/src/SharpCompress/Archives/Tar/TarArchiveEntry.cs index 99a2498f..d2ac353f 100644 --- a/src/SharpCompress/Archives/Tar/TarArchiveEntry.cs +++ b/src/SharpCompress/Archives/Tar/TarArchiveEntry.cs @@ -1,32 +1,31 @@ -using System.IO; +using System.IO; using System.Linq; using SharpCompress.Common; using SharpCompress.Common.Tar; -namespace SharpCompress.Archives.Tar +namespace SharpCompress.Archives.Tar; + +public class TarArchiveEntry : TarEntry, IArchiveEntry { - public class TarArchiveEntry : TarEntry, IArchiveEntry + internal TarArchiveEntry( + TarArchive archive, + TarFilePart part, + CompressionType compressionType + ) : base(part, compressionType) { - internal TarArchiveEntry( - TarArchive archive, - TarFilePart part, - CompressionType compressionType - ) : base(part, compressionType) - { - Archive = archive; - } - - public virtual Stream OpenEntryStream() - { - return Parts.Single().GetCompressedStream(); - } - - #region IArchiveEntry Members - - public IArchive Archive { get; } - - public bool IsComplete => true; - - #endregion + Archive = archive; } + + public virtual Stream OpenEntryStream() + { + return Parts.Single().GetCompressedStream(); + } + + #region IArchiveEntry Members + + public IArchive Archive { get; } + + public bool IsComplete => true; + + #endregion } diff --git a/src/SharpCompress/Archives/Tar/TarWritableArchiveEntry.cs b/src/SharpCompress/Archives/Tar/TarWritableArchiveEntry.cs index 7c6cfbcb..e110987b 100644 --- a/src/SharpCompress/Archives/Tar/TarWritableArchiveEntry.cs +++ b/src/SharpCompress/Archives/Tar/TarWritableArchiveEntry.cs @@ -6,68 +6,67 @@ using System.IO; using SharpCompress.Common; using SharpCompress.IO; -namespace SharpCompress.Archives.Tar +namespace SharpCompress.Archives.Tar; + +internal sealed class TarWritableArchiveEntry : TarArchiveEntry, IWritableArchiveEntry { - internal sealed class TarWritableArchiveEntry : TarArchiveEntry, IWritableArchiveEntry + private readonly bool closeStream; + private readonly Stream stream; + + internal TarWritableArchiveEntry( + TarArchive archive, + Stream stream, + CompressionType compressionType, + string path, + long size, + DateTime? lastModified, + bool closeStream + ) : base(archive, null, compressionType) { - private readonly bool closeStream; - private readonly Stream stream; + this.stream = stream; + Key = path; + Size = size; + LastModifiedTime = lastModified; + this.closeStream = closeStream; + } - internal TarWritableArchiveEntry( - TarArchive archive, - Stream stream, - CompressionType compressionType, - string path, - long size, - DateTime? lastModified, - bool closeStream - ) : base(archive, null, compressionType) + public override long Crc => 0; + + public override string Key { get; } + + public override long CompressedSize => 0; + + public override long Size { get; } + + public override DateTime? LastModifiedTime { get; } + + public override DateTime? CreatedTime => null; + + public override DateTime? LastAccessedTime => null; + + public override DateTime? ArchivedTime => null; + + public override bool IsEncrypted => false; + + public override bool IsDirectory => false; + + public override bool IsSplitAfter => false; + + internal override IEnumerable Parts => throw new NotImplementedException(); + Stream IWritableArchiveEntry.Stream => stream; + + public override Stream OpenEntryStream() + { + //ensure new stream is at the start, this could be reset + stream.Seek(0, SeekOrigin.Begin); + return NonDisposingStream.Create(stream); + } + + internal override void Close() + { + if (closeStream) { - this.stream = stream; - Key = path; - Size = size; - LastModifiedTime = lastModified; - this.closeStream = closeStream; - } - - public override long Crc => 0; - - public override string Key { get; } - - public override long CompressedSize => 0; - - public override long Size { get; } - - public override DateTime? LastModifiedTime { get; } - - public override DateTime? CreatedTime => null; - - public override DateTime? LastAccessedTime => null; - - public override DateTime? ArchivedTime => null; - - public override bool IsEncrypted => false; - - public override bool IsDirectory => false; - - public override bool IsSplitAfter => false; - - internal override IEnumerable Parts => throw new NotImplementedException(); - Stream IWritableArchiveEntry.Stream => stream; - - public override Stream OpenEntryStream() - { - //ensure new stream is at the start, this could be reset - stream.Seek(0, SeekOrigin.Begin); - return NonDisposingStream.Create(stream); - } - - internal override void Close() - { - if (closeStream) - { - stream.Dispose(); - } + stream.Dispose(); } } } diff --git a/src/SharpCompress/Archives/Zip/ZipArchive.cs b/src/SharpCompress/Archives/Zip/ZipArchive.cs index f5c52863..11b8cd0a 100644 --- a/src/SharpCompress/Archives/Zip/ZipArchive.cs +++ b/src/SharpCompress/Archives/Zip/ZipArchive.cs @@ -12,319 +12,317 @@ using SharpCompress.Readers.Zip; using SharpCompress.Writers; using SharpCompress.Writers.Zip; -namespace SharpCompress.Archives.Zip +namespace SharpCompress.Archives.Zip; + +public class ZipArchive : AbstractWritableArchive { - public class ZipArchive : AbstractWritableArchive - { #nullable disable - private readonly SeekableZipHeaderFactory headerFactory; + private readonly SeekableZipHeaderFactory headerFactory; #nullable enable - /// - /// Gets or sets the compression level applied to files added to the archive, - /// if the compression method is set to deflate - /// - public CompressionLevel DeflateCompressionLevel { get; set; } + /// + /// Gets or sets the compression level applied to files added to the archive, + /// if the compression method is set to deflate + /// + public CompressionLevel DeflateCompressionLevel { get; set; } - /// - /// Constructor with a SourceStream able to handle FileInfo and Streams. - /// - /// - /// - internal ZipArchive(SourceStream srcStream) : base(ArchiveType.Zip, srcStream) + /// + /// Constructor with a SourceStream able to handle FileInfo and Streams. + /// + /// + /// + internal ZipArchive(SourceStream srcStream) : base(ArchiveType.Zip, srcStream) + { + headerFactory = new SeekableZipHeaderFactory( + srcStream.ReaderOptions.Password, + srcStream.ReaderOptions.ArchiveEncoding + ); + } + + /// + /// Constructor expects a filepath to an existing file. + /// + /// + /// + public static ZipArchive Open(string filePath, ReaderOptions? readerOptions = null) + { + filePath.CheckNotNullOrEmpty(nameof(filePath)); + return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); + } + + /// + /// Constructor with a FileInfo object to an existing file. + /// + /// + /// + public static ZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + { + fileInfo.CheckNotNull(nameof(fileInfo)); + return new ZipArchive( + new SourceStream( + fileInfo, + i => ZipArchiveVolumeFactory.GetFilePart(i, fileInfo), + readerOptions ?? new ReaderOptions() + ) + ); + } + + /// + /// Constructor with all file parts passed in + /// + /// + /// + public static ZipArchive Open( + IEnumerable fileInfos, + ReaderOptions? readerOptions = null + ) + { + fileInfos.CheckNotNull(nameof(fileInfos)); + var files = fileInfos.ToArray(); + return new ZipArchive( + new SourceStream( + files[0], + i => i < files.Length ? files[i] : null, + readerOptions ?? new ReaderOptions() + ) + ); + } + + /// + /// Constructor with all stream parts passed in + /// + /// + /// + public static ZipArchive Open( + IEnumerable streams, + ReaderOptions? readerOptions = null + ) + { + streams.CheckNotNull(nameof(streams)); + var strms = streams.ToArray(); + return new ZipArchive( + new SourceStream( + strms[0], + i => i < strms.Length ? strms[i] : null, + readerOptions ?? new ReaderOptions() + ) + ); + } + + /// + /// Takes a seekable Stream as a source + /// + /// + /// + public static ZipArchive Open(Stream stream, ReaderOptions? readerOptions = null) + { + stream.CheckNotNull(nameof(stream)); + return new ZipArchive( + new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()) + ); + } + + public static bool IsZipFile(string filePath, string? password = null) + { + return IsZipFile(new FileInfo(filePath), password); + } + + public static bool IsZipFile(FileInfo fileInfo, string? password = null) + { + if (!fileInfo.Exists) { - headerFactory = new SeekableZipHeaderFactory( - srcStream.ReaderOptions.Password, - srcStream.ReaderOptions.ArchiveEncoding - ); + return false; } + using Stream stream = fileInfo.OpenRead(); + return IsZipFile(stream, password); + } - /// - /// Constructor expects a filepath to an existing file. - /// - /// - /// - public static ZipArchive Open(string filePath, ReaderOptions? readerOptions = null) + public static bool IsZipFile(Stream stream, string? password = null) + { + var headerFactory = new StreamingZipHeaderFactory( + password, + new ArchiveEncoding() + ); + try { - filePath.CheckNotNullOrEmpty(nameof(filePath)); - return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); - } - - /// - /// Constructor with a FileInfo object to an existing file. - /// - /// - /// - public static ZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) - { - fileInfo.CheckNotNull(nameof(fileInfo)); - return new ZipArchive( - new SourceStream( - fileInfo, - i => ZipArchiveVolumeFactory.GetFilePart(i, fileInfo), - readerOptions ?? new ReaderOptions() - ) - ); - } - - /// - /// Constructor with all file parts passed in - /// - /// - /// - public static ZipArchive Open( - IEnumerable fileInfos, - ReaderOptions? readerOptions = null - ) - { - fileInfos.CheckNotNull(nameof(fileInfos)); - FileInfo[] files = fileInfos.ToArray(); - return new ZipArchive( - new SourceStream( - files[0], - i => i < files.Length ? files[i] : null, - readerOptions ?? new ReaderOptions() - ) - ); - } - - /// - /// Constructor with all stream parts passed in - /// - /// - /// - public static ZipArchive Open( - IEnumerable streams, - ReaderOptions? readerOptions = null - ) - { - streams.CheckNotNull(nameof(streams)); - Stream[] strms = streams.ToArray(); - return new ZipArchive( - new SourceStream( - strms[0], - i => i < strms.Length ? strms[i] : null, - readerOptions ?? new ReaderOptions() - ) - ); - } - - /// - /// Takes a seekable Stream as a source - /// - /// - /// - public static ZipArchive Open(Stream stream, ReaderOptions? readerOptions = null) - { - stream.CheckNotNull(nameof(stream)); - return new ZipArchive( - new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()) - ); - } - - public static bool IsZipFile(string filePath, string? password = null) - { - return IsZipFile(new FileInfo(filePath), password); - } - - public static bool IsZipFile(FileInfo fileInfo, string? password = null) - { - if (!fileInfo.Exists) + var header = headerFactory + .ReadStreamHeader(stream) + .FirstOrDefault(x => x.ZipHeaderType != ZipHeaderType.Split); + if (header is null) { return false; } - using (Stream stream = fileInfo.OpenRead()) - { - return IsZipFile(stream, password); - } + return Enum.IsDefined(typeof(ZipHeaderType), header.ZipHeaderType); } - - public static bool IsZipFile(Stream stream, string? password = null) + catch (CryptographicException) { - StreamingZipHeaderFactory headerFactory = new StreamingZipHeaderFactory( - password, - new ArchiveEncoding() - ); - try + return true; + } + catch + { + return false; + } + } + + public static bool IsZipMulti(Stream stream, string? password = null) + { + var headerFactory = new StreamingZipHeaderFactory( + password, + new ArchiveEncoding() + ); + try + { + var header = headerFactory + .ReadStreamHeader(stream) + .FirstOrDefault(x => x.ZipHeaderType != ZipHeaderType.Split); + if (header is null) { - ZipHeader? header = headerFactory - .ReadStreamHeader(stream) - .FirstOrDefault(x => x.ZipHeaderType != ZipHeaderType.Split); - if (header is null) + if (stream.CanSeek) //could be multipart. Test for central directory - might not be z64 safe + { + var z = new SeekableZipHeaderFactory( + password, + new ArchiveEncoding() + ); + var x = z.ReadSeekableHeader(stream).FirstOrDefault(); + return x?.ZipHeaderType == ZipHeaderType.DirectoryEntry; + } + else { return false; } - return Enum.IsDefined(typeof(ZipHeaderType), header.ZipHeaderType); - } - catch (CryptographicException) - { - return true; - } - catch - { - return false; } + return Enum.IsDefined(typeof(ZipHeaderType), header.ZipHeaderType); } - - public static bool IsZipMulti(Stream stream, string? password = null) + catch (CryptographicException) { - StreamingZipHeaderFactory headerFactory = new StreamingZipHeaderFactory( - password, - new ArchiveEncoding() - ); - try - { - ZipHeader? header = headerFactory - .ReadStreamHeader(stream) - .FirstOrDefault(x => x.ZipHeaderType != ZipHeaderType.Split); - if (header is null) - { - if (stream.CanSeek) //could be multipart. Test for central directory - might not be z64 safe - { - SeekableZipHeaderFactory z = new SeekableZipHeaderFactory( - password, - new ArchiveEncoding() - ); - var x = z.ReadSeekableHeader(stream).FirstOrDefault(); - return x?.ZipHeaderType == ZipHeaderType.DirectoryEntry; - } - else - return false; - } - return Enum.IsDefined(typeof(ZipHeaderType), header.ZipHeaderType); - } - catch (CryptographicException) - { - return true; - } - catch - { - return false; - } + return true; } - - protected override IEnumerable LoadVolumes(SourceStream srcStream) + catch { - base.SrcStream.LoadAllParts(); //request all streams - base.SrcStream.Position = 0; - - List streams = base.SrcStream.Streams.ToList(); - int idx = 0; - if (streams.Count > 1) //test part 2 - true = multipart not split - { - streams[1].Position += 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception - bool isZip = IsZipFile(streams[1], ReaderOptions.Password); - streams[1].Position -= 4; - if (isZip) - { - base.SrcStream.IsVolumes = true; - - var tmp = streams[0]; //arcs as zip, z01 ... swap the zip the end - streams.RemoveAt(0); - streams.Add(tmp); - - //streams[0].Position = 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception - return streams.Select(a => new ZipVolume(a, ReaderOptions, idx++)); - } - } - - //split mode or single file - return new ZipVolume(base.SrcStream, ReaderOptions, idx++).AsEnumerable(); - } - - internal ZipArchive() : base(ArchiveType.Zip) { } - - protected override IEnumerable LoadEntries(IEnumerable volumes) - { - var vols = volumes.ToArray(); - foreach (ZipHeader h in headerFactory.ReadSeekableHeader(vols.Last().Stream)) - { - if (h != null) - { - switch (h.ZipHeaderType) - { - case ZipHeaderType.DirectoryEntry: - - { - DirectoryEntryHeader deh = (DirectoryEntryHeader)h; - Stream s; - if ( - deh.RelativeOffsetOfEntryHeader + deh.CompressedSize - > vols[deh.DiskNumberStart].Stream.Length - ) - { - var v = vols.Skip(deh.DiskNumberStart).ToArray(); - s = new SourceStream( - v[0].Stream, - i => i < v.Length ? v[i].Stream : null, - new ReaderOptions() { LeaveStreamOpen = true } - ); - } - else - s = vols[deh.DiskNumberStart].Stream; - yield return new ZipArchiveEntry( - this, - new SeekableZipFilePart(headerFactory, deh, s) - ); - } - break; - case ZipHeaderType.DirectoryEnd: - { - byte[] bytes = ((DirectoryEndHeader)h).Comment ?? Array.Empty(); - volumes.Last().Comment = ReaderOptions.ArchiveEncoding.Decode(bytes); - yield break; - } - } - } - } - } - - public void SaveTo(Stream stream) - { - SaveTo(stream, new WriterOptions(CompressionType.Deflate)); - } - - protected override void SaveTo( - Stream stream, - WriterOptions options, - IEnumerable oldEntries, - IEnumerable newEntries - ) - { - using (var writer = new ZipWriter(stream, new ZipWriterOptions(options))) - { - foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory)) - { - using (var entryStream = entry.OpenEntryStream()) - { - writer.Write(entry.Key, entryStream, entry.LastModifiedTime); - } - } - } - } - - protected override ZipArchiveEntry CreateEntryInternal( - string filePath, - Stream source, - long size, - DateTime? modified, - bool closeStream - ) - { - return new ZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream); - } - - public static ZipArchive Create() - { - return new ZipArchive(); - } - - protected override IReader CreateReaderForSolidExtraction() - { - var stream = Volumes.Single().Stream; - stream.Position = 0; - return ZipReader.Open(stream, ReaderOptions); + return false; } } + + protected override IEnumerable LoadVolumes(SourceStream srcStream) + { + SrcStream.LoadAllParts(); //request all streams + SrcStream.Position = 0; + + var streams = SrcStream.Streams.ToList(); + var idx = 0; + if (streams.Count > 1) //test part 2 - true = multipart not split + { + streams[1].Position += 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception + var isZip = IsZipFile(streams[1], ReaderOptions.Password); + streams[1].Position -= 4; + if (isZip) + { + SrcStream.IsVolumes = true; + + var tmp = streams[0]; //arcs as zip, z01 ... swap the zip the end + streams.RemoveAt(0); + streams.Add(tmp); + + //streams[0].Position = 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception + return streams.Select(a => new ZipVolume(a, ReaderOptions, idx++)); + } + } + + //split mode or single file + return new ZipVolume(SrcStream, ReaderOptions, idx++).AsEnumerable(); + } + + internal ZipArchive() : base(ArchiveType.Zip) { } + + protected override IEnumerable LoadEntries(IEnumerable volumes) + { + var vols = volumes.ToArray(); + foreach (var h in headerFactory.ReadSeekableHeader(vols.Last().Stream)) + { + if (h != null) + { + switch (h.ZipHeaderType) + { + case ZipHeaderType.DirectoryEntry: + + { + var deh = (DirectoryEntryHeader)h; + Stream s; + if ( + deh.RelativeOffsetOfEntryHeader + deh.CompressedSize + > vols[deh.DiskNumberStart].Stream.Length + ) + { + var v = vols.Skip(deh.DiskNumberStart).ToArray(); + s = new SourceStream( + v[0].Stream, + i => i < v.Length ? v[i].Stream : null, + new ReaderOptions() { LeaveStreamOpen = true } + ); + } + else + { + s = vols[deh.DiskNumberStart].Stream; + } + + yield return new ZipArchiveEntry( + this, + new SeekableZipFilePart(headerFactory, deh, s) + ); + } + break; + case ZipHeaderType.DirectoryEnd: + { + var bytes = ((DirectoryEndHeader)h).Comment ?? Array.Empty(); + volumes.Last().Comment = ReaderOptions.ArchiveEncoding.Decode(bytes); + yield break; + } + } + } + } + } + + public void SaveTo(Stream stream) + { + SaveTo(stream, new WriterOptions(CompressionType.Deflate)); + } + + protected override void SaveTo( + Stream stream, + WriterOptions options, + IEnumerable oldEntries, + IEnumerable newEntries + ) + { + using var writer = new ZipWriter(stream, new ZipWriterOptions(options)); + foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory)) + { + using var entryStream = entry.OpenEntryStream(); + writer.Write(entry.Key, entryStream, entry.LastModifiedTime); + } + } + + protected override ZipArchiveEntry CreateEntryInternal( + string filePath, + Stream source, + long size, + DateTime? modified, + bool closeStream + ) + { + return new ZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream); + } + + public static ZipArchive Create() + { + return new ZipArchive(); + } + + protected override IReader CreateReaderForSolidExtraction() + { + var stream = Volumes.Single().Stream; + stream.Position = 0; + return ZipReader.Open(stream, ReaderOptions); + } } diff --git a/src/SharpCompress/Archives/Zip/ZipArchiveEntry.cs b/src/SharpCompress/Archives/Zip/ZipArchiveEntry.cs index dd1354fd..5059f61a 100644 --- a/src/SharpCompress/Archives/Zip/ZipArchiveEntry.cs +++ b/src/SharpCompress/Archives/Zip/ZipArchiveEntry.cs @@ -1,29 +1,28 @@ -using System.IO; +using System.IO; using System.Linq; using SharpCompress.Common.Zip; -namespace SharpCompress.Archives.Zip +namespace SharpCompress.Archives.Zip; + +public class ZipArchiveEntry : ZipEntry, IArchiveEntry { - public class ZipArchiveEntry : ZipEntry, IArchiveEntry + internal ZipArchiveEntry(ZipArchive archive, SeekableZipFilePart? part) : base(part) { - internal ZipArchiveEntry(ZipArchive archive, SeekableZipFilePart? part) : base(part) - { - Archive = archive; - } - - public virtual Stream OpenEntryStream() - { - return Parts.Single().GetCompressedStream(); - } - - #region IArchiveEntry Members - - public IArchive Archive { get; } - - public bool IsComplete => true; - - #endregion - - public string? Comment => ((SeekableZipFilePart)Parts.Single()).Comment; + Archive = archive; } + + public virtual Stream OpenEntryStream() + { + return Parts.Single().GetCompressedStream(); + } + + #region IArchiveEntry Members + + public IArchive Archive { get; } + + public bool IsComplete => true; + + #endregion + + public string? Comment => ((SeekableZipFilePart)Parts.Single()).Comment; } diff --git a/src/SharpCompress/Archives/Zip/ZipWritableArchiveEntry.cs b/src/SharpCompress/Archives/Zip/ZipWritableArchiveEntry.cs index ac595792..a13a3dbb 100644 --- a/src/SharpCompress/Archives/Zip/ZipWritableArchiveEntry.cs +++ b/src/SharpCompress/Archives/Zip/ZipWritableArchiveEntry.cs @@ -4,70 +4,69 @@ using System.IO; using SharpCompress.Common; using SharpCompress.IO; -namespace SharpCompress.Archives.Zip +namespace SharpCompress.Archives.Zip; + +internal class ZipWritableArchiveEntry : ZipArchiveEntry, IWritableArchiveEntry { - internal class ZipWritableArchiveEntry : ZipArchiveEntry, IWritableArchiveEntry + private readonly bool closeStream; + private readonly Stream stream; + private bool isDisposed; + + internal ZipWritableArchiveEntry( + ZipArchive archive, + Stream stream, + string path, + long size, + DateTime? lastModified, + bool closeStream + ) : base(archive, null) { - private readonly bool closeStream; - private readonly Stream stream; - private bool isDisposed; + this.stream = stream; + Key = path; + Size = size; + LastModifiedTime = lastModified; + this.closeStream = closeStream; + } - internal ZipWritableArchiveEntry( - ZipArchive archive, - Stream stream, - string path, - long size, - DateTime? lastModified, - bool closeStream - ) : base(archive, null) + public override long Crc => 0; + + public override string Key { get; } + + public override long CompressedSize => 0; + + public override long Size { get; } + + public override DateTime? LastModifiedTime { get; } + + public override DateTime? CreatedTime => null; + + public override DateTime? LastAccessedTime => null; + + public override DateTime? ArchivedTime => null; + + public override bool IsEncrypted => false; + + public override bool IsDirectory => false; + + public override bool IsSplitAfter => false; + + internal override IEnumerable Parts => throw new NotImplementedException(); + + Stream IWritableArchiveEntry.Stream => stream; + + public override Stream OpenEntryStream() + { + //ensure new stream is at the start, this could be reset + stream.Seek(0, SeekOrigin.Begin); + return NonDisposingStream.Create(stream); + } + + internal override void Close() + { + if (closeStream && !isDisposed) { - this.stream = stream; - Key = path; - Size = size; - LastModifiedTime = lastModified; - this.closeStream = closeStream; - } - - public override long Crc => 0; - - public override string Key { get; } - - public override long CompressedSize => 0; - - public override long Size { get; } - - public override DateTime? LastModifiedTime { get; } - - public override DateTime? CreatedTime => null; - - public override DateTime? LastAccessedTime => null; - - public override DateTime? ArchivedTime => null; - - public override bool IsEncrypted => false; - - public override bool IsDirectory => false; - - public override bool IsSplitAfter => false; - - internal override IEnumerable Parts => throw new NotImplementedException(); - - Stream IWritableArchiveEntry.Stream => stream; - - public override Stream OpenEntryStream() - { - //ensure new stream is at the start, this could be reset - stream.Seek(0, SeekOrigin.Begin); - return NonDisposingStream.Create(stream); - } - - internal override void Close() - { - if (closeStream && !isDisposed) - { - stream.Dispose(); - isDisposed = true; - } + stream.Dispose(); + isDisposed = true; } } } diff --git a/src/SharpCompress/AssemblyInfo.cs b/src/SharpCompress/AssemblyInfo.cs index 8c114539..0270020d 100644 --- a/src/SharpCompress/AssemblyInfo.cs +++ b/src/SharpCompress/AssemblyInfo.cs @@ -1,3 +1,3 @@ -using System; +using System; [assembly: CLSCompliant(true)] diff --git a/src/SharpCompress/Common/ArchiveEncoding.cs b/src/SharpCompress/Common/ArchiveEncoding.cs index d50f0930..a5ee35b2 100644 --- a/src/SharpCompress/Common/ArchiveEncoding.cs +++ b/src/SharpCompress/Common/ArchiveEncoding.cs @@ -1,75 +1,74 @@ -using System; +using System; using System.Text; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public class ArchiveEncoding { - public class ArchiveEncoding + /// + /// Default encoding to use when archive format doesn't specify one. + /// + public Encoding Default { get; set; } + + /// + /// ArchiveEncoding used by encryption schemes which don't comply with RFC 2898. + /// + public Encoding Password { get; set; } + + /// + /// Set this encoding when you want to force it for all encoding operations. + /// + public Encoding? Forced { get; set; } + + /// + /// Set this when you want to use a custom method for all decoding operations. + /// + /// string Func(bytes, index, length) + public Func? CustomDecoder { get; set; } + + public ArchiveEncoding() : this(Encoding.Default, Encoding.Default) { } + + public ArchiveEncoding(Encoding def, Encoding password) { - /// - /// Default encoding to use when archive format doesn't specify one. - /// - public Encoding Default { get; set; } - - /// - /// ArchiveEncoding used by encryption schemes which don't comply with RFC 2898. - /// - public Encoding Password { get; set; } - - /// - /// Set this encoding when you want to force it for all encoding operations. - /// - public Encoding? Forced { get; set; } - - /// - /// Set this when you want to use a custom method for all decoding operations. - /// - /// string Func(bytes, index, length) - public Func? CustomDecoder { get; set; } - - public ArchiveEncoding() : this(Encoding.Default, Encoding.Default) { } - - public ArchiveEncoding(Encoding def, Encoding password) - { - Default = def; - Password = password; - } + Default = def; + Password = password; + } #if !NETFRAMEWORK - static ArchiveEncoding() - { - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - } + static ArchiveEncoding() + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + } #endif - public string Decode(byte[] bytes) - { - return Decode(bytes, 0, bytes.Length); - } + public string Decode(byte[] bytes) + { + return Decode(bytes, 0, bytes.Length); + } - public string Decode(byte[] bytes, int start, int length) - { - return GetDecoder().Invoke(bytes, start, length); - } + public string Decode(byte[] bytes, int start, int length) + { + return GetDecoder().Invoke(bytes, start, length); + } - public string DecodeUTF8(byte[] bytes) - { - return Encoding.UTF8.GetString(bytes, 0, bytes.Length); - } + public string DecodeUTF8(byte[] bytes) + { + return Encoding.UTF8.GetString(bytes, 0, bytes.Length); + } - public byte[] Encode(string str) - { - return GetEncoding().GetBytes(str); - } + public byte[] Encode(string str) + { + return GetEncoding().GetBytes(str); + } - public Encoding GetEncoding() - { - return Forced ?? Default ?? Encoding.UTF8; - } + public Encoding GetEncoding() + { + return Forced ?? Default ?? Encoding.UTF8; + } - public Func GetDecoder() - { - return CustomDecoder - ?? ((bytes, index, count) => GetEncoding().GetString(bytes, index, count)); - } + public Func GetDecoder() + { + return CustomDecoder + ?? ((bytes, index, count) => GetEncoding().GetString(bytes, index, count)); } } diff --git a/src/SharpCompress/Common/ArchiveException.cs b/src/SharpCompress/Common/ArchiveException.cs index 51fe7463..0fc98baa 100644 --- a/src/SharpCompress/Common/ArchiveException.cs +++ b/src/SharpCompress/Common/ArchiveException.cs @@ -1,9 +1,8 @@ -using System; +using System; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public class ArchiveException : Exception { - public class ArchiveException : Exception - { - public ArchiveException(string message) : base(message) { } - } + public ArchiveException(string message) : base(message) { } } diff --git a/src/SharpCompress/Common/ArchiveExtractionEventArgs.cs b/src/SharpCompress/Common/ArchiveExtractionEventArgs.cs index 0be0383c..74d419a3 100644 --- a/src/SharpCompress/Common/ArchiveExtractionEventArgs.cs +++ b/src/SharpCompress/Common/ArchiveExtractionEventArgs.cs @@ -1,14 +1,13 @@ -using System; +using System; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public class ArchiveExtractionEventArgs : EventArgs { - public class ArchiveExtractionEventArgs : EventArgs + internal ArchiveExtractionEventArgs(T entry) { - internal ArchiveExtractionEventArgs(T entry) - { - Item = entry; - } - - public T Item { get; } + Item = entry; } + + public T Item { get; } } diff --git a/src/SharpCompress/Common/ArchiveType.cs b/src/SharpCompress/Common/ArchiveType.cs index 1a899398..49b35387 100644 --- a/src/SharpCompress/Common/ArchiveType.cs +++ b/src/SharpCompress/Common/ArchiveType.cs @@ -1,11 +1,10 @@ -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public enum ArchiveType { - public enum ArchiveType - { - Rar, - Zip, - Tar, - SevenZip, - GZip - } + Rar, + Zip, + Tar, + SevenZip, + GZip } diff --git a/src/SharpCompress/Common/CompressedBytesReadEventArgs.cs b/src/SharpCompress/Common/CompressedBytesReadEventArgs.cs index c87a3588..34ca461f 100644 --- a/src/SharpCompress/Common/CompressedBytesReadEventArgs.cs +++ b/src/SharpCompress/Common/CompressedBytesReadEventArgs.cs @@ -1,26 +1,25 @@ -using System; +using System; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public sealed class CompressedBytesReadEventArgs : EventArgs { - public sealed class CompressedBytesReadEventArgs : EventArgs + public CompressedBytesReadEventArgs( + long compressedBytesRead, + long currentFilePartCompressedBytesRead + ) { - public CompressedBytesReadEventArgs( - long compressedBytesRead, - long currentFilePartCompressedBytesRead - ) - { - CompressedBytesRead = compressedBytesRead; - CurrentFilePartCompressedBytesRead = currentFilePartCompressedBytesRead; - } - - /// - /// Compressed bytes read for the current entry - /// - public long CompressedBytesRead { get; } - - /// - /// Current file part read for Multipart files (e.g. Rar) - /// - public long CurrentFilePartCompressedBytesRead { get; } + CompressedBytesRead = compressedBytesRead; + CurrentFilePartCompressedBytesRead = currentFilePartCompressedBytesRead; } + + /// + /// Compressed bytes read for the current entry + /// + public long CompressedBytesRead { get; } + + /// + /// Current file part read for Multipart files (e.g. Rar) + /// + public long CurrentFilePartCompressedBytesRead { get; } } diff --git a/src/SharpCompress/Common/CompressionType.cs b/src/SharpCompress/Common/CompressionType.cs index 7dfc857c..11263245 100644 --- a/src/SharpCompress/Common/CompressionType.cs +++ b/src/SharpCompress/Common/CompressionType.cs @@ -1,19 +1,18 @@ -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public enum CompressionType { - public enum CompressionType - { - None, - GZip, - BZip2, - PPMd, - Deflate, - Rar, - LZMA, - BCJ, - BCJ2, - LZip, - Xz, - Unknown, - Deflate64 - } + None, + GZip, + BZip2, + PPMd, + Deflate, + Rar, + LZMA, + BCJ, + BCJ2, + LZip, + Xz, + Unknown, + Deflate64 } diff --git a/src/SharpCompress/Common/CryptographicException.cs b/src/SharpCompress/Common/CryptographicException.cs index 3bdfccb5..c20775a8 100644 --- a/src/SharpCompress/Common/CryptographicException.cs +++ b/src/SharpCompress/Common/CryptographicException.cs @@ -1,9 +1,8 @@ -using System; +using System; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public class CryptographicException : Exception { - public class CryptographicException : Exception - { - public CryptographicException(string message) : base(message) { } - } + public CryptographicException(string message) : base(message) { } } diff --git a/src/SharpCompress/Common/Entry.cs b/src/SharpCompress/Common/Entry.cs index 17ecc31b..85219a43 100644 --- a/src/SharpCompress/Common/Entry.cs +++ b/src/SharpCompress/Common/Entry.cs @@ -2,90 +2,89 @@ using System; using System.Collections.Generic; using System.Linq; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public abstract class Entry : IEntry { - public abstract class Entry : IEntry - { - /// - /// The File's 32 bit CRC Hash - /// - public abstract long Crc { get; } + /// + /// The File's 32 bit CRC Hash + /// + public abstract long Crc { get; } - /// - /// The string key of the file internal to the Archive. - /// - public abstract string Key { get; } + /// + /// The string key of the file internal to the Archive. + /// + public abstract string Key { get; } - /// - /// The target of a symlink entry internal to the Archive. Will be null if not a symlink. - /// - public abstract string? LinkTarget { get; } + /// + /// The target of a symlink entry internal to the Archive. Will be null if not a symlink. + /// + public abstract string? LinkTarget { get; } - /// - /// The compressed file size - /// - public abstract long CompressedSize { get; } + /// + /// The compressed file size + /// + public abstract long CompressedSize { get; } - /// - /// The compression type - /// - public abstract CompressionType CompressionType { get; } + /// + /// The compression type + /// + public abstract CompressionType CompressionType { get; } - /// - /// The uncompressed file size - /// - public abstract long Size { get; } + /// + /// The uncompressed file size + /// + public abstract long Size { get; } - /// - /// The entry last modified time in the archive, if recorded - /// - public abstract DateTime? LastModifiedTime { get; } + /// + /// The entry last modified time in the archive, if recorded + /// + public abstract DateTime? LastModifiedTime { get; } - /// - /// The entry create time in the archive, if recorded - /// - public abstract DateTime? CreatedTime { get; } + /// + /// The entry create time in the archive, if recorded + /// + public abstract DateTime? CreatedTime { get; } - /// - /// The entry last accessed time in the archive, if recorded - /// - public abstract DateTime? LastAccessedTime { get; } + /// + /// The entry last accessed time in the archive, if recorded + /// + public abstract DateTime? LastAccessedTime { get; } - /// - /// The entry time when archived, if recorded - /// - public abstract DateTime? ArchivedTime { get; } + /// + /// The entry time when archived, if recorded + /// + public abstract DateTime? ArchivedTime { get; } - /// - /// Entry is password protected and encrypted and cannot be extracted. - /// - public abstract bool IsEncrypted { get; } + /// + /// Entry is password protected and encrypted and cannot be extracted. + /// + public abstract bool IsEncrypted { get; } - /// - /// Entry is directory. - /// - public abstract bool IsDirectory { get; } + /// + /// Entry is directory. + /// + public abstract bool IsDirectory { get; } - /// - /// Entry is split among multiple volumes - /// - public abstract bool IsSplitAfter { get; } + /// + /// Entry is split among multiple volumes + /// + public abstract bool IsSplitAfter { get; } - public int VolumeIndexFirst => this.Parts?.FirstOrDefault()?.Index ?? 0; - public int VolumeIndexLast => this.Parts?.LastOrDefault()?.Index ?? 0; + public int VolumeIndexFirst => Parts?.FirstOrDefault()?.Index ?? 0; + public int VolumeIndexLast => Parts?.LastOrDefault()?.Index ?? 0; - /// - public override string ToString() => Key; + /// + public override string ToString() => Key; - internal abstract IEnumerable Parts { get; } + internal abstract IEnumerable Parts { get; } - public bool IsSolid { get; set; } + public bool IsSolid { get; set; } - internal virtual void Close() { } + internal virtual void Close() { } - /// - /// Entry file attribute. - /// - public virtual int? Attrib => throw new NotImplementedException(); - } + /// + /// Entry file attribute. + /// + public virtual int? Attrib => throw new NotImplementedException(); } diff --git a/src/SharpCompress/Common/EntryStream.cs b/src/SharpCompress/Common/EntryStream.cs index 44d8e235..6dcac04b 100644 --- a/src/SharpCompress/Common/EntryStream.cs +++ b/src/SharpCompress/Common/EntryStream.cs @@ -1,95 +1,94 @@ -using System; +using System; using System.IO; using SharpCompress.Readers; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public class EntryStream : Stream { - public class EntryStream : Stream + private readonly IReader _reader; + private readonly Stream _stream; + private bool _completed; + private bool _isDisposed; + + internal EntryStream(IReader reader, Stream stream) { - private readonly IReader _reader; - private readonly Stream _stream; - private bool _completed; - private bool _isDisposed; + _reader = reader; + _stream = stream; + } - internal EntryStream(IReader reader, Stream stream) + /// + /// When reading a stream from OpenEntryStream, the stream must be completed so use this to finish reading the entire entry. + /// + public void SkipEntry() + { + this.Skip(); + _completed = true; + } + + protected override void Dispose(bool disposing) + { + if (!(_completed || _reader.Cancelled)) { - _reader = reader; - _stream = stream; + SkipEntry(); } - - /// - /// When reading a stream from OpenEntryStream, the stream must be completed so use this to finish reading the entire entry. - /// - public void SkipEntry() + if (_isDisposed) + { + return; + } + _isDisposed = true; + base.Dispose(disposing); + _stream.Dispose(); + } + + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override void Flush() { } + + public override long Length => _stream.Length; + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + var read = _stream.Read(buffer, offset, count); + if (read <= 0) { - this.Skip(); _completed = true; } + return read; + } - protected override void Dispose(bool disposing) + public override int ReadByte() + { + var value = _stream.ReadByte(); + if (value == -1) { - if (!(_completed || _reader.Cancelled)) - { - SkipEntry(); - } - if (_isDisposed) - { - return; - } - _isDisposed = true; - base.Dispose(disposing); - _stream.Dispose(); + _completed = true; } + return value; + } - public override bool CanRead => true; + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } - public override bool CanSeek => false; + public override void SetLength(long value) + { + throw new NotSupportedException(); + } - public override bool CanWrite => false; - - public override void Flush() { } - - public override long Length => _stream.Length; - - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - int read = _stream.Read(buffer, offset, count); - if (read <= 0) - { - _completed = true; - } - return read; - } - - public override int ReadByte() - { - int value = _stream.ReadByte(); - if (value == -1) - { - _completed = true; - } - return value; - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); } } diff --git a/src/SharpCompress/Common/ExtractionException.cs b/src/SharpCompress/Common/ExtractionException.cs index 51f9bc1e..44316b73 100644 --- a/src/SharpCompress/Common/ExtractionException.cs +++ b/src/SharpCompress/Common/ExtractionException.cs @@ -1,11 +1,10 @@ -using System; +using System; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public class ExtractionException : Exception { - public class ExtractionException : Exception - { - public ExtractionException(string message) : base(message) { } + public ExtractionException(string message) : base(message) { } - public ExtractionException(string message, Exception inner) : base(message, inner) { } - } + public ExtractionException(string message, Exception inner) : base(message, inner) { } } diff --git a/src/SharpCompress/Common/ExtractionMethods.cs b/src/SharpCompress/Common/ExtractionMethods.cs index 7a6cb9db..390a409b 100644 --- a/src/SharpCompress/Common/ExtractionMethods.cs +++ b/src/SharpCompress/Common/ExtractionMethods.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; namespace SharpCompress.Common diff --git a/src/SharpCompress/Common/ExtractionOptions.cs b/src/SharpCompress/Common/ExtractionOptions.cs index 650708cc..18c0f0ac 100644 --- a/src/SharpCompress/Common/ExtractionOptions.cs +++ b/src/SharpCompress/Common/ExtractionOptions.cs @@ -1,41 +1,40 @@ -using System; +using System; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public class ExtractionOptions { - public class ExtractionOptions + /// + /// overwrite target if it exists + /// + public bool Overwrite { get; set; } + + /// + /// extract with internal directory structure + /// + public bool ExtractFullPath { get; set; } + + /// + /// preserve file time + /// + public bool PreserveFileTime { get; set; } + + /// + /// preserve windows file attributes + /// + public bool PreserveAttributes { get; set; } + + /// + /// Delegate for writing symbolic links to disk. + /// sourcePath is where the symlink is created. + /// targetPath is what the symlink refers to. + /// + public delegate void SymbolicLinkWriterDelegate(string sourcePath, string targetPath); + + public SymbolicLinkWriterDelegate WriteSymbolicLink = (sourcePath, targetPath) => { - /// - /// overwrite target if it exists - /// - public bool Overwrite { get; set; } - - /// - /// extract with internal directory structure - /// - public bool ExtractFullPath { get; set; } - - /// - /// preserve file time - /// - public bool PreserveFileTime { get; set; } - - /// - /// preserve windows file attributes - /// - public bool PreserveAttributes { get; set; } - - /// - /// Delegate for writing symbolic links to disk. - /// sourcePath is where the symlink is created. - /// targetPath is what the symlink refers to. - /// - public delegate void SymbolicLinkWriterDelegate(string sourcePath, string targetPath); - - public SymbolicLinkWriterDelegate WriteSymbolicLink = (sourcePath, targetPath) => - { - Console.WriteLine( - $"Could not write symlink {sourcePath} -> {targetPath}, for more information please see https://github.com/dotnet/runtime/issues/24271" - ); - }; - } + Console.WriteLine( + $"Could not write symlink {sourcePath} -> {targetPath}, for more information please see https://github.com/dotnet/runtime/issues/24271" + ); + }; } diff --git a/src/SharpCompress/Common/FilePart.cs b/src/SharpCompress/Common/FilePart.cs index fad184aa..e196618f 100644 --- a/src/SharpCompress/Common/FilePart.cs +++ b/src/SharpCompress/Common/FilePart.cs @@ -1,21 +1,20 @@ using System.IO; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public abstract class FilePart { - public abstract class FilePart + protected FilePart(ArchiveEncoding archiveEncoding) { - protected FilePart(ArchiveEncoding archiveEncoding) - { - ArchiveEncoding = archiveEncoding; - } - - internal ArchiveEncoding ArchiveEncoding { get; } - - internal abstract string FilePartName { get; } - public int Index { get; set; } - - internal abstract Stream GetCompressedStream(); - internal abstract Stream? GetRawStream(); - internal bool Skipped { get; set; } + ArchiveEncoding = archiveEncoding; } + + internal ArchiveEncoding ArchiveEncoding { get; } + + internal abstract string FilePartName { get; } + public int Index { get; set; } + + internal abstract Stream GetCompressedStream(); + internal abstract Stream? GetRawStream(); + internal bool Skipped { get; set; } } diff --git a/src/SharpCompress/Common/FilePartExtractionBeginEventArgs.cs b/src/SharpCompress/Common/FilePartExtractionBeginEventArgs.cs index c05ff46e..d5b8328c 100644 --- a/src/SharpCompress/Common/FilePartExtractionBeginEventArgs.cs +++ b/src/SharpCompress/Common/FilePartExtractionBeginEventArgs.cs @@ -1,29 +1,28 @@ -using System; +using System; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public sealed class FilePartExtractionBeginEventArgs : EventArgs { - public sealed class FilePartExtractionBeginEventArgs : EventArgs + public FilePartExtractionBeginEventArgs(string name, long size, long compressedSize) { - public FilePartExtractionBeginEventArgs(string name, long size, long compressedSize) - { - Name = name; - Size = size; - CompressedSize = compressedSize; - } - - /// - /// File name for the part for the current entry - /// - public string Name { get; } - - /// - /// Uncompressed size of the current entry in the part - /// - public long Size { get; } - - /// - /// Compressed size of the current entry in the part - /// - public long CompressedSize { get; } + Name = name; + Size = size; + CompressedSize = compressedSize; } + + /// + /// File name for the part for the current entry + /// + public string Name { get; } + + /// + /// Uncompressed size of the current entry in the part + /// + public long Size { get; } + + /// + /// Compressed size of the current entry in the part + /// + public long CompressedSize { get; } } diff --git a/src/SharpCompress/Common/FlagUtility.cs b/src/SharpCompress/Common/FlagUtility.cs index 67aa1428..63cad03b 100644 --- a/src/SharpCompress/Common/FlagUtility.cs +++ b/src/SharpCompress/Common/FlagUtility.cs @@ -1,104 +1,103 @@ -using System; +using System; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +internal static class FlagUtility { - internal static class FlagUtility + /// + /// Returns true if the flag is set on the specified bit field. + /// Currently only works with 32-bit bitfields. + /// + /// Enumeration with Flags attribute + /// Flagged variable + /// Flag to test + /// + public static bool HasFlag(long bitField, T flag) where T : struct { - /// - /// Returns true if the flag is set on the specified bit field. - /// Currently only works with 32-bit bitfields. - /// - /// Enumeration with Flags attribute - /// Flagged variable - /// Flag to test - /// - public static bool HasFlag(long bitField, T flag) where T : struct - { - return HasFlag(bitField, flag); - } + return HasFlag(bitField, flag); + } - /// - /// Returns true if the flag is set on the specified bit field. - /// Currently only works with 32-bit bitfields. - /// - /// Enumeration with Flags attribute - /// Flagged variable - /// Flag to test - /// - public static bool HasFlag(ulong bitField, T flag) where T : struct - { - return HasFlag(bitField, flag); - } + /// + /// Returns true if the flag is set on the specified bit field. + /// Currently only works with 32-bit bitfields. + /// + /// Enumeration with Flags attribute + /// Flagged variable + /// Flag to test + /// + public static bool HasFlag(ulong bitField, T flag) where T : struct + { + return HasFlag(bitField, flag); + } - /// - /// Returns true if the flag is set on the specified bit field. - /// Currently only works with 32-bit bitfields. - /// - /// Flagged variable - /// Flag to test - /// - public static bool HasFlag(ulong bitField, ulong flag) - { - return ((bitField & flag) == flag); - } + /// + /// Returns true if the flag is set on the specified bit field. + /// Currently only works with 32-bit bitfields. + /// + /// Flagged variable + /// Flag to test + /// + public static bool HasFlag(ulong bitField, ulong flag) + { + return ((bitField & flag) == flag); + } - public static bool HasFlag(short bitField, short flag) - { - return ((bitField & flag) == flag); - } + public static bool HasFlag(short bitField, short flag) + { + return ((bitField & flag) == flag); + } - /// - /// Returns true if the flag is set on the specified bit field. - /// Currently only works with 32-bit bitfields. - /// - /// Enumeration with Flags attribute - /// Flagged variable - /// Flag to test - /// - public static bool HasFlag(T bitField, T flag) where T : struct - { - return HasFlag(Convert.ToInt64(bitField), Convert.ToInt64(flag)); - } + /// + /// Returns true if the flag is set on the specified bit field. + /// Currently only works with 32-bit bitfields. + /// + /// Enumeration with Flags attribute + /// Flagged variable + /// Flag to test + /// + public static bool HasFlag(T bitField, T flag) where T : struct + { + return HasFlag(Convert.ToInt64(bitField), Convert.ToInt64(flag)); + } - /// - /// Returns true if the flag is set on the specified bit field. - /// Currently only works with 32-bit bitfields. - /// - /// Flagged variable - /// Flag to test - /// - public static bool HasFlag(long bitField, long flag) - { - return ((bitField & flag) == flag); - } + /// + /// Returns true if the flag is set on the specified bit field. + /// Currently only works with 32-bit bitfields. + /// + /// Flagged variable + /// Flag to test + /// + public static bool HasFlag(long bitField, long flag) + { + return ((bitField & flag) == flag); + } - /// - /// Sets a bit-field to either on or off for the specified flag. - /// - /// Flagged variable - /// Flag to change - /// bool - /// The flagged variable with the flag changed - public static long SetFlag(long bitField, long flag, bool on) + /// + /// Sets a bit-field to either on or off for the specified flag. + /// + /// Flagged variable + /// Flag to change + /// bool + /// The flagged variable with the flag changed + public static long SetFlag(long bitField, long flag, bool on) + { + if (on) { - if (on) - { - return bitField | flag; - } - return bitField & (~flag); + return bitField | flag; } + return bitField & (~flag); + } - /// - /// Sets a bit-field to either on or off for the specified flag. - /// - /// Enumeration with Flags attribute - /// Flagged variable - /// Flag to change - /// bool - /// The flagged variable with the flag changed - public static long SetFlag(T bitField, T flag, bool on) where T : struct - { - return SetFlag(Convert.ToInt64(bitField), Convert.ToInt64(flag), on); - } + /// + /// Sets a bit-field to either on or off for the specified flag. + /// + /// Enumeration with Flags attribute + /// Flagged variable + /// Flag to change + /// bool + /// The flagged variable with the flag changed + public static long SetFlag(T bitField, T flag, bool on) where T : struct + { + return SetFlag(Convert.ToInt64(bitField), Convert.ToInt64(flag), on); } } diff --git a/src/SharpCompress/Common/GZip/GZipEntry.cs b/src/SharpCompress/Common/GZip/GZipEntry.cs index 9da986ee..ec16c979 100644 --- a/src/SharpCompress/Common/GZip/GZipEntry.cs +++ b/src/SharpCompress/Common/GZip/GZipEntry.cs @@ -1,49 +1,48 @@ -using System; +using System; using System.Collections.Generic; using System.IO; -namespace SharpCompress.Common.GZip +namespace SharpCompress.Common.GZip; + +public class GZipEntry : Entry { - public class GZipEntry : Entry + private readonly GZipFilePart _filePart; + + internal GZipEntry(GZipFilePart filePart) { - private readonly GZipFilePart _filePart; + _filePart = filePart; + } - internal GZipEntry(GZipFilePart filePart) - { - _filePart = filePart; - } + public override CompressionType CompressionType => CompressionType.GZip; - public override CompressionType CompressionType => CompressionType.GZip; + public override long Crc => _filePart.Crc ?? 0; - public override long Crc => _filePart.Crc ?? 0; + public override string Key => _filePart.FilePartName; - public override string Key => _filePart.FilePartName; + public override string? LinkTarget => null; - public override string? LinkTarget => null; + public override long CompressedSize => 0; - public override long CompressedSize => 0; + public override long Size => _filePart.UncompressedSize ?? 0; - public override long Size => _filePart.UncompressedSize ?? 0; + public override DateTime? LastModifiedTime => _filePart.DateModified; - public override DateTime? LastModifiedTime => _filePart.DateModified; + public override DateTime? CreatedTime => null; - public override DateTime? CreatedTime => null; + public override DateTime? LastAccessedTime => null; - public override DateTime? LastAccessedTime => null; + public override DateTime? ArchivedTime => null; - public override DateTime? ArchivedTime => null; + public override bool IsEncrypted => false; - public override bool IsEncrypted => false; + public override bool IsDirectory => false; - public override bool IsDirectory => false; + public override bool IsSplitAfter => false; - public override bool IsSplitAfter => false; + internal override IEnumerable Parts => _filePart.AsEnumerable(); - internal override IEnumerable Parts => _filePart.AsEnumerable(); - - internal static IEnumerable GetEntries(Stream stream, OptionsBase options) - { - yield return new GZipEntry(new GZipFilePart(stream, options.ArchiveEncoding)); - } + internal static IEnumerable GetEntries(Stream stream, OptionsBase options) + { + yield return new GZipEntry(new GZipFilePart(stream, options.ArchiveEncoding)); } } diff --git a/src/SharpCompress/Common/GZip/GZipFilePart.cs b/src/SharpCompress/Common/GZip/GZipFilePart.cs index a5062267..e5e969f1 100644 --- a/src/SharpCompress/Common/GZip/GZipFilePart.cs +++ b/src/SharpCompress/Common/GZip/GZipFilePart.cs @@ -6,132 +6,131 @@ using SharpCompress.Common.Tar.Headers; using SharpCompress.Compressors; using SharpCompress.Compressors.Deflate; -namespace SharpCompress.Common.GZip +namespace SharpCompress.Common.GZip; + +internal sealed class GZipFilePart : FilePart { - internal sealed class GZipFilePart : FilePart + private string? _name; + private readonly Stream _stream; + + internal GZipFilePart(Stream stream, ArchiveEncoding archiveEncoding) + : base(archiveEncoding) { - private string? _name; - private readonly Stream _stream; - - internal GZipFilePart(Stream stream, ArchiveEncoding archiveEncoding) - : base(archiveEncoding) + _stream = stream; + ReadAndValidateGzipHeader(); + if (stream.CanSeek) { - _stream = stream; - ReadAndValidateGzipHeader(); - if (stream.CanSeek) - { - long position = stream.Position; - stream.Position = stream.Length - 8; - ReadTrailer(); - stream.Position = position; - } - EntryStartPosition = stream.Position; + var position = stream.Position; + stream.Position = stream.Length - 8; + ReadTrailer(); + stream.Position = position; + } + EntryStartPosition = stream.Position; + } + + internal long EntryStartPosition { get; } + + internal DateTime? DateModified { get; private set; } + internal uint? Crc { get; private set; } + internal uint? UncompressedSize { get; private set; } + + internal override string FilePartName => _name!; + + internal override Stream GetCompressedStream() + { + return new DeflateStream(_stream, CompressionMode.Decompress, CompressionLevel.Default); + } + + internal override Stream GetRawStream() + { + return _stream; + } + + private void ReadTrailer() + { + // Read and potentially verify the GZIP trailer: CRC32 and size mod 2^32 + Span trailer = stackalloc byte[8]; + var n = _stream.Read(trailer); + + Crc = BinaryPrimitives.ReadUInt32LittleEndian(trailer); + UncompressedSize = BinaryPrimitives.ReadUInt32LittleEndian(trailer.Slice(4)); + } + + private void ReadAndValidateGzipHeader() + { + // read the header on the first read + Span header = stackalloc byte[10]; + var n = _stream.Read(header); + + // workitem 8501: handle edge case (decompress empty stream) + if (n == 0) + { + return; } - internal long EntryStartPosition { get; } - - internal DateTime? DateModified { get; private set; } - internal uint? Crc { get; private set; } - internal uint? UncompressedSize { get; private set; } - - internal override string FilePartName => _name!; - - internal override Stream GetCompressedStream() + if (n != 10) { - return new DeflateStream(_stream, CompressionMode.Decompress, CompressionLevel.Default); + throw new ZlibException("Not a valid GZIP stream."); } - internal override Stream GetRawStream() + if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8) { - return _stream; + throw new ZlibException("Bad GZIP header."); } - private void ReadTrailer() + var timet = BinaryPrimitives.ReadInt32LittleEndian(header.Slice(4)); + DateModified = TarHeader.EPOCH.AddSeconds(timet); + if ((header[3] & 0x04) == 0x04) { - // Read and potentially verify the GZIP trailer: CRC32 and size mod 2^32 - Span trailer = stackalloc byte[8]; - int n = _stream.Read(trailer); + // read and discard extra field + n = _stream.Read(header.Slice(0, 2)); // 2-byte length field - Crc = BinaryPrimitives.ReadUInt32LittleEndian(trailer); - UncompressedSize = BinaryPrimitives.ReadUInt32LittleEndian(trailer.Slice(4)); + var extraLength = (short)(header[0] + (header[1] * 256)); + var extra = new byte[extraLength]; + + if (!_stream.ReadFully(extra)) + { + throw new ZlibException("Unexpected end-of-file reading GZIP header."); + } + n = extraLength; } - - private void ReadAndValidateGzipHeader() + if ((header[3] & 0x08) == 0x08) { - // read the header on the first read - Span header = stackalloc byte[10]; - int n = _stream.Read(header); - - // workitem 8501: handle edge case (decompress empty stream) - if (n == 0) - { - return; - } - - if (n != 10) - { - throw new ZlibException("Not a valid GZIP stream."); - } - - if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8) - { - throw new ZlibException("Bad GZIP header."); - } - - int timet = BinaryPrimitives.ReadInt32LittleEndian(header.Slice(4)); - DateModified = TarHeader.EPOCH.AddSeconds(timet); - if ((header[3] & 0x04) == 0x04) - { - // read and discard extra field - n = _stream.Read(header.Slice(0, 2)); // 2-byte length field - - short extraLength = (short)(header[0] + header[1] * 256); - byte[] extra = new byte[extraLength]; - - if (!_stream.ReadFully(extra)) - { - throw new ZlibException("Unexpected end-of-file reading GZIP header."); - } - n = extraLength; - } - if ((header[3] & 0x08) == 0x08) - { - _name = ReadZeroTerminatedString(_stream); - } - if ((header[3] & 0x10) == 0x010) - { - ReadZeroTerminatedString(_stream); - } - if ((header[3] & 0x02) == 0x02) - { - _stream.ReadByte(); // CRC16, ignore - } + _name = ReadZeroTerminatedString(_stream); } - - private string ReadZeroTerminatedString(Stream stream) + if ((header[3] & 0x10) == 0x010) { - Span buf1 = stackalloc byte[1]; - var list = new List(); - bool done = false; - do - { - // workitem 7740 - int n = stream.Read(buf1); - if (n != 1) - { - throw new ZlibException("Unexpected EOF reading GZIP header."); - } - if (buf1[0] == 0) - { - done = true; - } - else - { - list.Add(buf1[0]); - } - } while (!done); - byte[] buffer = list.ToArray(); - return ArchiveEncoding.Decode(buffer); + ReadZeroTerminatedString(_stream); + } + if ((header[3] & 0x02) == 0x02) + { + _stream.ReadByte(); // CRC16, ignore } } + + private string ReadZeroTerminatedString(Stream stream) + { + Span buf1 = stackalloc byte[1]; + var list = new List(); + var done = false; + do + { + // workitem 7740 + var n = stream.Read(buf1); + if (n != 1) + { + throw new ZlibException("Unexpected EOF reading GZIP header."); + } + if (buf1[0] == 0) + { + done = true; + } + else + { + list.Add(buf1[0]); + } + } while (!done); + var buffer = list.ToArray(); + return ArchiveEncoding.Decode(buffer); + } } diff --git a/src/SharpCompress/Common/GZip/GZipVolume.cs b/src/SharpCompress/Common/GZip/GZipVolume.cs index 10e0574d..cc2ec364 100644 --- a/src/SharpCompress/Common/GZip/GZipVolume.cs +++ b/src/SharpCompress/Common/GZip/GZipVolume.cs @@ -1,21 +1,20 @@ using System.IO; using SharpCompress.Readers; -namespace SharpCompress.Common.GZip +namespace SharpCompress.Common.GZip; + +public class GZipVolume : Volume { - public class GZipVolume : Volume + public GZipVolume(Stream stream, ReaderOptions options, int index = 0) + : base(stream, options, index) { } + + public GZipVolume(FileInfo fileInfo, ReaderOptions options) + : base(fileInfo.OpenRead(), options) { - public GZipVolume(Stream stream, ReaderOptions options, int index = 0) - : base(stream, options, index) { } - - public GZipVolume(FileInfo fileInfo, ReaderOptions options) - : base(fileInfo.OpenRead(), options) - { - options.LeaveStreamOpen = false; - } - - public override bool IsFirstVolume => true; - - public override bool IsMultiVolume => true; + options.LeaveStreamOpen = false; } + + public override bool IsFirstVolume => true; + + public override bool IsMultiVolume => true; } diff --git a/src/SharpCompress/Common/IEntry.Extensions.cs b/src/SharpCompress/Common/IEntry.Extensions.cs index fdf321bd..7e9b79a3 100644 --- a/src/SharpCompress/Common/IEntry.Extensions.cs +++ b/src/SharpCompress/Common/IEntry.Extensions.cs @@ -1,49 +1,48 @@ -using System.IO; +using System.IO; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +internal static class EntryExtensions { - internal static class EntryExtensions + internal static void PreserveExtractionOptions( + this IEntry entry, + string destinationFileName, + ExtractionOptions options + ) { - internal static void PreserveExtractionOptions( - this IEntry entry, - string destinationFileName, - ExtractionOptions options - ) + if (options.PreserveFileTime || options.PreserveAttributes) { - if (options.PreserveFileTime || options.PreserveAttributes) + var nf = new FileInfo(destinationFileName); + if (!nf.Exists) { - FileInfo nf = new FileInfo(destinationFileName); - if (!nf.Exists) + return; + } + + // update file time to original packed time + if (options.PreserveFileTime) + { + if (entry.CreatedTime.HasValue) { - return; + nf.CreationTime = entry.CreatedTime.Value; } - // update file time to original packed time - if (options.PreserveFileTime) + if (entry.LastModifiedTime.HasValue) { - if (entry.CreatedTime.HasValue) - { - nf.CreationTime = entry.CreatedTime.Value; - } - - if (entry.LastModifiedTime.HasValue) - { - nf.LastWriteTime = entry.LastModifiedTime.Value; - } - - if (entry.LastAccessedTime.HasValue) - { - nf.LastAccessTime = entry.LastAccessedTime.Value; - } + nf.LastWriteTime = entry.LastModifiedTime.Value; } - if (options.PreserveAttributes) + if (entry.LastAccessedTime.HasValue) { - if (entry.Attrib.HasValue) - { - nf.Attributes = (FileAttributes) - System.Enum.ToObject(typeof(FileAttributes), entry.Attrib.Value); - } + nf.LastAccessTime = entry.LastAccessedTime.Value; + } + } + + if (options.PreserveAttributes) + { + if (entry.Attrib.HasValue) + { + nf.Attributes = (FileAttributes) + System.Enum.ToObject(typeof(FileAttributes), entry.Attrib.Value); } } } diff --git a/src/SharpCompress/Common/IEntry.cs b/src/SharpCompress/Common/IEntry.cs index 63e8f9ca..df1fa603 100644 --- a/src/SharpCompress/Common/IEntry.cs +++ b/src/SharpCompress/Common/IEntry.cs @@ -1,26 +1,24 @@ using System; -using System.Collections.Generic; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public interface IEntry { - public interface IEntry - { - CompressionType CompressionType { get; } - DateTime? ArchivedTime { get; } - long CompressedSize { get; } - long Crc { get; } - DateTime? CreatedTime { get; } - string Key { get; } - string? LinkTarget { get; } - bool IsDirectory { get; } - bool IsEncrypted { get; } - bool IsSplitAfter { get; } - bool IsSolid { get; } - int VolumeIndexFirst { get; } - int VolumeIndexLast { get; } - DateTime? LastAccessedTime { get; } - DateTime? LastModifiedTime { get; } - long Size { get; } - int? Attrib { get; } - } + CompressionType CompressionType { get; } + DateTime? ArchivedTime { get; } + long CompressedSize { get; } + long Crc { get; } + DateTime? CreatedTime { get; } + string Key { get; } + string? LinkTarget { get; } + bool IsDirectory { get; } + bool IsEncrypted { get; } + bool IsSplitAfter { get; } + bool IsSolid { get; } + int VolumeIndexFirst { get; } + int VolumeIndexLast { get; } + DateTime? LastAccessedTime { get; } + DateTime? LastModifiedTime { get; } + long Size { get; } + int? Attrib { get; } } diff --git a/src/SharpCompress/Common/IExtractionListener.cs b/src/SharpCompress/Common/IExtractionListener.cs index 3ec0abfb..e1389810 100644 --- a/src/SharpCompress/Common/IExtractionListener.cs +++ b/src/SharpCompress/Common/IExtractionListener.cs @@ -1,8 +1,7 @@ -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public interface IExtractionListener { - public interface IExtractionListener - { - void FireFilePartExtractionBegin(string name, long size, long compressedSize); - void FireCompressedBytesRead(long currentPartCompressedBytes, long compressedReadBytes); - } + void FireFilePartExtractionBegin(string name, long size, long compressedSize); + void FireCompressedBytesRead(long currentPartCompressedBytes, long compressedReadBytes); } diff --git a/src/SharpCompress/Common/IVolume.cs b/src/SharpCompress/Common/IVolume.cs index b29d6be9..abbc0406 100644 --- a/src/SharpCompress/Common/IVolume.cs +++ b/src/SharpCompress/Common/IVolume.cs @@ -1,11 +1,10 @@ using System; -namespace SharpCompress.Common -{ - public interface IVolume : IDisposable - { - int Index { get; } +namespace SharpCompress.Common; - string FileName { get; } - } +public interface IVolume : IDisposable +{ + int Index { get; } + + string FileName { get; } } diff --git a/src/SharpCompress/Common/IncompleteArchiveException.cs b/src/SharpCompress/Common/IncompleteArchiveException.cs index aca34349..f1ce3641 100644 --- a/src/SharpCompress/Common/IncompleteArchiveException.cs +++ b/src/SharpCompress/Common/IncompleteArchiveException.cs @@ -1,7 +1,6 @@ -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public class IncompleteArchiveException : ArchiveException { - public class IncompleteArchiveException : ArchiveException - { - public IncompleteArchiveException(string message) : base(message) { } - } + public IncompleteArchiveException(string message) : base(message) { } } diff --git a/src/SharpCompress/Common/InvalidFormatException.cs b/src/SharpCompress/Common/InvalidFormatException.cs index ffb16003..b9501369 100644 --- a/src/SharpCompress/Common/InvalidFormatException.cs +++ b/src/SharpCompress/Common/InvalidFormatException.cs @@ -1,11 +1,10 @@ -using System; +using System; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public class InvalidFormatException : ExtractionException { - public class InvalidFormatException : ExtractionException - { - public InvalidFormatException(string message) : base(message) { } + public InvalidFormatException(string message) : base(message) { } - public InvalidFormatException(string message, Exception inner) : base(message, inner) { } - } + public InvalidFormatException(string message, Exception inner) : base(message, inner) { } } diff --git a/src/SharpCompress/Common/MultiVolumeExtractionException.cs b/src/SharpCompress/Common/MultiVolumeExtractionException.cs index 7e6da358..7e6f14e3 100644 --- a/src/SharpCompress/Common/MultiVolumeExtractionException.cs +++ b/src/SharpCompress/Common/MultiVolumeExtractionException.cs @@ -1,12 +1,11 @@ -using System; +using System; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public class MultiVolumeExtractionException : ExtractionException { - public class MultiVolumeExtractionException : ExtractionException - { - public MultiVolumeExtractionException(string message) : base(message) { } + public MultiVolumeExtractionException(string message) : base(message) { } - public MultiVolumeExtractionException(string message, Exception inner) - : base(message, inner) { } - } + public MultiVolumeExtractionException(string message, Exception inner) + : base(message, inner) { } } diff --git a/src/SharpCompress/Common/MultipartStreamRequiredException.cs b/src/SharpCompress/Common/MultipartStreamRequiredException.cs index 84b673ef..bbe3ae93 100644 --- a/src/SharpCompress/Common/MultipartStreamRequiredException.cs +++ b/src/SharpCompress/Common/MultipartStreamRequiredException.cs @@ -1,7 +1,6 @@ -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public class MultipartStreamRequiredException : ExtractionException { - public class MultipartStreamRequiredException : ExtractionException - { - public MultipartStreamRequiredException(string message) : base(message) { } - } + public MultipartStreamRequiredException(string message) : base(message) { } } diff --git a/src/SharpCompress/Common/OptionsBase.cs b/src/SharpCompress/Common/OptionsBase.cs index 6591f975..61a6cb55 100644 --- a/src/SharpCompress/Common/OptionsBase.cs +++ b/src/SharpCompress/Common/OptionsBase.cs @@ -1,12 +1,11 @@ -namespace SharpCompress.Common -{ - public class OptionsBase - { - /// - /// SharpCompress will keep the supplied streams open. Default is true. - /// - public bool LeaveStreamOpen { get; set; } = true; +namespace SharpCompress.Common; - public ArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding(); - } +public class OptionsBase +{ + /// + /// SharpCompress will keep the supplied streams open. Default is true. + /// + public bool LeaveStreamOpen { get; set; } = true; + + public ArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding(); } diff --git a/src/SharpCompress/Common/PasswordProtectedException.cs b/src/SharpCompress/Common/PasswordProtectedException.cs index ade0a55d..ab5317c2 100644 --- a/src/SharpCompress/Common/PasswordProtectedException.cs +++ b/src/SharpCompress/Common/PasswordProtectedException.cs @@ -1,12 +1,11 @@ -using System; +using System; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public class PasswordProtectedException : ExtractionException { - public class PasswordProtectedException : ExtractionException - { - public PasswordProtectedException(string message) : base(message) { } + public PasswordProtectedException(string message) : base(message) { } - public PasswordProtectedException(string message, Exception inner) : base(message, inner) - { } - } + public PasswordProtectedException(string message, Exception inner) : base(message, inner) + { } } diff --git a/src/SharpCompress/Common/Rar/Headers/AVHeader.cs b/src/SharpCompress/Common/Rar/Headers/AVHeader.cs index b235e5ce..cd51d80b 100644 --- a/src/SharpCompress/Common/Rar/Headers/AVHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/AVHeader.cs @@ -1,32 +1,31 @@ -using SharpCompress.IO; +using SharpCompress.IO; -namespace SharpCompress.Common.Rar.Headers +namespace SharpCompress.Common.Rar.Headers; + +internal class AvHeader : RarHeader { - internal class AvHeader : RarHeader + public AvHeader(RarHeader header, RarCrcBinaryReader reader) + : base(header, reader, HeaderType.Av) { - public AvHeader(RarHeader header, RarCrcBinaryReader reader) - : base(header, reader, HeaderType.Av) + if (IsRar5) { - if (IsRar5) - { - throw new InvalidFormatException("unexpected rar5 record"); - } + throw new InvalidFormatException("unexpected rar5 record"); } - - protected override void ReadFinish(MarkingBinaryReader reader) - { - UnpackVersion = reader.ReadByte(); - Method = reader.ReadByte(); - AvVersion = reader.ReadByte(); - AvInfoCrc = reader.ReadInt32(); - } - - internal int AvInfoCrc { get; private set; } - - internal byte UnpackVersion { get; private set; } - - internal byte Method { get; private set; } - - internal byte AvVersion { get; private set; } } + + protected override void ReadFinish(MarkingBinaryReader reader) + { + UnpackVersion = reader.ReadByte(); + Method = reader.ReadByte(); + AvVersion = reader.ReadByte(); + AvInfoCrc = reader.ReadInt32(); + } + + internal int AvInfoCrc { get; private set; } + + internal byte UnpackVersion { get; private set; } + + internal byte Method { get; private set; } + + internal byte AvVersion { get; private set; } } diff --git a/src/SharpCompress/Common/Rar/Headers/ArchiveCryptHeader.cs b/src/SharpCompress/Common/Rar/Headers/ArchiveCryptHeader.cs index 56784c52..64edb8c4 100644 --- a/src/SharpCompress/Common/Rar/Headers/ArchiveCryptHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ArchiveCryptHeader.cs @@ -1,57 +1,53 @@ -#nullable disable +#nullable disable using SharpCompress.IO; -namespace SharpCompress.Common.Rar.Headers +namespace SharpCompress.Common.Rar.Headers; + +internal class ArchiveCryptHeader : RarHeader { - internal class ArchiveCryptHeader : RarHeader + private const int CRYPT_VERSION = 0; // Supported encryption version. + private const int SIZE_SALT50 = 16; + private const int SIZE_PSWCHECK = 8; + private const int SIZE_PSWCHECK_CSUM = 4; + private const int CRYPT5_KDF_LG2_COUNT_MAX = 24; // LOG2 of maximum accepted iteration count. + + private bool _usePswCheck; + private uint _lg2Count; // Log2 of PBKDF2 repetition count. + private byte[] _salt; + private byte[] _pswCheck; + private byte[] _pswCheckCsm; + + public ArchiveCryptHeader(RarHeader header, RarCrcBinaryReader reader) + : base(header, reader, HeaderType.Crypt) { } + + protected override void ReadFinish(MarkingBinaryReader reader) { - private const int CRYPT_VERSION = 0; // Supported encryption version. - private const int SIZE_SALT50 = 16; - private const int SIZE_SALT30 = 8; - private const int SIZE_INITV = 16; - private const int SIZE_PSWCHECK = 8; - private const int SIZE_PSWCHECK_CSUM = 4; - private const int CRYPT5_KDF_LG2_COUNT = 15; // LOG2 of PDKDF2 iteration count. - private const int CRYPT5_KDF_LG2_COUNT_MAX = 24; // LOG2 of maximum accepted iteration count. - - private bool _usePswCheck; - private uint _lg2Count; // Log2 of PBKDF2 repetition count. - private byte[] _salt; - private byte[] _pswCheck; - private byte[] _pswCheckCsm; - - public ArchiveCryptHeader(RarHeader header, RarCrcBinaryReader reader) - : base(header, reader, HeaderType.Crypt) { } - - protected override void ReadFinish(MarkingBinaryReader reader) + var cryptVersion = reader.ReadRarVIntUInt32(); + if (cryptVersion > CRYPT_VERSION) { - var cryptVersion = reader.ReadRarVIntUInt32(); - if (cryptVersion > CRYPT_VERSION) - { - //error? - return; - } - var encryptionFlags = reader.ReadRarVIntUInt32(); - _usePswCheck = FlagUtility.HasFlag( - encryptionFlags, - EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK - ); - _lg2Count = reader.ReadRarVIntByte(1); + //error? + return; + } + var encryptionFlags = reader.ReadRarVIntUInt32(); + _usePswCheck = FlagUtility.HasFlag( + encryptionFlags, + EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK + ); + _lg2Count = reader.ReadRarVIntByte(1); - //UsePswCheck = HasHeaderFlag(EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK); - if (_lg2Count > CRYPT5_KDF_LG2_COUNT_MAX) - { - //error? - return; - } + //UsePswCheck = HasHeaderFlag(EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK); + if (_lg2Count > CRYPT5_KDF_LG2_COUNT_MAX) + { + //error? + return; + } - _salt = reader.ReadBytes(SIZE_SALT50); - if (_usePswCheck) - { - _pswCheck = reader.ReadBytes(SIZE_PSWCHECK); - _pswCheckCsm = reader.ReadBytes(SIZE_PSWCHECK_CSUM); - } + _salt = reader.ReadBytes(SIZE_SALT50); + if (_usePswCheck) + { + _pswCheck = reader.ReadBytes(SIZE_PSWCHECK); + _pswCheckCsm = reader.ReadBytes(SIZE_PSWCHECK_CSUM); } } } diff --git a/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs b/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs index 33bc6e9a..c46ffa84 100644 --- a/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs @@ -1,87 +1,62 @@ using SharpCompress.IO; -namespace SharpCompress.Common.Rar.Headers +namespace SharpCompress.Common.Rar.Headers; + +internal sealed class ArchiveHeader : RarHeader { - internal sealed class ArchiveHeader : RarHeader + public ArchiveHeader(RarHeader header, RarCrcBinaryReader reader) + : base(header, reader, HeaderType.Archive) { } + + protected override void ReadFinish(MarkingBinaryReader reader) { - public ArchiveHeader(RarHeader header, RarCrcBinaryReader reader) - : base(header, reader, HeaderType.Archive) { } - - protected override void ReadFinish(MarkingBinaryReader reader) + if (IsRar5) { - if (IsRar5) + Flags = reader.ReadRarVIntUInt16(); + if (HasFlag(ArchiveFlagsV5.HAS_VOLUME_NUMBER)) { - Flags = reader.ReadRarVIntUInt16(); - if (HasFlag(ArchiveFlagsV5.HAS_VOLUME_NUMBER)) - { - VolumeNumber = (int)reader.ReadRarVIntUInt32(); - } - // later: we may have a locator record if we need it - //if (ExtraSize != 0) { - // ReadLocator(reader); - //} + VolumeNumber = (int)reader.ReadRarVIntUInt32(); } - else + // later: we may have a locator record if we need it + //if (ExtraSize != 0) { + // ReadLocator(reader); + //} + } + else + { + Flags = HeaderFlags; + HighPosAv = reader.ReadInt16(); + PosAv = reader.ReadInt32(); + if (HasFlag(ArchiveFlagsV4.ENCRYPT_VER)) { - Flags = HeaderFlags; - HighPosAv = reader.ReadInt16(); - PosAv = reader.ReadInt32(); - if (HasFlag(ArchiveFlagsV4.ENCRYPT_VER)) - { - EncryptionVersion = reader.ReadByte(); - } + EncryptionVersion = reader.ReadByte(); } } - - private void ReadLocator(MarkingBinaryReader reader) - { - var size = reader.ReadRarVIntUInt16(); - var type = reader.ReadRarVIntUInt16(); - if (type != 1) - { - throw new InvalidFormatException("expected locator record"); - } - - var flags = reader.ReadRarVIntUInt16(); - const ushort hasQuickOpenOffset = 0x01; - const ushort hasRecoveryOffset = 0x02; - ulong quickOpenOffset = 0; - if ((flags & hasQuickOpenOffset) == hasQuickOpenOffset) - { - quickOpenOffset = reader.ReadRarVInt(); - } - ulong recoveryOffset = 0; - if ((flags & hasRecoveryOffset) == hasRecoveryOffset) - { - recoveryOffset = reader.ReadRarVInt(); - } - } - - private ushort Flags { get; set; } - - private bool HasFlag(ushort flag) - { - return (Flags & flag) == flag; - } - - internal int? VolumeNumber { get; private set; } - - internal short? HighPosAv { get; private set; } - - internal int? PosAv { get; private set; } - - private byte? EncryptionVersion { get; set; } - - public bool? IsEncrypted => IsRar5 ? (bool?)null : HasFlag(ArchiveFlagsV4.PASSWORD); - - public bool OldNumberingFormat => !IsRar5 && !HasFlag(ArchiveFlagsV4.NEW_NUMBERING); - - public bool IsVolume => HasFlag(IsRar5 ? ArchiveFlagsV5.VOLUME : ArchiveFlagsV4.VOLUME); - - // RAR5: Volume number field is present. True for all volumes except first. - public bool IsFirstVolume => - IsRar5 ? VolumeNumber is null : HasFlag(ArchiveFlagsV4.FIRST_VOLUME); - - public bool IsSolid => HasFlag(IsRar5 ? ArchiveFlagsV5.SOLID : ArchiveFlagsV4.SOLID); } + + private ushort Flags { get; set; } + + private bool HasFlag(ushort flag) + { + return (Flags & flag) == flag; + } + + internal int? VolumeNumber { get; private set; } + + internal short? HighPosAv { get; private set; } + + internal int? PosAv { get; private set; } + + private byte? EncryptionVersion { get; set; } + + public bool? IsEncrypted => IsRar5 ? null : HasFlag(ArchiveFlagsV4.PASSWORD); + + public bool OldNumberingFormat => !IsRar5 && !HasFlag(ArchiveFlagsV4.NEW_NUMBERING); + + public bool IsVolume => HasFlag(IsRar5 ? ArchiveFlagsV5.VOLUME : ArchiveFlagsV4.VOLUME); + + // RAR5: Volume number field is present. True for all volumes except first. + public bool IsFirstVolume => + IsRar5 ? VolumeNumber is null : HasFlag(ArchiveFlagsV4.FIRST_VOLUME); + + public bool IsSolid => HasFlag(IsRar5 ? ArchiveFlagsV5.SOLID : ArchiveFlagsV4.SOLID); } diff --git a/src/SharpCompress/Common/Rar/Headers/CommentHeader.cs b/src/SharpCompress/Common/Rar/Headers/CommentHeader.cs index 1742aabe..54abf9cc 100644 --- a/src/SharpCompress/Common/Rar/Headers/CommentHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/CommentHeader.cs @@ -1,31 +1,30 @@ -using SharpCompress.IO; +using SharpCompress.IO; -namespace SharpCompress.Common.Rar.Headers +namespace SharpCompress.Common.Rar.Headers; + +internal class CommentHeader : RarHeader { - internal class CommentHeader : RarHeader + protected CommentHeader(RarHeader header, RarCrcBinaryReader reader) + : base(header, reader, HeaderType.Comment) { - protected CommentHeader(RarHeader header, RarCrcBinaryReader reader) - : base(header, reader, HeaderType.Comment) + if (IsRar5) { - if (IsRar5) - { - throw new InvalidFormatException("unexpected rar5 record"); - } + throw new InvalidFormatException("unexpected rar5 record"); } - - protected override void ReadFinish(MarkingBinaryReader reader) - { - UnpSize = reader.ReadInt16(); - UnpVersion = reader.ReadByte(); - UnpMethod = reader.ReadByte(); - CommCrc = reader.ReadInt16(); - } - - internal short UnpSize { get; private set; } - - internal byte UnpVersion { get; private set; } - - internal byte UnpMethod { get; private set; } - internal short CommCrc { get; private set; } } + + protected override void ReadFinish(MarkingBinaryReader reader) + { + UnpSize = reader.ReadInt16(); + UnpVersion = reader.ReadByte(); + UnpMethod = reader.ReadByte(); + CommCrc = reader.ReadInt16(); + } + + internal short UnpSize { get; private set; } + + internal byte UnpVersion { get; private set; } + + internal byte UnpMethod { get; private set; } + internal short CommCrc { get; private set; } } diff --git a/src/SharpCompress/Common/Rar/Headers/EndArchiveHeader.cs b/src/SharpCompress/Common/Rar/Headers/EndArchiveHeader.cs index bff19b76..10c79850 100644 --- a/src/SharpCompress/Common/Rar/Headers/EndArchiveHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/EndArchiveHeader.cs @@ -1,41 +1,40 @@ -using SharpCompress.IO; +using SharpCompress.IO; -namespace SharpCompress.Common.Rar.Headers +namespace SharpCompress.Common.Rar.Headers; + +internal class EndArchiveHeader : RarHeader { - internal class EndArchiveHeader : RarHeader + public EndArchiveHeader(RarHeader header, RarCrcBinaryReader reader) + : base(header, reader, HeaderType.EndArchive) { } + + protected override void ReadFinish(MarkingBinaryReader reader) { - public EndArchiveHeader(RarHeader header, RarCrcBinaryReader reader) - : base(header, reader, HeaderType.EndArchive) { } - - protected override void ReadFinish(MarkingBinaryReader reader) + if (IsRar5) { - if (IsRar5) + Flags = reader.ReadRarVIntUInt16(); + } + else + { + Flags = HeaderFlags; + if (HasFlag(EndArchiveFlagsV4.DATA_CRC)) { - Flags = reader.ReadRarVIntUInt16(); + ArchiveCrc = reader.ReadInt32(); } - else + if (HasFlag(EndArchiveFlagsV4.VOLUME_NUMBER)) { - Flags = HeaderFlags; - if (HasFlag(EndArchiveFlagsV4.DATA_CRC)) - { - ArchiveCrc = reader.ReadInt32(); - } - if (HasFlag(EndArchiveFlagsV4.VOLUME_NUMBER)) - { - VolumeNumber = reader.ReadInt16(); - } + VolumeNumber = reader.ReadInt16(); } } - - private ushort Flags { get; set; } - - private bool HasFlag(ushort flag) - { - return (Flags & flag) == flag; - } - - internal int? ArchiveCrc { get; private set; } - - internal short? VolumeNumber { get; private set; } } + + private ushort Flags { get; set; } + + private bool HasFlag(ushort flag) + { + return (Flags & flag) == flag; + } + + internal int? ArchiveCrc { get; private set; } + + internal short? VolumeNumber { get; private set; } } diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs index 8ef40ec5..66a8a78b 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs @@ -1,8 +1,6 @@ #nullable disable #if !Rar2017_64bit -using nint = System.Int32; -using nuint = System.UInt32; using size_t = System.UInt32; #else using nint = System.Int64; @@ -15,458 +13,457 @@ using System; using System.IO; using System.Text; -namespace SharpCompress.Common.Rar.Headers +namespace SharpCompress.Common.Rar.Headers; + +internal class FileHeader : RarHeader { - internal class FileHeader : RarHeader + private uint _fileCrc; + + public FileHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType) + : base(header, reader, headerType) { } + + protected override void ReadFinish(MarkingBinaryReader reader) { - private uint _fileCrc; - - public FileHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType) - : base(header, reader, headerType) { } - - protected override void ReadFinish(MarkingBinaryReader reader) + if (IsRar5) { - if (IsRar5) - { - ReadFromReaderV5(reader); - } - else - { - ReadFromReaderV4(reader); - } + ReadFromReaderV5(reader); } - - private void ReadFromReaderV5(MarkingBinaryReader reader) + else { - Flags = reader.ReadRarVIntUInt16(); - - var lvalue = checked((long)reader.ReadRarVInt()); - - // long.MaxValue causes the unpack code to finish when the input stream is exhausted - UncompressedSize = HasFlag(FileFlagsV5.UNPACKED_SIZE_UNKNOWN) ? long.MaxValue : lvalue; - - FileAttributes = reader.ReadRarVIntUInt32(); - - if (HasFlag(FileFlagsV5.HAS_MOD_TIME)) - { - FileLastModifiedTime = Utility.UnixTimeToDateTime(reader.ReadUInt32()); - } - - if (HasFlag(FileFlagsV5.HAS_CRC32)) - { - FileCrc = reader.ReadUInt32(); - } - - var compressionInfo = reader.ReadRarVIntUInt16(); - - // Lower 6 bits (0x003f mask) contain the version of compression algorithm, resulting in possible 0 - 63 values. Current version is 0. - // "+ 50" to not mix with old RAR format algorithms. For example, - // we may need to use the compression algorithm 15 in the future, - // but it was already used in RAR 1.5 and Unpack needs to distinguish - // them. - CompressionAlgorithm = (byte)((compressionInfo & 0x3f) + 50); - - // 7th bit (0x0040) defines the solid flag. If it is set, RAR continues to use the compression dictionary left after processing preceding files. - // It can be set only for file headers and is never set for service headers. - IsSolid = (compressionInfo & 0x40) == 0x40; - - // Bits 8 - 10 (0x0380 mask) define the compression method. Currently only values 0 - 5 are used. 0 means no compression. - CompressionMethod = (byte)((compressionInfo >> 7) & 0x7); - - // Bits 11 - 14 (0x3c00) define the minimum size of dictionary size required to extract data. Value 0 means 128 KB, 1 - 256 KB, ..., 14 - 2048 MB, 15 - 4096 MB. - WindowSize = IsDirectory ? 0 : ((size_t)0x20000) << ((compressionInfo >> 10) & 0xf); - - HostOs = reader.ReadRarVIntByte(); - - var nameSize = reader.ReadRarVIntUInt16(); - - // Variable length field containing Name length bytes in UTF-8 format without trailing zero. - // For file header this is a name of archived file. Forward slash character is used as the path separator both for Unix and Windows names. - // Backslashes are treated as a part of name for Unix names and as invalid character for Windows file names. Type of name is defined by Host OS field. - // - // TODO: not sure if anything needs to be done to handle the following: - // If Unix file name contains any high ASCII characters which cannot be correctly converted to Unicode and UTF-8 - // we map such characters to to 0xE080 - 0xE0FF private use Unicode area and insert 0xFFFE Unicode non-character - // to resulting string to indicate that it contains mapped characters, which need to be converted back when extracting. - // Concrete position of 0xFFFE is not defined, we need to search the entire string for it. Such mapped names are not - // portable and can be correctly unpacked only on the same system where they were created. - // - // For service header this field contains a name of service header. Now the following names are used: - // CMT Archive comment - // QO Archive quick open data - // ACL NTFS file permissions - // STM NTFS alternate data stream - // RR Recovery record - var b = reader.ReadBytes(nameSize); - FileName = ConvertPathV5(Encoding.UTF8.GetString(b, 0, b.Length)); - - // extra size seems to be redudant since we know the total header size - if (ExtraSize != RemainingHeaderBytes(reader)) - { - throw new InvalidFormatException("rar5 header size / extra size inconsistency"); - } - - isEncryptedRar5 = false; - - while (RemainingHeaderBytes(reader) > 0) - { - var size = reader.ReadRarVIntUInt16(); - int n = RemainingHeaderBytes(reader); - var type = reader.ReadRarVIntUInt16(); - switch (type) - { - //TODO - case 1: // file encryption - - { - isEncryptedRar5 = true; - - //var version = reader.ReadRarVIntByte(); - //if (version != 0) throw new InvalidFormatException("unknown encryption algorithm " + version); - } - break; - // case 2: // file hash - // { - // - // } - // break; - case 3: // file time - - { - ushort flags = reader.ReadRarVIntUInt16(); - var isWindowsTime = (flags & 1) == 0; - if ((flags & 0x2) == 0x2) - { - FileLastModifiedTime = ReadExtendedTimeV5(reader, isWindowsTime); - } - if ((flags & 0x4) == 0x4) - { - FileCreatedTime = ReadExtendedTimeV5(reader, isWindowsTime); - } - if ((flags & 0x8) == 0x8) - { - FileLastAccessedTime = ReadExtendedTimeV5(reader, isWindowsTime); - } - } - break; - //TODO - // case 4: // file version - // { - // - // } - // break; - // case 5: // file system redirection - // { - // - // } - // break; - // case 6: // unix owner - // { - // - // } - // break; - // case 7: // service data - // { - // - // } - // break; - - default: - // skip unknown record types to allow new record types to be added in the future - break; - } - // drain any trailing bytes of extra record - int did = n - RemainingHeaderBytes(reader); - int drain = size - did; - if (drain > 0) - { - reader.ReadBytes(drain); - } - } - - if (AdditionalDataSize != 0) - { - CompressedSize = AdditionalDataSize; - } + ReadFromReaderV4(reader); } - - private static DateTime ReadExtendedTimeV5(MarkingBinaryReader reader, bool isWindowsTime) - { - if (isWindowsTime) - { - return DateTime.FromFileTime(reader.ReadInt64()); - } - else - { - return Utility.UnixTimeToDateTime(reader.ReadUInt32()); - } - } - - private static string ConvertPathV5(string path) - { - if (Path.DirectorySeparatorChar == '\\') - { - // replace embedded \\ with valid filename char - return path.Replace('\\', '-').Replace('/', '\\'); - } - return path; - } - - private void ReadFromReaderV4(MarkingBinaryReader reader) - { - Flags = HeaderFlags; - IsSolid = HasFlag(FileFlagsV4.SOLID); - WindowSize = IsDirectory - ? 0U - : ((size_t)0x10000) << ((Flags & FileFlagsV4.WINDOW_MASK) >> 5); - - uint lowUncompressedSize = reader.ReadUInt32(); - - HostOs = reader.ReadByte(); - - FileCrc = reader.ReadUInt32(); - - FileLastModifiedTime = Utility.DosDateToDateTime(reader.ReadUInt32()); - - CompressionAlgorithm = reader.ReadByte(); - CompressionMethod = (byte)(reader.ReadByte() - 0x30); - - short nameSize = reader.ReadInt16(); - - FileAttributes = reader.ReadUInt32(); - - uint highCompressedSize = 0; - uint highUncompressedkSize = 0; - if (HasFlag(FileFlagsV4.LARGE)) - { - highCompressedSize = reader.ReadUInt32(); - highUncompressedkSize = reader.ReadUInt32(); - } - else - { - if (lowUncompressedSize == 0xffffffff) - { - lowUncompressedSize = 0xffffffff; - highUncompressedkSize = int.MaxValue; - } - } - CompressedSize = UInt32To64(highCompressedSize, checked((uint)AdditionalDataSize)); - UncompressedSize = UInt32To64(highUncompressedkSize, lowUncompressedSize); - - nameSize = nameSize > 4 * 1024 ? (short)(4 * 1024) : nameSize; - - byte[] fileNameBytes = reader.ReadBytes(nameSize); - - const int saltSize = 8; - const int newLhdSize = 32; - - switch (HeaderCode) - { - case HeaderCodeV.RAR4_FILE_HEADER: - - { - if (HasFlag(FileFlagsV4.UNICODE)) - { - int length = 0; - while (length < fileNameBytes.Length && fileNameBytes[length] != 0) - { - length++; - } - if (length != nameSize) - { - length++; - FileName = FileNameDecoder.Decode(fileNameBytes, length); - } - else - { - FileName = ArchiveEncoding.Decode(fileNameBytes); - } - } - else - { - FileName = ArchiveEncoding.Decode(fileNameBytes); - } - FileName = ConvertPathV4(FileName); - } - break; - case HeaderCodeV.RAR4_NEW_SUB_HEADER: - - { - int datasize = HeaderSize - newLhdSize - nameSize; - if (HasFlag(FileFlagsV4.SALT)) - { - datasize -= saltSize; - } - if (datasize > 0) - { - SubData = reader.ReadBytes(datasize); - } - - if (NewSubHeaderType.SUBHEAD_TYPE_RR.Equals(fileNameBytes)) - { - RecoverySectors = - SubData[8] - + (SubData[9] << 8) - + (SubData[10] << 16) - + (SubData[11] << 24); - } - } - break; - } - - if (HasFlag(FileFlagsV4.SALT)) - { - R4Salt = reader.ReadBytes(saltSize); - } - if (HasFlag(FileFlagsV4.EXT_TIME)) - { - // verify that the end of the header hasn't been reached before reading the Extended Time. - // some tools incorrectly omit Extended Time despite specifying FileFlags.EXTTIME, which most parsers tolerate. - if (RemainingHeaderBytes(reader) >= 2) - { - ushort extendedFlags = reader.ReadUInt16(); - FileLastModifiedTime = ProcessExtendedTimeV4( - extendedFlags, - FileLastModifiedTime, - reader, - 0 - ); - FileCreatedTime = ProcessExtendedTimeV4(extendedFlags, null, reader, 1); - FileLastAccessedTime = ProcessExtendedTimeV4(extendedFlags, null, reader, 2); - FileArchivedTime = ProcessExtendedTimeV4(extendedFlags, null, reader, 3); - } - } - } - - private static long UInt32To64(uint x, uint y) - { - long l = x; - l <<= 32; - return l + y; - } - - private static DateTime? ProcessExtendedTimeV4( - ushort extendedFlags, - DateTime? time, - MarkingBinaryReader reader, - int i - ) - { - uint rmode = (uint)extendedFlags >> (3 - i) * 4; - if ((rmode & 8) == 0) - { - return null; - } - if (i != 0) - { - uint dosTime = reader.ReadUInt32(); - time = Utility.DosDateToDateTime(dosTime); - } - if ((rmode & 4) == 0) - { - time = time.Value.AddSeconds(1); - } - uint nanosecondHundreds = 0; - int count = (int)rmode & 3; - for (int j = 0; j < count; j++) - { - byte b = reader.ReadByte(); - nanosecondHundreds |= (((uint)b) << ((j + 3 - count) * 8)); - } - - //10^-7 to 10^-3 - return time.Value.AddMilliseconds(nanosecondHundreds * Math.Pow(10, -4)); - } - - private static string ConvertPathV4(string path) - { - if (Path.DirectorySeparatorChar == '/') - { - return path.Replace('\\', '/'); - } - else if (Path.DirectorySeparatorChar == '\\') - { - return path.Replace('/', '\\'); - } - return path; - } - - public override string ToString() - { - return FileName; - } - - private ushort Flags { get; set; } - - private bool HasFlag(ushort flag) - { - return (Flags & flag) == flag; - } - - internal uint FileCrc - { - get - { - if (IsRar5 && !HasFlag(FileFlagsV5.HAS_CRC32)) - { - //!!! rar5: - throw new InvalidOperationException("TODO rar5"); - } - return _fileCrc; - } - private set => _fileCrc = value; - } - - // 0 - storing - // 1 - fastest compression - // 2 - fast compression - // 3 - normal compression - // 4 - good compression - // 5 - best compression - internal byte CompressionMethod { get; private set; } - internal bool IsStored => CompressionMethod == 0; - - // eg (see DoUnpack()) - //case 15: // rar 1.5 compression - //case 20: // rar 2.x compression - //case 26: // files larger than 2GB - //case 29: // rar 3.x compression - //case 50: // RAR 5.0 compression algorithm. - internal byte CompressionAlgorithm { get; private set; } - - public bool IsSolid { get; private set; } - - // unused for UnpackV1 implementation (limitation) - internal size_t WindowSize { get; private set; } - - internal byte[] R4Salt { get; private set; } - - private byte HostOs { get; set; } - internal uint FileAttributes { get; private set; } - internal long CompressedSize { get; private set; } - internal long UncompressedSize { get; private set; } - internal string FileName { get; private set; } - internal byte[] SubData { get; private set; } - internal int RecoverySectors { get; private set; } - internal long DataStartPosition { get; set; } - public Stream PackedStream { get; set; } - - public bool IsSplitBefore => - IsRar5 ? HasHeaderFlag(HeaderFlagsV5.SPLIT_BEFORE) : HasFlag(FileFlagsV4.SPLIT_BEFORE); - public bool IsSplitAfter => - IsRar5 ? HasHeaderFlag(HeaderFlagsV5.SPLIT_AFTER) : HasFlag(FileFlagsV4.SPLIT_AFTER); - - public bool IsDirectory => HasFlag(IsRar5 ? FileFlagsV5.DIRECTORY : FileFlagsV4.DIRECTORY); - - private bool isEncryptedRar5 = false; - public bool IsEncrypted => IsRar5 ? isEncryptedRar5 : HasFlag(FileFlagsV4.PASSWORD); - - internal DateTime? FileLastModifiedTime { get; private set; } - - internal DateTime? FileCreatedTime { get; private set; } - - internal DateTime? FileLastAccessedTime { get; private set; } - - internal DateTime? FileArchivedTime { get; private set; } } + + private void ReadFromReaderV5(MarkingBinaryReader reader) + { + Flags = reader.ReadRarVIntUInt16(); + + var lvalue = checked((long)reader.ReadRarVInt()); + + // long.MaxValue causes the unpack code to finish when the input stream is exhausted + UncompressedSize = HasFlag(FileFlagsV5.UNPACKED_SIZE_UNKNOWN) ? long.MaxValue : lvalue; + + FileAttributes = reader.ReadRarVIntUInt32(); + + if (HasFlag(FileFlagsV5.HAS_MOD_TIME)) + { + FileLastModifiedTime = Utility.UnixTimeToDateTime(reader.ReadUInt32()); + } + + if (HasFlag(FileFlagsV5.HAS_CRC32)) + { + FileCrc = reader.ReadUInt32(); + } + + var compressionInfo = reader.ReadRarVIntUInt16(); + + // Lower 6 bits (0x003f mask) contain the version of compression algorithm, resulting in possible 0 - 63 values. Current version is 0. + // "+ 50" to not mix with old RAR format algorithms. For example, + // we may need to use the compression algorithm 15 in the future, + // but it was already used in RAR 1.5 and Unpack needs to distinguish + // them. + CompressionAlgorithm = (byte)((compressionInfo & 0x3f) + 50); + + // 7th bit (0x0040) defines the solid flag. If it is set, RAR continues to use the compression dictionary left after processing preceding files. + // It can be set only for file headers and is never set for service headers. + IsSolid = (compressionInfo & 0x40) == 0x40; + + // Bits 8 - 10 (0x0380 mask) define the compression method. Currently only values 0 - 5 are used. 0 means no compression. + CompressionMethod = (byte)((compressionInfo >> 7) & 0x7); + + // Bits 11 - 14 (0x3c00) define the minimum size of dictionary size required to extract data. Value 0 means 128 KB, 1 - 256 KB, ..., 14 - 2048 MB, 15 - 4096 MB. + WindowSize = IsDirectory ? 0 : ((size_t)0x20000) << ((compressionInfo >> 10) & 0xf); + + HostOs = reader.ReadRarVIntByte(); + + var nameSize = reader.ReadRarVIntUInt16(); + + // Variable length field containing Name length bytes in UTF-8 format without trailing zero. + // For file header this is a name of archived file. Forward slash character is used as the path separator both for Unix and Windows names. + // Backslashes are treated as a part of name for Unix names and as invalid character for Windows file names. Type of name is defined by Host OS field. + // + // TODO: not sure if anything needs to be done to handle the following: + // If Unix file name contains any high ASCII characters which cannot be correctly converted to Unicode and UTF-8 + // we map such characters to to 0xE080 - 0xE0FF private use Unicode area and insert 0xFFFE Unicode non-character + // to resulting string to indicate that it contains mapped characters, which need to be converted back when extracting. + // Concrete position of 0xFFFE is not defined, we need to search the entire string for it. Such mapped names are not + // portable and can be correctly unpacked only on the same system where they were created. + // + // For service header this field contains a name of service header. Now the following names are used: + // CMT Archive comment + // QO Archive quick open data + // ACL NTFS file permissions + // STM NTFS alternate data stream + // RR Recovery record + var b = reader.ReadBytes(nameSize); + FileName = ConvertPathV5(Encoding.UTF8.GetString(b, 0, b.Length)); + + // extra size seems to be redudant since we know the total header size + if (ExtraSize != RemainingHeaderBytes(reader)) + { + throw new InvalidFormatException("rar5 header size / extra size inconsistency"); + } + + isEncryptedRar5 = false; + + while (RemainingHeaderBytes(reader) > 0) + { + var size = reader.ReadRarVIntUInt16(); + var n = RemainingHeaderBytes(reader); + var type = reader.ReadRarVIntUInt16(); + switch (type) + { + //TODO + case 1: // file encryption + + { + isEncryptedRar5 = true; + + //var version = reader.ReadRarVIntByte(); + //if (version != 0) throw new InvalidFormatException("unknown encryption algorithm " + version); + } + break; + // case 2: // file hash + // { + // + // } + // break; + case 3: // file time + + { + var flags = reader.ReadRarVIntUInt16(); + var isWindowsTime = (flags & 1) == 0; + if ((flags & 0x2) == 0x2) + { + FileLastModifiedTime = ReadExtendedTimeV5(reader, isWindowsTime); + } + if ((flags & 0x4) == 0x4) + { + FileCreatedTime = ReadExtendedTimeV5(reader, isWindowsTime); + } + if ((flags & 0x8) == 0x8) + { + FileLastAccessedTime = ReadExtendedTimeV5(reader, isWindowsTime); + } + } + break; + //TODO + // case 4: // file version + // { + // + // } + // break; + // case 5: // file system redirection + // { + // + // } + // break; + // case 6: // unix owner + // { + // + // } + // break; + // case 7: // service data + // { + // + // } + // break; + + default: + // skip unknown record types to allow new record types to be added in the future + break; + } + // drain any trailing bytes of extra record + var did = n - RemainingHeaderBytes(reader); + var drain = size - did; + if (drain > 0) + { + reader.ReadBytes(drain); + } + } + + if (AdditionalDataSize != 0) + { + CompressedSize = AdditionalDataSize; + } + } + + private static DateTime ReadExtendedTimeV5(MarkingBinaryReader reader, bool isWindowsTime) + { + if (isWindowsTime) + { + return DateTime.FromFileTime(reader.ReadInt64()); + } + else + { + return Utility.UnixTimeToDateTime(reader.ReadUInt32()); + } + } + + private static string ConvertPathV5(string path) + { + if (Path.DirectorySeparatorChar == '\\') + { + // replace embedded \\ with valid filename char + return path.Replace('\\', '-').Replace('/', '\\'); + } + return path; + } + + private void ReadFromReaderV4(MarkingBinaryReader reader) + { + Flags = HeaderFlags; + IsSolid = HasFlag(FileFlagsV4.SOLID); + WindowSize = IsDirectory + ? 0U + : ((size_t)0x10000) << ((Flags & FileFlagsV4.WINDOW_MASK) >> 5); + + var lowUncompressedSize = reader.ReadUInt32(); + + HostOs = reader.ReadByte(); + + FileCrc = reader.ReadUInt32(); + + FileLastModifiedTime = Utility.DosDateToDateTime(reader.ReadUInt32()); + + CompressionAlgorithm = reader.ReadByte(); + CompressionMethod = (byte)(reader.ReadByte() - 0x30); + + var nameSize = reader.ReadInt16(); + + FileAttributes = reader.ReadUInt32(); + + uint highCompressedSize = 0; + uint highUncompressedkSize = 0; + if (HasFlag(FileFlagsV4.LARGE)) + { + highCompressedSize = reader.ReadUInt32(); + highUncompressedkSize = reader.ReadUInt32(); + } + else + { + if (lowUncompressedSize == 0xffffffff) + { + lowUncompressedSize = 0xffffffff; + highUncompressedkSize = int.MaxValue; + } + } + CompressedSize = UInt32To64(highCompressedSize, checked((uint)AdditionalDataSize)); + UncompressedSize = UInt32To64(highUncompressedkSize, lowUncompressedSize); + + nameSize = nameSize > 4 * 1024 ? (short)(4 * 1024) : nameSize; + + var fileNameBytes = reader.ReadBytes(nameSize); + + const int saltSize = 8; + const int newLhdSize = 32; + + switch (HeaderCode) + { + case HeaderCodeV.RAR4_FILE_HEADER: + + { + if (HasFlag(FileFlagsV4.UNICODE)) + { + var length = 0; + while (length < fileNameBytes.Length && fileNameBytes[length] != 0) + { + length++; + } + if (length != nameSize) + { + length++; + FileName = FileNameDecoder.Decode(fileNameBytes, length); + } + else + { + FileName = ArchiveEncoding.Decode(fileNameBytes); + } + } + else + { + FileName = ArchiveEncoding.Decode(fileNameBytes); + } + FileName = ConvertPathV4(FileName); + } + break; + case HeaderCodeV.RAR4_NEW_SUB_HEADER: + + { + var datasize = HeaderSize - newLhdSize - nameSize; + if (HasFlag(FileFlagsV4.SALT)) + { + datasize -= saltSize; + } + if (datasize > 0) + { + SubData = reader.ReadBytes(datasize); + } + + if (NewSubHeaderType.SUBHEAD_TYPE_RR.Equals(fileNameBytes)) + { + RecoverySectors = + SubData[8] + + (SubData[9] << 8) + + (SubData[10] << 16) + + (SubData[11] << 24); + } + } + break; + } + + if (HasFlag(FileFlagsV4.SALT)) + { + R4Salt = reader.ReadBytes(saltSize); + } + if (HasFlag(FileFlagsV4.EXT_TIME)) + { + // verify that the end of the header hasn't been reached before reading the Extended Time. + // some tools incorrectly omit Extended Time despite specifying FileFlags.EXTTIME, which most parsers tolerate. + if (RemainingHeaderBytes(reader) >= 2) + { + var extendedFlags = reader.ReadUInt16(); + FileLastModifiedTime = ProcessExtendedTimeV4( + extendedFlags, + FileLastModifiedTime, + reader, + 0 + ); + FileCreatedTime = ProcessExtendedTimeV4(extendedFlags, null, reader, 1); + FileLastAccessedTime = ProcessExtendedTimeV4(extendedFlags, null, reader, 2); + FileArchivedTime = ProcessExtendedTimeV4(extendedFlags, null, reader, 3); + } + } + } + + private static long UInt32To64(uint x, uint y) + { + long l = x; + l <<= 32; + return l + y; + } + + private static DateTime? ProcessExtendedTimeV4( + ushort extendedFlags, + DateTime? time, + MarkingBinaryReader reader, + int i + ) + { + var rmode = (uint)extendedFlags >> ((3 - i) * 4); + if ((rmode & 8) == 0) + { + return null; + } + if (i != 0) + { + var dosTime = reader.ReadUInt32(); + time = Utility.DosDateToDateTime(dosTime); + } + if ((rmode & 4) == 0) + { + time = time.Value.AddSeconds(1); + } + uint nanosecondHundreds = 0; + var count = (int)rmode & 3; + for (var j = 0; j < count; j++) + { + var b = reader.ReadByte(); + nanosecondHundreds |= (((uint)b) << ((j + 3 - count) * 8)); + } + + //10^-7 to 10^-3 + return time.Value.AddMilliseconds(nanosecondHundreds * Math.Pow(10, -4)); + } + + private static string ConvertPathV4(string path) + { + if (Path.DirectorySeparatorChar == '/') + { + return path.Replace('\\', '/'); + } + else if (Path.DirectorySeparatorChar == '\\') + { + return path.Replace('/', '\\'); + } + return path; + } + + public override string ToString() + { + return FileName; + } + + private ushort Flags { get; set; } + + private bool HasFlag(ushort flag) + { + return (Flags & flag) == flag; + } + + internal uint FileCrc + { + get + { + if (IsRar5 && !HasFlag(FileFlagsV5.HAS_CRC32)) + { + //!!! rar5: + throw new InvalidOperationException("TODO rar5"); + } + return _fileCrc; + } + private set => _fileCrc = value; + } + + // 0 - storing + // 1 - fastest compression + // 2 - fast compression + // 3 - normal compression + // 4 - good compression + // 5 - best compression + internal byte CompressionMethod { get; private set; } + internal bool IsStored => CompressionMethod == 0; + + // eg (see DoUnpack()) + //case 15: // rar 1.5 compression + //case 20: // rar 2.x compression + //case 26: // files larger than 2GB + //case 29: // rar 3.x compression + //case 50: // RAR 5.0 compression algorithm. + internal byte CompressionAlgorithm { get; private set; } + + public bool IsSolid { get; private set; } + + // unused for UnpackV1 implementation (limitation) + internal size_t WindowSize { get; private set; } + + internal byte[] R4Salt { get; private set; } + + private byte HostOs { get; set; } + internal uint FileAttributes { get; private set; } + internal long CompressedSize { get; private set; } + internal long UncompressedSize { get; private set; } + internal string FileName { get; private set; } + internal byte[] SubData { get; private set; } + internal int RecoverySectors { get; private set; } + internal long DataStartPosition { get; set; } + public Stream PackedStream { get; set; } + + public bool IsSplitBefore => + IsRar5 ? HasHeaderFlag(HeaderFlagsV5.SPLIT_BEFORE) : HasFlag(FileFlagsV4.SPLIT_BEFORE); + public bool IsSplitAfter => + IsRar5 ? HasHeaderFlag(HeaderFlagsV5.SPLIT_AFTER) : HasFlag(FileFlagsV4.SPLIT_AFTER); + + public bool IsDirectory => HasFlag(IsRar5 ? FileFlagsV5.DIRECTORY : FileFlagsV4.DIRECTORY); + + private bool isEncryptedRar5 = false; + public bool IsEncrypted => IsRar5 ? isEncryptedRar5 : HasFlag(FileFlagsV4.PASSWORD); + + internal DateTime? FileLastModifiedTime { get; private set; } + + internal DateTime? FileCreatedTime { get; private set; } + + internal DateTime? FileLastAccessedTime { get; private set; } + + internal DateTime? FileArchivedTime { get; private set; } } diff --git a/src/SharpCompress/Common/Rar/Headers/FileNameDecoder.cs b/src/SharpCompress/Common/Rar/Headers/FileNameDecoder.cs index f2afaa39..c6b85001 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileNameDecoder.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileNameDecoder.cs @@ -1,86 +1,85 @@ using System.Text; -namespace SharpCompress.Common.Rar.Headers +namespace SharpCompress.Common.Rar.Headers; + +/// +/// This is for the crazy Rar encoding that I don't understand +/// +internal static class FileNameDecoder { - /// - /// This is for the crazy Rar encoding that I don't understand - /// - internal static class FileNameDecoder + internal static int GetChar(byte[] name, int pos) { - internal static int GetChar(byte[] name, int pos) - { - return name[pos] & 0xff; - } + return name[pos] & 0xff; + } - internal static string Decode(byte[] name, int encPos) - { - int decPos = 0; - int flags = 0; - int flagBits = 0; + internal static string Decode(byte[] name, int encPos) + { + var decPos = 0; + var flags = 0; + var flagBits = 0; - int low = 0; - int high = 0; - int highByte = GetChar(name, encPos++); - StringBuilder buf = new StringBuilder(); - while (encPos < name.Length) + var low = 0; + var high = 0; + var highByte = GetChar(name, encPos++); + var buf = new StringBuilder(); + while (encPos < name.Length) + { + if (flagBits == 0) { - if (flagBits == 0) - { - flags = GetChar(name, encPos++); - flagBits = 8; - } - switch (flags >> 6) - { - case 0: - buf.Append((char)(GetChar(name, encPos++))); - ++decPos; - break; - - case 1: - buf.Append((char)(GetChar(name, encPos++) + (highByte << 8))); - ++decPos; - break; - - case 2: - low = GetChar(name, encPos); - high = GetChar(name, encPos + 1); - buf.Append((char)((high << 8) + low)); - ++decPos; - encPos += 2; - break; - - case 3: - int length = GetChar(name, encPos++); - if ((length & 0x80) != 0) - { - int correction = GetChar(name, encPos++); - for ( - length = (length & 0x7f) + 2; - length > 0 && decPos < name.Length; - length--, decPos++ - ) - { - low = (GetChar(name, decPos) + correction) & 0xff; - buf.Append((char)((highByte << 8) + low)); - } - } - else - { - for ( - length += 2; - length > 0 && decPos < name.Length; - length--, decPos++ - ) - { - buf.Append((char)(GetChar(name, decPos))); - } - } - break; - } - flags = (flags << 2) & 0xff; - flagBits -= 2; + flags = GetChar(name, encPos++); + flagBits = 8; } - return buf.ToString(); + switch (flags >> 6) + { + case 0: + buf.Append((char)(GetChar(name, encPos++))); + ++decPos; + break; + + case 1: + buf.Append((char)(GetChar(name, encPos++) + (highByte << 8))); + ++decPos; + break; + + case 2: + low = GetChar(name, encPos); + high = GetChar(name, encPos + 1); + buf.Append((char)((high << 8) + low)); + ++decPos; + encPos += 2; + break; + + case 3: + var length = GetChar(name, encPos++); + if ((length & 0x80) != 0) + { + var correction = GetChar(name, encPos++); + for ( + length = (length & 0x7f) + 2; + length > 0 && decPos < name.Length; + length--, decPos++ + ) + { + low = (GetChar(name, decPos) + correction) & 0xff; + buf.Append((char)((highByte << 8) + low)); + } + } + else + { + for ( + length += 2; + length > 0 && decPos < name.Length; + length--, decPos++ + ) + { + buf.Append((char)(GetChar(name, decPos))); + } + } + break; + } + flags = (flags << 2) & 0xff; + flagBits -= 2; } + return buf.ToString(); } } diff --git a/src/SharpCompress/Common/Rar/Headers/Flags.cs b/src/SharpCompress/Common/Rar/Headers/Flags.cs index 7f6ad0aa..5a52f001 100644 --- a/src/SharpCompress/Common/Rar/Headers/Flags.cs +++ b/src/SharpCompress/Common/Rar/Headers/Flags.cs @@ -1,149 +1,148 @@ -namespace SharpCompress.Common.Rar.Headers +namespace SharpCompress.Common.Rar.Headers; + +public enum HeaderType : byte { - public enum HeaderType : byte - { - Null, - Mark, - Archive, - File, - Service, - Comment, - Av, - Protect, - Sign, - NewSub, - EndArchive, - Crypt - } - - internal static class HeaderCodeV - { - public const byte RAR4_MARK_HEADER = 0x72; - public const byte RAR4_ARCHIVE_HEADER = 0x73; - public const byte RAR4_FILE_HEADER = 0x74; - public const byte RAR4_COMMENT_HEADER = 0x75; - public const byte RAR4_AV_HEADER = 0x76; - public const byte RAR4_SUB_HEADER = 0x77; - public const byte RAR4_PROTECT_HEADER = 0x78; - public const byte RAR4_SIGN_HEADER = 0x79; - public const byte RAR4_NEW_SUB_HEADER = 0x7a; - public const byte RAR4_END_ARCHIVE_HEADER = 0x7b; - - public const byte RAR5_ARCHIVE_HEADER = 0x01; - public const byte RAR5_FILE_HEADER = 0x02; - public const byte RAR5_SERVICE_HEADER = 0x03; - public const byte RAR5_ARCHIVE_ENCRYPTION_HEADER = 0x04; - public const byte RAR5_END_ARCHIVE_HEADER = 0x05; - } - - internal static class HeaderFlagsV4 - { - public const ushort HAS_DATA = 0x8000; - } - - internal static class EncryptionFlagsV5 - { - // RAR 5.0 archive encryption header specific flags. - public const uint CHFL_CRYPT_PSWCHECK = 0x01; // Password check data is present. - - public const uint FHEXTRA_CRYPT_PSWCHECK = 0x01; // Password check data is present. - public const uint FHEXTRA_CRYPT_HASHMAC = 0x02; - } - - internal static class HeaderFlagsV5 - { - public const ushort HAS_EXTRA = 0x0001; - public const ushort HAS_DATA = 0x0002; - public const ushort KEEP = 0x0004; // block must be kept during an update - public const ushort SPLIT_BEFORE = 0x0008; - public const ushort SPLIT_AFTER = 0x0010; - public const ushort CHILD = 0x0020; // ??? Block depends on preceding file block. - public const ushort PRESERVE_CHILD = 0x0040; // ???? Preserve a child block if host block is modified - } - - internal static class ArchiveFlagsV4 - { - public const ushort VOLUME = 0x0001; - public const ushort COMMENT = 0x0002; - public const ushort LOCK = 0x0004; - public const ushort SOLID = 0x0008; - public const ushort NEW_NUMBERING = 0x0010; - public const ushort AV = 0x0020; - public const ushort PROTECT = 0x0040; - public const ushort PASSWORD = 0x0080; - public const ushort FIRST_VOLUME = 0x0100; - public const ushort ENCRYPT_VER = 0x0200; - } - - internal static class ArchiveFlagsV5 - { - public const ushort VOLUME = 0x0001; - public const ushort HAS_VOLUME_NUMBER = 0x0002; - public const ushort SOLID = 0x0004; - public const ushort PROTECT = 0x0008; - public const ushort LOCK = 0x0010; - } - - internal static class HostOsV4 - { - public const byte MS_DOS = 0; - public const byte OS2 = 1; - public const byte WIN32 = 2; - public const byte UNIX = 3; - public const byte MAC_OS = 4; - public const byte BE_OS = 5; - } - - internal static class HostOsV5 - { - public const byte WINDOWS = 0; - public const byte UNIX = 1; - } - - internal static class FileFlagsV4 - { - public const ushort SPLIT_BEFORE = 0x0001; - public const ushort SPLIT_AFTER = 0x0002; - public const ushort PASSWORD = 0x0004; - public const ushort COMMENT = 0x0008; - public const ushort SOLID = 0x0010; - - public const ushort WINDOW_MASK = 0x00e0; - public const ushort WINDOW64 = 0x0000; - public const ushort WINDOW128 = 0x0020; - public const ushort WINDOW256 = 0x0040; - public const ushort WINDOW512 = 0x0060; - public const ushort WINDOW1024 = 0x0080; - public const ushort WINDOW2048 = 0x00a0; - public const ushort WINDOW4096 = 0x00c0; - public const ushort DIRECTORY = 0x00e0; - - public const ushort LARGE = 0x0100; - public const ushort UNICODE = 0x0200; - public const ushort SALT = 0x0400; - public const ushort VERSION = 0x0800; - public const ushort EXT_TIME = 0x1000; - public const ushort EXT_FLAGS = 0x2000; - } - - internal static class FileFlagsV5 - { - public const ushort DIRECTORY = 0x0001; - public const ushort HAS_MOD_TIME = 0x0002; - public const ushort HAS_CRC32 = 0x0004; - public const ushort UNPACKED_SIZE_UNKNOWN = 0x0008; - } - - internal static class EndArchiveFlagsV4 - { - public const ushort NEXT_VOLUME = 0x0001; - public const ushort DATA_CRC = 0x0002; - public const ushort REV_SPACE = 0x0004; - public const ushort VOLUME_NUMBER = 0x0008; - } - - internal static class EndArchiveFlagsV5 - { - public const ushort HAS_NEXT_VOLUME = 0x0001; - } + Null, + Mark, + Archive, + File, + Service, + Comment, + Av, + Protect, + Sign, + NewSub, + EndArchive, + Crypt +} + +internal static class HeaderCodeV +{ + public const byte RAR4_MARK_HEADER = 0x72; + public const byte RAR4_ARCHIVE_HEADER = 0x73; + public const byte RAR4_FILE_HEADER = 0x74; + public const byte RAR4_COMMENT_HEADER = 0x75; + public const byte RAR4_AV_HEADER = 0x76; + public const byte RAR4_SUB_HEADER = 0x77; + public const byte RAR4_PROTECT_HEADER = 0x78; + public const byte RAR4_SIGN_HEADER = 0x79; + public const byte RAR4_NEW_SUB_HEADER = 0x7a; + public const byte RAR4_END_ARCHIVE_HEADER = 0x7b; + + public const byte RAR5_ARCHIVE_HEADER = 0x01; + public const byte RAR5_FILE_HEADER = 0x02; + public const byte RAR5_SERVICE_HEADER = 0x03; + public const byte RAR5_ARCHIVE_ENCRYPTION_HEADER = 0x04; + public const byte RAR5_END_ARCHIVE_HEADER = 0x05; +} + +internal static class HeaderFlagsV4 +{ + public const ushort HAS_DATA = 0x8000; +} + +internal static class EncryptionFlagsV5 +{ + // RAR 5.0 archive encryption header specific flags. + public const uint CHFL_CRYPT_PSWCHECK = 0x01; // Password check data is present. + + public const uint FHEXTRA_CRYPT_PSWCHECK = 0x01; // Password check data is present. + public const uint FHEXTRA_CRYPT_HASHMAC = 0x02; +} + +internal static class HeaderFlagsV5 +{ + public const ushort HAS_EXTRA = 0x0001; + public const ushort HAS_DATA = 0x0002; + public const ushort KEEP = 0x0004; // block must be kept during an update + public const ushort SPLIT_BEFORE = 0x0008; + public const ushort SPLIT_AFTER = 0x0010; + public const ushort CHILD = 0x0020; // ??? Block depends on preceding file block. + public const ushort PRESERVE_CHILD = 0x0040; // ???? Preserve a child block if host block is modified +} + +internal static class ArchiveFlagsV4 +{ + public const ushort VOLUME = 0x0001; + public const ushort COMMENT = 0x0002; + public const ushort LOCK = 0x0004; + public const ushort SOLID = 0x0008; + public const ushort NEW_NUMBERING = 0x0010; + public const ushort AV = 0x0020; + public const ushort PROTECT = 0x0040; + public const ushort PASSWORD = 0x0080; + public const ushort FIRST_VOLUME = 0x0100; + public const ushort ENCRYPT_VER = 0x0200; +} + +internal static class ArchiveFlagsV5 +{ + public const ushort VOLUME = 0x0001; + public const ushort HAS_VOLUME_NUMBER = 0x0002; + public const ushort SOLID = 0x0004; + public const ushort PROTECT = 0x0008; + public const ushort LOCK = 0x0010; +} + +internal static class HostOsV4 +{ + public const byte MS_DOS = 0; + public const byte OS2 = 1; + public const byte WIN32 = 2; + public const byte UNIX = 3; + public const byte MAC_OS = 4; + public const byte BE_OS = 5; +} + +internal static class HostOsV5 +{ + public const byte WINDOWS = 0; + public const byte UNIX = 1; +} + +internal static class FileFlagsV4 +{ + public const ushort SPLIT_BEFORE = 0x0001; + public const ushort SPLIT_AFTER = 0x0002; + public const ushort PASSWORD = 0x0004; + public const ushort COMMENT = 0x0008; + public const ushort SOLID = 0x0010; + + public const ushort WINDOW_MASK = 0x00e0; + public const ushort WINDOW64 = 0x0000; + public const ushort WINDOW128 = 0x0020; + public const ushort WINDOW256 = 0x0040; + public const ushort WINDOW512 = 0x0060; + public const ushort WINDOW1024 = 0x0080; + public const ushort WINDOW2048 = 0x00a0; + public const ushort WINDOW4096 = 0x00c0; + public const ushort DIRECTORY = 0x00e0; + + public const ushort LARGE = 0x0100; + public const ushort UNICODE = 0x0200; + public const ushort SALT = 0x0400; + public const ushort VERSION = 0x0800; + public const ushort EXT_TIME = 0x1000; + public const ushort EXT_FLAGS = 0x2000; +} + +internal static class FileFlagsV5 +{ + public const ushort DIRECTORY = 0x0001; + public const ushort HAS_MOD_TIME = 0x0002; + public const ushort HAS_CRC32 = 0x0004; + public const ushort UNPACKED_SIZE_UNKNOWN = 0x0008; +} + +internal static class EndArchiveFlagsV4 +{ + public const ushort NEXT_VOLUME = 0x0001; + public const ushort DATA_CRC = 0x0002; + public const ushort REV_SPACE = 0x0004; + public const ushort VOLUME_NUMBER = 0x0008; +} + +internal static class EndArchiveFlagsV5 +{ + public const ushort HAS_NEXT_VOLUME = 0x0001; } diff --git a/src/SharpCompress/Common/Rar/Headers/IRarHeader.cs b/src/SharpCompress/Common/Rar/Headers/IRarHeader.cs index a168690c..17a1a930 100644 --- a/src/SharpCompress/Common/Rar/Headers/IRarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/IRarHeader.cs @@ -1,7 +1,6 @@ -namespace SharpCompress.Common.Rar.Headers +namespace SharpCompress.Common.Rar.Headers; + +public interface IRarHeader { - public interface IRarHeader - { - HeaderType HeaderType { get; } - } + HeaderType HeaderType { get; } } diff --git a/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs b/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs index 8416270d..d359aef4 100644 --- a/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs @@ -1,136 +1,135 @@ using System; using System.IO; -namespace SharpCompress.Common.Rar.Headers +namespace SharpCompress.Common.Rar.Headers; + +internal class MarkHeader : IRarHeader { - internal class MarkHeader : IRarHeader + private const int MAX_SFX_SIZE = 0x80000 - 16; //archive.cpp line 136 + + internal bool OldNumberingFormat { get; private set; } + + public bool IsRar5 { get; } + + private MarkHeader(bool isRar5) { - private const int MAX_SFX_SIZE = 0x80000 - 16; //archive.cpp line 136 + IsRar5 = isRar5; + } - internal bool OldNumberingFormat { get; private set; } + public HeaderType HeaderType => HeaderType.Mark; - public bool IsRar5 { get; } - - private MarkHeader(bool isRar5) + private static byte GetByte(Stream stream) + { + var b = stream.ReadByte(); + if (b != -1) { - IsRar5 = isRar5; + return (byte)b; } + throw new EndOfStreamException(); + } - public HeaderType HeaderType => HeaderType.Mark; - - private static byte GetByte(Stream stream) + public static MarkHeader Read(Stream stream, bool leaveStreamOpen, bool lookForHeader) + { + var maxScanIndex = lookForHeader ? MAX_SFX_SIZE : 0; + try { - var b = stream.ReadByte(); - if (b != -1) + var start = -1; + var b = GetByte(stream); + start++; + while (start <= maxScanIndex) { - return (byte)b; - } - throw new EndOfStreamException(); - } - - public static MarkHeader Read(Stream stream, bool leaveStreamOpen, bool lookForHeader) - { - int maxScanIndex = lookForHeader ? MAX_SFX_SIZE : 0; - try - { - int start = -1; - var b = GetByte(stream); - start++; - while (start <= maxScanIndex) + // Rar old signature: 52 45 7E 5E + // Rar4 signature: 52 61 72 21 1A 07 00 + // Rar5 signature: 52 61 72 21 1A 07 01 00 + if (b == 0x52) { - // Rar old signature: 52 45 7E 5E - // Rar4 signature: 52 61 72 21 1A 07 00 - // Rar5 signature: 52 61 72 21 1A 07 01 00 - if (b == 0x52) + b = GetByte(stream); + start++; + if (b == 0x61) { b = GetByte(stream); start++; - if (b == 0x61) + if (b != 0x72) { - b = GetByte(stream); - start++; - if (b != 0x72) - { - continue; - } - - b = GetByte(stream); - start++; - if (b != 0x21) - { - continue; - } - - b = GetByte(stream); - start++; - if (b != 0x1a) - { - continue; - } - - b = GetByte(stream); - start++; - if (b != 0x07) - { - continue; - } - - b = GetByte(stream); - start++; - if (b == 1) - { - b = GetByte(stream); - start++; - if (b != 0) - { - continue; - } - - return new MarkHeader(true); // Rar5 - } - else if (b == 0) - { - return new MarkHeader(false); // Rar4 - } + continue; } - else if (b == 0x45) + + b = GetByte(stream); + start++; + if (b != 0x21) + { + continue; + } + + b = GetByte(stream); + start++; + if (b != 0x1a) + { + continue; + } + + b = GetByte(stream); + start++; + if (b != 0x07) + { + continue; + } + + b = GetByte(stream); + start++; + if (b == 1) { b = GetByte(stream); start++; - if (b != 0x7e) + if (b != 0) { continue; } - b = GetByte(stream); - start++; - if (b != 0x5e) - { - continue; - } - - throw new InvalidFormatException( - "Rar format version pre-4 is unsupported." - ); + return new MarkHeader(true); // Rar5 + } + else if (b == 0) + { + return new MarkHeader(false); // Rar4 } } - else + else if (b == 0x45) { b = GetByte(stream); start++; + if (b != 0x7e) + { + continue; + } + + b = GetByte(stream); + start++; + if (b != 0x5e) + { + continue; + } + + throw new InvalidFormatException( + "Rar format version pre-4 is unsupported." + ); } } - } - catch (Exception e) - { - if (!leaveStreamOpen) + else { - stream.Dispose(); + b = GetByte(stream); + start++; } - throw new InvalidFormatException("Error trying to read rar signature.", e); } - - throw new InvalidFormatException("Rar signature not found"); } + catch (Exception e) + { + if (!leaveStreamOpen) + { + stream.Dispose(); + } + throw new InvalidFormatException("Error trying to read rar signature.", e); + } + + throw new InvalidFormatException("Rar signature not found"); } } diff --git a/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs b/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs index 9814d6bc..01b2ffc0 100644 --- a/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs +++ b/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs @@ -1,49 +1,48 @@ -using System; +using System; -namespace SharpCompress.Common.Rar.Headers +namespace SharpCompress.Common.Rar.Headers; + +internal sealed class NewSubHeaderType : IEquatable { - internal sealed class NewSubHeaderType : IEquatable + internal static readonly NewSubHeaderType SUBHEAD_TYPE_CMT = new('C', 'M', 'T'); + + //internal static final NewSubHeaderType SUBHEAD_TYPE_ACL = new (new byte[]{'A','C','L'}); + + //internal static final NewSubHeaderType SUBHEAD_TYPE_STREAM = new (new byte[]{'S','T','M'}); + + //internal static final NewSubHeaderType SUBHEAD_TYPE_UOWNER = new (new byte[]{'U','O','W'}); + + //internal static final NewSubHeaderType SUBHEAD_TYPE_AV = new (new byte[]{'A','V'}); + + internal static readonly NewSubHeaderType SUBHEAD_TYPE_RR = new('R', 'R'); + + //internal static final NewSubHeaderType SUBHEAD_TYPE_OS2EA = new (new byte[]{'E','A','2'}); + + //internal static final NewSubHeaderType SUBHEAD_TYPE_BEOSEA = new (new byte[]{'E','A','B','E'}); + + private readonly byte[] _bytes; + + private NewSubHeaderType(params char[] chars) { - internal static readonly NewSubHeaderType SUBHEAD_TYPE_CMT = new('C', 'M', 'T'); - - //internal static final NewSubHeaderType SUBHEAD_TYPE_ACL = new (new byte[]{'A','C','L'}); - - //internal static final NewSubHeaderType SUBHEAD_TYPE_STREAM = new (new byte[]{'S','T','M'}); - - //internal static final NewSubHeaderType SUBHEAD_TYPE_UOWNER = new (new byte[]{'U','O','W'}); - - //internal static final NewSubHeaderType SUBHEAD_TYPE_AV = new (new byte[]{'A','V'}); - - internal static readonly NewSubHeaderType SUBHEAD_TYPE_RR = new('R', 'R'); - - //internal static final NewSubHeaderType SUBHEAD_TYPE_OS2EA = new (new byte[]{'E','A','2'}); - - //internal static final NewSubHeaderType SUBHEAD_TYPE_BEOSEA = new (new byte[]{'E','A','B','E'}); - - private readonly byte[] _bytes; - - private NewSubHeaderType(params char[] chars) + _bytes = new byte[chars.Length]; + for (var i = 0; i < chars.Length; ++i) { - _bytes = new byte[chars.Length]; - for (int i = 0; i < chars.Length; ++i) - { - _bytes[i] = (byte)chars[i]; - } - } - - internal bool Equals(byte[] bytes) - { - if (_bytes.Length != bytes.Length) - { - return false; - } - - return _bytes.AsSpan().SequenceEqual(bytes); - } - - public bool Equals(NewSubHeaderType? other) - { - return other is not null && Equals(other._bytes); + _bytes[i] = (byte)chars[i]; } } + + internal bool Equals(byte[] bytes) + { + if (_bytes.Length != bytes.Length) + { + return false; + } + + return _bytes.AsSpan().SequenceEqual(bytes); + } + + public bool Equals(NewSubHeaderType? other) + { + return other is not null && Equals(other._bytes); + } } diff --git a/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs b/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs index 9785f369..cf8db3c0 100644 --- a/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs @@ -1,31 +1,30 @@ -using SharpCompress.IO; +using SharpCompress.IO; -namespace SharpCompress.Common.Rar.Headers +namespace SharpCompress.Common.Rar.Headers; + +// ProtectHeader is part of the Recovery Record feature +internal sealed class ProtectHeader : RarHeader { - // ProtectHeader is part of the Recovery Record feature - internal sealed class ProtectHeader : RarHeader + public ProtectHeader(RarHeader header, RarCrcBinaryReader reader) + : base(header, reader, HeaderType.Protect) { - public ProtectHeader(RarHeader header, RarCrcBinaryReader reader) - : base(header, reader, HeaderType.Protect) + if (IsRar5) { - if (IsRar5) - { - throw new InvalidFormatException("unexpected rar5 record"); - } + throw new InvalidFormatException("unexpected rar5 record"); } - - protected override void ReadFinish(MarkingBinaryReader reader) - { - Version = reader.ReadByte(); - RecSectors = reader.ReadUInt16(); - TotalBlocks = reader.ReadUInt32(); - Mark = reader.ReadBytes(8); - } - - internal uint DataSize => checked((uint)AdditionalDataSize); - internal byte Version { get; private set; } - internal ushort RecSectors { get; private set; } - internal uint TotalBlocks { get; private set; } - internal byte[]? Mark { get; private set; } } + + protected override void ReadFinish(MarkingBinaryReader reader) + { + Version = reader.ReadByte(); + RecSectors = reader.ReadUInt16(); + TotalBlocks = reader.ReadUInt32(); + Mark = reader.ReadBytes(8); + } + + internal uint DataSize => checked((uint)AdditionalDataSize); + internal byte Version { get; private set; } + internal ushort RecSectors { get; private set; } + internal uint TotalBlocks { get; private set; } + internal byte[]? Mark { get; private set; } } diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs index 732bfab4..571da972 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs @@ -1,138 +1,137 @@ -using System; +using System; using System.IO; using SharpCompress.IO; -namespace SharpCompress.Common.Rar.Headers +namespace SharpCompress.Common.Rar.Headers; + +// http://www.forensicswiki.org/w/images/5/5b/RARFileStructure.txt +// https://www.rarlab.com/technote.htm +internal class RarHeader : IRarHeader { - // http://www.forensicswiki.org/w/images/5/5b/RARFileStructure.txt - // https://www.rarlab.com/technote.htm - internal class RarHeader : IRarHeader + private readonly HeaderType _headerType; + private readonly bool _isRar5; + + internal static RarHeader? TryReadBase( + RarCrcBinaryReader reader, + bool isRar5, + ArchiveEncoding archiveEncoding + ) { - private readonly HeaderType _headerType; - private readonly bool _isRar5; - - internal static RarHeader? TryReadBase( - RarCrcBinaryReader reader, - bool isRar5, - ArchiveEncoding archiveEncoding - ) + try { - try - { - return new RarHeader(reader, isRar5, archiveEncoding); - } - catch (EndOfStreamException) - { - return null; - } + return new RarHeader(reader, isRar5, archiveEncoding); } - - private RarHeader(RarCrcBinaryReader reader, bool isRar5, ArchiveEncoding archiveEncoding) + catch (EndOfStreamException) { - _headerType = HeaderType.Null; - _isRar5 = isRar5; - ArchiveEncoding = archiveEncoding; - if (IsRar5) - { - HeaderCrc = reader.ReadUInt32(); - reader.ResetCrc(); - HeaderSize = (int)reader.ReadRarVIntUInt32(3); - reader.Mark(); - HeaderCode = reader.ReadRarVIntByte(); - HeaderFlags = reader.ReadRarVIntUInt16(2); - - if (HasHeaderFlag(HeaderFlagsV5.HAS_EXTRA)) - { - ExtraSize = reader.ReadRarVIntUInt32(); - } - if (HasHeaderFlag(HeaderFlagsV5.HAS_DATA)) - { - AdditionalDataSize = (long)reader.ReadRarVInt(); - } - } - else - { - reader.Mark(); - HeaderCrc = reader.ReadUInt16(); - reader.ResetCrc(); - HeaderCode = reader.ReadByte(); - HeaderFlags = reader.ReadUInt16(); - HeaderSize = reader.ReadInt16(); - if (HasHeaderFlag(HeaderFlagsV4.HAS_DATA)) - { - AdditionalDataSize = reader.ReadUInt32(); - } - } + return null; } - - protected RarHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType) - { - _headerType = headerType; - _isRar5 = header.IsRar5; - HeaderCrc = header.HeaderCrc; - HeaderCode = header.HeaderCode; - HeaderFlags = header.HeaderFlags; - HeaderSize = header.HeaderSize; - ExtraSize = header.ExtraSize; - AdditionalDataSize = header.AdditionalDataSize; - ArchiveEncoding = header.ArchiveEncoding; - ReadFinish(reader); - - int n = RemainingHeaderBytes(reader); - if (n > 0) - { - reader.ReadBytes(n); - } - - VerifyHeaderCrc(reader.GetCrc32()); - } - - protected int RemainingHeaderBytes(MarkingBinaryReader reader) - { - return checked(HeaderSize - (int)reader.CurrentReadByteCount); - } - - protected virtual void ReadFinish(MarkingBinaryReader reader) - { - throw new NotImplementedException(); - } - - private void VerifyHeaderCrc(uint crc32) - { - var b = (IsRar5 ? crc32 : (ushort)crc32) == HeaderCrc; - if (!b) - { - throw new InvalidFormatException("rar header crc mismatch"); - } - } - - public HeaderType HeaderType => _headerType; - - protected bool IsRar5 => _isRar5; - - protected uint HeaderCrc { get; } - - internal byte HeaderCode { get; } - - protected ushort HeaderFlags { get; } - - protected bool HasHeaderFlag(ushort flag) - { - return (HeaderFlags & flag) == flag; - } - - protected int HeaderSize { get; } - - internal ArchiveEncoding ArchiveEncoding { get; } - - /// - /// Extra header size. - /// - protected uint ExtraSize { get; } - - /// - /// Size of additional data (eg file contents) - /// - protected long AdditionalDataSize { get; } } + + private RarHeader(RarCrcBinaryReader reader, bool isRar5, ArchiveEncoding archiveEncoding) + { + _headerType = HeaderType.Null; + _isRar5 = isRar5; + ArchiveEncoding = archiveEncoding; + if (IsRar5) + { + HeaderCrc = reader.ReadUInt32(); + reader.ResetCrc(); + HeaderSize = (int)reader.ReadRarVIntUInt32(3); + reader.Mark(); + HeaderCode = reader.ReadRarVIntByte(); + HeaderFlags = reader.ReadRarVIntUInt16(2); + + if (HasHeaderFlag(HeaderFlagsV5.HAS_EXTRA)) + { + ExtraSize = reader.ReadRarVIntUInt32(); + } + if (HasHeaderFlag(HeaderFlagsV5.HAS_DATA)) + { + AdditionalDataSize = (long)reader.ReadRarVInt(); + } + } + else + { + reader.Mark(); + HeaderCrc = reader.ReadUInt16(); + reader.ResetCrc(); + HeaderCode = reader.ReadByte(); + HeaderFlags = reader.ReadUInt16(); + HeaderSize = reader.ReadInt16(); + if (HasHeaderFlag(HeaderFlagsV4.HAS_DATA)) + { + AdditionalDataSize = reader.ReadUInt32(); + } + } + } + + protected RarHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType) + { + _headerType = headerType; + _isRar5 = header.IsRar5; + HeaderCrc = header.HeaderCrc; + HeaderCode = header.HeaderCode; + HeaderFlags = header.HeaderFlags; + HeaderSize = header.HeaderSize; + ExtraSize = header.ExtraSize; + AdditionalDataSize = header.AdditionalDataSize; + ArchiveEncoding = header.ArchiveEncoding; + ReadFinish(reader); + + var n = RemainingHeaderBytes(reader); + if (n > 0) + { + reader.ReadBytes(n); + } + + VerifyHeaderCrc(reader.GetCrc32()); + } + + protected int RemainingHeaderBytes(MarkingBinaryReader reader) + { + return checked(HeaderSize - (int)reader.CurrentReadByteCount); + } + + protected virtual void ReadFinish(MarkingBinaryReader reader) + { + throw new NotImplementedException(); + } + + private void VerifyHeaderCrc(uint crc32) + { + var b = (IsRar5 ? crc32 : (ushort)crc32) == HeaderCrc; + if (!b) + { + throw new InvalidFormatException("rar header crc mismatch"); + } + } + + public HeaderType HeaderType => _headerType; + + protected bool IsRar5 => _isRar5; + + protected uint HeaderCrc { get; } + + internal byte HeaderCode { get; } + + protected ushort HeaderFlags { get; } + + protected bool HasHeaderFlag(ushort flag) + { + return (HeaderFlags & flag) == flag; + } + + protected int HeaderSize { get; } + + internal ArchiveEncoding ArchiveEncoding { get; } + + /// + /// Extra header size. + /// + protected uint ExtraSize { get; } + + /// + /// Size of additional data (eg file contents) + /// + protected long AdditionalDataSize { get; } } diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index d5641959..37a59d2d 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -3,204 +3,203 @@ using System.IO; using SharpCompress.IO; using SharpCompress.Readers; -namespace SharpCompress.Common.Rar.Headers +namespace SharpCompress.Common.Rar.Headers; + +public class RarHeaderFactory { - public class RarHeaderFactory + private bool _isRar5; + + public RarHeaderFactory(StreamingMode mode, ReaderOptions options) { - private bool _isRar5; + StreamingMode = mode; + Options = options; + } - public RarHeaderFactory(StreamingMode mode, ReaderOptions options) + public ReaderOptions Options { get; } + public StreamingMode StreamingMode { get; } + public bool IsEncrypted { get; private set; } + + public IEnumerable ReadHeaders(Stream stream) + { + var markHeader = MarkHeader.Read( + stream, + Options.LeaveStreamOpen, + Options.LookForHeader + ); + _isRar5 = markHeader.IsRar5; + yield return markHeader; + + RarHeader? header; + while ((header = TryReadNextHeader(stream)) != null) { - StreamingMode = mode; - Options = options; - } - - public ReaderOptions Options { get; } - public StreamingMode StreamingMode { get; } - public bool IsEncrypted { get; private set; } - - public IEnumerable ReadHeaders(Stream stream) - { - var markHeader = MarkHeader.Read( - stream, - Options.LeaveStreamOpen, - Options.LookForHeader - ); - _isRar5 = markHeader.IsRar5; - yield return markHeader; - - RarHeader? header; - while ((header = TryReadNextHeader(stream)) != null) + yield return header; + if (header.HeaderType == HeaderType.EndArchive) { - yield return header; - if (header.HeaderType == HeaderType.EndArchive) - { - // End of archive marker. RAR does not read anything after this header letting to use third - // party tools to add extra information such as a digital signature to archive. - yield break; - } + // End of archive marker. RAR does not read anything after this header letting to use third + // party tools to add extra information such as a digital signature to archive. + yield break; } } + } - private RarHeader? TryReadNextHeader(Stream stream) + private RarHeader? TryReadNextHeader(Stream stream) + { + RarCrcBinaryReader reader; + if (!IsEncrypted) { - RarCrcBinaryReader reader; - if (!IsEncrypted) + reader = new RarCrcBinaryReader(stream); + } + else + { + if (Options.Password is null) { - reader = new RarCrcBinaryReader(stream); + throw new CryptographicException( + "Encrypted Rar archive has no password specified." + ); } - else + reader = new RarCryptoBinaryReader(stream, Options.Password); + } + + var header = RarHeader.TryReadBase(reader, _isRar5, Options.ArchiveEncoding); + if (header is null) + { + return null; + } + switch (header.HeaderCode) + { + case HeaderCodeV.RAR5_ARCHIVE_HEADER: + case HeaderCodeV.RAR4_ARCHIVE_HEADER: { - if (Options.Password is null) + var ah = new ArchiveHeader(header, reader); + if (ah.IsEncrypted == true) { - throw new CryptographicException( - "Encrypted Rar archive has no password specified." - ); - } - reader = new RarCryptoBinaryReader(stream, Options.Password); - } - - var header = RarHeader.TryReadBase(reader, _isRar5, Options.ArchiveEncoding); - if (header is null) - { - return null; - } - switch (header.HeaderCode) - { - case HeaderCodeV.RAR5_ARCHIVE_HEADER: - case HeaderCodeV.RAR4_ARCHIVE_HEADER: - { - var ah = new ArchiveHeader(header, reader); - if (ah.IsEncrypted == true) - { - //!!! rar5 we don't know yet - IsEncrypted = true; - } - return ah; - } - - case HeaderCodeV.RAR4_PROTECT_HEADER: - { - var ph = new ProtectHeader(header, reader); - // skip the recovery record data, we do not use it. - switch (StreamingMode) - { - case StreamingMode.Seekable: - - { - reader.BaseStream.Position += ph.DataSize; - } - break; - case StreamingMode.Streaming: - - { - reader.BaseStream.Skip(ph.DataSize); - } - break; - default: - { - throw new InvalidFormatException("Invalid StreamingMode"); - } - } - - return ph; - } - - case HeaderCodeV.RAR5_SERVICE_HEADER: - { - var fh = new FileHeader(header, reader, HeaderType.Service); - SkipData(fh, reader); - return fh; - } - - case HeaderCodeV.RAR4_NEW_SUB_HEADER: - { - var fh = new FileHeader(header, reader, HeaderType.NewSub); - SkipData(fh, reader); - return fh; - } - - case HeaderCodeV.RAR5_FILE_HEADER: - case HeaderCodeV.RAR4_FILE_HEADER: - { - var fh = new FileHeader(header, reader, HeaderType.File); - switch (StreamingMode) - { - case StreamingMode.Seekable: - - { - fh.DataStartPosition = reader.BaseStream.Position; - reader.BaseStream.Position += fh.CompressedSize; - } - break; - case StreamingMode.Streaming: - - { - var ms = new ReadOnlySubStream( - reader.BaseStream, - fh.CompressedSize - ); - if (fh.R4Salt is null) - { - fh.PackedStream = ms; - } - else - { - fh.PackedStream = new RarCryptoWrapper( - ms, - Options.Password!, - fh.R4Salt - ); - } - } - break; - default: - { - throw new InvalidFormatException("Invalid StreamingMode"); - } - } - return fh; - } - case HeaderCodeV.RAR5_END_ARCHIVE_HEADER: - case HeaderCodeV.RAR4_END_ARCHIVE_HEADER: - { - return new EndArchiveHeader(header, reader); - } - case HeaderCodeV.RAR5_ARCHIVE_ENCRYPTION_HEADER: - { - var ch = new ArchiveCryptHeader(header, reader); + //!!! rar5 we don't know yet IsEncrypted = true; - return ch; - } - default: - { - throw new InvalidFormatException("Unknown Rar Header: " + header.HeaderCode); } + return ah; } - } - private void SkipData(FileHeader fh, RarCrcBinaryReader reader) - { - switch (StreamingMode) + case HeaderCodeV.RAR4_PROTECT_HEADER: { - case StreamingMode.Seekable: + var ph = new ProtectHeader(header, reader); + // skip the recovery record data, we do not use it. + switch (StreamingMode) + { + case StreamingMode.Seekable: + + { + reader.BaseStream.Position += ph.DataSize; + } + break; + case StreamingMode.Streaming: + + { + reader.BaseStream.Skip(ph.DataSize); + } + break; + default: + { + throw new InvalidFormatException("Invalid StreamingMode"); + } + } + + return ph; + } + + case HeaderCodeV.RAR5_SERVICE_HEADER: + { + var fh = new FileHeader(header, reader, HeaderType.Service); + SkipData(fh, reader); + return fh; + } + + case HeaderCodeV.RAR4_NEW_SUB_HEADER: + { + var fh = new FileHeader(header, reader, HeaderType.NewSub); + SkipData(fh, reader); + return fh; + } + + case HeaderCodeV.RAR5_FILE_HEADER: + case HeaderCodeV.RAR4_FILE_HEADER: + { + var fh = new FileHeader(header, reader, HeaderType.File); + switch (StreamingMode) + { + case StreamingMode.Seekable: { fh.DataStartPosition = reader.BaseStream.Position; reader.BaseStream.Position += fh.CompressedSize; } break; - case StreamingMode.Streaming: + case StreamingMode.Streaming: { - //skip the data because it's useless? - reader.BaseStream.Skip(fh.CompressedSize); + var ms = new ReadOnlySubStream( + reader.BaseStream, + fh.CompressedSize + ); + if (fh.R4Salt is null) + { + fh.PackedStream = ms; + } + else + { + fh.PackedStream = new RarCryptoWrapper( + ms, + Options.Password!, + fh.R4Salt + ); + } } break; - default: - { - throw new InvalidFormatException("Invalid StreamingMode"); + default: + { + throw new InvalidFormatException("Invalid StreamingMode"); + } } + return fh; + } + case HeaderCodeV.RAR5_END_ARCHIVE_HEADER: + case HeaderCodeV.RAR4_END_ARCHIVE_HEADER: + { + return new EndArchiveHeader(header, reader); + } + case HeaderCodeV.RAR5_ARCHIVE_ENCRYPTION_HEADER: + { + var ch = new ArchiveCryptHeader(header, reader); + IsEncrypted = true; + return ch; + } + default: + { + throw new InvalidFormatException("Unknown Rar Header: " + header.HeaderCode); + } + } + } + + private void SkipData(FileHeader fh, RarCrcBinaryReader reader) + { + switch (StreamingMode) + { + case StreamingMode.Seekable: + + { + fh.DataStartPosition = reader.BaseStream.Position; + reader.BaseStream.Position += fh.CompressedSize; + } + break; + case StreamingMode.Streaming: + + { + //skip the data because it's useless? + reader.BaseStream.Skip(fh.CompressedSize); + } + break; + default: + { + throw new InvalidFormatException("Invalid StreamingMode"); } } } diff --git a/src/SharpCompress/Common/Rar/Headers/SignHeader.cs b/src/SharpCompress/Common/Rar/Headers/SignHeader.cs index c03a60f1..837ff3dc 100644 --- a/src/SharpCompress/Common/Rar/Headers/SignHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/SignHeader.cs @@ -1,29 +1,28 @@ -using SharpCompress.IO; +using SharpCompress.IO; -namespace SharpCompress.Common.Rar.Headers +namespace SharpCompress.Common.Rar.Headers; + +internal class SignHeader : RarHeader { - internal class SignHeader : RarHeader + protected SignHeader(RarHeader header, RarCrcBinaryReader reader) + : base(header, reader, HeaderType.Sign) { - protected SignHeader(RarHeader header, RarCrcBinaryReader reader) - : base(header, reader, HeaderType.Sign) + if (IsRar5) { - if (IsRar5) - { - throw new InvalidFormatException("unexpected rar5 record"); - } + throw new InvalidFormatException("unexpected rar5 record"); } - - protected override void ReadFinish(MarkingBinaryReader reader) - { - CreationTime = reader.ReadInt32(); - ArcNameSize = reader.ReadInt16(); - UserNameSize = reader.ReadInt16(); - } - - internal int CreationTime { get; private set; } - - internal short ArcNameSize { get; private set; } - - internal short UserNameSize { get; private set; } } + + protected override void ReadFinish(MarkingBinaryReader reader) + { + CreationTime = reader.ReadInt32(); + ArcNameSize = reader.ReadInt16(); + UserNameSize = reader.ReadInt16(); + } + + internal int CreationTime { get; private set; } + + internal short ArcNameSize { get; private set; } + + internal short UserNameSize { get; private set; } } diff --git a/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs b/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs index 34a64d16..586b9a8f 100644 --- a/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs @@ -2,46 +2,45 @@ using System.IO; using SharpCompress.Compressors.Rar; using SharpCompress.IO; -namespace SharpCompress.Common.Rar +namespace SharpCompress.Common.Rar; + +internal class RarCrcBinaryReader : MarkingBinaryReader { - internal class RarCrcBinaryReader : MarkingBinaryReader + private uint _currentCrc; + + public RarCrcBinaryReader(Stream stream) : base(stream) { } + + public uint GetCrc32() { - private uint _currentCrc; + return ~_currentCrc; + } - public RarCrcBinaryReader(Stream stream) : base(stream) { } + public void ResetCrc() + { + _currentCrc = 0xffffffff; + } - public uint GetCrc32() - { - return ~_currentCrc; - } + protected void UpdateCrc(byte b) + { + _currentCrc = RarCRC.CheckCrc(_currentCrc, b); + } - public void ResetCrc() - { - _currentCrc = 0xffffffff; - } + protected byte[] ReadBytesNoCrc(int count) + { + return base.ReadBytes(count); + } - protected void UpdateCrc(byte b) - { - _currentCrc = RarCRC.CheckCrc(_currentCrc, b); - } + public override byte ReadByte() + { + var b = base.ReadByte(); + _currentCrc = RarCRC.CheckCrc(_currentCrc, b); + return b; + } - protected byte[] ReadBytesNoCrc(int count) - { - return base.ReadBytes(count); - } - - public override byte ReadByte() - { - var b = base.ReadByte(); - _currentCrc = RarCRC.CheckCrc(_currentCrc, b); - return b; - } - - public override byte[] ReadBytes(int count) - { - var result = base.ReadBytes(count); - _currentCrc = RarCRC.CheckCrc(_currentCrc, result, 0, result.Length); - return result; - } + public override byte[] ReadBytes(int count) + { + var result = base.ReadBytes(count); + _currentCrc = RarCRC.CheckCrc(_currentCrc, result, 0, result.Length); + return result; } } diff --git a/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs b/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs index 728690e3..d02750ed 100644 --- a/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs @@ -1,110 +1,109 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; -namespace SharpCompress.Common.Rar +namespace SharpCompress.Common.Rar; + +internal sealed class RarCryptoBinaryReader : RarCrcBinaryReader { - internal sealed class RarCryptoBinaryReader : RarCrcBinaryReader + private RarRijndael _rijndael; + private byte[] _salt; + private readonly string _password; + private readonly Queue _data = new Queue(); + private long _readCount; + + public RarCryptoBinaryReader(Stream stream, string password) : base(stream) { - private RarRijndael _rijndael; - private byte[] _salt; - private readonly string _password; - private readonly Queue _data = new Queue(); - private long _readCount; + _password = password; - public RarCryptoBinaryReader(Stream stream, string password) : base(stream) + // coderb: not sure why this was being done at this logical point + //SkipQueue(); + var salt = ReadBytes(8); + + _salt = salt; + _rijndael = RarRijndael.InitializeFrom(_password, salt); + } + + // track read count ourselves rather than using the underlying stream since we buffer + public override long CurrentReadByteCount + { + get => _readCount; + protected set { - _password = password; - - // coderb: not sure why this was being done at this logical point - //SkipQueue(); - byte[] salt = ReadBytes(8); - - _salt = salt; - _rijndael = RarRijndael.InitializeFrom(_password, salt); - } - - // track read count ourselves rather than using the underlying stream since we buffer - public override long CurrentReadByteCount - { - get => _readCount; - protected set - { - // ignore - } - } - - public override void Mark() - { - _readCount = 0; - } - - private bool UseEncryption => _salt != null; - - public override byte ReadByte() - { - if (UseEncryption) - { - return ReadAndDecryptBytes(1)[0]; - } - - _readCount++; - return base.ReadByte(); - } - - public override byte[] ReadBytes(int count) - { - if (UseEncryption) - { - return ReadAndDecryptBytes(count); - } - - _readCount += count; - return base.ReadBytes(count); - } - - private byte[] ReadAndDecryptBytes(int count) - { - int queueSize = _data.Count; - int sizeToRead = count - queueSize; - - if (sizeToRead > 0) - { - int alignedSize = sizeToRead + ((~sizeToRead + 1) & 0xf); - for (int i = 0; i < alignedSize / 16; i++) - { - //long ax = System.currentTimeMillis(); - byte[] cipherText = ReadBytesNoCrc(16); - var readBytes = _rijndael.ProcessBlock(cipherText); - foreach (var readByte in readBytes) - { - _data.Enqueue(readByte); - } - } - } - - var decryptedBytes = new byte[count]; - - for (int i = 0; i < count; i++) - { - var b = _data.Dequeue(); - decryptedBytes[i] = b; - UpdateCrc(b); - } - - _readCount += count; - return decryptedBytes; - } - - public void ClearQueue() - { - _data.Clear(); - } - - public void SkipQueue() - { - var position = BaseStream.Position; - BaseStream.Position = position + _data.Count; - ClearQueue(); + // ignore } } + + public override void Mark() + { + _readCount = 0; + } + + private bool UseEncryption => _salt != null; + + public override byte ReadByte() + { + if (UseEncryption) + { + return ReadAndDecryptBytes(1)[0]; + } + + _readCount++; + return base.ReadByte(); + } + + public override byte[] ReadBytes(int count) + { + if (UseEncryption) + { + return ReadAndDecryptBytes(count); + } + + _readCount += count; + return base.ReadBytes(count); + } + + private byte[] ReadAndDecryptBytes(int count) + { + var queueSize = _data.Count; + var sizeToRead = count - queueSize; + + if (sizeToRead > 0) + { + var alignedSize = sizeToRead + ((~sizeToRead + 1) & 0xf); + for (var i = 0; i < alignedSize / 16; i++) + { + //long ax = System.currentTimeMillis(); + var cipherText = ReadBytesNoCrc(16); + var readBytes = _rijndael.ProcessBlock(cipherText); + foreach (var readByte in readBytes) + { + _data.Enqueue(readByte); + } + } + } + + var decryptedBytes = new byte[count]; + + for (var i = 0; i < count; i++) + { + var b = _data.Dequeue(); + decryptedBytes[i] = b; + UpdateCrc(b); + } + + _readCount += count; + return decryptedBytes; + } + + public void ClearQueue() + { + _data.Clear(); + } + + public void SkipQueue() + { + var position = BaseStream.Position; + BaseStream.Position = position + _data.Count; + ClearQueue(); + } } diff --git a/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs b/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs index 78fee933..385ed2a0 100644 --- a/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs +++ b/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs @@ -2,98 +2,97 @@ using System; using System.Collections.Generic; using System.IO; -namespace SharpCompress.Common.Rar +namespace SharpCompress.Common.Rar; + +internal sealed class RarCryptoWrapper : Stream { - internal sealed class RarCryptoWrapper : Stream + private readonly Stream _actualStream; + private readonly byte[] _salt; + private RarRijndael _rijndael; + private readonly Queue _data = new Queue(); + + public RarCryptoWrapper(Stream actualStream, string password, byte[] salt) { - private readonly Stream _actualStream; - private readonly byte[] _salt; - private RarRijndael _rijndael; - private readonly Queue _data = new Queue(); + _actualStream = actualStream; + _salt = salt; + _rijndael = RarRijndael.InitializeFrom(password, salt); + } - public RarCryptoWrapper(Stream actualStream, string password, byte[] salt) + public override void Flush() + { + throw new NotSupportedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (_salt is null) { - _actualStream = actualStream; - _salt = salt; - _rijndael = RarRijndael.InitializeFrom(password, salt); + return _actualStream.Read(buffer, offset, count); } + return ReadAndDecrypt(buffer, offset, count); + } - public override void Flush() - { - throw new NotSupportedException(); - } + public int ReadAndDecrypt(byte[] buffer, int offset, int count) + { + var queueSize = _data.Count; + var sizeToRead = count - queueSize; - public override long Seek(long offset, SeekOrigin origin) + if (sizeToRead > 0) { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - if (_salt is null) + var alignedSize = sizeToRead + ((~sizeToRead + 1) & 0xf); + Span cipherText = stackalloc byte[RarRijndael.CRYPTO_BLOCK_SIZE]; + for (var i = 0; i < alignedSize / 16; i++) { - return _actualStream.Read(buffer, offset, count); - } - return ReadAndDecrypt(buffer, offset, count); - } + //long ax = System.currentTimeMillis(); + _actualStream.Read(cipherText); - public int ReadAndDecrypt(byte[] buffer, int offset, int count) - { - int queueSize = _data.Count; - int sizeToRead = count - queueSize; - - if (sizeToRead > 0) - { - int alignedSize = sizeToRead + ((~sizeToRead + 1) & 0xf); - Span cipherText = stackalloc byte[RarRijndael.CRYPTO_BLOCK_SIZE]; - for (int i = 0; i < alignedSize / 16; i++) + var readBytes = _rijndael.ProcessBlock(cipherText); + foreach (var readByte in readBytes) { - //long ax = System.currentTimeMillis(); - _actualStream.Read(cipherText); - - var readBytes = _rijndael.ProcessBlock(cipherText); - foreach (var readByte in readBytes) - { - _data.Enqueue(readByte); - } - } - - for (int i = 0; i < count; i++) - { - buffer[offset + i] = _data.Dequeue(); + _data.Enqueue(readByte); } } - return count; - } - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } - - public override bool CanRead => true; - - public override bool CanSeek => false; - - public override bool CanWrite => false; - - public override long Length => throw new NotSupportedException(); - - public override long Position { get; set; } - - protected override void Dispose(bool disposing) - { - if (_rijndael != null) + for (var i = 0; i < count; i++) { - _rijndael.Dispose(); - _rijndael = null!; + buffer[offset + i] = _data.Dequeue(); } - base.Dispose(disposing); } + return count; + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override long Length => throw new NotSupportedException(); + + public override long Position { get; set; } + + protected override void Dispose(bool disposing) + { + if (_rijndael != null) + { + _rijndael.Dispose(); + _rijndael = null!; + } + base.Dispose(disposing); } } diff --git a/src/SharpCompress/Common/Rar/RarEntry.cs b/src/SharpCompress/Common/Rar/RarEntry.cs index 05ab67ec..54c12b8c 100644 --- a/src/SharpCompress/Common/Rar/RarEntry.cs +++ b/src/SharpCompress/Common/Rar/RarEntry.cs @@ -1,74 +1,73 @@ -using System; +using System; using SharpCompress.Common.Rar.Headers; -namespace SharpCompress.Common.Rar +namespace SharpCompress.Common.Rar; + +public abstract class RarEntry : Entry { - public abstract class RarEntry : Entry + internal abstract FileHeader FileHeader { get; } + + /// + /// As the V2017 port isn't complete, add this check to use the legacy Rar code. + /// + internal bool IsRarV3 => + FileHeader.CompressionAlgorithm == 20 + || FileHeader.CompressionAlgorithm == 26 + || FileHeader.CompressionAlgorithm == 29 + || FileHeader.CompressionAlgorithm == 36; //Nanook - Added 20+26 as Test arc from WinRar2.8 (algo 20) was failing with 2017 code + + /// + /// The File's 32 bit CRC Hash + /// + public override long Crc => FileHeader.FileCrc; + + /// + /// The path of the file internal to the Rar Archive. + /// + public override string Key => FileHeader.FileName; + + public override string? LinkTarget => null; + + /// + /// The entry last modified time in the archive, if recorded + /// + public override DateTime? LastModifiedTime => FileHeader.FileLastModifiedTime; + + /// + /// The entry create time in the archive, if recorded + /// + public override DateTime? CreatedTime => FileHeader.FileCreatedTime; + + /// + /// The entry last accessed time in the archive, if recorded + /// + public override DateTime? LastAccessedTime => FileHeader.FileLastAccessedTime; + + /// + /// The entry time whend archived, if recorded + /// + public override DateTime? ArchivedTime => FileHeader.FileArchivedTime; + + /// + /// Entry is password protected and encrypted and cannot be extracted. + /// + public override bool IsEncrypted => FileHeader.IsEncrypted; + + /// + /// Entry is password protected and encrypted and cannot be extracted. + /// + public override bool IsDirectory => FileHeader.IsDirectory; + + public override bool IsSplitAfter => FileHeader.IsSplitAfter; + + public override string ToString() { - internal abstract FileHeader FileHeader { get; } - - /// - /// As the V2017 port isn't complete, add this check to use the legacy Rar code. - /// - internal bool IsRarV3 => - FileHeader.CompressionAlgorithm == 20 - || FileHeader.CompressionAlgorithm == 26 - || FileHeader.CompressionAlgorithm == 29 - || FileHeader.CompressionAlgorithm == 36; //Nanook - Added 20+26 as Test arc from WinRar2.8 (algo 20) was failing with 2017 code - - /// - /// The File's 32 bit CRC Hash - /// - public override long Crc => FileHeader.FileCrc; - - /// - /// The path of the file internal to the Rar Archive. - /// - public override string Key => FileHeader.FileName; - - public override string? LinkTarget => null; - - /// - /// The entry last modified time in the archive, if recorded - /// - public override DateTime? LastModifiedTime => FileHeader.FileLastModifiedTime; - - /// - /// The entry create time in the archive, if recorded - /// - public override DateTime? CreatedTime => FileHeader.FileCreatedTime; - - /// - /// The entry last accessed time in the archive, if recorded - /// - public override DateTime? LastAccessedTime => FileHeader.FileLastAccessedTime; - - /// - /// The entry time whend archived, if recorded - /// - public override DateTime? ArchivedTime => FileHeader.FileArchivedTime; - - /// - /// Entry is password protected and encrypted and cannot be extracted. - /// - public override bool IsEncrypted => FileHeader.IsEncrypted; - - /// - /// Entry is password protected and encrypted and cannot be extracted. - /// - public override bool IsDirectory => FileHeader.IsDirectory; - - public override bool IsSplitAfter => FileHeader.IsSplitAfter; - - public override string ToString() - { - return string.Format( - "Entry Path: {0} Compressed Size: {1} Uncompressed Size: {2} CRC: {3}", - Key, - CompressedSize, - Size, - Crc - ); - } + return string.Format( + "Entry Path: {0} Compressed Size: {1} Uncompressed Size: {2} CRC: {3}", + Key, + CompressedSize, + Size, + Crc + ); } } diff --git a/src/SharpCompress/Common/Rar/RarFilePart.cs b/src/SharpCompress/Common/Rar/RarFilePart.cs index b57ab184..fa99614b 100644 --- a/src/SharpCompress/Common/Rar/RarFilePart.cs +++ b/src/SharpCompress/Common/Rar/RarFilePart.cs @@ -1,27 +1,26 @@ using System.IO; using SharpCompress.Common.Rar.Headers; -namespace SharpCompress.Common.Rar +namespace SharpCompress.Common.Rar; + +/// +/// This represents a single file part that exists in a rar volume. A compressed file is one or many file parts that are spread across one or may rar parts. +/// +internal abstract class RarFilePart : FilePart { - /// - /// This represents a single file part that exists in a rar volume. A compressed file is one or many file parts that are spread across one or may rar parts. - /// - internal abstract class RarFilePart : FilePart + internal RarFilePart(MarkHeader mh, FileHeader fh, int index) : base(fh.ArchiveEncoding) { - internal RarFilePart(MarkHeader mh, FileHeader fh, int index) : base(fh.ArchiveEncoding) - { - MarkHeader = mh; - FileHeader = fh; - Index = index; - } + MarkHeader = mh; + FileHeader = fh; + Index = index; + } - internal MarkHeader MarkHeader { get; } + internal MarkHeader MarkHeader { get; } - internal FileHeader FileHeader { get; } + internal FileHeader FileHeader { get; } - internal override Stream? GetRawStream() - { - return null; - } + internal override Stream? GetRawStream() + { + return null; } } diff --git a/src/SharpCompress/Common/Rar/RarRijndael.cs b/src/SharpCompress/Common/Rar/RarRijndael.cs index a2a5731f..94592639 100644 --- a/src/SharpCompress/Common/Rar/RarRijndael.cs +++ b/src/SharpCompress/Common/Rar/RarRijndael.cs @@ -1,115 +1,114 @@ -#nullable disable +#nullable disable using System; using System.Security.Cryptography; using System.Text; using SharpCompress.Crypto; -namespace SharpCompress.Common.Rar +namespace SharpCompress.Common.Rar; + +internal class RarRijndael : IDisposable { - internal class RarRijndael : IDisposable + internal const int CRYPTO_BLOCK_SIZE = 16; + + private readonly string _password; + private readonly byte[] _salt; + private byte[] _aesInitializationVector; + private RijndaelEngine _rijndael; + + private RarRijndael(string password, byte[] salt) { - internal const int CRYPTO_BLOCK_SIZE = 16; - - private readonly string _password; - private readonly byte[] _salt; - private byte[] _aesInitializationVector; - private RijndaelEngine _rijndael; - - private RarRijndael(string password, byte[] salt) - { - _password = password; - _salt = salt; - } - - private void Initialize() - { - _rijndael = new RijndaelEngine(); - _aesInitializationVector = new byte[CRYPTO_BLOCK_SIZE]; - int rawLength = 2 * _password.Length; - byte[] rawPassword = new byte[rawLength + 8]; - byte[] passwordBytes = Encoding.UTF8.GetBytes(_password); - for (int i = 0; i < _password.Length; i++) - { - rawPassword[i * 2] = passwordBytes[i]; - rawPassword[i * 2 + 1] = 0; - } - for (int i = 0; i < _salt.Length; i++) - { - rawPassword[i + rawLength] = _salt[i]; - } - - const int noOfRounds = (1 << 18); - const int iblock = 3; - byte[] digest; - byte[] data = new byte[(rawPassword.Length + iblock) * noOfRounds]; - - //TODO slow code below, find ways to optimize - for (int i = 0; i < noOfRounds; i++) - { - rawPassword.CopyTo(data, i * (rawPassword.Length + iblock)); - - data[i * (rawPassword.Length + iblock) + rawPassword.Length + 0] = (byte)i; - data[i * (rawPassword.Length + iblock) + rawPassword.Length + 1] = (byte)(i >> 8); - data[i * (rawPassword.Length + iblock) + rawPassword.Length + 2] = (byte)( - i >> CRYPTO_BLOCK_SIZE - ); - - if (i % (noOfRounds / CRYPTO_BLOCK_SIZE) == 0) - { - digest = SHA1.Create() - .ComputeHash(data, 0, (i + 1) * (rawPassword.Length + iblock)); - _aesInitializationVector[i / (noOfRounds / CRYPTO_BLOCK_SIZE)] = digest[19]; - } - } - digest = SHA1.Create().ComputeHash(data); - //slow code ends - - byte[] aesKey = new byte[CRYPTO_BLOCK_SIZE]; - for (int i = 0; i < 4; i++) - { - for (int j = 0; j < 4; j++) - { - aesKey[i * 4 + j] = (byte)( - ( - (digest[i * 4] * 0x1000000) & 0xff000000 - | (uint)((digest[i * 4 + 1] * 0x10000) & 0xff0000) - | (uint)((digest[i * 4 + 2] * 0x100) & 0xff00) - | (uint)(digest[i * 4 + 3] & 0xff) - ) >> (j * 8) - ); - } - } - - _rijndael.Init(false, new KeyParameter(aesKey)); - } - - public static RarRijndael InitializeFrom(string password, byte[] salt) - { - var rijndael = new RarRijndael(password, salt); - rijndael.Initialize(); - return rijndael; - } - - public byte[] ProcessBlock(ReadOnlySpan cipherText) - { - Span plainText = stackalloc byte[CRYPTO_BLOCK_SIZE]; // 16 bytes - byte[] decryptedBytes = new byte[CRYPTO_BLOCK_SIZE]; - _rijndael.ProcessBlock(cipherText, plainText); - - for (int j = 0; j < CRYPTO_BLOCK_SIZE; j++) - { - decryptedBytes[j] = (byte)(plainText[j] ^ _aesInitializationVector[j % 16]); //32:114, 33:101 - } - - for (int j = 0; j < _aesInitializationVector.Length; j++) - { - _aesInitializationVector[j] = cipherText[j]; - } - - return decryptedBytes; - } - - public void Dispose() { } + _password = password; + _salt = salt; } + + private void Initialize() + { + _rijndael = new RijndaelEngine(); + _aesInitializationVector = new byte[CRYPTO_BLOCK_SIZE]; + var rawLength = 2 * _password.Length; + var rawPassword = new byte[rawLength + 8]; + var passwordBytes = Encoding.UTF8.GetBytes(_password); + for (var i = 0; i < _password.Length; i++) + { + rawPassword[i * 2] = passwordBytes[i]; + rawPassword[(i * 2) + 1] = 0; + } + for (var i = 0; i < _salt.Length; i++) + { + rawPassword[i + rawLength] = _salt[i]; + } + + const int noOfRounds = (1 << 18); + const int iblock = 3; + byte[] digest; + var data = new byte[(rawPassword.Length + iblock) * noOfRounds]; + + //TODO slow code below, find ways to optimize + for (var i = 0; i < noOfRounds; i++) + { + rawPassword.CopyTo(data, i * (rawPassword.Length + iblock)); + + data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 0] = (byte)i; + data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 1] = (byte)(i >> 8); + data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 2] = (byte)( + i >> CRYPTO_BLOCK_SIZE + ); + + if (i % (noOfRounds / CRYPTO_BLOCK_SIZE) == 0) + { + digest = SHA1.Create() + .ComputeHash(data, 0, (i + 1) * (rawPassword.Length + iblock)); + _aesInitializationVector[i / (noOfRounds / CRYPTO_BLOCK_SIZE)] = digest[19]; + } + } + digest = SHA1.Create().ComputeHash(data); + //slow code ends + + var aesKey = new byte[CRYPTO_BLOCK_SIZE]; + for (var i = 0; i < 4; i++) + { + for (var j = 0; j < 4; j++) + { + aesKey[(i * 4) + j] = (byte)( + ( + ((digest[i * 4] * 0x1000000) & 0xff000000) + | (uint)((digest[(i * 4) + 1] * 0x10000) & 0xff0000) + | (uint)((digest[(i * 4) + 2] * 0x100) & 0xff00) + | (uint)(digest[(i * 4) + 3] & 0xff) + ) >> (j * 8) + ); + } + } + + _rijndael.Init(false, new KeyParameter(aesKey)); + } + + public static RarRijndael InitializeFrom(string password, byte[] salt) + { + var rijndael = new RarRijndael(password, salt); + rijndael.Initialize(); + return rijndael; + } + + public byte[] ProcessBlock(ReadOnlySpan cipherText) + { + Span plainText = stackalloc byte[CRYPTO_BLOCK_SIZE]; // 16 bytes + var decryptedBytes = new byte[CRYPTO_BLOCK_SIZE]; + _rijndael.ProcessBlock(cipherText, plainText); + + for (var j = 0; j < CRYPTO_BLOCK_SIZE; j++) + { + decryptedBytes[j] = (byte)(plainText[j] ^ _aesInitializationVector[j % 16]); //32:114, 33:101 + } + + for (var j = 0; j < _aesInitializationVector.Length; j++) + { + _aesInitializationVector[j] = cipherText[j]; + } + + return decryptedBytes; + } + + public void Dispose() { } } diff --git a/src/SharpCompress/Common/Rar/RarVolume.cs b/src/SharpCompress/Common/Rar/RarVolume.cs index 8e6a8026..eac09e33 100644 --- a/src/SharpCompress/Common/Rar/RarVolume.cs +++ b/src/SharpCompress/Common/Rar/RarVolume.cs @@ -6,169 +6,187 @@ using SharpCompress.Common.Rar.Headers; using SharpCompress.IO; using SharpCompress.Readers; -namespace SharpCompress.Common.Rar -{ - /// - /// A RarArchiveVolume is a single rar file that may or may not be a split RarArchive. A Rar Archive is one to many Rar Parts - /// - public abstract class RarVolume : Volume - { - private readonly RarHeaderFactory _headerFactory; - internal int _maxCompressionAlgorithm; +namespace SharpCompress.Common.Rar; - internal RarVolume(StreamingMode mode, Stream stream, ReaderOptions options, int index = 0) - : base(stream, options, index) - { - _headerFactory = new RarHeaderFactory(mode, options); - } +/// +/// A RarArchiveVolume is a single rar file that may or may not be a split RarArchive. A Rar Archive is one to many Rar Parts +/// +public abstract class RarVolume : Volume +{ + private readonly RarHeaderFactory _headerFactory; + internal int _maxCompressionAlgorithm; + + internal RarVolume(StreamingMode mode, Stream stream, ReaderOptions options, int index = 0) + : base(stream, options, index) + { + _headerFactory = new RarHeaderFactory(mode, options); + } #nullable disable - internal ArchiveHeader ArchiveHeader { get; private set; } + internal ArchiveHeader ArchiveHeader { get; private set; } #nullable enable - internal StreamingMode Mode => _headerFactory.StreamingMode; + internal StreamingMode Mode => _headerFactory.StreamingMode; - internal abstract IEnumerable ReadFileParts(); + internal abstract IEnumerable ReadFileParts(); - internal abstract RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader); + internal abstract RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader); - internal IEnumerable GetVolumeFileParts() + internal IEnumerable GetVolumeFileParts() + { + MarkHeader? lastMarkHeader = null; + foreach (var header in _headerFactory.ReadHeaders(Stream)) { - MarkHeader? lastMarkHeader = null; - foreach (var header in _headerFactory.ReadHeaders(Stream)) + switch (header.HeaderType) { - switch (header.HeaderType) + case HeaderType.Mark: + { - case HeaderType.Mark: - - { - lastMarkHeader = (MarkHeader)header; - } - break; - case HeaderType.Archive: - - { - ArchiveHeader = (ArchiveHeader)header; - } - break; - case HeaderType.File: - - { - var fh = (FileHeader)header; - if (_maxCompressionAlgorithm < fh.CompressionAlgorithm) - _maxCompressionAlgorithm = fh.CompressionAlgorithm; - yield return CreateFilePart(lastMarkHeader!, fh); - } - break; - case HeaderType.Service: - - { - var fh = (FileHeader)header; - if (fh.FileName == "CMT") - { - var part = CreateFilePart(lastMarkHeader!, fh); - var buffer = new byte[fh.CompressedSize]; - part.GetCompressedStream().Read(buffer, 0, buffer.Length); - Comment = System.Text.Encoding.UTF8.GetString( - buffer, - 0, - buffer.Length - 1 - ); - } - } - break; + lastMarkHeader = (MarkHeader)header; } - } - } + break; + case HeaderType.Archive: - private void EnsureArchiveHeaderLoaded() - { - if (ArchiveHeader is null) - { - if (Mode == StreamingMode.Streaming) { - throw new InvalidOperationException( - "ArchiveHeader should never been null in a streaming read." - ); + ArchiveHeader = (ArchiveHeader)header; } + break; + case HeaderType.File: - // we only want to load the archive header to avoid overhead but have to do the nasty thing and reset the stream - GetVolumeFileParts().First(); - Stream.Position = 0; + { + var fh = (FileHeader)header; + if (_maxCompressionAlgorithm < fh.CompressionAlgorithm) + { + _maxCompressionAlgorithm = fh.CompressionAlgorithm; + } + + yield return CreateFilePart(lastMarkHeader!, fh); + } + break; + case HeaderType.Service: + + { + var fh = (FileHeader)header; + if (fh.FileName == "CMT") + { + var part = CreateFilePart(lastMarkHeader!, fh); + var buffer = new byte[fh.CompressedSize]; + part.GetCompressedStream().Read(buffer, 0, buffer.Length); + Comment = System.Text.Encoding.UTF8.GetString( + buffer, + 0, + buffer.Length - 1 + ); + } + } + break; } } - - /// - /// RarArchive is the first volume of a multi-part archive. - /// Only Rar 3.0 format and higher - /// - public override bool IsFirstVolume - { - get - { - EnsureArchiveHeaderLoaded(); - return ArchiveHeader.IsFirstVolume; - } - } - - /// - /// RarArchive is part of a multi-part archive. - /// - public override bool IsMultiVolume - { - get - { - EnsureArchiveHeaderLoaded(); - return ArchiveHeader.IsVolume; - } - } - - /// - /// RarArchive is SOLID (this means the Archive saved bytes by reusing information which helps for archives containing many small files). - /// Currently, SharpCompress cannot decompress SOLID archives. - /// - public bool IsSolidArchive - { - get - { - EnsureArchiveHeaderLoaded(); - return ArchiveHeader.IsSolid; - } - } - - public int MinVersion - { - get - { - EnsureArchiveHeaderLoaded(); - if (_maxCompressionAlgorithm >= 50) - return 5; //5-6 - else if (_maxCompressionAlgorithm >= 29) - return 3; //3-4 - else if (_maxCompressionAlgorithm >= 20) - return 2; //2 - else - return 1; - } - } - - public int MaxVersion - { - get - { - EnsureArchiveHeaderLoaded(); - if (_maxCompressionAlgorithm >= 50) - return 6; //5-6 - else if (_maxCompressionAlgorithm >= 29) - return 4; //3-4 - else if (_maxCompressionAlgorithm >= 20) - return 2; //2 - else - return 1; - } - } - - public string? Comment { get; internal set; } } + + private void EnsureArchiveHeaderLoaded() + { + if (ArchiveHeader is null) + { + if (Mode == StreamingMode.Streaming) + { + throw new InvalidOperationException( + "ArchiveHeader should never been null in a streaming read." + ); + } + + // we only want to load the archive header to avoid overhead but have to do the nasty thing and reset the stream + GetVolumeFileParts().First(); + Stream.Position = 0; + } + } + + /// + /// RarArchive is the first volume of a multi-part archive. + /// Only Rar 3.0 format and higher + /// + public override bool IsFirstVolume + { + get + { + EnsureArchiveHeaderLoaded(); + return ArchiveHeader.IsFirstVolume; + } + } + + /// + /// RarArchive is part of a multi-part archive. + /// + public override bool IsMultiVolume + { + get + { + EnsureArchiveHeaderLoaded(); + return ArchiveHeader.IsVolume; + } + } + + /// + /// RarArchive is SOLID (this means the Archive saved bytes by reusing information which helps for archives containing many small files). + /// Currently, SharpCompress cannot decompress SOLID archives. + /// + public bool IsSolidArchive + { + get + { + EnsureArchiveHeaderLoaded(); + return ArchiveHeader.IsSolid; + } + } + + public int MinVersion + { + get + { + EnsureArchiveHeaderLoaded(); + if (_maxCompressionAlgorithm >= 50) + { + return 5; //5-6 + } + else if (_maxCompressionAlgorithm >= 29) + { + return 3; //3-4 + } + else if (_maxCompressionAlgorithm >= 20) + { + return 2; //2 + } + else + { + return 1; + } + } + } + + public int MaxVersion + { + get + { + EnsureArchiveHeaderLoaded(); + if (_maxCompressionAlgorithm >= 50) + { + return 6; //5-6 + } + else if (_maxCompressionAlgorithm >= 29) + { + return 4; //3-4 + } + else if (_maxCompressionAlgorithm >= 20) + { + return 2; //2 + } + else + { + return 1; + } + } + } + + public string? Comment { get; internal set; } } diff --git a/src/SharpCompress/Common/ReaderExtractionEventArgs.cs b/src/SharpCompress/Common/ReaderExtractionEventArgs.cs index 87362b68..7c4363e2 100644 --- a/src/SharpCompress/Common/ReaderExtractionEventArgs.cs +++ b/src/SharpCompress/Common/ReaderExtractionEventArgs.cs @@ -1,18 +1,17 @@ -using System; +using System; using SharpCompress.Readers; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public sealed class ReaderExtractionEventArgs : EventArgs { - public sealed class ReaderExtractionEventArgs : EventArgs + internal ReaderExtractionEventArgs(T entry, ReaderProgress? readerProgress = null) { - internal ReaderExtractionEventArgs(T entry, ReaderProgress? readerProgress = null) - { - Item = entry; - ReaderProgress = readerProgress; - } - - public T Item { get; } - - public ReaderProgress? ReaderProgress { get; } + Item = entry; + ReaderProgress = readerProgress; } + + public T Item { get; } + + public ReaderProgress? ReaderProgress { get; } } diff --git a/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs b/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs index e16751e6..dbacf332 100644 --- a/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs +++ b/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable using System; using System.Collections.Generic; @@ -6,186 +6,167 @@ using System.IO; using SharpCompress.Compressors.LZMA; using SharpCompress.Compressors.LZMA.Utilites; -namespace SharpCompress.Common.SevenZip +namespace SharpCompress.Common.SevenZip; + +internal class ArchiveDatabase { - internal class ArchiveDatabase + internal byte _majorVersion; + internal byte _minorVersion; + internal long _startPositionAfterHeader; + internal long _dataStartPosition; + + internal List _packSizes = new List(); + internal List _packCrCs = new List(); + internal List _folders = new List(); + internal List _numUnpackStreamsVector; + internal List _files = new List(); + + internal List _packStreamStartPositions = new List(); + internal List _folderStartFileIndex = new List(); + internal List _fileIndexToFolderIndexMap = new List(); + + internal IPasswordProvider PasswordProvider { get; } + + public ArchiveDatabase(IPasswordProvider passwordProvider) { - internal byte _majorVersion; - internal byte _minorVersion; - internal long _startPositionAfterHeader; - internal long _dataStartPosition; + PasswordProvider = passwordProvider; + } - internal List _packSizes = new List(); - internal List _packCrCs = new List(); - internal List _folders = new List(); - internal List _numUnpackStreamsVector; - internal List _files = new List(); + internal void Clear() + { + _packSizes.Clear(); + _packCrCs.Clear(); + _folders.Clear(); + _numUnpackStreamsVector = null!; + _files.Clear(); - internal List _packStreamStartPositions = new List(); - internal List _folderStartFileIndex = new List(); - internal List _fileIndexToFolderIndexMap = new List(); + _packStreamStartPositions.Clear(); + _folderStartFileIndex.Clear(); + _fileIndexToFolderIndexMap.Clear(); + } - internal IPasswordProvider PasswordProvider { get; } + internal bool IsEmpty() + { + return _packSizes.Count == 0 + && _packCrCs.Count == 0 + && _folders.Count == 0 + && _numUnpackStreamsVector.Count == 0 + && _files.Count == 0; + } - public ArchiveDatabase(IPasswordProvider passwordProvider) + private void FillStartPos() + { + _packStreamStartPositions.Clear(); + + long startPos = 0; + for (var i = 0; i < _packSizes.Count; i++) { - PasswordProvider = passwordProvider; - } - - internal void Clear() - { - _packSizes.Clear(); - _packCrCs.Clear(); - _folders.Clear(); - _numUnpackStreamsVector = null!; - _files.Clear(); - - _packStreamStartPositions.Clear(); - _folderStartFileIndex.Clear(); - _fileIndexToFolderIndexMap.Clear(); - } - - internal bool IsEmpty() - { - return _packSizes.Count == 0 - && _packCrCs.Count == 0 - && _folders.Count == 0 - && _numUnpackStreamsVector.Count == 0 - && _files.Count == 0; - } - - private void FillStartPos() - { - _packStreamStartPositions.Clear(); - - long startPos = 0; - for (int i = 0; i < _packSizes.Count; i++) - { - _packStreamStartPositions.Add(startPos); - startPos += _packSizes[i]; - } - } - - private void FillFolderStartFileIndex() - { - _folderStartFileIndex.Clear(); - _fileIndexToFolderIndexMap.Clear(); - - int folderIndex = 0; - int indexInFolder = 0; - for (int i = 0; i < _files.Count; i++) - { - CFileItem file = _files[i]; - - bool emptyStream = !file.HasStream; - - if (emptyStream && indexInFolder == 0) - { - _fileIndexToFolderIndexMap.Add(-1); - continue; - } - - if (indexInFolder == 0) - { - // v3.13 incorrectly worked with empty folders - // v4.07: Loop for skipping empty folders - for (; ; ) - { - if (folderIndex >= _folders.Count) - { - throw new InvalidOperationException(); - } - - _folderStartFileIndex.Add(i); // check it - - if (_numUnpackStreamsVector![folderIndex] != 0) - { - break; - } - - folderIndex++; - } - } - - _fileIndexToFolderIndexMap.Add(folderIndex); - - if (emptyStream) - { - continue; - } - - indexInFolder++; - - if (indexInFolder >= _numUnpackStreamsVector![folderIndex]) - { - folderIndex++; - indexInFolder = 0; - } - } - } - - public void Fill() - { - FillStartPos(); - FillFolderStartFileIndex(); - } - - internal long GetFolderStreamPos(CFolder folder, int indexInFolder) - { - int index = folder._firstPackStreamId + indexInFolder; - return _dataStartPosition + _packStreamStartPositions[index]; - } - - internal long GetFolderFullPackSize(int folderIndex) - { - int packStreamIndex = _folders[folderIndex]._firstPackStreamId; - CFolder folder = _folders[folderIndex]; - - long size = 0; - for (int i = 0; i < folder._packStreams.Count; i++) - { - size += _packSizes[packStreamIndex + i]; - } - - return size; - } - - internal Stream GetFolderStream(Stream stream, CFolder folder, IPasswordProvider pw) - { - int packStreamIndex = folder._firstPackStreamId; - long folderStartPackPos = GetFolderStreamPos(folder, 0); - int count = folder._packStreams.Count; - long[] packSizes = new long[count]; - for (int j = 0; j < count; j++) - { - packSizes[j] = _packSizes[packStreamIndex + j]; - } - - return DecoderStreamHelper.CreateDecoderStream( - stream, - folderStartPackPos, - packSizes, - folder, - pw - ); - } - - private long GetFolderPackStreamSize(int folderIndex, int streamIndex) - { - return _packSizes[_folders[folderIndex]._firstPackStreamId + streamIndex]; - } - - private long GetFilePackSize(int fileIndex) - { - int folderIndex = _fileIndexToFolderIndexMap[fileIndex]; - if (folderIndex != -1) - { - if (_folderStartFileIndex[folderIndex] == fileIndex) - { - return GetFolderFullPackSize(folderIndex); - } - } - return 0; + _packStreamStartPositions.Add(startPos); + startPos += _packSizes[i]; } } + + private void FillFolderStartFileIndex() + { + _folderStartFileIndex.Clear(); + _fileIndexToFolderIndexMap.Clear(); + + var folderIndex = 0; + var indexInFolder = 0; + for (var i = 0; i < _files.Count; i++) + { + var file = _files[i]; + + var emptyStream = !file.HasStream; + + if (emptyStream && indexInFolder == 0) + { + _fileIndexToFolderIndexMap.Add(-1); + continue; + } + + if (indexInFolder == 0) + { + // v3.13 incorrectly worked with empty folders + // v4.07: Loop for skipping empty folders + for (; ; ) + { + if (folderIndex >= _folders.Count) + { + throw new InvalidOperationException(); + } + + _folderStartFileIndex.Add(i); // check it + + if (_numUnpackStreamsVector![folderIndex] != 0) + { + break; + } + + folderIndex++; + } + } + + _fileIndexToFolderIndexMap.Add(folderIndex); + + if (emptyStream) + { + continue; + } + + indexInFolder++; + + if (indexInFolder >= _numUnpackStreamsVector![folderIndex]) + { + folderIndex++; + indexInFolder = 0; + } + } + } + + public void Fill() + { + FillStartPos(); + FillFolderStartFileIndex(); + } + + internal long GetFolderStreamPos(CFolder folder, int indexInFolder) + { + var index = folder._firstPackStreamId + indexInFolder; + return _dataStartPosition + _packStreamStartPositions[index]; + } + + internal long GetFolderFullPackSize(int folderIndex) + { + var packStreamIndex = _folders[folderIndex]._firstPackStreamId; + var folder = _folders[folderIndex]; + + long size = 0; + for (var i = 0; i < folder._packStreams.Count; i++) + { + size += _packSizes[packStreamIndex + i]; + } + + return size; + } + + internal Stream GetFolderStream(Stream stream, CFolder folder, IPasswordProvider pw) + { + var packStreamIndex = folder._firstPackStreamId; + var folderStartPackPos = GetFolderStreamPos(folder, 0); + var count = folder._packStreams.Count; + var packSizes = new long[count]; + for (var j = 0; j < count; j++) + { + packSizes[j] = _packSizes[packStreamIndex + j]; + } + + return DecoderStreamHelper.CreateDecoderStream( + stream, + folderStartPackPos, + packSizes, + folder, + pw + ); + } } diff --git a/src/SharpCompress/Common/SevenZip/ArchiveReader.cs b/src/SharpCompress/Common/SevenZip/ArchiveReader.cs index babae147..908fb453 100644 --- a/src/SharpCompress/Common/SevenZip/ArchiveReader.cs +++ b/src/SharpCompress/Common/SevenZip/ArchiveReader.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable using System; using System.Collections.Generic; @@ -9,1704 +9,1696 @@ using SharpCompress.Compressors.LZMA; using SharpCompress.Compressors.LZMA.Utilites; using SharpCompress.IO; -namespace SharpCompress.Common.SevenZip +namespace SharpCompress.Common.SevenZip; + +internal class ArchiveReader { - internal class ArchiveReader + internal Stream _stream; + internal Stack _readerStack = new Stack(); + internal DataReader _currentReader; + internal long _streamOrigin; + internal long _streamEnding; + internal byte[] _header; + + private readonly Dictionary _cachedStreams = new Dictionary(); + + internal void AddByteStream(byte[] buffer, int offset, int length) { - internal Stream _stream; - internal Stack _readerStack = new Stack(); - internal DataReader _currentReader; - internal long _streamOrigin; - internal long _streamEnding; - internal byte[] _header; + _readerStack.Push(_currentReader); + _currentReader = new DataReader(buffer, offset, length); + } - private readonly Dictionary _cachedStreams = new Dictionary(); + internal void DeleteByteStream() + { + _currentReader = _readerStack.Pop(); + } - internal void AddByteStream(byte[] buffer, int offset, int length) + #region Private Methods - Data Reader + + internal byte ReadByte() + { + return _currentReader.ReadByte(); + } + + private void ReadBytes(byte[] buffer, int offset, int length) + { + _currentReader.ReadBytes(buffer, offset, length); + } + + private ulong ReadNumber() + { + return _currentReader.ReadNumber(); + } + + internal int ReadNum() + { + return _currentReader.ReadNum(); + } + + private uint ReadUInt32() + { + return _currentReader.ReadUInt32(); + } + + private ulong ReadUInt64() + { + return _currentReader.ReadUInt64(); + } + + private BlockType? ReadId() + { + var id = _currentReader.ReadNumber(); + if (id > 25) { - _readerStack.Push(_currentReader); - _currentReader = new DataReader(buffer, offset, length); - } - - internal void DeleteByteStream() - { - _currentReader = _readerStack.Pop(); - } - - #region Private Methods - Data Reader - - internal Byte ReadByte() - { - return _currentReader.ReadByte(); - } - - private void ReadBytes(byte[] buffer, int offset, int length) - { - _currentReader.ReadBytes(buffer, offset, length); - } - - private ulong ReadNumber() - { - return _currentReader.ReadNumber(); - } - - internal int ReadNum() - { - return _currentReader.ReadNum(); - } - - private uint ReadUInt32() - { - return _currentReader.ReadUInt32(); - } - - private ulong ReadUInt64() - { - return _currentReader.ReadUInt64(); - } - - private BlockType? ReadId() - { - ulong id = _currentReader.ReadNumber(); - if (id > 25) - { - return null; - } -#if DEBUG - Log.WriteLine("ReadId: {0}", (BlockType)id); -#endif - return (BlockType)id; - } - - private void SkipData(long size) - { - _currentReader.SkipData(size); - } - - private void SkipData() - { - _currentReader.SkipData(); - } - - private void WaitAttribute(BlockType attribute) - { - for (; ; ) - { - BlockType? type = ReadId(); - if (type == attribute) - { - return; - } - if (type == BlockType.End) - { - throw new InvalidOperationException(); - } - SkipData(); - } - } - - private void ReadArchiveProperties() - { - while (ReadId() != BlockType.End) - { - SkipData(); - } - } - - #endregion - - #region Private Methods - Reader Utilities - - private BitVector ReadBitVector(int length) - { - var bits = new BitVector(length); - - byte data = 0; - byte mask = 0; - - for (int i = 0; i < length; i++) - { - if (mask == 0) - { - data = ReadByte(); - mask = 0x80; - } - - if ((data & mask) != 0) - { - bits.SetBit(i); - } - - mask >>= 1; - } - - return bits; - } - - private BitVector ReadOptionalBitVector(int length) - { - byte allTrue = ReadByte(); - if (allTrue != 0) - { - return new BitVector(length, true); - } - - return ReadBitVector(length); - } - - private void ReadNumberVector( - List dataVector, - int numFiles, - Action action - ) - { - var defined = ReadOptionalBitVector(numFiles); - - using (CStreamSwitch streamSwitch = new CStreamSwitch()) - { - streamSwitch.Set(this, dataVector); - - for (int i = 0; i < numFiles; i++) - { - if (defined[i]) - { - action(i, checked((long)ReadUInt64())); - } - else - { - action(i, null); - } - } - } - } - - private DateTime TranslateTime(long time) - { - // FILETIME = 100-nanosecond intervals since January 1, 1601 (UTC) - return DateTime.FromFileTimeUtc(time).ToLocalTime(); - } - - private DateTime? TranslateTime(long? time) - { - if (time.HasValue && time.Value >= 0 && time.Value <= 2650467743999999999) //maximum Windows file time 31.12.9999 - { - return TranslateTime(time.Value); - } return null; } +#if DEBUG + Log.WriteLine("ReadId: {0}", (BlockType)id); +#endif + return (BlockType)id; + } - private void ReadDateTimeVector( - List dataVector, - int numFiles, - Action action - ) + private void SkipData(long size) + { + _currentReader.SkipData(size); + } + + private void SkipData() + { + _currentReader.SkipData(); + } + + private void WaitAttribute(BlockType attribute) + { + for (; ; ) { - ReadNumberVector( - dataVector, - numFiles, - (index, value) => action(index, TranslateTime(value)) - ); + var type = ReadId(); + if (type == attribute) + { + return; + } + if (type == BlockType.End) + { + throw new InvalidOperationException(); + } + SkipData(); + } + } + + private void ReadArchiveProperties() + { + while (ReadId() != BlockType.End) + { + SkipData(); + } + } + + #endregion + + #region Private Methods - Reader Utilities + + private BitVector ReadBitVector(int length) + { + var bits = new BitVector(length); + + byte data = 0; + byte mask = 0; + + for (var i = 0; i < length; i++) + { + if (mask == 0) + { + data = ReadByte(); + mask = 0x80; + } + + if ((data & mask) != 0) + { + bits.SetBit(i); + } + + mask >>= 1; } - private void ReadAttributeVector( - List dataVector, - int numFiles, - Action action - ) + return bits; + } + + private BitVector ReadOptionalBitVector(int length) + { + var allTrue = ReadByte(); + if (allTrue != 0) { - BitVector boolVector = ReadOptionalBitVector(numFiles); - using (var streamSwitch = new CStreamSwitch()) + return new BitVector(length, true); + } + + return ReadBitVector(length); + } + + private void ReadNumberVector( + List dataVector, + int numFiles, + Action action + ) + { + var defined = ReadOptionalBitVector(numFiles); + + using var streamSwitch = new CStreamSwitch(); + streamSwitch.Set(this, dataVector); + + for (var i = 0; i < numFiles; i++) + { + if (defined[i]) { - streamSwitch.Set(this, dataVector); - for (int i = 0; i < numFiles; i++) - { - if (boolVector[i]) - { - action(i, ReadUInt32()); - } - else - { - action(i, null); - } - } + action(i, checked((long)ReadUInt64())); + } + else + { + action(i, null); } } + } - #endregion + private DateTime TranslateTime(long time) + { + // FILETIME = 100-nanosecond intervals since January 1, 1601 (UTC) + return DateTime.FromFileTimeUtc(time).ToLocalTime(); + } - #region Private Methods - - private void GetNextFolderItem(CFolder folder) + private DateTime? TranslateTime(long? time) + { + if (time.HasValue && time.Value >= 0 && time.Value <= 2650467743999999999) //maximum Windows file time 31.12.9999 { -#if DEBUG - Log.WriteLine("-- GetNextFolderItem --"); - Log.PushIndent(); -#endif - try + return TranslateTime(time.Value); + } + return null; + } + + private void ReadDateTimeVector( + List dataVector, + int numFiles, + Action action + ) + { + ReadNumberVector( + dataVector, + numFiles, + (index, value) => action(index, TranslateTime(value)) + ); + } + + private void ReadAttributeVector( + List dataVector, + int numFiles, + Action action + ) + { + var boolVector = ReadOptionalBitVector(numFiles); + using var streamSwitch = new CStreamSwitch(); + streamSwitch.Set(this, dataVector); + for (var i = 0; i < numFiles; i++) + { + if (boolVector[i]) { - int numCoders = ReadNum(); -#if DEBUG - Log.WriteLine("NumCoders: " + numCoders); -#endif - folder._coders = new List(numCoders); - int numInStreams = 0; - int numOutStreams = 0; - for (int i = 0; i < numCoders; i++) - { -#if DEBUG - Log.WriteLine("-- Coder --"); - Log.PushIndent(); -#endif - try - { - CCoderInfo coder = new CCoderInfo(); - folder._coders.Add(coder); + action(i, ReadUInt32()); + } + else + { + action(i, null); + } + } + } - byte mainByte = ReadByte(); - int idSize = (mainByte & 0xF); - byte[] longId = new byte[idSize]; - ReadBytes(longId, 0, idSize); -#if DEBUG - Log.WriteLine( - "MethodId: " - + String.Join( - "", - Enumerable - .Range(0, idSize) - .Select(x => longId[x].ToString("x2")) - .ToArray() - ) - ); -#endif - if (idSize > 8) - { - throw new NotSupportedException(); - } - ulong id = 0; - for (int j = 0; j < idSize; j++) - { - id |= (ulong)longId[idSize - 1 - j] << (8 * j); - } - coder._methodId = new CMethodId(id); + #endregion - if ((mainByte & 0x10) != 0) - { - coder._numInStreams = ReadNum(); - coder._numOutStreams = ReadNum(); -#if DEBUG - Log.WriteLine( - "Complex Stream (In: " - + coder._numInStreams - + " - Out: " - + coder._numOutStreams - + ")" - ); -#endif - } - else - { -#if DEBUG - Log.WriteLine("Simple Stream (In: 1 - Out: 1)"); -#endif - coder._numInStreams = 1; - coder._numOutStreams = 1; - } + #region Private Methods - if ((mainByte & 0x20) != 0) - { - int propsSize = ReadNum(); - coder._props = new byte[propsSize]; - ReadBytes(coder._props, 0, propsSize); + private void GetNextFolderItem(CFolder folder) + { #if DEBUG - Log.WriteLine( - "Settings: " - + String.Join( - "", - coder._props.Select(bt => bt.ToString("x2")).ToArray() - ) - ); + Log.WriteLine("-- GetNextFolderItem --"); + Log.PushIndent(); #endif - } - - if ((mainByte & 0x80) != 0) - { - throw new NotSupportedException(); - } - - numInStreams += coder._numInStreams; - numOutStreams += coder._numOutStreams; - } - finally - { + try + { + var numCoders = ReadNum(); #if DEBUG - Log.PopIndent(); + Log.WriteLine("NumCoders: " + numCoders); #endif - } - } - - int numBindPairs = numOutStreams - 1; - folder._bindPairs = new List(numBindPairs); + folder._coders = new List(numCoders); + var numInStreams = 0; + var numOutStreams = 0; + for (var i = 0; i < numCoders; i++) + { #if DEBUG - Log.WriteLine("BindPairs: " + numBindPairs); + Log.WriteLine("-- Coder --"); Log.PushIndent(); #endif - for (int i = 0; i < numBindPairs; i++) + try { - CBindPair bp = new CBindPair(); - bp._inIndex = ReadNum(); - bp._outIndex = ReadNum(); - folder._bindPairs.Add(bp); + var coder = new CCoderInfo(); + folder._coders.Add(coder); + + var mainByte = ReadByte(); + var idSize = (mainByte & 0xF); + var longId = new byte[idSize]; + ReadBytes(longId, 0, idSize); #if DEBUG - Log.WriteLine("#" + i + " - In: " + bp._inIndex + " - Out: " + bp._outIndex); + Log.WriteLine( + "MethodId: " + + string.Join( + "", + Enumerable + .Range(0, idSize) + .Select(x => longId[x].ToString("x2")) + .ToArray() + ) + ); #endif - } -#if DEBUG - Log.PopIndent(); -#endif - - if (numInStreams < numBindPairs) - { - throw new NotSupportedException(); - } - - int numPackStreams = numInStreams - numBindPairs; - - //folder.PackStreams.Reserve(numPackStreams); - if (numPackStreams == 1) - { - for (int i = 0; i < numInStreams; i++) - { - if (folder.FindBindPairForInStream(i) < 0) - { -#if DEBUG - Log.WriteLine("Single PackStream: #" + i); -#endif - folder._packStreams.Add(i); - break; - } - } - - if (folder._packStreams.Count != 1) + if (idSize > 8) { throw new NotSupportedException(); } - } - else - { -#if DEBUG - Log.WriteLine("Multiple PackStreams ..."); - Log.PushIndent(); -#endif - for (int i = 0; i < numPackStreams; i++) + ulong id = 0; + for (var j = 0; j < idSize; j++) { - var num = ReadNum(); -#if DEBUG - Log.WriteLine("#" + i + " - " + num); -#endif - folder._packStreams.Add(num); + id |= (ulong)longId[idSize - 1 - j] << (8 * j); } + coder._methodId = new CMethodId(id); + + if ((mainByte & 0x10) != 0) + { + coder._numInStreams = ReadNum(); + coder._numOutStreams = ReadNum(); +#if DEBUG + Log.WriteLine( + "Complex Stream (In: " + + coder._numInStreams + + " - Out: " + + coder._numOutStreams + + ")" + ); +#endif + } + else + { +#if DEBUG + Log.WriteLine("Simple Stream (In: 1 - Out: 1)"); +#endif + coder._numInStreams = 1; + coder._numOutStreams = 1; + } + + if ((mainByte & 0x20) != 0) + { + var propsSize = ReadNum(); + coder._props = new byte[propsSize]; + ReadBytes(coder._props, 0, propsSize); +#if DEBUG + Log.WriteLine( + "Settings: " + + string.Join( + "", + coder._props.Select(bt => bt.ToString("x2")).ToArray() + ) + ); +#endif + } + + if ((mainByte & 0x80) != 0) + { + throw new NotSupportedException(); + } + + numInStreams += coder._numInStreams; + numOutStreams += coder._numOutStreams; + } + finally + { #if DEBUG Log.PopIndent(); #endif } } - finally + + var numBindPairs = numOutStreams - 1; + folder._bindPairs = new List(numBindPairs); +#if DEBUG + Log.WriteLine("BindPairs: " + numBindPairs); + Log.PushIndent(); +#endif + for (var i = 0; i < numBindPairs; i++) { + var bp = new CBindPair(); + bp._inIndex = ReadNum(); + bp._outIndex = ReadNum(); + folder._bindPairs.Add(bp); +#if DEBUG + Log.WriteLine("#" + i + " - In: " + bp._inIndex + " - Out: " + bp._outIndex); +#endif + } +#if DEBUG + Log.PopIndent(); +#endif + + if (numInStreams < numBindPairs) + { + throw new NotSupportedException(); + } + + var numPackStreams = numInStreams - numBindPairs; + + //folder.PackStreams.Reserve(numPackStreams); + if (numPackStreams == 1) + { + for (var i = 0; i < numInStreams; i++) + { + if (folder.FindBindPairForInStream(i) < 0) + { +#if DEBUG + Log.WriteLine("Single PackStream: #" + i); +#endif + folder._packStreams.Add(i); + break; + } + } + + if (folder._packStreams.Count != 1) + { + throw new NotSupportedException(); + } + } + else + { +#if DEBUG + Log.WriteLine("Multiple PackStreams ..."); + Log.PushIndent(); +#endif + for (var i = 0; i < numPackStreams; i++) + { + var num = ReadNum(); +#if DEBUG + Log.WriteLine("#" + i + " - " + num); +#endif + folder._packStreams.Add(num); + } #if DEBUG Log.PopIndent(); #endif } } - - private List ReadHashDigests(int count) + finally { #if DEBUG - Log.Write("ReadHashDigests:"); + Log.PopIndent(); +#endif + } + } + + private List ReadHashDigests(int count) + { +#if DEBUG + Log.Write("ReadHashDigests:"); #endif - var defined = ReadOptionalBitVector(count); - var digests = new List(count); - for (int i = 0; i < count; i++) + var defined = ReadOptionalBitVector(count); + var digests = new List(count); + for (var i = 0; i < count; i++) + { + if (defined[i]) { - if (defined[i]) - { - uint crc = ReadUInt32(); + var crc = ReadUInt32(); #if DEBUG - Log.Write(" " + crc.ToString("x8")); + Log.Write(" " + crc.ToString("x8")); #endif - digests.Add(crc); - } - else - { + digests.Add(crc); + } + else + { #if DEBUG - Log.Write(" ########"); + Log.Write(" ########"); #endif - digests.Add(null); - } + digests.Add(null); + } + } +#if DEBUG + + Log.WriteLine(); +#endif + return digests; + } + + private void ReadPackInfo( + out long dataOffset, + out List packSizes, + out List packCrCs + ) + { +#if DEBUG + Log.WriteLine("-- ReadPackInfo --"); + Log.PushIndent(); +#endif + try + { + packCrCs = null; + + dataOffset = checked((long)ReadNumber()); +#if DEBUG + Log.WriteLine("DataOffset: " + dataOffset); +#endif + + var numPackStreams = ReadNum(); +#if DEBUG + Log.WriteLine("NumPackStreams: " + numPackStreams); +#endif + + WaitAttribute(BlockType.Size); + packSizes = new List(numPackStreams); +#if DEBUG + Log.Write("Sizes:"); +#endif + for (var i = 0; i < numPackStreams; i++) + { + var size = checked((long)ReadNumber()); +#if DEBUG + Log.Write(" " + size); +#endif + packSizes.Add(size); } #if DEBUG - Log.WriteLine(); #endif - return digests; - } - private void ReadPackInfo( - out long dataOffset, - out List packSizes, - out List packCrCs - ) + BlockType? type; + for (; ; ) + { + type = ReadId(); + if (type == BlockType.End) + { + break; + } + if (type == BlockType.Crc) + { + packCrCs = ReadHashDigests(numPackStreams); + continue; + } + SkipData(); + } + + if (packCrCs is null) + { + packCrCs = new List(numPackStreams); + for (var i = 0; i < numPackStreams; i++) + { + packCrCs.Add(null); + } + } + } + finally { #if DEBUG - Log.WriteLine("-- ReadPackInfo --"); - Log.PushIndent(); + Log.PopIndent(); #endif - try + } + } + + private void ReadUnpackInfo(List dataVector, out List folders) + { +#if DEBUG + Log.WriteLine("-- ReadUnpackInfo --"); + Log.PushIndent(); +#endif + try + { + WaitAttribute(BlockType.Folder); + var numFolders = ReadNum(); +#if DEBUG + Log.WriteLine("NumFolders: {0}", numFolders); +#endif + + using (var streamSwitch = new CStreamSwitch()) { - packCrCs = null; + streamSwitch.Set(this, dataVector); - dataOffset = checked((long)ReadNumber()); -#if DEBUG - Log.WriteLine("DataOffset: " + dataOffset); -#endif + //folders.Clear(); + //folders.Reserve(numFolders); + folders = new List(numFolders); + var index = 0; + for (var i = 0; i < numFolders; i++) + { + var f = new CFolder { _firstPackStreamId = index }; + folders.Add(f); + GetNextFolderItem(f); + index += f._packStreams.Count; + } + } - int numPackStreams = ReadNum(); + WaitAttribute(BlockType.CodersUnpackSize); #if DEBUG - Log.WriteLine("NumPackStreams: " + numPackStreams); + Log.WriteLine("UnpackSizes:"); #endif - - WaitAttribute(BlockType.Size); - packSizes = new List(numPackStreams); + for (var i = 0; i < numFolders; i++) + { + var folder = folders[i]; #if DEBUG - Log.Write("Sizes:"); + Log.Write(" #" + i + ":"); #endif - for (int i = 0; i < numPackStreams; i++) + var numOutStreams = folder.GetNumOutStreams(); + for (var j = 0; j < numOutStreams; j++) { var size = checked((long)ReadNumber()); #if DEBUG Log.Write(" " + size); #endif - packSizes.Add(size); + folder._unpackSizes.Add(size); } #if DEBUG Log.WriteLine(); #endif - - BlockType? type; - for (; ; ) - { - type = ReadId(); - if (type == BlockType.End) - { - break; - } - if (type == BlockType.Crc) - { - packCrCs = ReadHashDigests(numPackStreams); - continue; - } - SkipData(); - } - - if (packCrCs is null) - { - packCrCs = new List(numPackStreams); - for (int i = 0; i < numPackStreams; i++) - { - packCrCs.Add(null); - } - } } - finally + + for (; ; ) { -#if DEBUG - Log.PopIndent(); -#endif - } - } - - private void ReadUnpackInfo(List dataVector, out List folders) - { -#if DEBUG - Log.WriteLine("-- ReadUnpackInfo --"); - Log.PushIndent(); -#endif - try - { - WaitAttribute(BlockType.Folder); - int numFolders = ReadNum(); -#if DEBUG - Log.WriteLine("NumFolders: {0}", numFolders); -#endif - - using (CStreamSwitch streamSwitch = new CStreamSwitch()) - { - streamSwitch.Set(this, dataVector); - - //folders.Clear(); - //folders.Reserve(numFolders); - folders = new List(numFolders); - int index = 0; - for (int i = 0; i < numFolders; i++) - { - var f = new CFolder { _firstPackStreamId = index }; - folders.Add(f); - GetNextFolderItem(f); - index += f._packStreams.Count; - } - } - - WaitAttribute(BlockType.CodersUnpackSize); -#if DEBUG - Log.WriteLine("UnpackSizes:"); -#endif - for (int i = 0; i < numFolders; i++) - { - CFolder folder = folders[i]; -#if DEBUG - Log.Write(" #" + i + ":"); -#endif - int numOutStreams = folder.GetNumOutStreams(); - for (int j = 0; j < numOutStreams; j++) - { - long size = checked((long)ReadNumber()); -#if DEBUG - Log.Write(" " + size); -#endif - folder._unpackSizes.Add(size); - } -#if DEBUG - Log.WriteLine(); -#endif - } - - for (; ; ) - { - BlockType? type = ReadId(); - if (type == BlockType.End) - { - return; - } - - if (type == BlockType.Crc) - { - List crcs = ReadHashDigests(numFolders); - for (int i = 0; i < numFolders; i++) - { - folders[i]._unpackCrc = crcs[i]; - } - continue; - } - - SkipData(); - } - } - finally - { -#if DEBUG - Log.PopIndent(); -#endif - } - } - - private void ReadSubStreamsInfo( - List folders, - out List numUnpackStreamsInFolders, - out List unpackSizes, - out List digests - ) - { -#if DEBUG - Log.WriteLine("-- ReadSubStreamsInfo --"); - Log.PushIndent(); -#endif - try - { - numUnpackStreamsInFolders = null; - - BlockType? type; - for (; ; ) - { - type = ReadId(); - if (type == BlockType.NumUnpackStream) - { - numUnpackStreamsInFolders = new List(folders.Count); -#if DEBUG - Log.Write("NumUnpackStreams:"); -#endif - for (int i = 0; i < folders.Count; i++) - { - var num = ReadNum(); -#if DEBUG - Log.Write(" " + num); -#endif - numUnpackStreamsInFolders.Add(num); - } -#if DEBUG - Log.WriteLine(); -#endif - continue; - } - if (type is BlockType.Crc or BlockType.Size) - { - break; - } - if (type == BlockType.End) - { - break; - } - SkipData(); - } - - if (numUnpackStreamsInFolders is null) - { - numUnpackStreamsInFolders = new List(folders.Count); - for (int i = 0; i < folders.Count; i++) - { - numUnpackStreamsInFolders.Add(1); - } - } - - unpackSizes = new List(folders.Count); - for (int i = 0; i < numUnpackStreamsInFolders.Count; i++) - { - // v3.13 incorrectly worked with empty folders - // v4.07: we check that folder is empty - int numSubstreams = numUnpackStreamsInFolders[i]; - if (numSubstreams == 0) - { - continue; - } -#if DEBUG - Log.Write("#{0} StreamSizes:", i); -#endif - long sum = 0; - for (int j = 1; j < numSubstreams; j++) - { - if (type == BlockType.Size) - { - long size = checked((long)ReadNumber()); -#if DEBUG - Log.Write(" " + size); -#endif - unpackSizes.Add(size); - sum += size; - } - } - unpackSizes.Add(folders[i].GetUnpackSize() - sum); -#if DEBUG - Log.WriteLine(" - rest: " + unpackSizes.Last()); -#endif - } - if (type == BlockType.Size) - { - type = ReadId(); - } - - int numDigests = 0; - int numDigestsTotal = 0; - for (int i = 0; i < folders.Count; i++) - { - int numSubstreams = numUnpackStreamsInFolders[i]; - if (numSubstreams != 1 || !folders[i].UnpackCrcDefined) - { - numDigests += numSubstreams; - } - numDigestsTotal += numSubstreams; - } - - digests = null; - - for (; ; ) - { - if (type == BlockType.Crc) - { - digests = new List(numDigestsTotal); - - List digests2 = ReadHashDigests(numDigests); - - int digestIndex = 0; - for (int i = 0; i < folders.Count; i++) - { - int numSubstreams = numUnpackStreamsInFolders[i]; - CFolder folder = folders[i]; - if (numSubstreams == 1 && folder.UnpackCrcDefined) - { - digests.Add(folder._unpackCrc.Value); - } - else - { - for (int j = 0; j < numSubstreams; j++, digestIndex++) - { - digests.Add(digests2[digestIndex]); - } - } - } - - if (digestIndex != numDigests || numDigestsTotal != digests.Count) - { - Debugger.Break(); - } - } - else if (type == BlockType.End) - { - if (digests is null) - { - digests = new List(numDigestsTotal); - for (int i = 0; i < numDigestsTotal; i++) - { - digests.Add(null); - } - } - return; - } - else - { - SkipData(); - } - - type = ReadId(); - } - } - finally - { -#if DEBUG - Log.PopIndent(); -#endif - } - } - - private void ReadStreamsInfo( - List dataVector, - out long dataOffset, - out List packSizes, - out List packCrCs, - out List folders, - out List numUnpackStreamsInFolders, - out List unpackSizes, - out List digests - ) - { -#if DEBUG - Log.WriteLine("-- ReadStreamsInfo --"); - Log.PushIndent(); -#endif - try - { - dataOffset = long.MinValue; - packSizes = null; - packCrCs = null; - folders = null; - numUnpackStreamsInFolders = null; - unpackSizes = null; - digests = null; - - for (; ; ) - { - switch (ReadId()) - { - case BlockType.End: - return; - case BlockType.PackInfo: - ReadPackInfo(out dataOffset, out packSizes, out packCrCs); - break; - case BlockType.UnpackInfo: - ReadUnpackInfo(dataVector, out folders); - break; - case BlockType.SubStreamsInfo: - ReadSubStreamsInfo( - folders, - out numUnpackStreamsInFolders, - out unpackSizes, - out digests - ); - break; - default: - throw new InvalidOperationException(); - } - } - } - finally - { -#if DEBUG - Log.PopIndent(); -#endif - } - } - - private List ReadAndDecodePackedStreams(long baseOffset, IPasswordProvider pass) - { -#if DEBUG - Log.WriteLine("-- ReadAndDecodePackedStreams --"); - Log.PushIndent(); -#endif - try - { - ReadStreamsInfo( - null, - out long dataStartPos, - out List packSizes, - out List packCrCs, - out List folders, - out List numUnpackStreamsInFolders, - out List unpackSizes, - out List digests - ); - - dataStartPos += baseOffset; - - var dataVector = new List(folders.Count); - int packIndex = 0; - foreach (var folder in folders) - { - long oldDataStartPos = dataStartPos; - long[] myPackSizes = new long[folder._packStreams.Count]; - for (int i = 0; i < myPackSizes.Length; i++) - { - long packSize = packSizes[packIndex + i]; - myPackSizes[i] = packSize; - dataStartPos += packSize; - } - - var outStream = DecoderStreamHelper.CreateDecoderStream( - _stream, - oldDataStartPos, - myPackSizes, - folder, - pass - ); - - int unpackSize = checked((int)folder.GetUnpackSize()); - byte[] data = new byte[unpackSize]; - outStream.ReadExact(data, 0, data.Length); - if (outStream.ReadByte() >= 0) - { - throw new InvalidOperationException( - "Decoded stream is longer than expected." - ); - } - dataVector.Add(data); - - if (folder.UnpackCrcDefined) - { - if ( - Crc.Finish(Crc.Update(Crc.INIT_CRC, data, 0, unpackSize)) - != folder._unpackCrc - ) - { - throw new InvalidOperationException( - "Decoded stream does not match expected CRC." - ); - } - } - } - return dataVector; - } - finally - { -#if DEBUG - Log.PopIndent(); -#endif - } - } - - private void ReadHeader(ArchiveDatabase db, IPasswordProvider getTextPassword) - { -#if DEBUG - Log.WriteLine("-- ReadHeader --"); - Log.PushIndent(); -#endif - try - { - BlockType? type = ReadId(); - - if (type == BlockType.ArchiveProperties) - { - ReadArchiveProperties(); - type = ReadId(); - } - - List dataVector = null; - if (type == BlockType.AdditionalStreamsInfo) - { - dataVector = ReadAndDecodePackedStreams( - db._startPositionAfterHeader, - getTextPassword - ); - type = ReadId(); - } - - List unpackSizes; - List digests; - - if (type == BlockType.MainStreamsInfo) - { - ReadStreamsInfo( - dataVector, - out db._dataStartPosition, - out db._packSizes, - out db._packCrCs, - out db._folders, - out db._numUnpackStreamsVector, - out unpackSizes, - out digests - ); - - db._dataStartPosition += db._startPositionAfterHeader; - type = ReadId(); - } - else - { - unpackSizes = new List(db._folders.Count); - digests = new List(db._folders.Count); - db._numUnpackStreamsVector = new List(db._folders.Count); - for (int i = 0; i < db._folders.Count; i++) - { - var folder = db._folders[i]; - unpackSizes.Add(folder.GetUnpackSize()); - digests.Add(folder._unpackCrc); - db._numUnpackStreamsVector.Add(1); - } - } - - db._files.Clear(); - + var type = ReadId(); if (type == BlockType.End) { return; } - if (type != BlockType.FilesInfo) + if (type == BlockType.Crc) { - throw new InvalidOperationException(); + var crcs = ReadHashDigests(numFolders); + for (var i = 0; i < numFolders; i++) + { + folders[i]._unpackCrc = crcs[i]; + } + continue; } - int numFiles = ReadNum(); -#if DEBUG - Log.WriteLine("NumFiles: " + numFiles); -#endif - db._files = new List(numFiles); - for (int i = 0; i < numFiles; i++) - { - db._files.Add(new CFileItem()); - } - - BitVector emptyStreamVector = new BitVector(numFiles); - BitVector emptyFileVector = null; - BitVector antiFileVector = null; - int numEmptyStreams = 0; - - for (; ; ) - { - type = ReadId(); - if (type == BlockType.End) - { - break; - } - - long size = checked((long)ReadNumber()); // TODO: throw invalid data on negative - int oldPos = _currentReader.Offset; - switch (type) - { - case BlockType.Name: - using (var streamSwitch = new CStreamSwitch()) - { - streamSwitch.Set(this, dataVector); -#if DEBUG - Log.Write("FileNames:"); -#endif - for (int i = 0; i < db._files.Count; i++) - { - db._files[i].Name = _currentReader.ReadString(); -#if DEBUG - Log.Write(" " + db._files[i].Name); -#endif - } -#if DEBUG - Log.WriteLine(); -#endif - } - break; - case BlockType.WinAttributes: -#if DEBUG - Log.Write("WinAttributes:"); -#endif - ReadAttributeVector( - dataVector, - numFiles, - delegate(int i, uint? attr) - { - // Some third party implementations established an unofficial extension - // of the 7z archive format by placing posix file attributes in the high - // bits of the windows file attributes. This makes use of the fact that - // the official implementation does not perform checks on this value. - // - // Newer versions of the official 7z GUI client will try to parse this - // extension, thus acknowledging the unofficial use of these bits. - // - // For us it is safe to just discard the upper bits if they are set and - // keep the windows attributes from the lower bits (which should be set - // properly even if posix file attributes are present, in order to be - // compatible with older 7z archive readers) - // - // Note that the 15th bit is used by some implementations to indicate - // presence of the extension, but not all implementations do that so - // we can't trust that bit and must ignore it. - // - if (attr.HasValue && (attr.Value >> 16) != 0) - { - attr = attr.Value & 0x7FFFu; - } - - db._files[i].Attrib = attr; -#if DEBUG - Log.Write( - " " + (attr.HasValue ? attr.Value.ToString("x8") : "n/a") - ); -#endif - } - ); -#if DEBUG - Log.WriteLine(); -#endif - break; - case BlockType.EmptyStream: - emptyStreamVector = ReadBitVector(numFiles); -#if DEBUG - - Log.Write("EmptyStream: "); -#endif - for (int i = 0; i < emptyStreamVector.Length; i++) - { - if (emptyStreamVector[i]) - { -#if DEBUG - Log.Write("x"); -#endif - numEmptyStreams++; - } - else - { -#if DEBUG - Log.Write("."); -#endif - } - } -#if DEBUG - Log.WriteLine(); -#endif - - emptyFileVector = new BitVector(numEmptyStreams); - antiFileVector = new BitVector(numEmptyStreams); - break; - case BlockType.EmptyFile: - emptyFileVector = ReadBitVector(numEmptyStreams); -#if DEBUG - Log.Write("EmptyFile: "); - for (int i = 0; i < numEmptyStreams; i++) - { - Log.Write(emptyFileVector[i] ? "x" : "."); - } - Log.WriteLine(); -#endif - break; - case BlockType.Anti: - antiFileVector = ReadBitVector(numEmptyStreams); -#if DEBUG - Log.Write("Anti: "); - for (int i = 0; i < numEmptyStreams; i++) - { - Log.Write(antiFileVector[i] ? "x" : "."); - } - Log.WriteLine(); -#endif - break; - case BlockType.StartPos: -#if DEBUG - Log.Write("StartPos:"); -#endif - ReadNumberVector( - dataVector, - numFiles, - delegate(int i, long? startPos) - { - db._files[i].StartPos = startPos; -#if DEBUG - Log.Write( - " " - + ( - startPos.HasValue - ? startPos.Value.ToString() - : "n/a" - ) - ); -#endif - } - ); -#if DEBUG - Log.WriteLine(); -#endif - break; - case BlockType.CTime: -#if DEBUG - Log.Write("CTime:"); -#endif - ReadDateTimeVector( - dataVector, - numFiles, - delegate(int i, DateTime? time) - { - db._files[i].CTime = time; -#if DEBUG - Log.Write( - " " + (time.HasValue ? time.Value.ToString() : "n/a") - ); -#endif - } - ); -#if DEBUG - Log.WriteLine(); -#endif - break; - case BlockType.ATime: -#if DEBUG - Log.Write("ATime:"); -#endif - ReadDateTimeVector( - dataVector, - numFiles, - delegate(int i, DateTime? time) - { - db._files[i].ATime = time; -#if DEBUG - Log.Write( - " " + (time.HasValue ? time.Value.ToString() : "n/a") - ); -#endif - } - ); -#if DEBUG - Log.WriteLine(); -#endif - break; - case BlockType.MTime: -#if DEBUG - Log.Write("MTime:"); -#endif - ReadDateTimeVector( - dataVector, - numFiles, - delegate(int i, DateTime? time) - { - db._files[i].MTime = time; -#if DEBUG - Log.Write( - " " + (time.HasValue ? time.Value.ToString() : "n/a") - ); -#endif - } - ); -#if DEBUG - Log.WriteLine(); -#endif - break; - case BlockType.Dummy: -#if DEBUG - Log.Write("Dummy: " + size); -#endif - for (long j = 0; j < size; j++) - { - if (ReadByte() != 0) - { - throw new InvalidOperationException(); - } - } - break; - default: - SkipData(size); - break; - } - - // since 0.3 record sizes must be correct - bool checkRecordsSize = (db._majorVersion > 0 || db._minorVersion > 2); - if (checkRecordsSize && _currentReader.Offset - oldPos != size) - { - throw new InvalidOperationException(); - } - } - - int emptyFileIndex = 0; - int sizeIndex = 0; - for (int i = 0; i < numFiles; i++) - { - CFileItem file = db._files[i]; - file.HasStream = !emptyStreamVector[i]; - if (file.HasStream) - { - file.IsDir = false; - file.IsAnti = false; - file.Size = unpackSizes[sizeIndex]; - file.Crc = digests[sizeIndex]; - sizeIndex++; - } - else - { - file.IsDir = !emptyFileVector[emptyFileIndex]; - file.IsAnti = antiFileVector[emptyFileIndex]; - emptyFileIndex++; - file.Size = 0; - file.Crc = null; - } - } - } - finally - { -#if DEBUG - Log.PopIndent(); -#endif + SkipData(); } } - - #endregion - - #region Public Methods - - public void Open(Stream stream) + finally { - Close(); - - _streamOrigin = stream.Position; - _streamEnding = stream.Length; - - // TODO: Check Signature! - _header = new byte[0x20]; - for (int offset = 0; offset < 0x20; ) - { - int delta = stream.Read(_header, offset, 0x20 - offset); - if (delta == 0) - { - throw new EndOfStreamException(); - } - offset += delta; - } - - _stream = stream; - } - - public void Close() - { - if (_stream != null) - { - _stream.Dispose(); - } - - foreach (var stream in _cachedStreams.Values) - { - stream.Dispose(); - } - - _cachedStreams.Clear(); - } - - public ArchiveDatabase ReadDatabase(IPasswordProvider pass) - { - var db = new ArchiveDatabase(pass); - db.Clear(); - - db._majorVersion = _header[6]; - db._minorVersion = _header[7]; - - if (db._majorVersion != 0) - { - throw new InvalidOperationException(); - } - - uint crcFromArchive = DataReader.Get32(_header, 8); - long nextHeaderOffset = (long)DataReader.Get64(_header, 0xC); - long nextHeaderSize = (long)DataReader.Get64(_header, 0x14); - uint nextHeaderCrc = DataReader.Get32(_header, 0x1C); - - uint crc = Crc.INIT_CRC; - crc = Crc.Update(crc, nextHeaderOffset); - crc = Crc.Update(crc, nextHeaderSize); - crc = Crc.Update(crc, nextHeaderCrc); - crc = Crc.Finish(crc); - - if (crc != crcFromArchive) - { - throw new InvalidOperationException(); - } - - db._startPositionAfterHeader = _streamOrigin + 0x20; - - // empty header is ok - if (nextHeaderSize == 0) - { - db.Fill(); - return db; - } - - if (nextHeaderOffset < 0 || nextHeaderSize < 0 || nextHeaderSize > Int32.MaxValue) - { - throw new InvalidOperationException(); - } - - if (nextHeaderOffset > _streamEnding - db._startPositionAfterHeader) - { - throw new InvalidOperationException("nextHeaderOffset is invalid"); - } - - _stream.Seek(nextHeaderOffset, SeekOrigin.Current); - - byte[] header = new byte[nextHeaderSize]; - _stream.ReadExact(header, 0, header.Length); - - if (Crc.Finish(Crc.Update(Crc.INIT_CRC, header, 0, header.Length)) != nextHeaderCrc) - { - throw new InvalidOperationException(); - } - - using (CStreamSwitch streamSwitch = new CStreamSwitch()) - { - streamSwitch.Set(this, header); - - BlockType? type = ReadId(); - if (type != BlockType.Header) - { - if (type != BlockType.EncodedHeader) - { - throw new InvalidOperationException(); - } - - var dataVector = ReadAndDecodePackedStreams( - db._startPositionAfterHeader, - db.PasswordProvider - ); - - // compressed header without content is odd but ok - if (dataVector.Count == 0) - { - db.Fill(); - return db; - } - - if (dataVector.Count != 1) - { - throw new InvalidOperationException(); - } - - streamSwitch.Set(this, dataVector[0]); - - if (ReadId() != BlockType.Header) - { - throw new InvalidOperationException(); - } - } - - ReadHeader(db, db.PasswordProvider); - } - db.Fill(); - return db; - } - - internal class CExtractFolderInfo - { - internal int _fileIndex; - internal int _folderIndex; - internal List _extractStatuses = new List(); - - internal CExtractFolderInfo(int fileIndex, int folderIndex) - { - _fileIndex = fileIndex; - _folderIndex = folderIndex; - if (fileIndex != -1) - { - _extractStatuses.Add(true); - } - } - } - - private class FolderUnpackStream : Stream - { - private readonly ArchiveDatabase _db; - private readonly int _startIndex; - private readonly List _extractStatuses; - - public FolderUnpackStream(ArchiveDatabase db, int p, int startIndex, List list) - { - _db = db; - _startIndex = startIndex; - _extractStatuses = list; - } - - #region Stream - - public override bool CanRead => true; - - public override bool CanSeek => false; - - public override bool CanWrite => false; - - public override void Flush() - { - throw new NotSupportedException(); - } - - public override long Length => throw new NotSupportedException(); - - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - private Stream _stream; - private long _rem; - private int _currentIndex; - - private void ProcessEmptyFiles() - { - while ( - _currentIndex < _extractStatuses.Count - && _db._files[_startIndex + _currentIndex].Size == 0 - ) - { - OpenFile(); - _stream.Dispose(); - _stream = null; - _currentIndex++; - } - } - - private void OpenFile() - { - int index = _startIndex + _currentIndex; #if DEBUG - Log.WriteLine(_db._files[index].Name); + Log.PopIndent(); #endif - if (_db._files[index].CrcDefined) + } + } + + private void ReadSubStreamsInfo( + List folders, + out List numUnpackStreamsInFolders, + out List unpackSizes, + out List digests + ) + { +#if DEBUG + Log.WriteLine("-- ReadSubStreamsInfo --"); + Log.PushIndent(); +#endif + try + { + numUnpackStreamsInFolders = null; + + BlockType? type; + for (; ; ) + { + type = ReadId(); + if (type == BlockType.NumUnpackStream) { - _stream = new CrcCheckStream(_db._files[index].Crc.Value); + numUnpackStreamsInFolders = new List(folders.Count); +#if DEBUG + Log.Write("NumUnpackStreams:"); +#endif + for (var i = 0; i < folders.Count; i++) + { + var num = ReadNum(); +#if DEBUG + Log.Write(" " + num); +#endif + numUnpackStreamsInFolders.Add(num); + } +#if DEBUG + Log.WriteLine(); +#endif + continue; + } + if (type is BlockType.Crc or BlockType.Size) + { + break; + } + if (type == BlockType.End) + { + break; + } + SkipData(); + } + + if (numUnpackStreamsInFolders is null) + { + numUnpackStreamsInFolders = new List(folders.Count); + for (var i = 0; i < folders.Count; i++) + { + numUnpackStreamsInFolders.Add(1); + } + } + + unpackSizes = new List(folders.Count); + for (var i = 0; i < numUnpackStreamsInFolders.Count; i++) + { + // v3.13 incorrectly worked with empty folders + // v4.07: we check that folder is empty + var numSubstreams = numUnpackStreamsInFolders[i]; + if (numSubstreams == 0) + { + continue; + } +#if DEBUG + Log.Write("#{0} StreamSizes:", i); +#endif + long sum = 0; + for (var j = 1; j < numSubstreams; j++) + { + if (type == BlockType.Size) + { + var size = checked((long)ReadNumber()); +#if DEBUG + Log.Write(" " + size); +#endif + unpackSizes.Add(size); + sum += size; + } + } + unpackSizes.Add(folders[i].GetUnpackSize() - sum); +#if DEBUG + Log.WriteLine(" - rest: " + unpackSizes.Last()); +#endif + } + if (type == BlockType.Size) + { + type = ReadId(); + } + + var numDigests = 0; + var numDigestsTotal = 0; + for (var i = 0; i < folders.Count; i++) + { + var numSubstreams = numUnpackStreamsInFolders[i]; + if (numSubstreams != 1 || !folders[i].UnpackCrcDefined) + { + numDigests += numSubstreams; + } + numDigestsTotal += numSubstreams; + } + + digests = null; + + for (; ; ) + { + if (type == BlockType.Crc) + { + digests = new List(numDigestsTotal); + + var digests2 = ReadHashDigests(numDigests); + + var digestIndex = 0; + for (var i = 0; i < folders.Count; i++) + { + var numSubstreams = numUnpackStreamsInFolders[i]; + var folder = folders[i]; + if (numSubstreams == 1 && folder.UnpackCrcDefined) + { + digests.Add(folder._unpackCrc.Value); + } + else + { + for (var j = 0; j < numSubstreams; j++, digestIndex++) + { + digests.Add(digests2[digestIndex]); + } + } + } + + if (digestIndex != numDigests || numDigestsTotal != digests.Count) + { + Debugger.Break(); + } + } + else if (type == BlockType.End) + { + if (digests is null) + { + digests = new List(numDigestsTotal); + for (var i = 0; i < numDigestsTotal; i++) + { + digests.Add(null); + } + } + return; } else { - _stream = new MemoryStream(); + SkipData(); } - _rem = _db._files[index].Size; - } - public override void Write(byte[] buffer, int offset, int count) - { - while (count != 0) - { - if (_stream != null) - { - int write = count; - if (write > _rem) - { - write = (int)_rem; - } - _stream.Write(buffer, offset, write); - count -= write; - _rem -= write; - offset += write; - if (_rem == 0) - { - _stream.Dispose(); - _stream = null; - _currentIndex++; - ProcessEmptyFiles(); - } - } - else - { - ProcessEmptyFiles(); - if (_currentIndex == _extractStatuses.Count) - { - // we support partial extracting - Debugger.Break(); - throw new NotSupportedException(); - } - OpenFile(); - } - } + type = ReadId(); } - - #endregion } - - private Stream GetCachedDecoderStream(ArchiveDatabase db, int folderIndex) + finally { - if (!_cachedStreams.TryGetValue(folderIndex, out Stream s)) +#if DEBUG + Log.PopIndent(); +#endif + } + } + + private void ReadStreamsInfo( + List dataVector, + out long dataOffset, + out List packSizes, + out List packCrCs, + out List folders, + out List numUnpackStreamsInFolders, + out List unpackSizes, + out List digests + ) + { +#if DEBUG + Log.WriteLine("-- ReadStreamsInfo --"); + Log.PushIndent(); +#endif + try + { + dataOffset = long.MinValue; + packSizes = null; + packCrCs = null; + folders = null; + numUnpackStreamsInFolders = null; + unpackSizes = null; + digests = null; + + for (; ; ) { - CFolder folderInfo = db._folders[folderIndex]; - int packStreamIndex = db._folders[folderIndex]._firstPackStreamId; - long folderStartPackPos = db.GetFolderStreamPos(folderInfo, 0); - var count = folderInfo._packStreams.Count; - long[] packSizes = new long[count]; - for (int j = 0; j < count; j++) + switch (ReadId()) { - packSizes[j] = db._packSizes[packStreamIndex + j]; + case BlockType.End: + return; + case BlockType.PackInfo: + ReadPackInfo(out dataOffset, out packSizes, out packCrCs); + break; + case BlockType.UnpackInfo: + ReadUnpackInfo(dataVector, out folders); + break; + case BlockType.SubStreamsInfo: + ReadSubStreamsInfo( + folders, + out numUnpackStreamsInFolders, + out unpackSizes, + out digests + ); + break; + default: + throw new InvalidOperationException(); + } + } + } + finally + { +#if DEBUG + Log.PopIndent(); +#endif + } + } + + private List ReadAndDecodePackedStreams(long baseOffset, IPasswordProvider pass) + { +#if DEBUG + Log.WriteLine("-- ReadAndDecodePackedStreams --"); + Log.PushIndent(); +#endif + try + { + ReadStreamsInfo( + null, + out var dataStartPos, + out var packSizes, + out var packCrCs, + out var folders, + out var numUnpackStreamsInFolders, + out var unpackSizes, + out var digests + ); + + dataStartPos += baseOffset; + + var dataVector = new List(folders.Count); + var packIndex = 0; + foreach (var folder in folders) + { + var oldDataStartPos = dataStartPos; + var myPackSizes = new long[folder._packStreams.Count]; + for (var i = 0; i < myPackSizes.Length; i++) + { + var packSize = packSizes[packIndex + i]; + myPackSizes[i] = packSize; + dataStartPos += packSize; } - s = DecoderStreamHelper.CreateDecoderStream( + var outStream = DecoderStreamHelper.CreateDecoderStream( _stream, - folderStartPackPos, - packSizes, - folderInfo, - db.PasswordProvider + oldDataStartPos, + myPackSizes, + folder, + pass ); - _cachedStreams.Add(folderIndex, s); + + var unpackSize = checked((int)folder.GetUnpackSize()); + var data = new byte[unpackSize]; + outStream.ReadExact(data, 0, data.Length); + if (outStream.ReadByte() >= 0) + { + throw new InvalidOperationException( + "Decoded stream is longer than expected." + ); + } + dataVector.Add(data); + + if (folder.UnpackCrcDefined) + { + if ( + Crc.Finish(Crc.Update(Crc.INIT_CRC, data, 0, unpackSize)) + != folder._unpackCrc + ) + { + throw new InvalidOperationException( + "Decoded stream does not match expected CRC." + ); + } + } } - return s; + return dataVector; } - - public Stream OpenStream(ArchiveDatabase db, int fileIndex) + finally { - int folderIndex = db._fileIndexToFolderIndexMap[fileIndex]; - int numFilesInFolder = db._numUnpackStreamsVector[folderIndex]; - int firstFileIndex = db._folderStartFileIndex[folderIndex]; - if (firstFileIndex > fileIndex || fileIndex - firstFileIndex >= numFilesInFolder) - { - throw new InvalidOperationException(); - } - - int skipCount = fileIndex - firstFileIndex; - long skipSize = 0; - for (int i = 0; i < skipCount; i++) - { - skipSize += db._files[firstFileIndex + i].Size; - } - - Stream s = GetCachedDecoderStream(db, folderIndex); - s.Position = skipSize; - return new ReadOnlySubStream(s, db._files[fileIndex].Size); +#if DEBUG + Log.PopIndent(); +#endif } + } - public void Extract(ArchiveDatabase db, int[] indices) + private void ReadHeader(ArchiveDatabase db, IPasswordProvider getTextPassword) + { +#if DEBUG + Log.WriteLine("-- ReadHeader --"); + Log.PushIndent(); +#endif + try { - bool allFilesMode = (indices is null); + var type = ReadId(); - int numItems = allFilesMode ? db._files.Count : indices.Length; + if (type == BlockType.ArchiveProperties) + { + ReadArchiveProperties(); + type = ReadId(); + } - if (numItems == 0) + List dataVector = null; + if (type == BlockType.AdditionalStreamsInfo) + { + dataVector = ReadAndDecodePackedStreams( + db._startPositionAfterHeader, + getTextPassword + ); + type = ReadId(); + } + + List unpackSizes; + List digests; + + if (type == BlockType.MainStreamsInfo) + { + ReadStreamsInfo( + dataVector, + out db._dataStartPosition, + out db._packSizes, + out db._packCrCs, + out db._folders, + out db._numUnpackStreamsVector, + out unpackSizes, + out digests + ); + + db._dataStartPosition += db._startPositionAfterHeader; + type = ReadId(); + } + else + { + unpackSizes = new List(db._folders.Count); + digests = new List(db._folders.Count); + db._numUnpackStreamsVector = new List(db._folders.Count); + for (var i = 0; i < db._folders.Count; i++) + { + var folder = db._folders[i]; + unpackSizes.Add(folder.GetUnpackSize()); + digests.Add(folder._unpackCrc); + db._numUnpackStreamsVector.Add(1); + } + } + + db._files.Clear(); + + if (type == BlockType.End) { return; } - List extractFolderInfoVector = new List(); - for (int i = 0; i < numItems; i++) + if (type != BlockType.FilesInfo) { - int fileIndex = allFilesMode ? i : indices[i]; + throw new InvalidOperationException(); + } - int folderIndex = db._fileIndexToFolderIndexMap[fileIndex]; - if (folderIndex == -1) + var numFiles = ReadNum(); +#if DEBUG + Log.WriteLine("NumFiles: " + numFiles); +#endif + db._files = new List(numFiles); + for (var i = 0; i < numFiles; i++) + { + db._files.Add(new CFileItem()); + } + + var emptyStreamVector = new BitVector(numFiles); + BitVector emptyFileVector = null; + BitVector antiFileVector = null; + var numEmptyStreams = 0; + + for (; ; ) + { + type = ReadId(); + if (type == BlockType.End) { - extractFolderInfoVector.Add(new CExtractFolderInfo(fileIndex, -1)); - continue; + break; } - if ( - extractFolderInfoVector.Count == 0 - || folderIndex != extractFolderInfoVector.Last()._folderIndex - ) + var size = checked((long)ReadNumber()); // TODO: throw invalid data on negative + var oldPos = _currentReader.Offset; + switch (type) { - extractFolderInfoVector.Add(new CExtractFolderInfo(-1, folderIndex)); + case BlockType.Name: + using (var streamSwitch = new CStreamSwitch()) + { + streamSwitch.Set(this, dataVector); +#if DEBUG + Log.Write("FileNames:"); +#endif + for (var i = 0; i < db._files.Count; i++) + { + db._files[i].Name = _currentReader.ReadString(); +#if DEBUG + Log.Write(" " + db._files[i].Name); +#endif + } +#if DEBUG + Log.WriteLine(); +#endif + } + break; + case BlockType.WinAttributes: +#if DEBUG + Log.Write("WinAttributes:"); +#endif + ReadAttributeVector( + dataVector, + numFiles, + delegate (int i, uint? attr) + { + // Some third party implementations established an unofficial extension + // of the 7z archive format by placing posix file attributes in the high + // bits of the windows file attributes. This makes use of the fact that + // the official implementation does not perform checks on this value. + // + // Newer versions of the official 7z GUI client will try to parse this + // extension, thus acknowledging the unofficial use of these bits. + // + // For us it is safe to just discard the upper bits if they are set and + // keep the windows attributes from the lower bits (which should be set + // properly even if posix file attributes are present, in order to be + // compatible with older 7z archive readers) + // + // Note that the 15th bit is used by some implementations to indicate + // presence of the extension, but not all implementations do that so + // we can't trust that bit and must ignore it. + // + if (attr.HasValue && (attr.Value >> 16) != 0) + { + attr = attr.Value & 0x7FFFu; + } + + db._files[i].Attrib = attr; +#if DEBUG + Log.Write( + " " + (attr.HasValue ? attr.Value.ToString("x8") : "n/a") + ); +#endif + } + ); +#if DEBUG + Log.WriteLine(); +#endif + break; + case BlockType.EmptyStream: + emptyStreamVector = ReadBitVector(numFiles); +#if DEBUG + + Log.Write("EmptyStream: "); +#endif + for (var i = 0; i < emptyStreamVector.Length; i++) + { + if (emptyStreamVector[i]) + { +#if DEBUG + Log.Write("x"); +#endif + numEmptyStreams++; + } + else + { +#if DEBUG + Log.Write("."); +#endif + } + } +#if DEBUG + Log.WriteLine(); +#endif + + emptyFileVector = new BitVector(numEmptyStreams); + antiFileVector = new BitVector(numEmptyStreams); + break; + case BlockType.EmptyFile: + emptyFileVector = ReadBitVector(numEmptyStreams); +#if DEBUG + Log.Write("EmptyFile: "); + for (var i = 0; i < numEmptyStreams; i++) + { + Log.Write(emptyFileVector[i] ? "x" : "."); + } + Log.WriteLine(); +#endif + break; + case BlockType.Anti: + antiFileVector = ReadBitVector(numEmptyStreams); +#if DEBUG + Log.Write("Anti: "); + for (var i = 0; i < numEmptyStreams; i++) + { + Log.Write(antiFileVector[i] ? "x" : "."); + } + Log.WriteLine(); +#endif + break; + case BlockType.StartPos: +#if DEBUG + Log.Write("StartPos:"); +#endif + ReadNumberVector( + dataVector, + numFiles, + delegate (int i, long? startPos) + { + db._files[i].StartPos = startPos; +#if DEBUG + Log.Write( + " " + + ( + startPos.HasValue + ? startPos.Value.ToString() + : "n/a" + ) + ); +#endif + } + ); +#if DEBUG + Log.WriteLine(); +#endif + break; + case BlockType.CTime: +#if DEBUG + Log.Write("CTime:"); +#endif + ReadDateTimeVector( + dataVector, + numFiles, + delegate (int i, DateTime? time) + { + db._files[i].CTime = time; +#if DEBUG + Log.Write( + " " + (time.HasValue ? time.Value.ToString() : "n/a") + ); +#endif + } + ); +#if DEBUG + Log.WriteLine(); +#endif + break; + case BlockType.ATime: +#if DEBUG + Log.Write("ATime:"); +#endif + ReadDateTimeVector( + dataVector, + numFiles, + delegate (int i, DateTime? time) + { + db._files[i].ATime = time; +#if DEBUG + Log.Write( + " " + (time.HasValue ? time.Value.ToString() : "n/a") + ); +#endif + } + ); +#if DEBUG + Log.WriteLine(); +#endif + break; + case BlockType.MTime: +#if DEBUG + Log.Write("MTime:"); +#endif + ReadDateTimeVector( + dataVector, + numFiles, + delegate (int i, DateTime? time) + { + db._files[i].MTime = time; +#if DEBUG + Log.Write( + " " + (time.HasValue ? time.Value.ToString() : "n/a") + ); +#endif + } + ); +#if DEBUG + Log.WriteLine(); +#endif + break; + case BlockType.Dummy: +#if DEBUG + Log.Write("Dummy: " + size); +#endif + for (long j = 0; j < size; j++) + { + if (ReadByte() != 0) + { + throw new InvalidOperationException(); + } + } + break; + default: + SkipData(size); + break; } - CExtractFolderInfo efi = extractFolderInfoVector.Last(); - - int startIndex = db._folderStartFileIndex[folderIndex]; - for ( - int index = efi._extractStatuses.Count; - index <= fileIndex - startIndex; - index++ - ) + // since 0.3 record sizes must be correct + var checkRecordsSize = (db._majorVersion > 0 || db._minorVersion > 2); + if (checkRecordsSize && _currentReader.Offset - oldPos != size) { - efi._extractStatuses.Add(index == fileIndex - startIndex); + throw new InvalidOperationException(); } } - byte[] buffer = null; - foreach (CExtractFolderInfo efi in extractFolderInfoVector) + var emptyFileIndex = 0; + var sizeIndex = 0; + for (var i = 0; i < numFiles; i++) { - int startIndex; - if (efi._fileIndex != -1) + var file = db._files[i]; + file.HasStream = !emptyStreamVector[i]; + if (file.HasStream) { - startIndex = efi._fileIndex; + file.IsDir = false; + file.IsAnti = false; + file.Size = unpackSizes[sizeIndex]; + file.Crc = digests[sizeIndex]; + sizeIndex++; } else { - startIndex = db._folderStartFileIndex[efi._folderIndex]; - } - - var outStream = new FolderUnpackStream(db, 0, startIndex, efi._extractStatuses); - - if (efi._fileIndex != -1) - { - continue; - } - - int folderIndex = efi._folderIndex; - CFolder folderInfo = db._folders[folderIndex]; - - int packStreamIndex = db._folders[folderIndex]._firstPackStreamId; - long folderStartPackPos = db.GetFolderStreamPos(folderInfo, 0); - - var count = folderInfo._packStreams.Count; - long[] packSizes = new long[count]; - for (int j = 0; j < count; j++) - { - packSizes[j] = db._packSizes[packStreamIndex + j]; - } - - // TODO: If the decoding fails the last file may be extracted incompletely. Delete it? - - Stream s = DecoderStreamHelper.CreateDecoderStream( - _stream, - folderStartPackPos, - packSizes, - folderInfo, - db.PasswordProvider - ); - buffer ??= new byte[4 << 10]; - for (; ; ) - { - int processed = s.Read(buffer, 0, buffer.Length); - if (processed == 0) - { - break; - } - outStream.Write(buffer, 0, processed); + file.IsDir = !emptyFileVector[emptyFileIndex]; + file.IsAnti = antiFileVector[emptyFileIndex]; + emptyFileIndex++; + file.Size = 0; + file.Crc = null; } } } - - public IEnumerable GetFiles(ArchiveDatabase db) + finally { - return db._files; +#if DEBUG + Log.PopIndent(); +#endif + } + } + + #endregion + + #region Public Methods + + public void Open(Stream stream) + { + Close(); + + _streamOrigin = stream.Position; + _streamEnding = stream.Length; + + // TODO: Check Signature! + _header = new byte[0x20]; + for (var offset = 0; offset < 0x20;) + { + var delta = stream.Read(_header, offset, 0x20 - offset); + if (delta == 0) + { + throw new EndOfStreamException(); + } + offset += delta; } - public int GetFileIndex(ArchiveDatabase db, CFileItem item) + _stream = stream; + } + + public void Close() + { + _stream?.Dispose(); + + foreach (var stream in _cachedStreams.Values) { - return db._files.IndexOf(item); + stream.Dispose(); + } + + _cachedStreams.Clear(); + } + + public ArchiveDatabase ReadDatabase(IPasswordProvider pass) + { + var db = new ArchiveDatabase(pass); + db.Clear(); + + db._majorVersion = _header[6]; + db._minorVersion = _header[7]; + + if (db._majorVersion != 0) + { + throw new InvalidOperationException(); + } + + var crcFromArchive = DataReader.Get32(_header, 8); + var nextHeaderOffset = (long)DataReader.Get64(_header, 0xC); + var nextHeaderSize = (long)DataReader.Get64(_header, 0x14); + var nextHeaderCrc = DataReader.Get32(_header, 0x1C); + + var crc = Crc.INIT_CRC; + crc = Crc.Update(crc, nextHeaderOffset); + crc = Crc.Update(crc, nextHeaderSize); + crc = Crc.Update(crc, nextHeaderCrc); + crc = Crc.Finish(crc); + + if (crc != crcFromArchive) + { + throw new InvalidOperationException(); + } + + db._startPositionAfterHeader = _streamOrigin + 0x20; + + // empty header is ok + if (nextHeaderSize == 0) + { + db.Fill(); + return db; + } + + if (nextHeaderOffset < 0 || nextHeaderSize < 0 || nextHeaderSize > int.MaxValue) + { + throw new InvalidOperationException(); + } + + if (nextHeaderOffset > _streamEnding - db._startPositionAfterHeader) + { + throw new InvalidOperationException("nextHeaderOffset is invalid"); + } + + _stream.Seek(nextHeaderOffset, SeekOrigin.Current); + + var header = new byte[nextHeaderSize]; + _stream.ReadExact(header, 0, header.Length); + + if (Crc.Finish(Crc.Update(Crc.INIT_CRC, header, 0, header.Length)) != nextHeaderCrc) + { + throw new InvalidOperationException(); + } + + using (var streamSwitch = new CStreamSwitch()) + { + streamSwitch.Set(this, header); + + var type = ReadId(); + if (type != BlockType.Header) + { + if (type != BlockType.EncodedHeader) + { + throw new InvalidOperationException(); + } + + var dataVector = ReadAndDecodePackedStreams( + db._startPositionAfterHeader, + db.PasswordProvider + ); + + // compressed header without content is odd but ok + if (dataVector.Count == 0) + { + db.Fill(); + return db; + } + + if (dataVector.Count != 1) + { + throw new InvalidOperationException(); + } + + streamSwitch.Set(this, dataVector[0]); + + if (ReadId() != BlockType.Header) + { + throw new InvalidOperationException(); + } + } + + ReadHeader(db, db.PasswordProvider); + } + db.Fill(); + return db; + } + + internal class CExtractFolderInfo + { + internal int _fileIndex; + internal int _folderIndex; + internal List _extractStatuses = new List(); + + internal CExtractFolderInfo(int fileIndex, int folderIndex) + { + _fileIndex = fileIndex; + _folderIndex = folderIndex; + if (fileIndex != -1) + { + _extractStatuses.Add(true); + } + } + } + + private class FolderUnpackStream : Stream + { + private readonly ArchiveDatabase _db; + private readonly int _startIndex; + private readonly List _extractStatuses; + + public FolderUnpackStream(ArchiveDatabase db, int p, int startIndex, List list) + { + _db = db; + _startIndex = startIndex; + _extractStatuses = list; + } + + #region Stream + + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override void Flush() + { + throw new NotSupportedException(); + } + + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + private Stream _stream; + private long _rem; + private int _currentIndex; + + private void ProcessEmptyFiles() + { + while ( + _currentIndex < _extractStatuses.Count + && _db._files[_startIndex + _currentIndex].Size == 0 + ) + { + OpenFile(); + _stream.Dispose(); + _stream = null; + _currentIndex++; + } + } + + private void OpenFile() + { + var index = _startIndex + _currentIndex; +#if DEBUG + Log.WriteLine(_db._files[index].Name); +#endif + if (_db._files[index].CrcDefined) + { + _stream = new CrcCheckStream(_db._files[index].Crc.Value); + } + else + { + _stream = new MemoryStream(); + } + _rem = _db._files[index].Size; + } + + public override void Write(byte[] buffer, int offset, int count) + { + while (count != 0) + { + if (_stream != null) + { + var write = count; + if (write > _rem) + { + write = (int)_rem; + } + _stream.Write(buffer, offset, write); + count -= write; + _rem -= write; + offset += write; + if (_rem == 0) + { + _stream.Dispose(); + _stream = null; + _currentIndex++; + ProcessEmptyFiles(); + } + } + else + { + ProcessEmptyFiles(); + if (_currentIndex == _extractStatuses.Count) + { + // we support partial extracting + Debugger.Break(); + throw new NotSupportedException(); + } + OpenFile(); + } + } } #endregion } + + private Stream GetCachedDecoderStream(ArchiveDatabase db, int folderIndex) + { + if (!_cachedStreams.TryGetValue(folderIndex, out var s)) + { + var folderInfo = db._folders[folderIndex]; + var packStreamIndex = db._folders[folderIndex]._firstPackStreamId; + var folderStartPackPos = db.GetFolderStreamPos(folderInfo, 0); + var count = folderInfo._packStreams.Count; + var packSizes = new long[count]; + for (var j = 0; j < count; j++) + { + packSizes[j] = db._packSizes[packStreamIndex + j]; + } + + s = DecoderStreamHelper.CreateDecoderStream( + _stream, + folderStartPackPos, + packSizes, + folderInfo, + db.PasswordProvider + ); + _cachedStreams.Add(folderIndex, s); + } + return s; + } + + public Stream OpenStream(ArchiveDatabase db, int fileIndex) + { + var folderIndex = db._fileIndexToFolderIndexMap[fileIndex]; + var numFilesInFolder = db._numUnpackStreamsVector[folderIndex]; + var firstFileIndex = db._folderStartFileIndex[folderIndex]; + if (firstFileIndex > fileIndex || fileIndex - firstFileIndex >= numFilesInFolder) + { + throw new InvalidOperationException(); + } + + var skipCount = fileIndex - firstFileIndex; + long skipSize = 0; + for (var i = 0; i < skipCount; i++) + { + skipSize += db._files[firstFileIndex + i].Size; + } + + var s = GetCachedDecoderStream(db, folderIndex); + s.Position = skipSize; + return new ReadOnlySubStream(s, db._files[fileIndex].Size); + } + + public void Extract(ArchiveDatabase db, int[] indices) + { + var allFilesMode = (indices is null); + + var numItems = allFilesMode ? db._files.Count : indices.Length; + + if (numItems == 0) + { + return; + } + + var extractFolderInfoVector = new List(); + for (var i = 0; i < numItems; i++) + { + var fileIndex = allFilesMode ? i : indices[i]; + + var folderIndex = db._fileIndexToFolderIndexMap[fileIndex]; + if (folderIndex == -1) + { + extractFolderInfoVector.Add(new CExtractFolderInfo(fileIndex, -1)); + continue; + } + + if ( + extractFolderInfoVector.Count == 0 + || folderIndex != extractFolderInfoVector.Last()._folderIndex + ) + { + extractFolderInfoVector.Add(new CExtractFolderInfo(-1, folderIndex)); + } + + var efi = extractFolderInfoVector.Last(); + + var startIndex = db._folderStartFileIndex[folderIndex]; + for ( + var index = efi._extractStatuses.Count; + index <= fileIndex - startIndex; + index++ + ) + { + efi._extractStatuses.Add(index == fileIndex - startIndex); + } + } + + byte[] buffer = null; + foreach (var efi in extractFolderInfoVector) + { + int startIndex; + if (efi._fileIndex != -1) + { + startIndex = efi._fileIndex; + } + else + { + startIndex = db._folderStartFileIndex[efi._folderIndex]; + } + + var outStream = new FolderUnpackStream(db, 0, startIndex, efi._extractStatuses); + + if (efi._fileIndex != -1) + { + continue; + } + + var folderIndex = efi._folderIndex; + var folderInfo = db._folders[folderIndex]; + + var packStreamIndex = db._folders[folderIndex]._firstPackStreamId; + var folderStartPackPos = db.GetFolderStreamPos(folderInfo, 0); + + var count = folderInfo._packStreams.Count; + var packSizes = new long[count]; + for (var j = 0; j < count; j++) + { + packSizes[j] = db._packSizes[packStreamIndex + j]; + } + + // TODO: If the decoding fails the last file may be extracted incompletely. Delete it? + + var s = DecoderStreamHelper.CreateDecoderStream( + _stream, + folderStartPackPos, + packSizes, + folderInfo, + db.PasswordProvider + ); + buffer ??= new byte[4 << 10]; + for (; ; ) + { + var processed = s.Read(buffer, 0, buffer.Length); + if (processed == 0) + { + break; + } + outStream.Write(buffer, 0, processed); + } + } + } + + public IEnumerable GetFiles(ArchiveDatabase db) + { + return db._files; + } + + public int GetFileIndex(ArchiveDatabase db, CFileItem item) + { + return db._files.IndexOf(item); + } + + #endregion } diff --git a/src/SharpCompress/Common/SevenZip/CBindPair.cs b/src/SharpCompress/Common/SevenZip/CBindPair.cs index 1d76acf0..f9d752e6 100644 --- a/src/SharpCompress/Common/SevenZip/CBindPair.cs +++ b/src/SharpCompress/Common/SevenZip/CBindPair.cs @@ -1,8 +1,7 @@ -namespace SharpCompress.Common.SevenZip +namespace SharpCompress.Common.SevenZip; + +internal class CBindPair { - internal class CBindPair - { - internal int _inIndex; - internal int _outIndex; - } + internal int _inIndex; + internal int _outIndex; } diff --git a/src/SharpCompress/Common/SevenZip/CCoderInfo.cs b/src/SharpCompress/Common/SevenZip/CCoderInfo.cs index 5db367b2..d53c44f0 100644 --- a/src/SharpCompress/Common/SevenZip/CCoderInfo.cs +++ b/src/SharpCompress/Common/SevenZip/CCoderInfo.cs @@ -1,12 +1,11 @@ -#nullable disable +#nullable disable -namespace SharpCompress.Common.SevenZip +namespace SharpCompress.Common.SevenZip; + +internal class CCoderInfo { - internal class CCoderInfo - { - internal CMethodId _methodId; - internal byte[] _props; - internal int _numInStreams; - internal int _numOutStreams; - } + internal CMethodId _methodId; + internal byte[] _props; + internal int _numInStreams; + internal int _numOutStreams; } diff --git a/src/SharpCompress/Common/SevenZip/CFileItem.cs b/src/SharpCompress/Common/SevenZip/CFileItem.cs index a97d2c9b..f83019fe 100644 --- a/src/SharpCompress/Common/SevenZip/CFileItem.cs +++ b/src/SharpCompress/Common/SevenZip/CFileItem.cs @@ -1,38 +1,37 @@ -#nullable disable +#nullable disable using System; -namespace SharpCompress.Common.SevenZip +namespace SharpCompress.Common.SevenZip; + +internal class CFileItem { - internal class CFileItem + public long Size { get; internal set; } + public uint? Attrib { get; internal set; } + public uint? Crc { get; internal set; } + public string Name { get; internal set; } + + public bool HasStream { get; internal set; } + public bool IsDir { get; internal set; } + + public bool CrcDefined => Crc != null; + + public bool AttribDefined => Attrib != null; + + public void SetAttrib(uint attrib) { - public long Size { get; internal set; } - public uint? Attrib { get; internal set; } - public uint? Crc { get; internal set; } - public string Name { get; internal set; } + Attrib = attrib; + } - public bool HasStream { get; internal set; } - public bool IsDir { get; internal set; } + public DateTime? CTime { get; internal set; } + public DateTime? ATime { get; internal set; } + public DateTime? MTime { get; internal set; } - public bool CrcDefined => Crc != null; + public long? StartPos { get; internal set; } + public bool IsAnti { get; internal set; } - public bool AttribDefined => Attrib != null; - - public void SetAttrib(uint attrib) - { - Attrib = attrib; - } - - public DateTime? CTime { get; internal set; } - public DateTime? ATime { get; internal set; } - public DateTime? MTime { get; internal set; } - - public long? StartPos { get; internal set; } - public bool IsAnti { get; internal set; } - - internal CFileItem() - { - HasStream = true; - } + internal CFileItem() + { + HasStream = true; } } diff --git a/src/SharpCompress/Common/SevenZip/CFolder.cs b/src/SharpCompress/Common/SevenZip/CFolder.cs index c15da80c..9d3516b1 100644 --- a/src/SharpCompress/Common/SevenZip/CFolder.cs +++ b/src/SharpCompress/Common/SevenZip/CFolder.cs @@ -1,188 +1,187 @@ -using System; +using System; using System.Collections.Generic; using SharpCompress.Compressors.LZMA; -namespace SharpCompress.Common.SevenZip +namespace SharpCompress.Common.SevenZip; + +internal class CFolder { - internal class CFolder + internal List _coders = new List(); + internal List _bindPairs = new List(); + internal List _packStreams = new List(); + internal int _firstPackStreamId; + internal List _unpackSizes = new List(); + internal uint? _unpackCrc; + + internal bool UnpackCrcDefined => _unpackCrc != null; + + public long GetUnpackSize() { - internal List _coders = new List(); - internal List _bindPairs = new List(); - internal List _packStreams = new List(); - internal int _firstPackStreamId; - internal List _unpackSizes = new List(); - internal uint? _unpackCrc; - - internal bool UnpackCrcDefined => _unpackCrc != null; - - public long GetUnpackSize() + if (_unpackSizes.Count == 0) { - if (_unpackSizes.Count == 0) - { - return 0; - } - - for (int i = _unpackSizes.Count - 1; i >= 0; i--) - { - if (FindBindPairForOutStream(i) < 0) - { - return _unpackSizes[i]; - } - } - - throw new InvalidOperationException(); + return 0; } - public int GetNumOutStreams() + for (var i = _unpackSizes.Count - 1; i >= 0; i--) { - int count = 0; - for (int i = 0; i < _coders.Count; i++) + if (FindBindPairForOutStream(i) < 0) { - count += _coders[i]._numOutStreams; + return _unpackSizes[i]; } - - return count; } - public int FindBindPairForInStream(int inStreamIndex) - { - for (int i = 0; i < _bindPairs.Count; i++) - { - if (_bindPairs[i]._inIndex == inStreamIndex) - { - return i; - } - } + throw new InvalidOperationException(); + } - return -1; + public int GetNumOutStreams() + { + var count = 0; + for (var i = 0; i < _coders.Count; i++) + { + count += _coders[i]._numOutStreams; } - public int FindBindPairForOutStream(int outStreamIndex) - { - for (int i = 0; i < _bindPairs.Count; i++) - { - if (_bindPairs[i]._outIndex == outStreamIndex) - { - return i; - } - } + return count; + } - return -1; + public int FindBindPairForInStream(int inStreamIndex) + { + for (var i = 0; i < _bindPairs.Count; i++) + { + if (_bindPairs[i]._inIndex == inStreamIndex) + { + return i; + } } - public int FindPackStreamArrayIndex(int inStreamIndex) - { - for (int i = 0; i < _packStreams.Count; i++) - { - if (_packStreams[i] == inStreamIndex) - { - return i; - } - } + return -1; + } - return -1; + public int FindBindPairForOutStream(int outStreamIndex) + { + for (var i = 0; i < _bindPairs.Count; i++) + { + if (_bindPairs[i]._outIndex == outStreamIndex) + { + return i; + } } - public bool IsEncrypted() - { - for (int i = _coders.Count - 1; i >= 0; i--) - { - if (_coders[i]._methodId == CMethodId.K_AES) - { - return true; - } - } + return -1; + } + public int FindPackStreamArrayIndex(int inStreamIndex) + { + for (var i = 0; i < _packStreams.Count; i++) + { + if (_packStreams[i] == inStreamIndex) + { + return i; + } + } + + return -1; + } + + public bool IsEncrypted() + { + for (var i = _coders.Count - 1; i >= 0; i--) + { + if (_coders[i]._methodId == CMethodId.K_AES) + { + return true; + } + } + + return false; + } + + public bool CheckStructure() + { + const int kNumCodersMax = 32; // don't change it + const int kMaskSize = 32; // it must be >= kNumCodersMax + const int kNumBindsMax = 32; + + if (_coders.Count > kNumCodersMax || _bindPairs.Count > kNumBindsMax) + { return false; } - public bool CheckStructure() { - const int kNumCodersMax = 32; // don't change it - const int kMaskSize = 32; // it must be >= kNumCodersMax - const int kNumBindsMax = 32; + var v = new BitVector(_bindPairs.Count + _packStreams.Count); - if (_coders.Count > kNumCodersMax || _bindPairs.Count > kNumBindsMax) + for (var i = 0; i < _bindPairs.Count; i++) { - return false; - } - - { - var v = new BitVector(_bindPairs.Count + _packStreams.Count); - - for (int i = 0; i < _bindPairs.Count; i++) - { - if (v.GetAndSet(_bindPairs[i]._inIndex)) - { - return false; - } - } - - for (int i = 0; i < _packStreams.Count; i++) - { - if (v.GetAndSet(_packStreams[i])) - { - return false; - } - } - } - - { - var v = new BitVector(_unpackSizes.Count); - for (int i = 0; i < _bindPairs.Count; i++) - { - if (v.GetAndSet(_bindPairs[i]._outIndex)) - { - return false; - } - } - } - - uint[] mask = new uint[kMaskSize]; - - { - List inStreamToCoder = new List(); - List outStreamToCoder = new List(); - for (int i = 0; i < _coders.Count; i++) - { - CCoderInfo coder = _coders[i]; - for (int j = 0; j < coder._numInStreams; j++) - { - inStreamToCoder.Add(i); - } - for (int j = 0; j < coder._numOutStreams; j++) - { - outStreamToCoder.Add(i); - } - } - - for (int i = 0; i < _bindPairs.Count; i++) - { - CBindPair bp = _bindPairs[i]; - mask[inStreamToCoder[bp._inIndex]] |= (1u << outStreamToCoder[bp._outIndex]); - } - } - - for (int i = 0; i < kMaskSize; i++) - { - for (int j = 0; j < kMaskSize; j++) - { - if (((1u << j) & mask[i]) != 0) - { - mask[i] |= mask[j]; - } - } - } - - for (int i = 0; i < kMaskSize; i++) - { - if (((1u << i) & mask[i]) != 0) + if (v.GetAndSet(_bindPairs[i]._inIndex)) { return false; } } - return true; + for (var i = 0; i < _packStreams.Count; i++) + { + if (v.GetAndSet(_packStreams[i])) + { + return false; + } + } } + + { + var v = new BitVector(_unpackSizes.Count); + for (var i = 0; i < _bindPairs.Count; i++) + { + if (v.GetAndSet(_bindPairs[i]._outIndex)) + { + return false; + } + } + } + + var mask = new uint[kMaskSize]; + + { + var inStreamToCoder = new List(); + var outStreamToCoder = new List(); + for (var i = 0; i < _coders.Count; i++) + { + var coder = _coders[i]; + for (var j = 0; j < coder._numInStreams; j++) + { + inStreamToCoder.Add(i); + } + for (var j = 0; j < coder._numOutStreams; j++) + { + outStreamToCoder.Add(i); + } + } + + for (var i = 0; i < _bindPairs.Count; i++) + { + var bp = _bindPairs[i]; + mask[inStreamToCoder[bp._inIndex]] |= (1u << outStreamToCoder[bp._outIndex]); + } + } + + for (var i = 0; i < kMaskSize; i++) + { + for (var j = 0; j < kMaskSize; j++) + { + if (((1u << j) & mask[i]) != 0) + { + mask[i] |= mask[j]; + } + } + } + + for (var i = 0; i < kMaskSize; i++) + { + if (((1u << i) & mask[i]) != 0) + { + return false; + } + } + + return true; } } diff --git a/src/SharpCompress/Common/SevenZip/CMethodId.cs b/src/SharpCompress/Common/SevenZip/CMethodId.cs index 871989e4..33a331c9 100644 --- a/src/SharpCompress/Common/SevenZip/CMethodId.cs +++ b/src/SharpCompress/Common/SevenZip/CMethodId.cs @@ -1,57 +1,56 @@ -namespace SharpCompress.Common.SevenZip +namespace SharpCompress.Common.SevenZip; + +internal readonly struct CMethodId { - internal readonly struct CMethodId + public const ulong K_COPY_ID = 0; + public const ulong K_LZMA_ID = 0x030101; + public const ulong K_LZMA2_ID = 0x21; + public const ulong K_AES_ID = 0x06F10701; + + public static readonly CMethodId K_COPY = new CMethodId(K_COPY_ID); + public static readonly CMethodId K_LZMA = new CMethodId(K_LZMA_ID); + public static readonly CMethodId K_LZMA2 = new CMethodId(K_LZMA2_ID); + public static readonly CMethodId K_AES = new CMethodId(K_AES_ID); + + public readonly ulong _id; + + public CMethodId(ulong id) { - public const ulong K_COPY_ID = 0; - public const ulong K_LZMA_ID = 0x030101; - public const ulong K_LZMA2_ID = 0x21; - public const ulong K_AES_ID = 0x06F10701; + _id = id; + } - public static readonly CMethodId K_COPY = new CMethodId(K_COPY_ID); - public static readonly CMethodId K_LZMA = new CMethodId(K_LZMA_ID); - public static readonly CMethodId K_LZMA2 = new CMethodId(K_LZMA2_ID); - public static readonly CMethodId K_AES = new CMethodId(K_AES_ID); + public override int GetHashCode() + { + return _id.GetHashCode(); + } - public readonly ulong _id; + public override bool Equals(object? obj) + { + return obj is CMethodId other && Equals(other); + } - public CMethodId(ulong id) + public bool Equals(CMethodId other) + { + return _id == other._id; + } + + public static bool operator ==(CMethodId left, CMethodId right) + { + return left._id == right._id; + } + + public static bool operator !=(CMethodId left, CMethodId right) + { + return left._id != right._id; + } + + public int GetLength() + { + var bytes = 0; + for (var value = _id; value != 0; value >>= 8) { - _id = id; - } - - public override int GetHashCode() - { - return _id.GetHashCode(); - } - - public override bool Equals(object? obj) - { - return obj is CMethodId other && Equals(other); - } - - public bool Equals(CMethodId other) - { - return _id == other._id; - } - - public static bool operator ==(CMethodId left, CMethodId right) - { - return left._id == right._id; - } - - public static bool operator !=(CMethodId left, CMethodId right) - { - return left._id != right._id; - } - - public int GetLength() - { - int bytes = 0; - for (ulong value = _id; value != 0; value >>= 8) - { - bytes++; - } - return bytes; + bytes++; } + return bytes; } } diff --git a/src/SharpCompress/Common/SevenZip/CStreamSwitch.cs b/src/SharpCompress/Common/SevenZip/CStreamSwitch.cs index 706bfc55..9238a5a1 100644 --- a/src/SharpCompress/Common/SevenZip/CStreamSwitch.cs +++ b/src/SharpCompress/Common/SevenZip/CStreamSwitch.cs @@ -1,69 +1,68 @@ -using System; +using System; using System.Collections.Generic; using SharpCompress.Compressors.LZMA; -namespace SharpCompress.Common.SevenZip +namespace SharpCompress.Common.SevenZip; + +internal struct CStreamSwitch : IDisposable { - internal struct CStreamSwitch : IDisposable + private ArchiveReader _archive; + private bool _needRemove; + private bool _active; + + public void Dispose() { - private ArchiveReader _archive; - private bool _needRemove; - private bool _active; - - public void Dispose() + if (_active) { - if (_active) - { - _active = false; + _active = false; #if DEBUG - Log.WriteLine("[end of switch]"); + Log.WriteLine("[end of switch]"); #endif - } - - if (_needRemove) - { - _needRemove = false; - _archive.DeleteByteStream(); - } } - public void Set(ArchiveReader archive, byte[] dataVector) + if (_needRemove) { - Dispose(); + _needRemove = false; + _archive.DeleteByteStream(); + } + } + + public void Set(ArchiveReader archive, byte[] dataVector) + { + Dispose(); + _archive = archive; + _archive.AddByteStream(dataVector, 0, dataVector.Length); + _needRemove = true; + _active = true; + } + + public void Set(ArchiveReader archive, List dataVector) + { + Dispose(); + _active = true; + + var external = archive.ReadByte(); + if (external != 0) + { + var dataIndex = archive.ReadNum(); + if (dataIndex < 0 || dataIndex >= dataVector.Count) + { + throw new InvalidOperationException(); + } + +#if DEBUG + Log.WriteLine("[switch to stream {0}]", dataIndex); +#endif _archive = archive; - _archive.AddByteStream(dataVector, 0, dataVector.Length); + _archive.AddByteStream(dataVector[dataIndex], 0, dataVector[dataIndex].Length); _needRemove = true; _active = true; } - - public void Set(ArchiveReader archive, List dataVector) + else { - Dispose(); - _active = true; - - byte external = archive.ReadByte(); - if (external != 0) - { - int dataIndex = archive.ReadNum(); - if (dataIndex < 0 || dataIndex >= dataVector.Count) - { - throw new InvalidOperationException(); - } - #if DEBUG - Log.WriteLine("[switch to stream {0}]", dataIndex); + Log.WriteLine("[inline data]"); #endif - _archive = archive; - _archive.AddByteStream(dataVector[dataIndex], 0, dataVector[dataIndex].Length); - _needRemove = true; - _active = true; - } - else - { -#if DEBUG - Log.WriteLine("[inline data]"); -#endif - } } } } diff --git a/src/SharpCompress/Common/SevenZip/DataReader.cs b/src/SharpCompress/Common/SevenZip/DataReader.cs index ad20a491..48e9dabc 100644 --- a/src/SharpCompress/Common/SevenZip/DataReader.cs +++ b/src/SharpCompress/Common/SevenZip/DataReader.cs @@ -1,186 +1,185 @@ -using System; +using System; using System.IO; using System.Text; using SharpCompress.Compressors.LZMA; -namespace SharpCompress.Common.SevenZip +namespace SharpCompress.Common.SevenZip; + +internal class DataReader { - internal class DataReader + #region Static Methods + + public static uint Get32(byte[] buffer, int offset) { - #region Static Methods - - public static uint Get32(byte[] buffer, int offset) - { - return buffer[offset] - + ((uint)buffer[offset + 1] << 8) - + ((uint)buffer[offset + 2] << 16) - + ((uint)buffer[offset + 3] << 24); - } - - public static ulong Get64(byte[] buffer, int offset) - { - return buffer[offset] - + ((ulong)buffer[offset + 1] << 8) - + ((ulong)buffer[offset + 2] << 16) - + ((ulong)buffer[offset + 3] << 24) - + ((ulong)buffer[offset + 4] << 32) - + ((ulong)buffer[offset + 5] << 40) - + ((ulong)buffer[offset + 6] << 48) - + ((ulong)buffer[offset + 7] << 56); - } - - #endregion - - #region Variables - - private readonly byte[] _buffer; - private readonly int _ending; - - #endregion - - #region Public Methods - - public DataReader(byte[] buffer, int offset, int length) - { - _buffer = buffer; - Offset = offset; - _ending = offset + length; - } - - public int Offset { get; private set; } - - public Byte ReadByte() - { - if (Offset >= _ending) - { - throw new EndOfStreamException(); - } - - return _buffer[Offset++]; - } - - public void ReadBytes(byte[] buffer, int offset, int length) - { - if (length > _ending - Offset) - { - throw new EndOfStreamException(); - } - - while (length-- > 0) - { - buffer[offset++] = _buffer[Offset++]; - } - } - - public void SkipData(long size) - { - if (size > _ending - Offset) - { - throw new EndOfStreamException(); - } - - Offset += (int)size; -#if DEBUG - Log.WriteLine("SkipData {0}", size); -#endif - } - - public void SkipData() - { - SkipData(checked((long)ReadNumber())); - } - - public ulong ReadNumber() - { - if (Offset >= _ending) - { - throw new EndOfStreamException(); - } - - byte firstByte = _buffer[Offset++]; - byte mask = 0x80; - ulong value = 0; - - for (int i = 0; i < 8; i++) - { - if ((firstByte & mask) == 0) - { - ulong highPart = firstByte & (mask - 1u); - value += highPart << (i * 8); - return value; - } - - if (Offset >= _ending) - { - throw new EndOfStreamException(); - } - - value |= (ulong)_buffer[Offset++] << (8 * i); - mask >>= 1; - } - - return value; - } - - public int ReadNum() - { - ulong value = ReadNumber(); - if (value > Int32.MaxValue) - { - throw new NotSupportedException(); - } - - return (int)value; - } - - public uint ReadUInt32() - { - if (Offset + 4 > _ending) - { - throw new EndOfStreamException(); - } - - uint res = Get32(_buffer, Offset); - Offset += 4; - return res; - } - - public ulong ReadUInt64() - { - if (Offset + 8 > _ending) - { - throw new EndOfStreamException(); - } - - ulong res = Get64(_buffer, Offset); - Offset += 8; - return res; - } - - public string ReadString() - { - int ending = Offset; - - for (; ; ) - { - if (ending + 2 > _ending) - { - throw new EndOfStreamException(); - } - - if (_buffer[ending] == 0 && _buffer[ending + 1] == 0) - { - break; - } - - ending += 2; - } - - string str = Encoding.Unicode.GetString(_buffer, Offset, ending - Offset); - Offset = ending + 2; - return str; - } - - #endregion + return buffer[offset] + + ((uint)buffer[offset + 1] << 8) + + ((uint)buffer[offset + 2] << 16) + + ((uint)buffer[offset + 3] << 24); } + + public static ulong Get64(byte[] buffer, int offset) + { + return buffer[offset] + + ((ulong)buffer[offset + 1] << 8) + + ((ulong)buffer[offset + 2] << 16) + + ((ulong)buffer[offset + 3] << 24) + + ((ulong)buffer[offset + 4] << 32) + + ((ulong)buffer[offset + 5] << 40) + + ((ulong)buffer[offset + 6] << 48) + + ((ulong)buffer[offset + 7] << 56); + } + + #endregion + + #region Variables + + private readonly byte[] _buffer; + private readonly int _ending; + + #endregion + + #region Public Methods + + public DataReader(byte[] buffer, int offset, int length) + { + _buffer = buffer; + Offset = offset; + _ending = offset + length; + } + + public int Offset { get; private set; } + + public byte ReadByte() + { + if (Offset >= _ending) + { + throw new EndOfStreamException(); + } + + return _buffer[Offset++]; + } + + public void ReadBytes(byte[] buffer, int offset, int length) + { + if (length > _ending - Offset) + { + throw new EndOfStreamException(); + } + + while (length-- > 0) + { + buffer[offset++] = _buffer[Offset++]; + } + } + + public void SkipData(long size) + { + if (size > _ending - Offset) + { + throw new EndOfStreamException(); + } + + Offset += (int)size; +#if DEBUG + Log.WriteLine("SkipData {0}", size); +#endif + } + + public void SkipData() + { + SkipData(checked((long)ReadNumber())); + } + + public ulong ReadNumber() + { + if (Offset >= _ending) + { + throw new EndOfStreamException(); + } + + var firstByte = _buffer[Offset++]; + byte mask = 0x80; + ulong value = 0; + + for (var i = 0; i < 8; i++) + { + if ((firstByte & mask) == 0) + { + ulong highPart = firstByte & (mask - 1u); + value += highPart << (i * 8); + return value; + } + + if (Offset >= _ending) + { + throw new EndOfStreamException(); + } + + value |= (ulong)_buffer[Offset++] << (8 * i); + mask >>= 1; + } + + return value; + } + + public int ReadNum() + { + var value = ReadNumber(); + if (value > int.MaxValue) + { + throw new NotSupportedException(); + } + + return (int)value; + } + + public uint ReadUInt32() + { + if (Offset + 4 > _ending) + { + throw new EndOfStreamException(); + } + + var res = Get32(_buffer, Offset); + Offset += 4; + return res; + } + + public ulong ReadUInt64() + { + if (Offset + 8 > _ending) + { + throw new EndOfStreamException(); + } + + var res = Get64(_buffer, Offset); + Offset += 8; + return res; + } + + public string ReadString() + { + var ending = Offset; + + for (; ; ) + { + if (ending + 2 > _ending) + { + throw new EndOfStreamException(); + } + + if (_buffer[ending] == 0 && _buffer[ending + 1] == 0) + { + break; + } + + ending += 2; + } + + var str = Encoding.Unicode.GetString(_buffer, Offset, ending - Offset); + Offset = ending + 2; + return str; + } + + #endregion } diff --git a/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs b/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs index 7be12a96..3e806681 100644 --- a/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs +++ b/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs @@ -1,46 +1,45 @@ -using System; +using System; using System.Collections.Generic; -namespace SharpCompress.Common.SevenZip +namespace SharpCompress.Common.SevenZip; + +public class SevenZipEntry : Entry { - public class SevenZipEntry : Entry + internal SevenZipEntry(SevenZipFilePart filePart) { - internal SevenZipEntry(SevenZipFilePart filePart) - { - FilePart = filePart; - } - - internal SevenZipFilePart FilePart { get; } - - public override CompressionType CompressionType => FilePart.CompressionType; - - public override long Crc => FilePart.Header.Crc ?? 0; - - public override string Key => FilePart.Header.Name; - - public override string? LinkTarget => null; - - public override long CompressedSize => 0; - - public override long Size => FilePart.Header.Size; - - public override DateTime? LastModifiedTime => FilePart.Header.MTime; - - public override DateTime? CreatedTime => null; - - public override DateTime? LastAccessedTime => null; - - public override DateTime? ArchivedTime => null; - - public override bool IsEncrypted => FilePart.IsEncrypted; - - public override bool IsDirectory => FilePart.Header.IsDir; - - public override bool IsSplitAfter => false; - - public override int? Attrib => - FilePart.Header.Attrib.HasValue ? (int?)FilePart.Header.Attrib.Value : null; - - internal override IEnumerable Parts => FilePart.AsEnumerable(); + FilePart = filePart; } + + internal SevenZipFilePart FilePart { get; } + + public override CompressionType CompressionType => FilePart.CompressionType; + + public override long Crc => FilePart.Header.Crc ?? 0; + + public override string Key => FilePart.Header.Name; + + public override string? LinkTarget => null; + + public override long CompressedSize => 0; + + public override long Size => FilePart.Header.Size; + + public override DateTime? LastModifiedTime => FilePart.Header.MTime; + + public override DateTime? CreatedTime => null; + + public override DateTime? LastAccessedTime => null; + + public override DateTime? ArchivedTime => null; + + public override bool IsEncrypted => FilePart.IsEncrypted; + + public override bool IsDirectory => FilePart.Header.IsDir; + + public override bool IsSplitAfter => false; + + public override int? Attrib => + FilePart.Header.Attrib.HasValue ? (int?)FilePart.Header.Attrib.Value : null; + + internal override IEnumerable Parts => FilePart.AsEnumerable(); } diff --git a/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs b/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs index e6927712..234d4a1d 100644 --- a/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs +++ b/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs @@ -3,117 +3,107 @@ using System.IO; using System.Linq; using SharpCompress.IO; -namespace SharpCompress.Common.SevenZip +namespace SharpCompress.Common.SevenZip; + +internal class SevenZipFilePart : FilePart { - internal class SevenZipFilePart : FilePart + private CompressionType? _type; + private readonly Stream _stream; + private readonly ArchiveDatabase _database; + + internal SevenZipFilePart( + Stream stream, + ArchiveDatabase database, + int index, + CFileItem fileEntry, + ArchiveEncoding archiveEncoding + ) : base(archiveEncoding) { - private CompressionType? _type; - private readonly Stream _stream; - private readonly ArchiveDatabase _database; - - internal SevenZipFilePart( - Stream stream, - ArchiveDatabase database, - int index, - CFileItem fileEntry, - ArchiveEncoding archiveEncoding - ) : base(archiveEncoding) + _stream = stream; + _database = database; + Index = index; + Header = fileEntry; + if (Header.HasStream) { - _stream = stream; - _database = database; - Index = index; - Header = fileEntry; - if (Header.HasStream) - { - Folder = database._folders[database._fileIndexToFolderIndexMap[index]]; - } + Folder = database._folders[database._fileIndexToFolderIndexMap[index]]; } - - internal CFileItem Header { get; } - internal CFolder? Folder { get; } - - internal override string FilePartName => Header.Name; - - internal override Stream? GetRawStream() - { - return null; - } - - internal override Stream GetCompressedStream() - { - if (!Header.HasStream) - { - return null!; - } - var folderStream = _database.GetFolderStream( - _stream, - Folder!, - _database.PasswordProvider - ); - - int firstFileIndex = _database._folderStartFileIndex[ - _database._folders.IndexOf(Folder!) - ]; - int skipCount = Index - firstFileIndex; - long skipSize = 0; - for (int i = 0; i < skipCount; i++) - { - skipSize += _database._files[firstFileIndex + i].Size; - } - if (skipSize > 0) - { - folderStream.Skip(skipSize); - } - return new ReadOnlySubStream(folderStream, Header.Size); - } - - public CompressionType CompressionType - { - get - { - if (_type is null) - { - _type = GetCompression(); - } - return _type.Value; - } - } - - //copied from DecoderRegistry - private const uint K_COPY = 0x0; - private const uint K_DELTA = 3; - private const uint K_LZMA2 = 0x21; - private const uint K_LZMA = 0x030101; - private const uint K_PPMD = 0x030401; - private const uint K_BCJ = 0x03030103; - private const uint K_BCJ2 = 0x0303011B; - private const uint K_DEFLATE = 0x040108; - private const uint K_B_ZIP2 = 0x040202; - - internal CompressionType GetCompression() - { - var coder = Folder!._coders.First(); - switch (coder._methodId._id) - { - case K_LZMA: - case K_LZMA2: - { - return CompressionType.LZMA; - } - case K_PPMD: - { - return CompressionType.PPMd; - } - case K_B_ZIP2: - { - return CompressionType.BZip2; - } - default: - throw new NotImplementedException(); - } - } - - internal bool IsEncrypted => - Folder!._coders.FindIndex(c => c._methodId._id == CMethodId.K_AES_ID) != -1; } + + internal CFileItem Header { get; } + internal CFolder? Folder { get; } + + internal override string FilePartName => Header.Name; + + internal override Stream? GetRawStream() + { + return null; + } + + internal override Stream GetCompressedStream() + { + if (!Header.HasStream) + { + return null!; + } + var folderStream = _database.GetFolderStream( + _stream, + Folder!, + _database.PasswordProvider + ); + + var firstFileIndex = _database._folderStartFileIndex[ + _database._folders.IndexOf(Folder!) + ]; + var skipCount = Index - firstFileIndex; + long skipSize = 0; + for (var i = 0; i < skipCount; i++) + { + skipSize += _database._files[firstFileIndex + i].Size; + } + if (skipSize > 0) + { + folderStream.Skip(skipSize); + } + return new ReadOnlySubStream(folderStream, Header.Size); + } + + public CompressionType CompressionType + { + get + { + _type ??= GetCompression(); + return _type.Value; + } + } + + private const uint K_LZMA2 = 0x21; + private const uint K_LZMA = 0x030101; + private const uint K_PPMD = 0x030401; + private const uint K_B_ZIP2 = 0x040202; + + internal CompressionType GetCompression() + { + var coder = Folder!._coders.First(); + switch (coder._methodId._id) + { + case K_LZMA: + case K_LZMA2: + { + return CompressionType.LZMA; + } + case K_PPMD: + { + return CompressionType.PPMd; + } + case K_B_ZIP2: + { + return CompressionType.BZip2; + } + default: + throw new NotImplementedException(); + } + } + + internal bool IsEncrypted => + Folder!._coders.FindIndex(c => c._methodId._id == CMethodId.K_AES_ID) != -1; } diff --git a/src/SharpCompress/Common/SevenZip/SevenZipVolume.cs b/src/SharpCompress/Common/SevenZip/SevenZipVolume.cs index 19e7b2f0..e4b330af 100644 --- a/src/SharpCompress/Common/SevenZip/SevenZipVolume.cs +++ b/src/SharpCompress/Common/SevenZip/SevenZipVolume.cs @@ -1,11 +1,10 @@ using System.IO; using SharpCompress.Readers; -namespace SharpCompress.Common.SevenZip +namespace SharpCompress.Common.SevenZip; + +public class SevenZipVolume : Volume { - public class SevenZipVolume : Volume - { - public SevenZipVolume(Stream stream, ReaderOptions readerFactoryOptions, int index = 0) - : base(stream, readerFactoryOptions, index) { } - } + public SevenZipVolume(Stream stream, ReaderOptions readerFactoryOptions, int index = 0) + : base(stream, readerFactoryOptions, index) { } } diff --git a/src/SharpCompress/Common/Tar/Headers/EntryType.cs b/src/SharpCompress/Common/Tar/Headers/EntryType.cs index feeaa6cc..3e6877c7 100644 --- a/src/SharpCompress/Common/Tar/Headers/EntryType.cs +++ b/src/SharpCompress/Common/Tar/Headers/EntryType.cs @@ -1,19 +1,18 @@ -namespace SharpCompress.Common.Tar.Headers +namespace SharpCompress.Common.Tar.Headers; + +internal enum EntryType : byte { - internal enum EntryType : byte - { - File = 0, - OldFile = (byte)'0', - HardLink = (byte)'1', - SymLink = (byte)'2', - CharDevice = (byte)'3', - BlockDevice = (byte)'4', - Directory = (byte)'5', - Fifo = (byte)'6', - LongLink = (byte)'K', - LongName = (byte)'L', - SparseFile = (byte)'S', - VolumeHeader = (byte)'V', - GlobalExtendedHeader = (byte)'g' - } + File = 0, + OldFile = (byte)'0', + HardLink = (byte)'1', + SymLink = (byte)'2', + CharDevice = (byte)'3', + BlockDevice = (byte)'4', + Directory = (byte)'5', + Fifo = (byte)'6', + LongLink = (byte)'K', + LongName = (byte)'L', + SparseFile = (byte)'S', + VolumeHeader = (byte)'V', + GlobalExtendedHeader = (byte)'g' } diff --git a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs index e9b38971..78c2e0d9 100644 --- a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs +++ b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs @@ -1,322 +1,323 @@ -#nullable disable +#nullable disable using System; using System.Buffers.Binary; using System.IO; using System.Text; -namespace SharpCompress.Common.Tar.Headers +namespace SharpCompress.Common.Tar.Headers; + +internal sealed class TarHeader { - internal sealed class TarHeader + internal static readonly DateTime EPOCH = new DateTime( + 1970, + 1, + 1, + 0, + 0, + 0, + DateTimeKind.Utc + ); + + public TarHeader(ArchiveEncoding archiveEncoding) { - internal static readonly DateTime EPOCH = new DateTime( - 1970, - 1, - 1, - 0, - 0, - 0, - DateTimeKind.Utc - ); + ArchiveEncoding = archiveEncoding; + } - public TarHeader(ArchiveEncoding archiveEncoding) + internal string Name { get; set; } + internal string LinkName { get; set; } + + internal long Mode { get; set; } + internal long UserId { get; set; } + internal long GroupId { get; set; } + internal long Size { get; set; } + internal DateTime LastModifiedTime { get; set; } + internal EntryType EntryType { get; set; } + internal Stream PackedStream { get; set; } + internal ArchiveEncoding ArchiveEncoding { get; } + + internal const int BLOCK_SIZE = 512; + + internal void Write(Stream output) + { + var buffer = new byte[BLOCK_SIZE]; + + WriteOctalBytes(511, buffer, 100, 8); // file mode + WriteOctalBytes(0, buffer, 108, 8); // owner ID + WriteOctalBytes(0, buffer, 116, 8); // group ID + + //ArchiveEncoding.UTF8.GetBytes("magic").CopyTo(buffer, 257); + var nameByteCount = ArchiveEncoding.GetEncoding().GetByteCount(Name); + if (nameByteCount > 100) { - ArchiveEncoding = archiveEncoding; + // Set mock filename and filetype to indicate the next block is the actual name of the file + WriteStringBytes("././@LongLink", buffer, 0, 100); + buffer[156] = (byte)EntryType.LongName; + WriteOctalBytes(nameByteCount + 1, buffer, 124, 12); + } + else + { + WriteStringBytes(ArchiveEncoding.Encode(Name), buffer, 100); + WriteOctalBytes(Size, buffer, 124, 12); + var time = (long)(LastModifiedTime.ToUniversalTime() - EPOCH).TotalSeconds; + WriteOctalBytes(time, buffer, 136, 12); + buffer[156] = (byte)EntryType; + + if (Size >= 0x1FFFFFFFF) + { + Span bytes12 = stackalloc byte[12]; + BinaryPrimitives.WriteInt64BigEndian(bytes12.Slice(4), Size); + bytes12[0] |= 0x80; + bytes12.CopyTo(buffer.AsSpan(124)); + } } - internal string Name { get; set; } - internal string LinkName { get; set; } + var crc = RecalculateChecksum(buffer); + WriteOctalBytes(crc, buffer, 148, 8); - internal long Mode { get; set; } - internal long UserId { get; set; } - internal long GroupId { get; set; } - internal long Size { get; set; } - internal DateTime LastModifiedTime { get; set; } - internal EntryType EntryType { get; set; } - internal Stream PackedStream { get; set; } - internal ArchiveEncoding ArchiveEncoding { get; } + output.Write(buffer, 0, buffer.Length); - internal const int BLOCK_SIZE = 512; - - internal void Write(Stream output) + if (nameByteCount > 100) { - byte[] buffer = new byte[BLOCK_SIZE]; + WriteLongFilenameHeader(output); + // update to short name lower than 100 - [max bytes of one character]. + // subtracting bytes is needed because preventing infinite loop(example code is here). + // + // var bytes = Encoding.UTF8.GetBytes(new string(0x3042, 100)); + // var truncated = Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(bytes, 0, 100)); + // + // and then infinite recursion is occured in WriteLongFilenameHeader because truncated.Length is 102. + Name = ArchiveEncoding.Decode( + ArchiveEncoding.Encode(Name), + 0, + 100 - ArchiveEncoding.GetEncoding().GetMaxByteCount(1) + ); + Write(output); + } + } - WriteOctalBytes(511, buffer, 100, 8); // file mode - WriteOctalBytes(0, buffer, 108, 8); // owner ID - WriteOctalBytes(0, buffer, 116, 8); // group ID + private void WriteLongFilenameHeader(Stream output) + { + var nameBytes = ArchiveEncoding.Encode(Name); + output.Write(nameBytes, 0, nameBytes.Length); - //ArchiveEncoding.UTF8.GetBytes("magic").CopyTo(buffer, 257); - var nameByteCount = ArchiveEncoding.GetEncoding().GetByteCount(Name); - if (nameByteCount > 100) + // pad to multiple of BlockSize bytes, and make sure a terminating null is added + var numPaddingBytes = BLOCK_SIZE - (nameBytes.Length % BLOCK_SIZE); + if (numPaddingBytes == 0) + { + numPaddingBytes = BLOCK_SIZE; + } + output.Write(stackalloc byte[numPaddingBytes]); + } + + internal bool Read(BinaryReader reader) + { + var buffer = ReadBlock(reader); + if (buffer.Length == 0) + { + return false; + } + + // for symlinks, additionally read the linkname + if (ReadEntryType(buffer) == EntryType.SymLink) + { + LinkName = ArchiveEncoding.Decode(buffer, 157, 100).TrimNulls(); + } + + if (ReadEntryType(buffer) == EntryType.LongName) + { + Name = ReadLongName(reader, buffer); + buffer = ReadBlock(reader); + } + else + { + Name = ArchiveEncoding.Decode(buffer, 0, 100).TrimNulls(); + } + + EntryType = ReadEntryType(buffer); + Size = ReadSize(buffer); + + Mode = ReadAsciiInt64Base8(buffer, 100, 7); + if (EntryType == EntryType.Directory) + { + Mode |= 0b1_000_000_000; + } + + UserId = ReadAsciiInt64Base8(buffer, 108, 7); + GroupId = ReadAsciiInt64Base8(buffer, 116, 7); + var unixTimeStamp = ReadAsciiInt64Base8(buffer, 136, 11); + LastModifiedTime = EPOCH.AddSeconds(unixTimeStamp).ToLocalTime(); + + Magic = ArchiveEncoding.Decode(buffer, 257, 6).TrimNulls(); + + if (!string.IsNullOrEmpty(Magic) && "ustar".Equals(Magic)) + { + var namePrefix = ArchiveEncoding.Decode(buffer, 345, 157); + namePrefix = namePrefix.TrimNulls(); + if (!string.IsNullOrEmpty(namePrefix)) { - // Set mock filename and filetype to indicate the next block is the actual name of the file - WriteStringBytes("././@LongLink", buffer, 0, 100); - buffer[156] = (byte)EntryType.LongName; - WriteOctalBytes(nameByteCount + 1, buffer, 124, 12); + Name = namePrefix + "/" + Name; + } + } + if (EntryType != EntryType.LongName && Name.Length == 0) + { + return false; + } + return true; + } + + private string ReadLongName(BinaryReader reader, byte[] buffer) + { + var size = ReadSize(buffer); + var nameLength = (int)size; + var nameBytes = reader.ReadBytes(nameLength); + var remainingBytesToRead = BLOCK_SIZE - (nameLength % BLOCK_SIZE); + + // Read the rest of the block and discard the data + if (remainingBytesToRead < BLOCK_SIZE) + { + reader.ReadBytes(remainingBytesToRead); + } + return ArchiveEncoding.Decode(nameBytes, 0, nameBytes.Length).TrimNulls(); + } + + private static EntryType ReadEntryType(byte[] buffer) + { + return (EntryType)buffer[156]; + } + + private long ReadSize(byte[] buffer) + { + if ((buffer[124] & 0x80) == 0x80) // if size in binary + { + return BinaryPrimitives.ReadInt64BigEndian(buffer.AsSpan(0x80)); + } + + return ReadAsciiInt64Base8(buffer, 124, 11); + } + + private static byte[] ReadBlock(BinaryReader reader) + { + var buffer = reader.ReadBytes(BLOCK_SIZE); + + if (buffer.Length != 0 && buffer.Length < BLOCK_SIZE) + { + throw new InvalidOperationException("Buffer is invalid size"); + } + return buffer; + } + + private static void WriteStringBytes(ReadOnlySpan name, Span buffer, int length) + { + name.CopyTo(buffer); + var i = Math.Min(length, name.Length); + buffer.Slice(i, length - i).Clear(); + } + + private static void WriteStringBytes(string name, byte[] buffer, int offset, int length) + { + int i; + + for (i = 0; i < length && i < name.Length; ++i) + { + buffer[offset + i] = (byte)name[i]; + } + + for (; i < length; ++i) + { + buffer[offset + i] = 0; + } + } + + private static void WriteOctalBytes(long value, byte[] buffer, int offset, int length) + { + var val = Convert.ToString(value, 8); + var shift = length - val.Length - 1; + for (var i = 0; i < shift; i++) + { + buffer[offset + i] = (byte)' '; + } + for (var i = 0; i < val.Length; i++) + { + buffer[offset + i + shift] = (byte)val[i]; + } + } + + private static int ReadAsciiInt32Base8(byte[] buffer, int offset, int count) + { + var s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls(); + if (string.IsNullOrEmpty(s)) + { + return 0; + } + return Convert.ToInt32(s, 8); + } + + private static long ReadAsciiInt64Base8(byte[] buffer, int offset, int count) + { + var s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls(); + if (string.IsNullOrEmpty(s)) + { + return 0; + } + return Convert.ToInt64(s, 8); + } + + private static long ReadAsciiInt64(byte[] buffer, int offset, int count) + { + var s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls(); + if (string.IsNullOrEmpty(s)) + { + return 0; + } + return Convert.ToInt64(s); + } + + private static readonly byte[] eightSpaces = + { + (byte)' ', + (byte)' ', + (byte)' ', + (byte)' ', + (byte)' ', + (byte)' ', + (byte)' ', + (byte)' ' + }; + + internal static int RecalculateChecksum(byte[] buf) + { + // Set default value for checksum. That is 8 spaces. + eightSpaces.CopyTo(buf, 148); + + // Calculate checksum + var headerChecksum = 0; + foreach (var b in buf) + { + headerChecksum += b; + } + return headerChecksum; + } + + internal static int RecalculateAltChecksum(byte[] buf) + { + eightSpaces.CopyTo(buf, 148); + var headerChecksum = 0; + foreach (var b in buf) + { + if ((b & 0x80) == 0x80) + { + headerChecksum -= b ^ 0x80; } else - { - WriteStringBytes(ArchiveEncoding.Encode(Name), buffer, 100); - WriteOctalBytes(Size, buffer, 124, 12); - var time = (long)(LastModifiedTime.ToUniversalTime() - EPOCH).TotalSeconds; - WriteOctalBytes(time, buffer, 136, 12); - buffer[156] = (byte)EntryType; - - if (Size >= 0x1FFFFFFFF) - { - Span bytes12 = stackalloc byte[12]; - BinaryPrimitives.WriteInt64BigEndian(bytes12.Slice(4), Size); - bytes12[0] |= 0x80; - bytes12.CopyTo(buffer.AsSpan(124)); - } - } - - int crc = RecalculateChecksum(buffer); - WriteOctalBytes(crc, buffer, 148, 8); - - output.Write(buffer, 0, buffer.Length); - - if (nameByteCount > 100) - { - WriteLongFilenameHeader(output); - // update to short name lower than 100 - [max bytes of one character]. - // subtracting bytes is needed because preventing infinite loop(example code is here). - // - // var bytes = Encoding.UTF8.GetBytes(new string(0x3042, 100)); - // var truncated = Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(bytes, 0, 100)); - // - // and then infinite recursion is occured in WriteLongFilenameHeader because truncated.Length is 102. - Name = ArchiveEncoding.Decode( - ArchiveEncoding.Encode(Name), - 0, - 100 - ArchiveEncoding.GetEncoding().GetMaxByteCount(1) - ); - Write(output); - } - } - - private void WriteLongFilenameHeader(Stream output) - { - byte[] nameBytes = ArchiveEncoding.Encode(Name); - output.Write(nameBytes, 0, nameBytes.Length); - - // pad to multiple of BlockSize bytes, and make sure a terminating null is added - int numPaddingBytes = BLOCK_SIZE - (nameBytes.Length % BLOCK_SIZE); - if (numPaddingBytes == 0) - { - numPaddingBytes = BLOCK_SIZE; - } - output.Write(stackalloc byte[numPaddingBytes]); - } - - internal bool Read(BinaryReader reader) - { - var buffer = ReadBlock(reader); - if (buffer.Length == 0) - { - return false; - } - - // for symlinks, additionally read the linkname - if (ReadEntryType(buffer) == EntryType.SymLink) - { - LinkName = ArchiveEncoding.Decode(buffer, 157, 100).TrimNulls(); - } - - if (ReadEntryType(buffer) == EntryType.LongName) - { - Name = ReadLongName(reader, buffer); - buffer = ReadBlock(reader); - } - else - { - Name = ArchiveEncoding.Decode(buffer, 0, 100).TrimNulls(); - } - - EntryType = ReadEntryType(buffer); - Size = ReadSize(buffer); - - Mode = ReadAsciiInt64Base8(buffer, 100, 7); - if (EntryType == EntryType.Directory) - Mode |= 0b1_000_000_000; - - UserId = ReadAsciiInt64Base8(buffer, 108, 7); - GroupId = ReadAsciiInt64Base8(buffer, 116, 7); - long unixTimeStamp = ReadAsciiInt64Base8(buffer, 136, 11); - LastModifiedTime = EPOCH.AddSeconds(unixTimeStamp).ToLocalTime(); - - Magic = ArchiveEncoding.Decode(buffer, 257, 6).TrimNulls(); - - if (!string.IsNullOrEmpty(Magic) && "ustar".Equals(Magic)) - { - string namePrefix = ArchiveEncoding.Decode(buffer, 345, 157); - namePrefix = namePrefix.TrimNulls(); - if (!string.IsNullOrEmpty(namePrefix)) - { - Name = namePrefix + "/" + Name; - } - } - if (EntryType != EntryType.LongName && Name.Length == 0) - { - return false; - } - return true; - } - - private string ReadLongName(BinaryReader reader, byte[] buffer) - { - var size = ReadSize(buffer); - var nameLength = (int)size; - var nameBytes = reader.ReadBytes(nameLength); - var remainingBytesToRead = BLOCK_SIZE - (nameLength % BLOCK_SIZE); - - // Read the rest of the block and discard the data - if (remainingBytesToRead < BLOCK_SIZE) - { - reader.ReadBytes(remainingBytesToRead); - } - return ArchiveEncoding.Decode(nameBytes, 0, nameBytes.Length).TrimNulls(); - } - - private static EntryType ReadEntryType(byte[] buffer) - { - return (EntryType)buffer[156]; - } - - private long ReadSize(byte[] buffer) - { - if ((buffer[124] & 0x80) == 0x80) // if size in binary - { - return BinaryPrimitives.ReadInt64BigEndian(buffer.AsSpan(0x80)); - } - - return ReadAsciiInt64Base8(buffer, 124, 11); - } - - private static byte[] ReadBlock(BinaryReader reader) - { - byte[] buffer = reader.ReadBytes(BLOCK_SIZE); - - if (buffer.Length != 0 && buffer.Length < BLOCK_SIZE) - { - throw new InvalidOperationException("Buffer is invalid size"); - } - return buffer; - } - - private static void WriteStringBytes(ReadOnlySpan name, Span buffer, int length) - { - name.CopyTo(buffer); - int i = Math.Min(length, name.Length); - buffer.Slice(i, length - i).Clear(); - } - - private static void WriteStringBytes(string name, byte[] buffer, int offset, int length) - { - int i; - - for (i = 0; i < length && i < name.Length; ++i) - { - buffer[offset + i] = (byte)name[i]; - } - - for (; i < length; ++i) - { - buffer[offset + i] = 0; - } - } - - private static void WriteOctalBytes(long value, byte[] buffer, int offset, int length) - { - string val = Convert.ToString(value, 8); - int shift = length - val.Length - 1; - for (int i = 0; i < shift; i++) - { - buffer[offset + i] = (byte)' '; - } - for (int i = 0; i < val.Length; i++) - { - buffer[offset + i + shift] = (byte)val[i]; - } - } - - private static int ReadAsciiInt32Base8(byte[] buffer, int offset, int count) - { - string s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls(); - if (string.IsNullOrEmpty(s)) - { - return 0; - } - return Convert.ToInt32(s, 8); - } - - private static long ReadAsciiInt64Base8(byte[] buffer, int offset, int count) - { - string s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls(); - if (string.IsNullOrEmpty(s)) - { - return 0; - } - return Convert.ToInt64(s, 8); - } - - private static long ReadAsciiInt64(byte[] buffer, int offset, int count) - { - string s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls(); - if (string.IsNullOrEmpty(s)) - { - return 0; - } - return Convert.ToInt64(s); - } - - private static readonly byte[] eightSpaces = - { - (byte)' ', - (byte)' ', - (byte)' ', - (byte)' ', - (byte)' ', - (byte)' ', - (byte)' ', - (byte)' ' - }; - - internal static int RecalculateChecksum(byte[] buf) - { - // Set default value for checksum. That is 8 spaces. - eightSpaces.CopyTo(buf, 148); - - // Calculate checksum - int headerChecksum = 0; - foreach (byte b in buf) { headerChecksum += b; } - return headerChecksum; } - - internal static int RecalculateAltChecksum(byte[] buf) - { - eightSpaces.CopyTo(buf, 148); - int headerChecksum = 0; - foreach (byte b in buf) - { - if ((b & 0x80) == 0x80) - { - headerChecksum -= b ^ 0x80; - } - else - { - headerChecksum += b; - } - } - return headerChecksum; - } - - public long? DataStartPosition { get; set; } - - public string Magic { get; set; } + return headerChecksum; } + + public long? DataStartPosition { get; set; } + + public string Magic { get; set; } } diff --git a/src/SharpCompress/Common/Tar/TarEntry.cs b/src/SharpCompress/Common/Tar/TarEntry.cs index 59434661..7b5e4a05 100644 --- a/src/SharpCompress/Common/Tar/TarEntry.cs +++ b/src/SharpCompress/Common/Tar/TarEntry.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable using System; using System.Collections.Generic; @@ -6,71 +6,70 @@ using System.IO; using SharpCompress.Common.Tar.Headers; using SharpCompress.IO; -namespace SharpCompress.Common.Tar +namespace SharpCompress.Common.Tar; + +public class TarEntry : Entry { - public class TarEntry : Entry + private readonly TarFilePart _filePart; + + internal TarEntry(TarFilePart filePart, CompressionType type) { - private readonly TarFilePart _filePart; + _filePart = filePart; + CompressionType = type; + } - internal TarEntry(TarFilePart filePart, CompressionType type) + public override CompressionType CompressionType { get; } + + public override long Crc => 0; + + public override string Key => _filePart.Header.Name; + + public override string LinkTarget => _filePart.Header.LinkName; + + public override long CompressedSize => _filePart.Header.Size; + + public override long Size => _filePart.Header.Size; + + public override DateTime? LastModifiedTime => _filePart.Header.LastModifiedTime; + + public override DateTime? CreatedTime => null; + + public override DateTime? LastAccessedTime => null; + + public override DateTime? ArchivedTime => null; + + public override bool IsEncrypted => false; + + public override bool IsDirectory => _filePart.Header.EntryType == EntryType.Directory; + + public override bool IsSplitAfter => false; + + public long Mode => _filePart.Header.Mode; + + public long UserID => _filePart.Header.UserId; + + public long GroupId => _filePart.Header.GroupId; + + internal override IEnumerable Parts => _filePart.AsEnumerable(); + + internal static IEnumerable GetEntries( + StreamingMode mode, + Stream stream, + CompressionType compressionType, + ArchiveEncoding archiveEncoding + ) + { + foreach (var h in TarHeaderFactory.ReadHeader(mode, stream, archiveEncoding)) { - this._filePart = filePart; - CompressionType = type; - } - - public override CompressionType CompressionType { get; } - - public override long Crc => 0; - - public override string Key => _filePart.Header.Name; - - public override string LinkTarget => _filePart.Header.LinkName; - - public override long CompressedSize => _filePart.Header.Size; - - public override long Size => _filePart.Header.Size; - - public override DateTime? LastModifiedTime => _filePart.Header.LastModifiedTime; - - public override DateTime? CreatedTime => null; - - public override DateTime? LastAccessedTime => null; - - public override DateTime? ArchivedTime => null; - - public override bool IsEncrypted => false; - - public override bool IsDirectory => _filePart.Header.EntryType == EntryType.Directory; - - public override bool IsSplitAfter => false; - - public long Mode => _filePart.Header.Mode; - - public long UserID => _filePart.Header.UserId; - - public long GroupId => _filePart.Header.GroupId; - - internal override IEnumerable Parts => _filePart.AsEnumerable(); - - internal static IEnumerable GetEntries( - StreamingMode mode, - Stream stream, - CompressionType compressionType, - ArchiveEncoding archiveEncoding - ) - { - foreach (TarHeader h in TarHeaderFactory.ReadHeader(mode, stream, archiveEncoding)) + if (h != null) { - if (h != null) + if (mode == StreamingMode.Seekable) { - if (mode == StreamingMode.Seekable) - { - yield return new TarEntry(new TarFilePart(h, stream), compressionType); - } - else - { - yield return new TarEntry(new TarFilePart(h, null), compressionType); - } + yield return new TarEntry(new TarFilePart(h, stream), compressionType); + } + else + { + yield return new TarEntry(new TarFilePart(h, null), compressionType); } } } diff --git a/src/SharpCompress/Common/Tar/TarFilePart.cs b/src/SharpCompress/Common/Tar/TarFilePart.cs index f71a41a8..8fa6223f 100644 --- a/src/SharpCompress/Common/Tar/TarFilePart.cs +++ b/src/SharpCompress/Common/Tar/TarFilePart.cs @@ -1,36 +1,34 @@ -using System.IO; +using System.IO; using SharpCompress.Common.Tar.Headers; -using SharpCompress.IO; -namespace SharpCompress.Common.Tar +namespace SharpCompress.Common.Tar; + +internal sealed class TarFilePart : FilePart { - internal sealed class TarFilePart : FilePart + private readonly Stream _seekableStream; + + internal TarFilePart(TarHeader header, Stream seekableStream) : base(header.ArchiveEncoding) { - private readonly Stream _seekableStream; + _seekableStream = seekableStream; + Header = header; + } - internal TarFilePart(TarHeader header, Stream seekableStream) : base(header.ArchiveEncoding) + internal TarHeader Header { get; } + + internal override string FilePartName => Header.Name; + + internal override Stream GetCompressedStream() + { + if (_seekableStream != null) { - _seekableStream = seekableStream; - Header = header; + _seekableStream.Position = Header.DataStartPosition!.Value; + return new TarReadOnlySubStream(_seekableStream, Header.Size); } + return Header.PackedStream; + } - internal TarHeader Header { get; } - - internal override string FilePartName => Header.Name; - - internal override Stream GetCompressedStream() - { - if (_seekableStream != null) - { - _seekableStream.Position = Header.DataStartPosition!.Value; - return new TarReadOnlySubStream(_seekableStream, Header.Size); - } - return Header.PackedStream; - } - - internal override Stream? GetRawStream() - { - return null; - } + internal override Stream? GetRawStream() + { + return null; } } diff --git a/src/SharpCompress/Common/Tar/TarHeaderFactory.cs b/src/SharpCompress/Common/Tar/TarHeaderFactory.cs index 6a9e8327..0edb7463 100644 --- a/src/SharpCompress/Common/Tar/TarHeaderFactory.cs +++ b/src/SharpCompress/Common/Tar/TarHeaderFactory.cs @@ -1,69 +1,68 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using SharpCompress.Common.Tar.Headers; using SharpCompress.IO; -namespace SharpCompress.Common.Tar +namespace SharpCompress.Common.Tar; + +internal static class TarHeaderFactory { - internal static class TarHeaderFactory + internal static IEnumerable ReadHeader( + StreamingMode mode, + Stream stream, + ArchiveEncoding archiveEncoding + ) { - internal static IEnumerable ReadHeader( - StreamingMode mode, - Stream stream, - ArchiveEncoding archiveEncoding - ) + while (true) { - while (true) + TarHeader? header = null; + try { - TarHeader? header = null; - try + var reader = new BinaryReader(stream); + header = new TarHeader(archiveEncoding); + + if (!header.Read(reader)) { - BinaryReader reader = new BinaryReader(stream); - header = new TarHeader(archiveEncoding); + yield break; + } + switch (mode) + { + case StreamingMode.Seekable: - if (!header.Read(reader)) { - yield break; + header.DataStartPosition = reader.BaseStream.Position; + + //skip to nearest 512 + reader.BaseStream.Position += PadTo512(header.Size); } - switch (mode) + break; + case StreamingMode.Streaming: + { - case StreamingMode.Seekable: - - { - header.DataStartPosition = reader.BaseStream.Position; - - //skip to nearest 512 - reader.BaseStream.Position += PadTo512(header.Size); - } - break; - case StreamingMode.Streaming: - - { - header.PackedStream = new TarReadOnlySubStream(stream, header.Size); - } - break; - default: - { - throw new InvalidFormatException("Invalid StreamingMode"); - } + header.PackedStream = new TarReadOnlySubStream(stream, header.Size); + } + break; + default: + { + throw new InvalidFormatException("Invalid StreamingMode"); } } - catch - { - header = null; - } - yield return header; } - } - - private static long PadTo512(long size) - { - int zeros = (int)(size % 512); - if (zeros == 0) + catch { - return size; + header = null; } - return 512 - zeros + size; + yield return header; } } + + private static long PadTo512(long size) + { + var zeros = (int)(size % 512); + if (zeros == 0) + { + return size; + } + return 512 - zeros + size; + } } diff --git a/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs b/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs index bea2858f..2678ebee 100644 --- a/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs +++ b/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs @@ -1,4 +1,4 @@ -using SharpCompress.IO; +using SharpCompress.IO; using System; using System.IO; diff --git a/src/SharpCompress/Common/Tar/TarVolume.cs b/src/SharpCompress/Common/Tar/TarVolume.cs index 4b914920..da00886a 100644 --- a/src/SharpCompress/Common/Tar/TarVolume.cs +++ b/src/SharpCompress/Common/Tar/TarVolume.cs @@ -1,11 +1,10 @@ using System.IO; using SharpCompress.Readers; -namespace SharpCompress.Common.Tar +namespace SharpCompress.Common.Tar; + +public class TarVolume : Volume { - public class TarVolume : Volume - { - public TarVolume(Stream stream, ReaderOptions readerOptions, int index = 0) - : base(stream, readerOptions, index) { } - } + public TarVolume(Stream stream, ReaderOptions readerOptions, int index = 0) + : base(stream, readerOptions, index) { } } diff --git a/src/SharpCompress/Common/Volume.cs b/src/SharpCompress/Common/Volume.cs index ac716b67..1872e65d 100644 --- a/src/SharpCompress/Common/Volume.cs +++ b/src/SharpCompress/Common/Volume.cs @@ -3,57 +3,56 @@ using System.IO; using SharpCompress.IO; using SharpCompress.Readers; -namespace SharpCompress.Common +namespace SharpCompress.Common; + +public abstract class Volume : IVolume { - public abstract class Volume : IVolume + private readonly Stream _actualStream; + + internal Volume(Stream stream, ReaderOptions readerOptions, int index = 0) { - private readonly Stream _actualStream; - - internal Volume(Stream stream, ReaderOptions readerOptions, int index = 0) + Index = index; + ReaderOptions = readerOptions; + if (readerOptions.LeaveStreamOpen) { - Index = index; - ReaderOptions = readerOptions; - if (readerOptions.LeaveStreamOpen) - { - stream = NonDisposingStream.Create(stream); - } - _actualStream = stream; + stream = NonDisposingStream.Create(stream); } + _actualStream = stream; + } - internal Stream Stream => _actualStream; + internal Stream Stream => _actualStream; - protected ReaderOptions ReaderOptions { get; } + protected ReaderOptions ReaderOptions { get; } - /// - /// RarArchive is the first volume of a multi-part archive. - /// Only Rar 3.0 format and higher - /// - public virtual bool IsFirstVolume => true; + /// + /// RarArchive is the first volume of a multi-part archive. + /// Only Rar 3.0 format and higher + /// + public virtual bool IsFirstVolume => true; - public virtual int Index { get; internal set; } + public virtual int Index { get; internal set; } - public string FileName + public string FileName + { + get { return (_actualStream as FileStream)?.Name!; } + } + + /// + /// RarArchive is part of a multi-part archive. + /// + public virtual bool IsMultiVolume => true; + + protected virtual void Dispose(bool disposing) + { + if (disposing) { - get { return (_actualStream as FileStream)?.Name!; } - } - - /// - /// RarArchive is part of a multi-part archive. - /// - public virtual bool IsMultiVolume => true; - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _actualStream.Dispose(); - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); + _actualStream.Dispose(); } } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } } diff --git a/src/SharpCompress/Common/Zip/Headers/DirectoryEndHeader.cs b/src/SharpCompress/Common/Zip/Headers/DirectoryEndHeader.cs index 42c8865c..943ae47c 100644 --- a/src/SharpCompress/Common/Zip/Headers/DirectoryEndHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/DirectoryEndHeader.cs @@ -1,42 +1,41 @@ -using System.IO; +using System.IO; -namespace SharpCompress.Common.Zip.Headers +namespace SharpCompress.Common.Zip.Headers; + +internal class DirectoryEndHeader : ZipHeader { - internal class DirectoryEndHeader : ZipHeader + public DirectoryEndHeader() : base(ZipHeaderType.DirectoryEnd) { } + + internal override void Read(BinaryReader reader) { - public DirectoryEndHeader() : base(ZipHeaderType.DirectoryEnd) { } - - internal override void Read(BinaryReader reader) - { - VolumeNumber = reader.ReadUInt16(); - FirstVolumeWithDirectory = reader.ReadUInt16(); - TotalNumberOfEntriesInDisk = reader.ReadUInt16(); - TotalNumberOfEntries = reader.ReadUInt16(); - DirectorySize = reader.ReadUInt32(); - DirectoryStartOffsetRelativeToDisk = reader.ReadUInt32(); - CommentLength = reader.ReadUInt16(); - Comment = reader.ReadBytes(CommentLength); - } - - public ushort VolumeNumber { get; private set; } - - public ushort FirstVolumeWithDirectory { get; private set; } - - public ushort TotalNumberOfEntriesInDisk { get; private set; } - - public uint DirectorySize { get; private set; } - - public uint DirectoryStartOffsetRelativeToDisk { get; private set; } - - public ushort CommentLength { get; private set; } - - public byte[]? Comment { get; private set; } - - public ushort TotalNumberOfEntries { get; private set; } - - public bool IsZip64 => - TotalNumberOfEntriesInDisk == ushort.MaxValue - || DirectorySize == uint.MaxValue - || DirectoryStartOffsetRelativeToDisk == uint.MaxValue; + VolumeNumber = reader.ReadUInt16(); + FirstVolumeWithDirectory = reader.ReadUInt16(); + TotalNumberOfEntriesInDisk = reader.ReadUInt16(); + TotalNumberOfEntries = reader.ReadUInt16(); + DirectorySize = reader.ReadUInt32(); + DirectoryStartOffsetRelativeToDisk = reader.ReadUInt32(); + CommentLength = reader.ReadUInt16(); + Comment = reader.ReadBytes(CommentLength); } + + public ushort VolumeNumber { get; private set; } + + public ushort FirstVolumeWithDirectory { get; private set; } + + public ushort TotalNumberOfEntriesInDisk { get; private set; } + + public uint DirectorySize { get; private set; } + + public uint DirectoryStartOffsetRelativeToDisk { get; private set; } + + public ushort CommentLength { get; private set; } + + public byte[]? Comment { get; private set; } + + public ushort TotalNumberOfEntries { get; private set; } + + public bool IsZip64 => + TotalNumberOfEntriesInDisk == ushort.MaxValue + || DirectorySize == uint.MaxValue + || DirectoryStartOffsetRelativeToDisk == uint.MaxValue; } diff --git a/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs index 0a28c602..dc566482 100644 --- a/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs @@ -1,106 +1,105 @@ -using System.IO; +using System.IO; using System.Linq; -namespace SharpCompress.Common.Zip.Headers +namespace SharpCompress.Common.Zip.Headers; + +internal class DirectoryEntryHeader : ZipFileEntry { - internal class DirectoryEntryHeader : ZipFileEntry + public DirectoryEntryHeader(ArchiveEncoding archiveEncoding) + : base(ZipHeaderType.DirectoryEntry, archiveEncoding) { } + + internal override void Read(BinaryReader reader) { - public DirectoryEntryHeader(ArchiveEncoding archiveEncoding) - : base(ZipHeaderType.DirectoryEntry, archiveEncoding) { } + Version = reader.ReadUInt16(); + VersionNeededToExtract = reader.ReadUInt16(); + Flags = (HeaderFlags)reader.ReadUInt16(); + CompressionMethod = (ZipCompressionMethod)reader.ReadUInt16(); + LastModifiedTime = reader.ReadUInt16(); + LastModifiedDate = reader.ReadUInt16(); + Crc = reader.ReadUInt32(); + CompressedSize = reader.ReadUInt32(); + UncompressedSize = reader.ReadUInt32(); + var nameLength = reader.ReadUInt16(); + var extraLength = reader.ReadUInt16(); + var commentLength = reader.ReadUInt16(); + DiskNumberStart = reader.ReadUInt16(); + InternalFileAttributes = reader.ReadUInt16(); + ExternalFileAttributes = reader.ReadUInt32(); + RelativeOffsetOfEntryHeader = reader.ReadUInt32(); - internal override void Read(BinaryReader reader) + var name = reader.ReadBytes(nameLength); + var extra = reader.ReadBytes(extraLength); + var comment = reader.ReadBytes(commentLength); + + // According to .ZIP File Format Specification + // + // For example: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT + // + // Bit 11: Language encoding flag (EFS). If this bit is set, + // the filename and comment fields for this file + // MUST be encoded using UTF-8. (see APPENDIX D) + + if (Flags.HasFlag(HeaderFlags.Efs)) { - Version = reader.ReadUInt16(); - VersionNeededToExtract = reader.ReadUInt16(); - Flags = (HeaderFlags)reader.ReadUInt16(); - CompressionMethod = (ZipCompressionMethod)reader.ReadUInt16(); - LastModifiedTime = reader.ReadUInt16(); - LastModifiedDate = reader.ReadUInt16(); - Crc = reader.ReadUInt32(); - CompressedSize = reader.ReadUInt32(); - UncompressedSize = reader.ReadUInt32(); - ushort nameLength = reader.ReadUInt16(); - ushort extraLength = reader.ReadUInt16(); - ushort commentLength = reader.ReadUInt16(); - DiskNumberStart = reader.ReadUInt16(); - InternalFileAttributes = reader.ReadUInt16(); - ExternalFileAttributes = reader.ReadUInt32(); - RelativeOffsetOfEntryHeader = reader.ReadUInt32(); - - byte[] name = reader.ReadBytes(nameLength); - byte[] extra = reader.ReadBytes(extraLength); - byte[] comment = reader.ReadBytes(commentLength); - - // According to .ZIP File Format Specification - // - // For example: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT - // - // Bit 11: Language encoding flag (EFS). If this bit is set, - // the filename and comment fields for this file - // MUST be encoded using UTF-8. (see APPENDIX D) - - if (Flags.HasFlag(HeaderFlags.Efs)) - { - Name = ArchiveEncoding.DecodeUTF8(name); - Comment = ArchiveEncoding.DecodeUTF8(comment); - } - else - { - Name = ArchiveEncoding.Decode(name); - Comment = ArchiveEncoding.Decode(comment); - } - - LoadExtra(extra); - - var unicodePathExtra = Extra.FirstOrDefault( - u => u.Type == ExtraDataType.UnicodePathExtraField - ); - if (unicodePathExtra != null && ArchiveEncoding.Forced == null) - { - Name = ((ExtraUnicodePathExtraField)unicodePathExtra).UnicodeName; - } - - var zip64ExtraData = Extra - .OfType() - .FirstOrDefault(); - if (zip64ExtraData != null) - { - zip64ExtraData.Process( - UncompressedSize, - CompressedSize, - RelativeOffsetOfEntryHeader, - DiskNumberStart - ); - - if (CompressedSize == uint.MaxValue) - { - CompressedSize = zip64ExtraData.CompressedSize; - } - - if (UncompressedSize == uint.MaxValue) - { - UncompressedSize = zip64ExtraData.UncompressedSize; - } - - if (RelativeOffsetOfEntryHeader == uint.MaxValue) - { - RelativeOffsetOfEntryHeader = zip64ExtraData.RelativeOffsetOfEntryHeader; - } - } + Name = ArchiveEncoding.DecodeUTF8(name); + Comment = ArchiveEncoding.DecodeUTF8(comment); + } + else + { + Name = ArchiveEncoding.Decode(name); + Comment = ArchiveEncoding.Decode(comment); } - internal ushort Version { get; private set; } + LoadExtra(extra); - public ushort VersionNeededToExtract { get; set; } + var unicodePathExtra = Extra.FirstOrDefault( + u => u.Type == ExtraDataType.UnicodePathExtraField + ); + if (unicodePathExtra != null && ArchiveEncoding.Forced == null) + { + Name = ((ExtraUnicodePathExtraField)unicodePathExtra).UnicodeName; + } - public long RelativeOffsetOfEntryHeader { get; set; } + var zip64ExtraData = Extra + .OfType() + .FirstOrDefault(); + if (zip64ExtraData != null) + { + zip64ExtraData.Process( + UncompressedSize, + CompressedSize, + RelativeOffsetOfEntryHeader, + DiskNumberStart + ); - public uint ExternalFileAttributes { get; set; } + if (CompressedSize == uint.MaxValue) + { + CompressedSize = zip64ExtraData.CompressedSize; + } - public ushort InternalFileAttributes { get; set; } + if (UncompressedSize == uint.MaxValue) + { + UncompressedSize = zip64ExtraData.UncompressedSize; + } - public ushort DiskNumberStart { get; set; } - - public string? Comment { get; private set; } + if (RelativeOffsetOfEntryHeader == uint.MaxValue) + { + RelativeOffsetOfEntryHeader = zip64ExtraData.RelativeOffsetOfEntryHeader; + } + } } + + internal ushort Version { get; private set; } + + public ushort VersionNeededToExtract { get; set; } + + public long RelativeOffsetOfEntryHeader { get; set; } + + public uint ExternalFileAttributes { get; set; } + + public ushort InternalFileAttributes { get; set; } + + public ushort DiskNumberStart { get; set; } + + public string? Comment { get; private set; } } diff --git a/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs b/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs index 09cf7f29..5aef3394 100644 --- a/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs +++ b/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs @@ -1,18 +1,17 @@ -using System; +using System; -namespace SharpCompress.Common.Zip.Headers +namespace SharpCompress.Common.Zip.Headers; + +[Flags] +internal enum HeaderFlags : ushort { - [Flags] - internal enum HeaderFlags : ushort - { - None = 0, - Encrypted = 1, // http://www.pkware.com/documents/casestudies/APPNOTE.TXT - Bit1 = 2, - Bit2 = 4, - UsePostDataDescriptor = 8, - EnhancedDeflate = 16, + None = 0, + Encrypted = 1, // http://www.pkware.com/documents/casestudies/APPNOTE.TXT + Bit1 = 2, + Bit2 = 4, + UsePostDataDescriptor = 8, + EnhancedDeflate = 16, - //Bit 11: Language encoding flag - Efs = 2048 - } + //Bit 11: Language encoding flag + Efs = 2048 } diff --git a/src/SharpCompress/Common/Zip/Headers/IgnoreHeader.cs b/src/SharpCompress/Common/Zip/Headers/IgnoreHeader.cs index 6957f62f..4852228c 100644 --- a/src/SharpCompress/Common/Zip/Headers/IgnoreHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/IgnoreHeader.cs @@ -1,11 +1,10 @@ -using System.IO; +using System.IO; -namespace SharpCompress.Common.Zip.Headers +namespace SharpCompress.Common.Zip.Headers; + +internal class IgnoreHeader : ZipHeader { - internal class IgnoreHeader : ZipHeader - { - public IgnoreHeader(ZipHeaderType type) : base(type) { } + public IgnoreHeader(ZipHeaderType type) : base(type) { } - internal override void Read(BinaryReader reader) { } - } + internal override void Read(BinaryReader reader) { } } diff --git a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs index e3fc8071..3cd8ce46 100644 --- a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs @@ -1,73 +1,72 @@ -using System.IO; +using System.IO; using System.Linq; -namespace SharpCompress.Common.Zip.Headers +namespace SharpCompress.Common.Zip.Headers; + +internal class LocalEntryHeader : ZipFileEntry { - internal class LocalEntryHeader : ZipFileEntry + public LocalEntryHeader(ArchiveEncoding archiveEncoding) + : base(ZipHeaderType.LocalEntry, archiveEncoding) { } + + internal override void Read(BinaryReader reader) { - public LocalEntryHeader(ArchiveEncoding archiveEncoding) - : base(ZipHeaderType.LocalEntry, archiveEncoding) { } + Version = reader.ReadUInt16(); + Flags = (HeaderFlags)reader.ReadUInt16(); + CompressionMethod = (ZipCompressionMethod)reader.ReadUInt16(); + LastModifiedTime = reader.ReadUInt16(); + LastModifiedDate = reader.ReadUInt16(); + Crc = reader.ReadUInt32(); + CompressedSize = reader.ReadUInt32(); + UncompressedSize = reader.ReadUInt32(); + var nameLength = reader.ReadUInt16(); + var extraLength = reader.ReadUInt16(); + var name = reader.ReadBytes(nameLength); + var extra = reader.ReadBytes(extraLength); - internal override void Read(BinaryReader reader) + // According to .ZIP File Format Specification + // + // For example: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT + // + // Bit 11: Language encoding flag (EFS). If this bit is set, + // the filename and comment fields for this file + // MUST be encoded using UTF-8. (see APPENDIX D) + + if (Flags.HasFlag(HeaderFlags.Efs)) { - Version = reader.ReadUInt16(); - Flags = (HeaderFlags)reader.ReadUInt16(); - CompressionMethod = (ZipCompressionMethod)reader.ReadUInt16(); - LastModifiedTime = reader.ReadUInt16(); - LastModifiedDate = reader.ReadUInt16(); - Crc = reader.ReadUInt32(); - CompressedSize = reader.ReadUInt32(); - UncompressedSize = reader.ReadUInt32(); - ushort nameLength = reader.ReadUInt16(); - ushort extraLength = reader.ReadUInt16(); - byte[] name = reader.ReadBytes(nameLength); - byte[] extra = reader.ReadBytes(extraLength); - - // According to .ZIP File Format Specification - // - // For example: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT - // - // Bit 11: Language encoding flag (EFS). If this bit is set, - // the filename and comment fields for this file - // MUST be encoded using UTF-8. (see APPENDIX D) - - if (Flags.HasFlag(HeaderFlags.Efs)) - { - Name = ArchiveEncoding.DecodeUTF8(name); - } - else - { - Name = ArchiveEncoding.Decode(name); - } - - LoadExtra(extra); - - var unicodePathExtra = Extra.FirstOrDefault( - u => u.Type == ExtraDataType.UnicodePathExtraField - ); - if (unicodePathExtra != null && ArchiveEncoding.Forced == null) - { - Name = ((ExtraUnicodePathExtraField)unicodePathExtra).UnicodeName; - } - - var zip64ExtraData = Extra - .OfType() - .FirstOrDefault(); - if (zip64ExtraData != null) - { - zip64ExtraData.Process(UncompressedSize, CompressedSize, 0, 0); - - if (CompressedSize == uint.MaxValue) - { - CompressedSize = zip64ExtraData.CompressedSize; - } - if (UncompressedSize == uint.MaxValue) - { - UncompressedSize = zip64ExtraData.UncompressedSize; - } - } + Name = ArchiveEncoding.DecodeUTF8(name); + } + else + { + Name = ArchiveEncoding.Decode(name); } - internal ushort Version { get; private set; } + LoadExtra(extra); + + var unicodePathExtra = Extra.FirstOrDefault( + u => u.Type == ExtraDataType.UnicodePathExtraField + ); + if (unicodePathExtra != null && ArchiveEncoding.Forced == null) + { + Name = ((ExtraUnicodePathExtraField)unicodePathExtra).UnicodeName; + } + + var zip64ExtraData = Extra + .OfType() + .FirstOrDefault(); + if (zip64ExtraData != null) + { + zip64ExtraData.Process(UncompressedSize, CompressedSize, 0, 0); + + if (CompressedSize == uint.MaxValue) + { + CompressedSize = zip64ExtraData.CompressedSize; + } + if (UncompressedSize == uint.MaxValue) + { + UncompressedSize = zip64ExtraData.UncompressedSize; + } + } } + + internal ushort Version { get; private set; } } diff --git a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs index 74d1b224..4920ca36 100644 --- a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs +++ b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs @@ -1,169 +1,168 @@ -using System; +using System; using System.Buffers.Binary; using System.Text; -namespace SharpCompress.Common.Zip.Headers +namespace SharpCompress.Common.Zip.Headers; + +internal enum ExtraDataType : ushort { - internal enum ExtraDataType : ushort + WinZipAes = 0x9901, + + NotImplementedExtraData = 0xFFFF, + + // Third Party Mappings + // -Info-ZIP Unicode Path Extra Field + UnicodePathExtraField = 0x7075, + Zip64ExtendedInformationExtraField = 0x0001 +} + +internal class ExtraData +{ + public ExtraData(ExtraDataType type, ushort length, byte[] dataBytes) { - WinZipAes = 0x9901, - - NotImplementedExtraData = 0xFFFF, - - // Third Party Mappings - // -Info-ZIP Unicode Path Extra Field - UnicodePathExtraField = 0x7075, - Zip64ExtendedInformationExtraField = 0x0001 + Type = type; + Length = length; + DataBytes = dataBytes; } - internal class ExtraData + internal ExtraDataType Type { get; } + internal ushort Length { get; } + internal byte[] DataBytes { get; } +} + +internal sealed class ExtraUnicodePathExtraField : ExtraData +{ + public ExtraUnicodePathExtraField(ExtraDataType type, ushort length, byte[] dataBytes) + : base(type, length, dataBytes) { } + + internal byte Version => DataBytes[0]; + + internal byte[] NameCrc32 { - public ExtraData(ExtraDataType type, ushort length, byte[] dataBytes) + get { - Type = type; - Length = length; - DataBytes = dataBytes; - } - - internal ExtraDataType Type { get; } - internal ushort Length { get; } - internal byte[] DataBytes { get; } - } - - internal sealed class ExtraUnicodePathExtraField : ExtraData - { - public ExtraUnicodePathExtraField(ExtraDataType type, ushort length, byte[] dataBytes) - : base(type, length, dataBytes) { } - - internal byte Version => DataBytes[0]; - - internal byte[] NameCrc32 - { - get - { - var crc = new byte[4]; - Buffer.BlockCopy(DataBytes, 1, crc, 0, 4); - return crc; - } - } - - internal string UnicodeName - { - get - { - // PathNamelength = dataLength - Version(1 byte) - NameCRC32(4 bytes) - var length = Length - 5; - var nameStr = Encoding.UTF8.GetString(DataBytes, 5, length); - return nameStr; - } + var crc = new byte[4]; + Buffer.BlockCopy(DataBytes, 1, crc, 0, 4); + return crc; } } - internal sealed class Zip64ExtendedInformationExtraField : ExtraData + internal string UnicodeName { - public Zip64ExtendedInformationExtraField( - ExtraDataType type, - ushort length, - byte[] dataBytes - ) : base(type, length, dataBytes) { } - - // From the spec, values are only in the extradata if the standard - // value is set to 0xFFFFFFFF (or 0xFFFF for the Disk Start Number). - // Values, if present, must appear in the following order: - // - Original Size - // - Compressed Size - // - Relative Header Offset - // - Disk Start Number - public void Process( - long uncompressedFileSize, - long compressedFileSize, - long relativeHeaderOffset, - ushort diskNumber - ) + get { - var bytesRequired = - ((uncompressedFileSize == uint.MaxValue) ? 8 : 0) - + ((compressedFileSize == uint.MaxValue) ? 8 : 0) - + ((relativeHeaderOffset == uint.MaxValue) ? 8 : 0) - + ((diskNumber == ushort.MaxValue) ? 4 : 0); - var currentIndex = 0; - - if (bytesRequired > DataBytes.Length) - { - throw new ArchiveException( - "Zip64 extended information extra field is not large enough for the required information" - ); - } - - if (uncompressedFileSize == uint.MaxValue) - { - UncompressedSize = BinaryPrimitives.ReadInt64LittleEndian( - DataBytes.AsSpan(currentIndex) - ); - currentIndex += 8; - } - - if (compressedFileSize == uint.MaxValue) - { - CompressedSize = BinaryPrimitives.ReadInt64LittleEndian( - DataBytes.AsSpan(currentIndex) - ); - currentIndex += 8; - } - - if (relativeHeaderOffset == uint.MaxValue) - { - RelativeOffsetOfEntryHeader = BinaryPrimitives.ReadInt64LittleEndian( - DataBytes.AsSpan(currentIndex) - ); - currentIndex += 8; - } - - if (diskNumber == ushort.MaxValue) - { - VolumeNumber = BinaryPrimitives.ReadUInt32LittleEndian( - DataBytes.AsSpan(currentIndex) - ); - } - } - - /// - /// Uncompressed file size. Only valid after has been called and if the - /// original entry header had a corresponding 0xFFFFFFFF value. - /// - public long UncompressedSize { get; private set; } - - /// - /// Compressed file size. Only valid after has been called and if the - /// original entry header had a corresponding 0xFFFFFFFF value. - /// - public long CompressedSize { get; private set; } - - /// - /// Relative offset of the entry header. Only valid after has been called and if the - /// original entry header had a corresponding 0xFFFFFFFF value. - /// - public long RelativeOffsetOfEntryHeader { get; private set; } - - /// - /// Volume number. Only valid after has been called and if the - /// original entry header had a corresponding 0xFFFF value. - /// - public uint VolumeNumber { get; private set; } - } - - internal static class LocalEntryHeaderExtraFactory - { - internal static ExtraData Create(ExtraDataType type, ushort length, byte[] extraData) - { - return type switch - { - ExtraDataType.UnicodePathExtraField - => new ExtraUnicodePathExtraField(type, length, extraData), - ExtraDataType.Zip64ExtendedInformationExtraField - => new Zip64ExtendedInformationExtraField(type, length, extraData), - _ => new ExtraData(type, length, extraData) - }; + // PathNamelength = dataLength - Version(1 byte) - NameCRC32(4 bytes) + var length = Length - 5; + var nameStr = Encoding.UTF8.GetString(DataBytes, 5, length); + return nameStr; } } } + +internal sealed class Zip64ExtendedInformationExtraField : ExtraData +{ + public Zip64ExtendedInformationExtraField( + ExtraDataType type, + ushort length, + byte[] dataBytes + ) : base(type, length, dataBytes) { } + + // From the spec, values are only in the extradata if the standard + // value is set to 0xFFFFFFFF (or 0xFFFF for the Disk Start Number). + // Values, if present, must appear in the following order: + // - Original Size + // - Compressed Size + // - Relative Header Offset + // - Disk Start Number + public void Process( + long uncompressedFileSize, + long compressedFileSize, + long relativeHeaderOffset, + ushort diskNumber + ) + { + var bytesRequired = + ((uncompressedFileSize == uint.MaxValue) ? 8 : 0) + + ((compressedFileSize == uint.MaxValue) ? 8 : 0) + + ((relativeHeaderOffset == uint.MaxValue) ? 8 : 0) + + ((diskNumber == ushort.MaxValue) ? 4 : 0); + var currentIndex = 0; + + if (bytesRequired > DataBytes.Length) + { + throw new ArchiveException( + "Zip64 extended information extra field is not large enough for the required information" + ); + } + + if (uncompressedFileSize == uint.MaxValue) + { + UncompressedSize = BinaryPrimitives.ReadInt64LittleEndian( + DataBytes.AsSpan(currentIndex) + ); + currentIndex += 8; + } + + if (compressedFileSize == uint.MaxValue) + { + CompressedSize = BinaryPrimitives.ReadInt64LittleEndian( + DataBytes.AsSpan(currentIndex) + ); + currentIndex += 8; + } + + if (relativeHeaderOffset == uint.MaxValue) + { + RelativeOffsetOfEntryHeader = BinaryPrimitives.ReadInt64LittleEndian( + DataBytes.AsSpan(currentIndex) + ); + currentIndex += 8; + } + + if (diskNumber == ushort.MaxValue) + { + VolumeNumber = BinaryPrimitives.ReadUInt32LittleEndian( + DataBytes.AsSpan(currentIndex) + ); + } + } + + /// + /// Uncompressed file size. Only valid after has been called and if the + /// original entry header had a corresponding 0xFFFFFFFF value. + /// + public long UncompressedSize { get; private set; } + + /// + /// Compressed file size. Only valid after has been called and if the + /// original entry header had a corresponding 0xFFFFFFFF value. + /// + public long CompressedSize { get; private set; } + + /// + /// Relative offset of the entry header. Only valid after has been called and if the + /// original entry header had a corresponding 0xFFFFFFFF value. + /// + public long RelativeOffsetOfEntryHeader { get; private set; } + + /// + /// Volume number. Only valid after has been called and if the + /// original entry header had a corresponding 0xFFFF value. + /// + public uint VolumeNumber { get; private set; } +} + +internal static class LocalEntryHeaderExtraFactory +{ + internal static ExtraData Create(ExtraDataType type, ushort length, byte[] extraData) + { + return type switch + { + ExtraDataType.UnicodePathExtraField + => new ExtraUnicodePathExtraField(type, length, extraData), + ExtraDataType.Zip64ExtendedInformationExtraField + => new Zip64ExtendedInformationExtraField(type, length, extraData), + _ => new ExtraData(type, length, extraData) + }; + } +} diff --git a/src/SharpCompress/Common/Zip/Headers/SplitHeader.cs b/src/SharpCompress/Common/Zip/Headers/SplitHeader.cs index 7a20430c..1e98517b 100644 --- a/src/SharpCompress/Common/Zip/Headers/SplitHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/SplitHeader.cs @@ -1,15 +1,14 @@ -using System; +using System; using System.IO; -namespace SharpCompress.Common.Zip.Headers -{ - internal class SplitHeader : ZipHeader - { - public SplitHeader() : base(ZipHeaderType.Split) { } +namespace SharpCompress.Common.Zip.Headers; - internal override void Read(BinaryReader reader) - { - throw new NotImplementedException(); - } +internal class SplitHeader : ZipHeader +{ + public SplitHeader() : base(ZipHeaderType.Split) { } + + internal override void Read(BinaryReader reader) + { + throw new NotImplementedException(); } } diff --git a/src/SharpCompress/Common/Zip/Headers/Zip64DirectoryEndHeader.cs b/src/SharpCompress/Common/Zip/Headers/Zip64DirectoryEndHeader.cs index fffbbabf..0356c48c 100644 --- a/src/SharpCompress/Common/Zip/Headers/Zip64DirectoryEndHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/Zip64DirectoryEndHeader.cs @@ -1,50 +1,49 @@ -using System.IO; +using System.IO; -namespace SharpCompress.Common.Zip.Headers +namespace SharpCompress.Common.Zip.Headers; + +internal class Zip64DirectoryEndHeader : ZipHeader { - internal class Zip64DirectoryEndHeader : ZipHeader + public Zip64DirectoryEndHeader() : base(ZipHeaderType.Zip64DirectoryEnd) { } + + internal override void Read(BinaryReader reader) { - public Zip64DirectoryEndHeader() : base(ZipHeaderType.Zip64DirectoryEnd) { } - - internal override void Read(BinaryReader reader) - { - SizeOfDirectoryEndRecord = (long)reader.ReadUInt64(); - VersionMadeBy = reader.ReadUInt16(); - VersionNeededToExtract = reader.ReadUInt16(); - VolumeNumber = reader.ReadUInt32(); - FirstVolumeWithDirectory = reader.ReadUInt32(); - TotalNumberOfEntriesInDisk = (long)reader.ReadUInt64(); - TotalNumberOfEntries = (long)reader.ReadUInt64(); - DirectorySize = (long)reader.ReadUInt64(); - DirectoryStartOffsetRelativeToDisk = (long)reader.ReadUInt64(); - DataSector = reader.ReadBytes( - (int)( - SizeOfDirectoryEndRecord - - SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS - ) - ); - } - - private const int SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS = 44; - - public long SizeOfDirectoryEndRecord { get; private set; } - - public ushort VersionMadeBy { get; private set; } - - public ushort VersionNeededToExtract { get; private set; } - - public uint VolumeNumber { get; private set; } - - public uint FirstVolumeWithDirectory { get; private set; } - - public long TotalNumberOfEntriesInDisk { get; private set; } - - public long TotalNumberOfEntries { get; private set; } - - public long DirectorySize { get; private set; } - - public long DirectoryStartOffsetRelativeToDisk { get; private set; } - - public byte[]? DataSector { get; private set; } + SizeOfDirectoryEndRecord = (long)reader.ReadUInt64(); + VersionMadeBy = reader.ReadUInt16(); + VersionNeededToExtract = reader.ReadUInt16(); + VolumeNumber = reader.ReadUInt32(); + FirstVolumeWithDirectory = reader.ReadUInt32(); + TotalNumberOfEntriesInDisk = (long)reader.ReadUInt64(); + TotalNumberOfEntries = (long)reader.ReadUInt64(); + DirectorySize = (long)reader.ReadUInt64(); + DirectoryStartOffsetRelativeToDisk = (long)reader.ReadUInt64(); + DataSector = reader.ReadBytes( + (int)( + SizeOfDirectoryEndRecord + - SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS + ) + ); } + + private const int SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS = 44; + + public long SizeOfDirectoryEndRecord { get; private set; } + + public ushort VersionMadeBy { get; private set; } + + public ushort VersionNeededToExtract { get; private set; } + + public uint VolumeNumber { get; private set; } + + public uint FirstVolumeWithDirectory { get; private set; } + + public long TotalNumberOfEntriesInDisk { get; private set; } + + public long TotalNumberOfEntries { get; private set; } + + public long DirectorySize { get; private set; } + + public long DirectoryStartOffsetRelativeToDisk { get; private set; } + + public byte[]? DataSector { get; private set; } } diff --git a/src/SharpCompress/Common/Zip/Headers/Zip64DirectoryEndLocatorHeader.cs b/src/SharpCompress/Common/Zip/Headers/Zip64DirectoryEndLocatorHeader.cs index 16724668..9bbf2670 100644 --- a/src/SharpCompress/Common/Zip/Headers/Zip64DirectoryEndLocatorHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/Zip64DirectoryEndLocatorHeader.cs @@ -1,22 +1,21 @@ using System.IO; -namespace SharpCompress.Common.Zip.Headers +namespace SharpCompress.Common.Zip.Headers; + +internal class Zip64DirectoryEndLocatorHeader : ZipHeader { - internal class Zip64DirectoryEndLocatorHeader : ZipHeader + public Zip64DirectoryEndLocatorHeader() : base(ZipHeaderType.Zip64DirectoryEndLocator) { } + + internal override void Read(BinaryReader reader) { - public Zip64DirectoryEndLocatorHeader() : base(ZipHeaderType.Zip64DirectoryEndLocator) { } - - internal override void Read(BinaryReader reader) - { - FirstVolumeWithDirectory = reader.ReadUInt32(); - RelativeOffsetOfTheEndOfDirectoryRecord = (long)reader.ReadUInt64(); - TotalNumberOfVolumes = reader.ReadUInt32(); - } - - public uint FirstVolumeWithDirectory { get; private set; } - - public long RelativeOffsetOfTheEndOfDirectoryRecord { get; private set; } - - public uint TotalNumberOfVolumes { get; private set; } + FirstVolumeWithDirectory = reader.ReadUInt32(); + RelativeOffsetOfTheEndOfDirectoryRecord = (long)reader.ReadUInt64(); + TotalNumberOfVolumes = reader.ReadUInt32(); } + + public uint FirstVolumeWithDirectory { get; private set; } + + public long RelativeOffsetOfTheEndOfDirectoryRecord { get; private set; } + + public uint TotalNumberOfVolumes { get; private set; } } diff --git a/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs b/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs index 40d5c313..7ac43a07 100644 --- a/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs +++ b/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs @@ -1,109 +1,108 @@ -#nullable disable +#nullable disable using System; using System.Buffers.Binary; using System.Collections.Generic; using System.IO; -namespace SharpCompress.Common.Zip.Headers +namespace SharpCompress.Common.Zip.Headers; + +internal abstract class ZipFileEntry : ZipHeader { - internal abstract class ZipFileEntry : ZipHeader + protected ZipFileEntry(ZipHeaderType type, ArchiveEncoding archiveEncoding) : base(type) { - protected ZipFileEntry(ZipHeaderType type, ArchiveEncoding archiveEncoding) : base(type) - { - Extra = new List(); - ArchiveEncoding = archiveEncoding; - } - - internal bool IsDirectory - { - get - { - if (Name.EndsWith('/')) - { - return true; - } - - //.NET Framework 4.5 : System.IO.Compression::CreateFromDirectory() probably writes backslashes to headers - return CompressedSize == 0 && UncompressedSize == 0 && Name.EndsWith('\\'); - } - } - - internal Stream PackedStream { get; set; } - - internal ArchiveEncoding ArchiveEncoding { get; } - - internal string Name { get; set; } - - internal HeaderFlags Flags { get; set; } - - internal ZipCompressionMethod CompressionMethod { get; set; } - - internal long CompressedSize { get; set; } - - internal long? DataStartPosition { get; set; } - - internal long UncompressedSize { get; set; } - - internal List Extra { get; set; } - - public string Password { get; set; } - - internal PkwareTraditionalEncryptionData ComposeEncryptionData(Stream archiveStream) - { - if (archiveStream is null) - { - throw new ArgumentNullException(nameof(archiveStream)); - } - - var buffer = new byte[12]; - archiveStream.ReadFully(buffer); - - PkwareTraditionalEncryptionData encryptionData = - PkwareTraditionalEncryptionData.ForRead(Password!, this, buffer); - - return encryptionData; - } - - internal WinzipAesEncryptionData WinzipAesEncryptionData { get; set; } - - internal ushort LastModifiedDate { get; set; } - - internal ushort LastModifiedTime { get; set; } - - internal uint Crc { get; set; } - - protected void LoadExtra(byte[] extra) - { - for (int i = 0; i < extra.Length - 4; ) - { - ExtraDataType type = (ExtraDataType) - BinaryPrimitives.ReadUInt16LittleEndian(extra.AsSpan(i)); - if (!Enum.IsDefined(typeof(ExtraDataType), type)) - { - type = ExtraDataType.NotImplementedExtraData; - } - - ushort length = BinaryPrimitives.ReadUInt16LittleEndian(extra.AsSpan(i + 2)); - - // 7zip has this same kind of check to ignore extras blocks that don't conform to the standard 2-byte ID, 2-byte length, N-byte value. - // CPP/7Zip/Zip/ZipIn.cpp: CInArchive::ReadExtra - if (length > extra.Length) - { - // bad extras block - return; - } - - byte[] data = new byte[length]; - Buffer.BlockCopy(extra, i + 4, data, 0, length); - Extra.Add(LocalEntryHeaderExtraFactory.Create(type, length, data)); - - i += length + 4; - } - } - - internal ZipFilePart Part { get; set; } - - internal bool IsZip64 => CompressedSize >= uint.MaxValue; + Extra = new List(); + ArchiveEncoding = archiveEncoding; } + + internal bool IsDirectory + { + get + { + if (Name.EndsWith('/')) + { + return true; + } + + //.NET Framework 4.5 : System.IO.Compression::CreateFromDirectory() probably writes backslashes to headers + return CompressedSize == 0 && UncompressedSize == 0 && Name.EndsWith('\\'); + } + } + + internal Stream PackedStream { get; set; } + + internal ArchiveEncoding ArchiveEncoding { get; } + + internal string Name { get; set; } + + internal HeaderFlags Flags { get; set; } + + internal ZipCompressionMethod CompressionMethod { get; set; } + + internal long CompressedSize { get; set; } + + internal long? DataStartPosition { get; set; } + + internal long UncompressedSize { get; set; } + + internal List Extra { get; set; } + + public string Password { get; set; } + + internal PkwareTraditionalEncryptionData ComposeEncryptionData(Stream archiveStream) + { + if (archiveStream is null) + { + throw new ArgumentNullException(nameof(archiveStream)); + } + + var buffer = new byte[12]; + archiveStream.ReadFully(buffer); + + var encryptionData = + PkwareTraditionalEncryptionData.ForRead(Password!, this, buffer); + + return encryptionData; + } + + internal WinzipAesEncryptionData WinzipAesEncryptionData { get; set; } + + internal ushort LastModifiedDate { get; set; } + + internal ushort LastModifiedTime { get; set; } + + internal uint Crc { get; set; } + + protected void LoadExtra(byte[] extra) + { + for (var i = 0; i < extra.Length - 4;) + { + var type = (ExtraDataType) + BinaryPrimitives.ReadUInt16LittleEndian(extra.AsSpan(i)); + if (!Enum.IsDefined(typeof(ExtraDataType), type)) + { + type = ExtraDataType.NotImplementedExtraData; + } + + var length = BinaryPrimitives.ReadUInt16LittleEndian(extra.AsSpan(i + 2)); + + // 7zip has this same kind of check to ignore extras blocks that don't conform to the standard 2-byte ID, 2-byte length, N-byte value. + // CPP/7Zip/Zip/ZipIn.cpp: CInArchive::ReadExtra + if (length > extra.Length) + { + // bad extras block + return; + } + + var data = new byte[length]; + Buffer.BlockCopy(extra, i + 4, data, 0, length); + Extra.Add(LocalEntryHeaderExtraFactory.Create(type, length, data)); + + i += length + 4; + } + } + + internal ZipFilePart Part { get; set; } + + internal bool IsZip64 => CompressedSize >= uint.MaxValue; } diff --git a/src/SharpCompress/Common/Zip/Headers/ZipHeader.cs b/src/SharpCompress/Common/Zip/Headers/ZipHeader.cs index 43042630..36d40a82 100644 --- a/src/SharpCompress/Common/Zip/Headers/ZipHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/ZipHeader.cs @@ -1,19 +1,18 @@ -using System.IO; +using System.IO; -namespace SharpCompress.Common.Zip.Headers +namespace SharpCompress.Common.Zip.Headers; + +internal abstract class ZipHeader { - internal abstract class ZipHeader + protected ZipHeader(ZipHeaderType type) { - protected ZipHeader(ZipHeaderType type) - { - ZipHeaderType = type; - HasData = true; - } - - internal ZipHeaderType ZipHeaderType { get; } - - internal abstract void Read(BinaryReader reader); - - internal bool HasData { get; set; } + ZipHeaderType = type; + HasData = true; } + + internal ZipHeaderType ZipHeaderType { get; } + + internal abstract void Read(BinaryReader reader); + + internal bool HasData { get; set; } } diff --git a/src/SharpCompress/Common/Zip/Headers/ZipHeaderType.cs b/src/SharpCompress/Common/Zip/Headers/ZipHeaderType.cs index 85f6c3b6..62c29d89 100644 --- a/src/SharpCompress/Common/Zip/Headers/ZipHeaderType.cs +++ b/src/SharpCompress/Common/Zip/Headers/ZipHeaderType.cs @@ -1,13 +1,12 @@ -namespace SharpCompress.Common.Zip.Headers +namespace SharpCompress.Common.Zip.Headers; + +internal enum ZipHeaderType { - internal enum ZipHeaderType - { - Ignore, - LocalEntry, - DirectoryEntry, - DirectoryEnd, - Split, - Zip64DirectoryEnd, - Zip64DirectoryEndLocator - } + Ignore, + LocalEntry, + DirectoryEntry, + DirectoryEnd, + Split, + Zip64DirectoryEnd, + Zip64DirectoryEndLocator } diff --git a/src/SharpCompress/Common/Zip/PkwareTraditionalCryptoStream.cs b/src/SharpCompress/Common/Zip/PkwareTraditionalCryptoStream.cs index 71556292..6ca28fc6 100644 --- a/src/SharpCompress/Common/Zip/PkwareTraditionalCryptoStream.cs +++ b/src/SharpCompress/Common/Zip/PkwareTraditionalCryptoStream.cs @@ -1,116 +1,115 @@ -using System; +using System; using System.IO; -namespace SharpCompress.Common.Zip +namespace SharpCompress.Common.Zip; + +internal enum CryptoMode { - internal enum CryptoMode + Encrypt, + Decrypt +} + +internal class PkwareTraditionalCryptoStream : Stream +{ + private readonly PkwareTraditionalEncryptionData _encryptor; + private readonly CryptoMode _mode; + private readonly Stream _stream; + private bool _isDisposed; + + public PkwareTraditionalCryptoStream( + Stream stream, + PkwareTraditionalEncryptionData encryptor, + CryptoMode mode + ) { - Encrypt, - Decrypt + _encryptor = encryptor; + _stream = stream; + _mode = mode; } - internal class PkwareTraditionalCryptoStream : Stream + public override bool CanRead => (_mode == CryptoMode.Decrypt); + + public override bool CanSeek => false; + + public override bool CanWrite => (_mode == CryptoMode.Encrypt); + + public override long Length => throw new NotSupportedException(); + + public override long Position { - private readonly PkwareTraditionalEncryptionData _encryptor; - private readonly CryptoMode _mode; - private readonly Stream _stream; - private bool _isDisposed; + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } - public PkwareTraditionalCryptoStream( - Stream stream, - PkwareTraditionalEncryptionData encryptor, - CryptoMode mode - ) + public override int Read(byte[] buffer, int offset, int count) + { + if (_mode == CryptoMode.Encrypt) { - this._encryptor = encryptor; - this._stream = stream; - this._mode = mode; + throw new NotSupportedException("This stream does not encrypt via Read()"); } - public override bool CanRead => (_mode == CryptoMode.Decrypt); - - public override bool CanSeek => false; - - public override bool CanWrite => (_mode == CryptoMode.Encrypt); - - public override long Length => throw new NotSupportedException(); - - public override long Position + if (buffer is null) { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); + throw new ArgumentNullException(nameof(buffer)); } - public override int Read(byte[] buffer, int offset, int count) + var temp = new byte[count]; + var readBytes = _stream.Read(temp, 0, count); + var decrypted = _encryptor.Decrypt(temp, readBytes); + Buffer.BlockCopy(decrypted, 0, buffer, offset, readBytes); + return readBytes; + } + + public override void Write(byte[] buffer, int offset, int count) + { + if (_mode == CryptoMode.Decrypt) { - if (_mode == CryptoMode.Encrypt) - { - throw new NotSupportedException("This stream does not encrypt via Read()"); - } - - if (buffer is null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - byte[] temp = new byte[count]; - int readBytes = _stream.Read(temp, 0, count); - byte[] decrypted = _encryptor.Decrypt(temp, readBytes); - Buffer.BlockCopy(decrypted, 0, buffer, offset, readBytes); - return readBytes; + throw new NotSupportedException("This stream does not Decrypt via Write()"); } - public override void Write(byte[] buffer, int offset, int count) + if (count == 0) { - if (_mode == CryptoMode.Decrypt) - { - throw new NotSupportedException("This stream does not Decrypt via Write()"); - } - - if (count == 0) - { - return; - } - - byte[] plaintext; - if (offset != 0) - { - plaintext = new byte[count]; - Buffer.BlockCopy(buffer, offset, plaintext, 0, count); - } - else - { - plaintext = buffer; - } - - byte[] encrypted = _encryptor.Encrypt(plaintext, count); - _stream.Write(encrypted, 0, encrypted.Length); + return; } - public override void Flush() + byte[] plaintext; + if (offset != 0) { - //throw new NotSupportedException(); + plaintext = new byte[count]; + Buffer.BlockCopy(buffer, offset, plaintext, 0, count); + } + else + { + plaintext = buffer; } - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } + var encrypted = _encryptor.Encrypt(plaintext, count); + _stream.Write(encrypted, 0, encrypted.Length); + } - public override void SetLength(long value) - { - throw new NotSupportedException(); - } + public override void Flush() + { + //throw new NotSupportedException(); + } - protected override void Dispose(bool disposing) + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + protected override void Dispose(bool disposing) + { + if (_isDisposed) { - if (_isDisposed) - { - return; - } - _isDisposed = true; - base.Dispose(disposing); - _stream.Dispose(); + return; } + _isDisposed = true; + base.Dispose(disposing); + _stream.Dispose(); } } diff --git a/src/SharpCompress/Common/Zip/PkwareTraditionalEncryptionData.cs b/src/SharpCompress/Common/Zip/PkwareTraditionalEncryptionData.cs index 96a88e94..353000d8 100644 --- a/src/SharpCompress/Common/Zip/PkwareTraditionalEncryptionData.cs +++ b/src/SharpCompress/Common/Zip/PkwareTraditionalEncryptionData.cs @@ -1,118 +1,117 @@ -using System; +using System; using SharpCompress.Common.Zip.Headers; using SharpCompress.Compressors.Deflate; -namespace SharpCompress.Common.Zip +namespace SharpCompress.Common.Zip; + +internal class PkwareTraditionalEncryptionData { - internal class PkwareTraditionalEncryptionData + private static readonly CRC32 CRC32 = new CRC32(); + private readonly uint[] _keys = { 0x12345678, 0x23456789, 0x34567890 }; + private readonly ArchiveEncoding _archiveEncoding; + + private PkwareTraditionalEncryptionData(string password, ArchiveEncoding archiveEncoding) { - private static readonly CRC32 CRC32 = new CRC32(); - private readonly UInt32[] _keys = { 0x12345678, 0x23456789, 0x34567890 }; - private readonly ArchiveEncoding _archiveEncoding; + _archiveEncoding = archiveEncoding; + Initialize(password); + } - private PkwareTraditionalEncryptionData(string password, ArchiveEncoding archiveEncoding) + private byte MagicByte + { + get { - _archiveEncoding = archiveEncoding; - Initialize(password); - } - - private byte MagicByte - { - get - { - ushort t = (ushort)((ushort)(_keys[2] & 0xFFFF) | 2); - return (byte)((t * (t ^ 1)) >> 8); - } - } - - public static PkwareTraditionalEncryptionData ForRead( - string password, - ZipFileEntry header, - byte[] encryptionHeader - ) - { - var encryptor = new PkwareTraditionalEncryptionData(password, header.ArchiveEncoding); - byte[] plainTextHeader = encryptor.Decrypt(encryptionHeader, encryptionHeader.Length); - if (plainTextHeader[11] != (byte)((header.Crc >> 24) & 0xff)) - { - if (!FlagUtility.HasFlag(header.Flags, HeaderFlags.UsePostDataDescriptor)) - { - throw new CryptographicException("The password did not match."); - } - if (plainTextHeader[11] != (byte)((header.LastModifiedTime >> 8) & 0xff)) - { - throw new CryptographicException("The password did not match."); - } - } - return encryptor; - } - - public byte[] Decrypt(byte[] cipherText, int length) - { - if (length > cipherText.Length) - { - throw new ArgumentOutOfRangeException( - nameof(length), - "Bad length during Decryption: the length parameter must be smaller than or equal to the size of the destination array." - ); - } - - var plainText = new byte[length]; - for (int i = 0; i < length; i++) - { - var c = (byte)(cipherText[i] ^ MagicByte); - UpdateKeys(c); - plainText[i] = c; - } - return plainText; - } - - public byte[] Encrypt(byte[] plainText, int length) - { - if (plainText is null) - { - throw new ArgumentNullException(nameof(plainText)); - } - - if (length > plainText.Length) - { - throw new ArgumentOutOfRangeException( - nameof(length), - "Bad length during Encryption: The length parameter must be smaller than or equal to the size of the destination array." - ); - } - - var cipherText = new byte[length]; - for (int i = 0; i < length; i++) - { - byte c = plainText[i]; - cipherText[i] = (byte)(plainText[i] ^ MagicByte); - UpdateKeys(c); - } - return cipherText; - } - - private void Initialize(string password) - { - byte[] p = StringToByteArray(password); - for (int i = 0; i < password.Length; i++) - { - UpdateKeys(p[i]); - } - } - - internal byte[] StringToByteArray(string value) - { - byte[] a = _archiveEncoding.Password.GetBytes(value); - return a; - } - - private void UpdateKeys(byte byteValue) - { - _keys[0] = (UInt32)CRC32.ComputeCrc32((int)_keys[0], byteValue); - _keys[1] = _keys[1] + (byte)_keys[0]; - _keys[1] = _keys[1] * 0x08088405 + 1; - _keys[2] = (UInt32)CRC32.ComputeCrc32((int)_keys[2], (byte)(_keys[1] >> 24)); + var t = (ushort)((ushort)(_keys[2] & 0xFFFF) | 2); + return (byte)((t * (t ^ 1)) >> 8); } } + + public static PkwareTraditionalEncryptionData ForRead( + string password, + ZipFileEntry header, + byte[] encryptionHeader + ) + { + var encryptor = new PkwareTraditionalEncryptionData(password, header.ArchiveEncoding); + var plainTextHeader = encryptor.Decrypt(encryptionHeader, encryptionHeader.Length); + if (plainTextHeader[11] != (byte)((header.Crc >> 24) & 0xff)) + { + if (!FlagUtility.HasFlag(header.Flags, HeaderFlags.UsePostDataDescriptor)) + { + throw new CryptographicException("The password did not match."); + } + if (plainTextHeader[11] != (byte)((header.LastModifiedTime >> 8) & 0xff)) + { + throw new CryptographicException("The password did not match."); + } + } + return encryptor; + } + + public byte[] Decrypt(byte[] cipherText, int length) + { + if (length > cipherText.Length) + { + throw new ArgumentOutOfRangeException( + nameof(length), + "Bad length during Decryption: the length parameter must be smaller than or equal to the size of the destination array." + ); + } + + var plainText = new byte[length]; + for (var i = 0; i < length; i++) + { + var c = (byte)(cipherText[i] ^ MagicByte); + UpdateKeys(c); + plainText[i] = c; + } + return plainText; + } + + public byte[] Encrypt(byte[] plainText, int length) + { + if (plainText is null) + { + throw new ArgumentNullException(nameof(plainText)); + } + + if (length > plainText.Length) + { + throw new ArgumentOutOfRangeException( + nameof(length), + "Bad length during Encryption: The length parameter must be smaller than or equal to the size of the destination array." + ); + } + + var cipherText = new byte[length]; + for (var i = 0; i < length; i++) + { + var c = plainText[i]; + cipherText[i] = (byte)(plainText[i] ^ MagicByte); + UpdateKeys(c); + } + return cipherText; + } + + private void Initialize(string password) + { + var p = StringToByteArray(password); + for (var i = 0; i < password.Length; i++) + { + UpdateKeys(p[i]); + } + } + + internal byte[] StringToByteArray(string value) + { + var a = _archiveEncoding.Password.GetBytes(value); + return a; + } + + private void UpdateKeys(byte byteValue) + { + _keys[0] = (uint)CRC32.ComputeCrc32((int)_keys[0], byteValue); + _keys[1] = _keys[1] + (byte)_keys[0]; + _keys[1] = (_keys[1] * 0x08088405) + 1; + _keys[2] = (uint)CRC32.ComputeCrc32((int)_keys[2], (byte)(_keys[1] >> 24)); + } } diff --git a/src/SharpCompress/Common/Zip/SeekableZipFilePart.cs b/src/SharpCompress/Common/Zip/SeekableZipFilePart.cs index 02a40c38..dc7e4783 100644 --- a/src/SharpCompress/Common/Zip/SeekableZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/SeekableZipFilePart.cs @@ -1,59 +1,58 @@ -using System.IO; +using System.IO; using SharpCompress.Common.Zip.Headers; using SharpCompress.IO; -namespace SharpCompress.Common.Zip +namespace SharpCompress.Common.Zip; + +internal class SeekableZipFilePart : ZipFilePart { - internal class SeekableZipFilePart : ZipFilePart + private bool _isLocalHeaderLoaded; + private readonly SeekableZipHeaderFactory _headerFactory; + private readonly DirectoryEntryHeader _directoryEntryHeader; + + internal SeekableZipFilePart( + SeekableZipHeaderFactory headerFactory, + DirectoryEntryHeader header, + Stream stream + ) : base(header, stream) { - private bool _isLocalHeaderLoaded; - private readonly SeekableZipHeaderFactory _headerFactory; - private readonly DirectoryEntryHeader _directoryEntryHeader; + _headerFactory = headerFactory; + _directoryEntryHeader = header; + } - internal SeekableZipFilePart( - SeekableZipHeaderFactory headerFactory, - DirectoryEntryHeader header, - Stream stream - ) : base(header, stream) + internal override Stream GetCompressedStream() + { + if (!_isLocalHeaderLoaded) { - _headerFactory = headerFactory; - _directoryEntryHeader = header; + LoadLocalHeader(); + _isLocalHeaderLoaded = true; + } + return base.GetCompressedStream(); + } + + internal string? Comment => ((DirectoryEntryHeader)Header).Comment; + + private void LoadLocalHeader() + { + var hasData = Header.HasData; + Header = _headerFactory.GetLocalHeader(BaseStream, ((DirectoryEntryHeader)Header)); + Header.HasData = hasData; + } + + protected override Stream CreateBaseStream() + { + BaseStream.Position = Header.DataStartPosition!.Value; + + if ( + (Header.CompressedSize == 0) + && FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor) + && (_directoryEntryHeader?.HasData == true) + && (_directoryEntryHeader?.CompressedSize != 0) + ) + { + return new ReadOnlySubStream(BaseStream, _directoryEntryHeader!.CompressedSize); } - internal override Stream GetCompressedStream() - { - if (!_isLocalHeaderLoaded) - { - LoadLocalHeader(); - _isLocalHeaderLoaded = true; - } - return base.GetCompressedStream(); - } - - internal string? Comment => ((DirectoryEntryHeader)Header).Comment; - - private void LoadLocalHeader() - { - bool hasData = Header.HasData; - Header = _headerFactory.GetLocalHeader(BaseStream, ((DirectoryEntryHeader)Header)); - Header.HasData = hasData; - } - - protected override Stream CreateBaseStream() - { - BaseStream.Position = Header.DataStartPosition!.Value; - - if ( - (Header.CompressedSize == 0) - && FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor) - && (_directoryEntryHeader?.HasData == true) - && (_directoryEntryHeader?.CompressedSize != 0) - ) - { - return new ReadOnlySubStream(BaseStream, _directoryEntryHeader!.CompressedSize); - } - - return BaseStream; - } + return BaseStream; } } diff --git a/src/SharpCompress/Common/Zip/SeekableZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/SeekableZipHeaderFactory.cs index ab730bf5..b99d0e2b 100644 --- a/src/SharpCompress/Common/Zip/SeekableZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/SeekableZipHeaderFactory.cs @@ -1,156 +1,154 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using SharpCompress.Common.Zip.Headers; using SharpCompress.IO; -namespace SharpCompress.Common.Zip +namespace SharpCompress.Common.Zip; + +internal sealed class SeekableZipHeaderFactory : ZipHeaderFactory { - internal sealed class SeekableZipHeaderFactory : ZipHeaderFactory + private const int MINIMUM_EOCD_LENGTH = 22; + private const int ZIP64_EOCD_LENGTH = 20; + + // Comment may be within 64kb + structure 22 bytes + private const int MAX_SEARCH_LENGTH_FOR_EOCD = 65557; + private bool _zip64; + + internal SeekableZipHeaderFactory(string? password, ArchiveEncoding archiveEncoding) + : base(StreamingMode.Seekable, password, archiveEncoding) { } + + internal IEnumerable ReadSeekableHeader(Stream stream) { - private const int MINIMUM_EOCD_LENGTH = 22; - private const int ZIP64_EOCD_LENGTH = 20; + var reader = new BinaryReader(stream); - // Comment may be within 64kb + structure 22 bytes - private const int MAX_SEARCH_LENGTH_FOR_EOCD = 65557; - private bool _zip64; + SeekBackToHeader(stream, reader); - internal SeekableZipHeaderFactory(string? password, ArchiveEncoding archiveEncoding) - : base(StreamingMode.Seekable, password, archiveEncoding) { } + var eocd_location = stream.Position; + var entry = new DirectoryEndHeader(); + entry.Read(reader); - internal IEnumerable ReadSeekableHeader(Stream stream) + if (entry.IsZip64) { - var reader = new BinaryReader(stream); + _zip64 = true; - SeekBackToHeader(stream, reader); - - var eocd_location = stream.Position; - var entry = new DirectoryEndHeader(); - entry.Read(reader); - - if (entry.IsZip64) + // ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR should be before the EOCD + stream.Seek(eocd_location - ZIP64_EOCD_LENGTH - 4, SeekOrigin.Begin); + var zip64_locator = reader.ReadUInt32(); + if (zip64_locator != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR) { - _zip64 = true; - - // ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR should be before the EOCD - stream.Seek(eocd_location - ZIP64_EOCD_LENGTH - 4, SeekOrigin.Begin); - uint zip64_locator = reader.ReadUInt32(); - if (zip64_locator != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR) - { - throw new ArchiveException("Failed to locate the Zip64 Directory Locator"); - } - - var zip64Locator = new Zip64DirectoryEndLocatorHeader(); - zip64Locator.Read(reader); - - stream.Seek(zip64Locator.RelativeOffsetOfTheEndOfDirectoryRecord, SeekOrigin.Begin); - uint zip64Signature = reader.ReadUInt32(); - if (zip64Signature != ZIP64_END_OF_CENTRAL_DIRECTORY) - { - throw new ArchiveException("Failed to locate the Zip64 Header"); - } - - var zip64Entry = new Zip64DirectoryEndHeader(); - zip64Entry.Read(reader); - stream.Seek(zip64Entry.DirectoryStartOffsetRelativeToDisk, SeekOrigin.Begin); - } - else - { - stream.Seek(entry.DirectoryStartOffsetRelativeToDisk, SeekOrigin.Begin); + throw new ArchiveException("Failed to locate the Zip64 Directory Locator"); } - long position = stream.Position; - while (true) + var zip64Locator = new Zip64DirectoryEndLocatorHeader(); + zip64Locator.Read(reader); + + stream.Seek(zip64Locator.RelativeOffsetOfTheEndOfDirectoryRecord, SeekOrigin.Begin); + var zip64Signature = reader.ReadUInt32(); + if (zip64Signature != ZIP64_END_OF_CENTRAL_DIRECTORY) { - stream.Position = position; - uint signature = reader.ReadUInt32(); - var nextHeader = ReadHeader(signature, reader, _zip64); - position = stream.Position; - - if (nextHeader is null) - { - yield break; - } - - if (nextHeader is DirectoryEntryHeader entryHeader) - { - //entry could be zero bytes so we need to know that. - entryHeader.HasData = entryHeader.CompressedSize != 0; - yield return entryHeader; - } - else if (nextHeader is DirectoryEndHeader endHeader) - { - yield return endHeader; - } + throw new ArchiveException("Failed to locate the Zip64 Header"); } + + var zip64Entry = new Zip64DirectoryEndHeader(); + zip64Entry.Read(reader); + stream.Seek(zip64Entry.DirectoryStartOffsetRelativeToDisk, SeekOrigin.Begin); + } + else + { + stream.Seek(entry.DirectoryStartOffsetRelativeToDisk, SeekOrigin.Begin); } - private static bool IsMatch(byte[] haystack, int position, byte[] needle) + var position = stream.Position; + while (true) { - for (int i = 0; i < needle.Length; i++) + stream.Position = position; + var signature = reader.ReadUInt32(); + var nextHeader = ReadHeader(signature, reader, _zip64); + position = stream.Position; + + if (nextHeader is null) { - if (haystack[position + i] != needle[i]) - { - return false; - } + yield break; } - return true; - } - - private static void SeekBackToHeader(Stream stream, BinaryReader reader) - { - // Minimum EOCD length - if (stream.Length < MINIMUM_EOCD_LENGTH) + if (nextHeader is DirectoryEntryHeader entryHeader) { - throw new ArchiveException( - "Could not find Zip file Directory at the end of the file. File may be corrupted." - ); + //entry could be zero bytes so we need to know that. + entryHeader.HasData = entryHeader.CompressedSize != 0; + yield return entryHeader; } - - int len = - stream.Length < MAX_SEARCH_LENGTH_FOR_EOCD - ? (int)stream.Length - : MAX_SEARCH_LENGTH_FOR_EOCD; - // We search for marker in reverse to find the first occurance - byte[] needle = { 0x06, 0x05, 0x4b, 0x50 }; - - stream.Seek(-len, SeekOrigin.End); - - byte[] seek = reader.ReadBytes(len); - - // Search in reverse - Array.Reverse(seek); - - // don't exclude the minimum eocd region, otherwise you fail to locate the header in empty zip files - var max_search_area = len; // - MINIMUM_EOCD_LENGTH; - - for (int pos_from_end = 0; pos_from_end < max_search_area; ++pos_from_end) + else if (nextHeader is DirectoryEndHeader endHeader) { - if (IsMatch(seek, pos_from_end, needle)) - { - stream.Seek(-pos_from_end, SeekOrigin.End); - return; - } + yield return endHeader; } - - throw new ArchiveException("Failed to locate the Zip Header"); - } - - internal LocalEntryHeader GetLocalHeader( - Stream stream, - DirectoryEntryHeader directoryEntryHeader - ) - { - stream.Seek(directoryEntryHeader.RelativeOffsetOfEntryHeader, SeekOrigin.Begin); - BinaryReader reader = new BinaryReader(stream); - uint signature = reader.ReadUInt32(); - var localEntryHeader = ReadHeader(signature, reader, _zip64) as LocalEntryHeader; - if (localEntryHeader is null) - { - throw new InvalidOperationException(); - } - return localEntryHeader; } } + + private static bool IsMatch(byte[] haystack, int position, byte[] needle) + { + for (var i = 0; i < needle.Length; i++) + { + if (haystack[position + i] != needle[i]) + { + return false; + } + } + + return true; + } + + private static void SeekBackToHeader(Stream stream, BinaryReader reader) + { + // Minimum EOCD length + if (stream.Length < MINIMUM_EOCD_LENGTH) + { + throw new ArchiveException( + "Could not find Zip file Directory at the end of the file. File may be corrupted." + ); + } + + var len = + stream.Length < MAX_SEARCH_LENGTH_FOR_EOCD + ? (int)stream.Length + : MAX_SEARCH_LENGTH_FOR_EOCD; + // We search for marker in reverse to find the first occurance + byte[] needle = { 0x06, 0x05, 0x4b, 0x50 }; + + stream.Seek(-len, SeekOrigin.End); + + var seek = reader.ReadBytes(len); + + // Search in reverse + Array.Reverse(seek); + + // don't exclude the minimum eocd region, otherwise you fail to locate the header in empty zip files + var max_search_area = len; // - MINIMUM_EOCD_LENGTH; + + for (var pos_from_end = 0; pos_from_end < max_search_area; ++pos_from_end) + { + if (IsMatch(seek, pos_from_end, needle)) + { + stream.Seek(-pos_from_end, SeekOrigin.End); + return; + } + } + + throw new ArchiveException("Failed to locate the Zip Header"); + } + + internal LocalEntryHeader GetLocalHeader( + Stream stream, + DirectoryEntryHeader directoryEntryHeader + ) + { + stream.Seek(directoryEntryHeader.RelativeOffsetOfEntryHeader, SeekOrigin.Begin); + var reader = new BinaryReader(stream); + var signature = reader.ReadUInt32(); + if (ReadHeader(signature, reader, _zip64) is not LocalEntryHeader localEntryHeader) + { + throw new InvalidOperationException(); + } + return localEntryHeader; + } } diff --git a/src/SharpCompress/Common/Zip/StreamingZipFilePart.cs b/src/SharpCompress/Common/Zip/StreamingZipFilePart.cs index 4dabc14d..1aaffe81 100644 --- a/src/SharpCompress/Common/Zip/StreamingZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/StreamingZipFilePart.cs @@ -3,100 +3,99 @@ using SharpCompress.Common.Zip.Headers; using SharpCompress.Compressors.Deflate; using SharpCompress.IO; -namespace SharpCompress.Common.Zip +namespace SharpCompress.Common.Zip; + +internal sealed class StreamingZipFilePart : ZipFilePart { - internal sealed class StreamingZipFilePart : ZipFilePart + private Stream? _decompressionStream; + + internal StreamingZipFilePart(ZipFileEntry header, Stream stream) : base(header, stream) { } + + protected override Stream CreateBaseStream() { - private Stream? _decompressionStream; + return Header.PackedStream; + } - internal StreamingZipFilePart(ZipFileEntry header, Stream stream) : base(header, stream) { } - - protected override Stream CreateBaseStream() + internal override Stream GetCompressedStream() + { + if (!Header.HasData) { - return Header.PackedStream; + return Stream.Null; } - - internal override Stream GetCompressedStream() + _decompressionStream = CreateDecompressionStream( + GetCryptoStream(CreateBaseStream()), + Header.CompressionMethod + ); + if (LeaveStreamOpen) { - if (!Header.HasData) - { - return Stream.Null; - } - _decompressionStream = CreateDecompressionStream( - GetCryptoStream(CreateBaseStream()), - Header.CompressionMethod - ); - if (LeaveStreamOpen) - { - return NonDisposingStream.Create(_decompressionStream); - } - return _decompressionStream; + return NonDisposingStream.Create(_decompressionStream); } + return _decompressionStream; + } - internal BinaryReader FixStreamedFileLocation(ref RewindableStream rewindableStream) + internal BinaryReader FixStreamedFileLocation(ref RewindableStream rewindableStream) + { + if (Header.IsDirectory) { - if (Header.IsDirectory) - { - return new BinaryReader(rewindableStream); - } - if (Header.HasData && !Skipped) - { - _decompressionStream ??= GetCompressedStream(); + return new BinaryReader(rewindableStream); + } + if (Header.HasData && !Skipped) + { + _decompressionStream ??= GetCompressedStream(); - if (Header.CompressionMethod != ZipCompressionMethod.None) + if (Header.CompressionMethod != ZipCompressionMethod.None) + { + _decompressionStream.Skip(); + + // If we had TotalIn / TotalOut we could have used them + Header.CompressedSize = _decompressionStream.Position; + + if (_decompressionStream is DeflateStream deflateStream) { - _decompressionStream.Skip(); - - // If we had TotalIn / TotalOut we could have used them - Header.CompressedSize = _decompressionStream.Position; - - if (_decompressionStream is DeflateStream deflateStream) - { - rewindableStream.Rewind(deflateStream.InputBuffer); - } + rewindableStream.Rewind(deflateStream.InputBuffer); } - else - { - // We would need to search for the magic word - rewindableStream.Position -= 4; - var pos = rewindableStream.Position; - while (Utility.Find(rewindableStream, new byte[] { 0x50, 0x4b, 0x07, 0x08 })) - { - // We should probably check CRC32 for positive matching as well - var size = rewindableStream.Position - pos; - var br = new BinaryReader(rewindableStream); - br.ReadUInt32(); - br.ReadUInt32(); // CRC32 - var compressed_size = br.ReadUInt32(); - var uncompressed_size = br.ReadUInt32(); - var uncompressed_64bit = br.ReadInt64(); - - long test_64bit = (long)uncompressed_size << 32 | (long)compressed_size; - - if (test_64bit == size && test_64bit == uncompressed_64bit) - { - Header.CompressedSize = test_64bit; - Header.UncompressedSize = uncompressed_64bit; - rewindableStream.Position -= 24; - break; - } - - if (compressed_size == size && compressed_size == uncompressed_size) - { - Header.CompressedSize = compressed_size; - Header.UncompressedSize = uncompressed_size; - rewindableStream.Position -= 24; - break; - } - rewindableStream.Position -= 20; - } - } - - Skipped = true; } - var reader = new BinaryReader(rewindableStream); - _decompressionStream = null; - return reader; + else + { + // We would need to search for the magic word + rewindableStream.Position -= 4; + var pos = rewindableStream.Position; + while (Utility.Find(rewindableStream, new byte[] { 0x50, 0x4b, 0x07, 0x08 })) + { + // We should probably check CRC32 for positive matching as well + var size = rewindableStream.Position - pos; + var br = new BinaryReader(rewindableStream); + br.ReadUInt32(); + br.ReadUInt32(); // CRC32 + var compressed_size = br.ReadUInt32(); + var uncompressed_size = br.ReadUInt32(); + var uncompressed_64bit = br.ReadInt64(); + + var test_64bit = ((long)uncompressed_size << 32) | compressed_size; + + if (test_64bit == size && test_64bit == uncompressed_64bit) + { + Header.CompressedSize = test_64bit; + Header.UncompressedSize = uncompressed_64bit; + rewindableStream.Position -= 24; + break; + } + + if (compressed_size == size && compressed_size == uncompressed_size) + { + Header.CompressedSize = compressed_size; + Header.UncompressedSize = uncompressed_size; + rewindableStream.Position -= 24; + break; + } + rewindableStream.Position -= 20; + } + } + + Skipped = true; } + var reader = new BinaryReader(rewindableStream); + _decompressionStream = null; + return reader; } } diff --git a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs index 620e3885..485ad901 100644 --- a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs @@ -3,118 +3,117 @@ using System.IO; using SharpCompress.Common.Zip.Headers; using SharpCompress.IO; -namespace SharpCompress.Common.Zip +namespace SharpCompress.Common.Zip; + +internal class StreamingZipHeaderFactory : ZipHeaderFactory { - internal class StreamingZipHeaderFactory : ZipHeaderFactory + internal StreamingZipHeaderFactory(string? password, ArchiveEncoding archiveEncoding) + : base(StreamingMode.Streaming, password, archiveEncoding) { } + + internal IEnumerable ReadStreamHeader(Stream stream) { - internal StreamingZipHeaderFactory(string? password, ArchiveEncoding archiveEncoding) - : base(StreamingMode.Streaming, password, archiveEncoding) { } + RewindableStream rewindableStream; - internal IEnumerable ReadStreamHeader(Stream stream) + if (stream is RewindableStream rs) { - RewindableStream rewindableStream; - - if (stream is RewindableStream rs) - { - rewindableStream = rs; - } - else - { - rewindableStream = new RewindableStream(stream); - } - while (true) - { - ZipHeader? header; - BinaryReader reader = new BinaryReader(rewindableStream); - uint headerBytes = 0; - if ( - _lastEntryHeader != null - && ( - FlagUtility.HasFlag( - _lastEntryHeader.Flags, - HeaderFlags.UsePostDataDescriptor - ) || _lastEntryHeader.IsZip64 - ) + rewindableStream = rs; + } + else + { + rewindableStream = new RewindableStream(stream); + } + while (true) + { + ZipHeader? header; + var reader = new BinaryReader(rewindableStream); + uint headerBytes = 0; + if ( + _lastEntryHeader != null + && ( + FlagUtility.HasFlag( + _lastEntryHeader.Flags, + HeaderFlags.UsePostDataDescriptor + ) || _lastEntryHeader.IsZip64 ) + ) + { + reader = ((StreamingZipFilePart)_lastEntryHeader.Part).FixStreamedFileLocation( + ref rewindableStream + ); + var pos = rewindableStream.CanSeek ? (long?)rewindableStream.Position : null; + var crc = reader.ReadUInt32(); + if (crc == POST_DATA_DESCRIPTOR) { - reader = ((StreamingZipFilePart)_lastEntryHeader.Part).FixStreamedFileLocation( - ref rewindableStream - ); - long? pos = rewindableStream.CanSeek ? (long?)rewindableStream.Position : null; - uint crc = reader.ReadUInt32(); - if (crc == POST_DATA_DESCRIPTOR) - { - crc = reader.ReadUInt32(); - } - _lastEntryHeader.Crc = crc; + crc = reader.ReadUInt32(); + } + _lastEntryHeader.Crc = crc; - // The DataDescriptor can be either 64bit or 32bit - var compressed_size = reader.ReadUInt32(); - var uncompressed_size = reader.ReadUInt32(); + // The DataDescriptor can be either 64bit or 32bit + var compressed_size = reader.ReadUInt32(); + var uncompressed_size = reader.ReadUInt32(); - // Check if we have header or 64bit DataDescriptor + // Check if we have header or 64bit DataDescriptor + headerBytes = reader.ReadUInt32(); + var test_header = !(headerBytes == 0x04034b50 || headerBytes == 0x02014b50); + + var test_64bit = ((long)uncompressed_size << 32) | compressed_size; + if (test_64bit == _lastEntryHeader.CompressedSize && test_header) + { + _lastEntryHeader.UncompressedSize = + ((long)reader.ReadUInt32() << 32) | headerBytes; headerBytes = reader.ReadUInt32(); - bool test_header = !(headerBytes == 0x04034b50 || headerBytes == 0x02014b50); - - long test_64bit = (long)uncompressed_size << 32 | (long)compressed_size; - if (test_64bit == _lastEntryHeader.CompressedSize && test_header) - { - _lastEntryHeader.UncompressedSize = - (long)reader.ReadUInt32() << 32 | (long)headerBytes; - headerBytes = reader.ReadUInt32(); - } - else - { - _lastEntryHeader.UncompressedSize = uncompressed_size; - } - - if (pos.HasValue) - { - _lastEntryHeader.DataStartPosition = pos - _lastEntryHeader.CompressedSize; - } } else { - headerBytes = reader.ReadUInt32(); + _lastEntryHeader.UncompressedSize = uncompressed_size; } - _lastEntryHeader = null; - header = ReadHeader(headerBytes, reader); - if (header is null) + if (pos.HasValue) { - yield break; + _lastEntryHeader.DataStartPosition = pos - _lastEntryHeader.CompressedSize; } - - //entry could be zero bytes so we need to know that. - if (header.ZipHeaderType == ZipHeaderType.LocalEntry) - { - var local_header = ((LocalEntryHeader)header); - - // If we have CompressedSize, there is data to be read - if (local_header.CompressedSize > 0) - { - header.HasData = true; - } // Check if zip is streaming ( Length is 0 and is declared in PostDataDescriptor ) - else if (local_header.Flags.HasFlag(HeaderFlags.UsePostDataDescriptor)) - { - bool isRecording = rewindableStream.IsRecording; - if (!isRecording) - { - rewindableStream.StartRecording(); - } - uint nextHeaderBytes = reader.ReadUInt32(); - - // Check if next data is PostDataDescriptor, streamed file with 0 length - header.HasData = !IsHeader(nextHeaderBytes); - rewindableStream.Rewind(!isRecording); - } - else // We are not streaming and compressed size is 0, we have no data - { - header.HasData = false; - } - } - yield return header; } + else + { + headerBytes = reader.ReadUInt32(); + } + + _lastEntryHeader = null; + header = ReadHeader(headerBytes, reader); + if (header is null) + { + yield break; + } + + //entry could be zero bytes so we need to know that. + if (header.ZipHeaderType == ZipHeaderType.LocalEntry) + { + var local_header = ((LocalEntryHeader)header); + + // If we have CompressedSize, there is data to be read + if (local_header.CompressedSize > 0) + { + header.HasData = true; + } // Check if zip is streaming ( Length is 0 and is declared in PostDataDescriptor ) + else if (local_header.Flags.HasFlag(HeaderFlags.UsePostDataDescriptor)) + { + var isRecording = rewindableStream.IsRecording; + if (!isRecording) + { + rewindableStream.StartRecording(); + } + var nextHeaderBytes = reader.ReadUInt32(); + + // Check if next data is PostDataDescriptor, streamed file with 0 length + header.HasData = !IsHeader(nextHeaderBytes); + rewindableStream.Rewind(!isRecording); + } + else // We are not streaming and compressed size is 0, we have no data + { + header.HasData = false; + } + } + yield return header; } } } diff --git a/src/SharpCompress/Common/Zip/WinzipAesCryptoStream.cs b/src/SharpCompress/Common/Zip/WinzipAesCryptoStream.cs index 00345a47..4425af1d 100644 --- a/src/SharpCompress/Common/Zip/WinzipAesCryptoStream.cs +++ b/src/SharpCompress/Common/Zip/WinzipAesCryptoStream.cs @@ -3,180 +3,179 @@ using System.Buffers.Binary; using System.IO; using System.Security.Cryptography; -namespace SharpCompress.Common.Zip +namespace SharpCompress.Common.Zip; + +internal class WinzipAesCryptoStream : Stream { - internal class WinzipAesCryptoStream : Stream + private const int BLOCK_SIZE_IN_BYTES = 16; + private readonly SymmetricAlgorithm _cipher; + private readonly byte[] _counter = new byte[BLOCK_SIZE_IN_BYTES]; + private readonly Stream _stream; + private readonly ICryptoTransform _transform; + private int _nonce = 1; + private byte[] _counterOut = new byte[BLOCK_SIZE_IN_BYTES]; + private bool _isFinalBlock; + private long _totalBytesLeftToRead; + private bool _isDisposed; + + internal WinzipAesCryptoStream( + Stream stream, + WinzipAesEncryptionData winzipAesEncryptionData, + long length + ) { - private const int BLOCK_SIZE_IN_BYTES = 16; - private readonly SymmetricAlgorithm _cipher; - private readonly byte[] _counter = new byte[BLOCK_SIZE_IN_BYTES]; - private readonly Stream _stream; - private readonly ICryptoTransform _transform; - private int _nonce = 1; - private byte[] _counterOut = new byte[BLOCK_SIZE_IN_BYTES]; - private bool _isFinalBlock; - private long _totalBytesLeftToRead; - private bool _isDisposed; + _stream = stream; + _totalBytesLeftToRead = length; - internal WinzipAesCryptoStream( - Stream stream, - WinzipAesEncryptionData winzipAesEncryptionData, - long length - ) + _cipher = CreateCipher(winzipAesEncryptionData); + + var iv = new byte[BLOCK_SIZE_IN_BYTES]; + _transform = _cipher.CreateEncryptor(winzipAesEncryptionData.KeyBytes, iv); + } + + private SymmetricAlgorithm CreateCipher(WinzipAesEncryptionData winzipAesEncryptionData) + { + var cipher = Aes.Create(); + cipher.BlockSize = BLOCK_SIZE_IN_BYTES * 8; + cipher.KeySize = winzipAesEncryptionData.KeyBytes.Length * 8; + cipher.Mode = CipherMode.ECB; + cipher.Padding = PaddingMode.None; + return cipher; + } + + public override bool CanRead + { + get { return true; } + } + + public override bool CanSeek + { + get { return false; } + } + + public override bool CanWrite + { + get { return false; } + } + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + protected override void Dispose(bool disposing) + { + if (_isDisposed) { - this._stream = stream; - _totalBytesLeftToRead = length; - - _cipher = CreateCipher(winzipAesEncryptionData); - - var iv = new byte[BLOCK_SIZE_IN_BYTES]; - _transform = _cipher.CreateEncryptor(winzipAesEncryptionData.KeyBytes, iv); + return; } - - private SymmetricAlgorithm CreateCipher(WinzipAesEncryptionData winzipAesEncryptionData) + _isDisposed = true; + if (disposing) { - var cipher = Aes.Create(); - cipher.BlockSize = BLOCK_SIZE_IN_BYTES * 8; - cipher.KeySize = winzipAesEncryptionData.KeyBytes.Length * 8; - cipher.Mode = CipherMode.ECB; - cipher.Padding = PaddingMode.None; - return cipher; - } - - public override bool CanRead - { - get { return true; } - } - - public override bool CanSeek - { - get { return false; } - } - - public override bool CanWrite - { - get { return false; } - } - - public override long Length - { - get { throw new NotSupportedException(); } - } - - public override long Position - { - get { throw new NotSupportedException(); } - set { throw new NotSupportedException(); } - } - - protected override void Dispose(bool disposing) - { - if (_isDisposed) - { - return; - } - _isDisposed = true; - if (disposing) - { - //read out last 10 auth bytes - Span ten = stackalloc byte[10]; - _stream.ReadFully(ten); - _stream.Dispose(); - } - } - - public override void Flush() - { - throw new NotSupportedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - if (_totalBytesLeftToRead == 0) - { - return 0; - } - int bytesToRead = count; - if (count > _totalBytesLeftToRead) - { - bytesToRead = (int)_totalBytesLeftToRead; - } - int read = _stream.Read(buffer, offset, bytesToRead); - _totalBytesLeftToRead -= read; - - ReadTransformBlocks(buffer, offset, read); - - return read; - } - - private int ReadTransformOneBlock(byte[] buffer, int offset, int last) - { - if (_isFinalBlock) - { - throw new InvalidOperationException(); - } - - int bytesRemaining = last - offset; - int bytesToRead = - (bytesRemaining > BLOCK_SIZE_IN_BYTES) ? BLOCK_SIZE_IN_BYTES : bytesRemaining; - - // update the counter - BinaryPrimitives.WriteInt32LittleEndian(_counter, _nonce++); - - // Determine if this is the final block - if ((bytesToRead == bytesRemaining) && (_totalBytesLeftToRead == 0)) - { - _counterOut = _transform.TransformFinalBlock(_counter, 0, BLOCK_SIZE_IN_BYTES); - _isFinalBlock = true; - } - else - { - _transform.TransformBlock( - _counter, - 0, // offset - BLOCK_SIZE_IN_BYTES, - _counterOut, - 0 - ); // offset - } - - XorInPlace(buffer, offset, bytesToRead); - return bytesToRead; - } - - private void XorInPlace(byte[] buffer, int offset, int count) - { - for (int i = 0; i < count; i++) - { - buffer[offset + i] = (byte)(_counterOut[i] ^ buffer[offset + i]); - } - } - - private void ReadTransformBlocks(byte[] buffer, int offset, int count) - { - int posn = offset; - int last = count + offset; - - while (posn < buffer.Length && posn < last) - { - int n = ReadTransformOneBlock(buffer, posn, last); - posn += n; - } - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); + //read out last 10 auth bytes + Span ten = stackalloc byte[10]; + _stream.ReadFully(ten); + _stream.Dispose(); } } + + public override void Flush() + { + throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (_totalBytesLeftToRead == 0) + { + return 0; + } + var bytesToRead = count; + if (count > _totalBytesLeftToRead) + { + bytesToRead = (int)_totalBytesLeftToRead; + } + var read = _stream.Read(buffer, offset, bytesToRead); + _totalBytesLeftToRead -= read; + + ReadTransformBlocks(buffer, offset, read); + + return read; + } + + private int ReadTransformOneBlock(byte[] buffer, int offset, int last) + { + if (_isFinalBlock) + { + throw new InvalidOperationException(); + } + + var bytesRemaining = last - offset; + var bytesToRead = + (bytesRemaining > BLOCK_SIZE_IN_BYTES) ? BLOCK_SIZE_IN_BYTES : bytesRemaining; + + // update the counter + BinaryPrimitives.WriteInt32LittleEndian(_counter, _nonce++); + + // Determine if this is the final block + if ((bytesToRead == bytesRemaining) && (_totalBytesLeftToRead == 0)) + { + _counterOut = _transform.TransformFinalBlock(_counter, 0, BLOCK_SIZE_IN_BYTES); + _isFinalBlock = true; + } + else + { + _transform.TransformBlock( + _counter, + 0, // offset + BLOCK_SIZE_IN_BYTES, + _counterOut, + 0 + ); // offset + } + + XorInPlace(buffer, offset, bytesToRead); + return bytesToRead; + } + + private void XorInPlace(byte[] buffer, int offset, int count) + { + for (var i = 0; i < count; i++) + { + buffer[offset + i] = (byte)(_counterOut[i] ^ buffer[offset + i]); + } + } + + private void ReadTransformBlocks(byte[] buffer, int offset, int count) + { + var posn = offset; + var last = count + offset; + + while (posn < buffer.Length && posn < last) + { + var n = ReadTransformOneBlock(buffer, posn, last); + posn += n; + } + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } } diff --git a/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs b/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs index cd5314c4..ce31eed9 100644 --- a/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs +++ b/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs @@ -4,78 +4,77 @@ using System; using System.Buffers.Binary; using System.Security.Cryptography; -namespace SharpCompress.Common.Zip +namespace SharpCompress.Common.Zip; + +internal class WinzipAesEncryptionData { - internal class WinzipAesEncryptionData + private const int RFC2898_ITERATIONS = 1000; + + private readonly byte[] _salt; + private readonly WinzipAesKeySize _keySize; + private readonly byte[] _passwordVerifyValue; + private readonly string _password; + + private byte[] _generatedVerifyValue; + + internal WinzipAesEncryptionData( + WinzipAesKeySize keySize, + byte[] salt, + byte[] passwordVerifyValue, + string password + ) { - private const int RFC2898_ITERATIONS = 1000; + _keySize = keySize; + _salt = salt; + _passwordVerifyValue = passwordVerifyValue; + _password = password; + Initialize(); + } - private readonly byte[] _salt; - private readonly WinzipAesKeySize _keySize; - private readonly byte[] _passwordVerifyValue; - private readonly string _password; + internal byte[] IvBytes { get; set; } - private byte[] _generatedVerifyValue; + internal byte[] KeyBytes { get; set; } - internal WinzipAesEncryptionData( - WinzipAesKeySize keySize, - byte[] salt, - byte[] passwordVerifyValue, - string password - ) + private int KeySizeInBytes + { + get { return KeyLengthInBytes(_keySize); } + } + + internal static int KeyLengthInBytes(WinzipAesKeySize keySize) + { + return keySize switch { - this._keySize = keySize; - this._salt = salt; - this._passwordVerifyValue = passwordVerifyValue; - this._password = password; - Initialize(); - } + WinzipAesKeySize.KeySize128 => 16, + WinzipAesKeySize.KeySize192 => 24, + WinzipAesKeySize.KeySize256 => 32, + _ => throw new InvalidOperationException(), + }; + } - internal byte[] IvBytes { get; set; } - - internal byte[] KeyBytes { get; set; } - - private int KeySizeInBytes - { - get { return KeyLengthInBytes(_keySize); } - } - - internal static int KeyLengthInBytes(WinzipAesKeySize keySize) - { - return keySize switch - { - WinzipAesKeySize.KeySize128 => 16, - WinzipAesKeySize.KeySize192 => 24, - WinzipAesKeySize.KeySize256 => 32, - _ => throw new InvalidOperationException(), - }; - } - - private void Initialize() - { + private void Initialize() + { #if NET7_0 - var rfc2898 = new Rfc2898DeriveBytes( - _password, - _salt, - RFC2898_ITERATIONS, - HashAlgorithmName.SHA1 - ); + var rfc2898 = new Rfc2898DeriveBytes( + _password, + _salt, + RFC2898_ITERATIONS, + HashAlgorithmName.SHA1 + ); #else - var rfc2898 = new Rfc2898DeriveBytes(_password, _salt, RFC2898_ITERATIONS); + var rfc2898 = new Rfc2898DeriveBytes(_password, _salt, RFC2898_ITERATIONS); #endif - KeyBytes = rfc2898.GetBytes(KeySizeInBytes); // 16 or 24 or 32 ??? - IvBytes = rfc2898.GetBytes(KeySizeInBytes); - _generatedVerifyValue = rfc2898.GetBytes(2); + KeyBytes = rfc2898.GetBytes(KeySizeInBytes); // 16 or 24 or 32 ??? + IvBytes = rfc2898.GetBytes(KeySizeInBytes); + _generatedVerifyValue = rfc2898.GetBytes(2); - short verify = BinaryPrimitives.ReadInt16LittleEndian(_passwordVerifyValue); - if (_password != null) + var verify = BinaryPrimitives.ReadInt16LittleEndian(_passwordVerifyValue); + if (_password != null) + { + var generated = BinaryPrimitives.ReadInt16LittleEndian(_generatedVerifyValue); + if (verify != generated) { - short generated = BinaryPrimitives.ReadInt16LittleEndian(_generatedVerifyValue); - if (verify != generated) - { - throw new InvalidFormatException("bad password"); - } + throw new InvalidFormatException("bad password"); } } } diff --git a/src/SharpCompress/Common/Zip/WinzipAesKeySize.cs b/src/SharpCompress/Common/Zip/WinzipAesKeySize.cs index 07602338..7772bcaa 100644 --- a/src/SharpCompress/Common/Zip/WinzipAesKeySize.cs +++ b/src/SharpCompress/Common/Zip/WinzipAesKeySize.cs @@ -1,9 +1,8 @@ -namespace SharpCompress.Common.Zip +namespace SharpCompress.Common.Zip; + +internal enum WinzipAesKeySize { - internal enum WinzipAesKeySize - { - KeySize128 = 1, - KeySize192 = 2, - KeySize256 = 3 - } + KeySize128 = 1, + KeySize192 = 2, + KeySize256 = 3 } diff --git a/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs b/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs index 87135269..599b1ee1 100644 --- a/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs +++ b/src/SharpCompress/Common/Zip/ZipCompressionMethod.cs @@ -1,13 +1,12 @@ -namespace SharpCompress.Common.Zip +namespace SharpCompress.Common.Zip; + +internal enum ZipCompressionMethod { - internal enum ZipCompressionMethod - { - None = 0, - Deflate = 8, - Deflate64 = 9, - BZip2 = 12, - LZMA = 14, - PPMd = 98, - WinzipAes = 0x63 //http://www.winzip.com/aes_info.htm - } + None = 0, + Deflate = 8, + Deflate64 = 9, + BZip2 = 12, + LZMA = 14, + PPMd = 98, + WinzipAes = 0x63 //http://www.winzip.com/aes_info.htm } diff --git a/src/SharpCompress/Common/Zip/ZipEntry.cs b/src/SharpCompress/Common/Zip/ZipEntry.cs index f77f397d..2c544b94 100644 --- a/src/SharpCompress/Common/Zip/ZipEntry.cs +++ b/src/SharpCompress/Common/Zip/ZipEntry.cs @@ -1,90 +1,89 @@ -#nullable disable +#nullable disable using System; using System.Collections.Generic; using SharpCompress.Common.Zip.Headers; -namespace SharpCompress.Common.Zip +namespace SharpCompress.Common.Zip; + +public class ZipEntry : Entry { - public class ZipEntry : Entry + private readonly ZipFilePart _filePart; + + internal ZipEntry(ZipFilePart filePart) { - private readonly ZipFilePart _filePart; - - internal ZipEntry(ZipFilePart filePart) + if (filePart != null) { - if (filePart != null) - { - this._filePart = filePart; - LastModifiedTime = Utility.DosDateToDateTime( - filePart.Header.LastModifiedDate, - filePart.Header.LastModifiedTime - ); - } + _filePart = filePart; + LastModifiedTime = Utility.DosDateToDateTime( + filePart.Header.LastModifiedDate, + filePart.Header.LastModifiedTime + ); } + } - public override CompressionType CompressionType + public override CompressionType CompressionType + { + get { - get + switch (_filePart.Header.CompressionMethod) { - switch (_filePart.Header.CompressionMethod) + case ZipCompressionMethod.BZip2: { - case ZipCompressionMethod.BZip2: - { - return CompressionType.BZip2; - } - case ZipCompressionMethod.Deflate: - { - return CompressionType.Deflate; - } - case ZipCompressionMethod.Deflate64: - { - return CompressionType.Deflate64; - } - case ZipCompressionMethod.LZMA: - { - return CompressionType.LZMA; - } - case ZipCompressionMethod.PPMd: - { - return CompressionType.PPMd; - } - case ZipCompressionMethod.None: - { - return CompressionType.None; - } - default: - { - return CompressionType.Unknown; - } + return CompressionType.BZip2; + } + case ZipCompressionMethod.Deflate: + { + return CompressionType.Deflate; + } + case ZipCompressionMethod.Deflate64: + { + return CompressionType.Deflate64; + } + case ZipCompressionMethod.LZMA: + { + return CompressionType.LZMA; + } + case ZipCompressionMethod.PPMd: + { + return CompressionType.PPMd; + } + case ZipCompressionMethod.None: + { + return CompressionType.None; + } + default: + { + return CompressionType.Unknown; } } } - - public override long Crc => _filePart.Header.Crc; - - public override string Key => _filePart.Header.Name; - - public override string LinkTarget => null; - - public override long CompressedSize => _filePart.Header.CompressedSize; - - public override long Size => _filePart.Header.UncompressedSize; - - public override DateTime? LastModifiedTime { get; } - - public override DateTime? CreatedTime => null; - - public override DateTime? LastAccessedTime => null; - - public override DateTime? ArchivedTime => null; - - public override bool IsEncrypted => - FlagUtility.HasFlag(_filePart.Header.Flags, HeaderFlags.Encrypted); - - public override bool IsDirectory => _filePart.Header.IsDirectory; - - public override bool IsSplitAfter => false; - - internal override IEnumerable Parts => _filePart.AsEnumerable(); } + + public override long Crc => _filePart.Header.Crc; + + public override string Key => _filePart.Header.Name; + + public override string LinkTarget => null; + + public override long CompressedSize => _filePart.Header.CompressedSize; + + public override long Size => _filePart.Header.UncompressedSize; + + public override DateTime? LastModifiedTime { get; } + + public override DateTime? CreatedTime => null; + + public override DateTime? LastAccessedTime => null; + + public override DateTime? ArchivedTime => null; + + public override bool IsEncrypted => + FlagUtility.HasFlag(_filePart.Header.Flags, HeaderFlags.Encrypted); + + public override bool IsDirectory => _filePart.Header.IsDirectory; + + public override bool IsSplitAfter => false; + + internal override IEnumerable Parts => _filePart.AsEnumerable(); } diff --git a/src/SharpCompress/Common/Zip/ZipFilePart.cs b/src/SharpCompress/Common/Zip/ZipFilePart.cs index c87ac3f1..54c64d16 100644 --- a/src/SharpCompress/Common/Zip/ZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/ZipFilePart.cs @@ -11,207 +11,206 @@ using SharpCompress.Compressors.LZMA; using SharpCompress.Compressors.PPMd; using SharpCompress.IO; -namespace SharpCompress.Common.Zip +namespace SharpCompress.Common.Zip; + +internal abstract class ZipFilePart : FilePart { - internal abstract class ZipFilePart : FilePart + internal ZipFilePart(ZipFileEntry header, Stream stream) : base(header.ArchiveEncoding) { - internal ZipFilePart(ZipFileEntry header, Stream stream) : base(header.ArchiveEncoding) + Header = header; + header.Part = this; + BaseStream = stream; + } + + internal Stream BaseStream { get; } + internal ZipFileEntry Header { get; set; } + + internal override string FilePartName => Header.Name; + + internal override Stream GetCompressedStream() + { + if (!Header.HasData) { - Header = header; - header.Part = this; - BaseStream = stream; + return Stream.Null; } - - internal Stream BaseStream { get; } - internal ZipFileEntry Header { get; set; } - - internal override string FilePartName => Header.Name; - - internal override Stream GetCompressedStream() + var decompressionStream = CreateDecompressionStream( + GetCryptoStream(CreateBaseStream()), + Header.CompressionMethod + ); + if (LeaveStreamOpen) { - if (!Header.HasData) - { - return Stream.Null; - } - Stream decompressionStream = CreateDecompressionStream( - GetCryptoStream(CreateBaseStream()), - Header.CompressionMethod - ); - if (LeaveStreamOpen) - { - return NonDisposingStream.Create(decompressionStream); - } - return decompressionStream; + return NonDisposingStream.Create(decompressionStream); } + return decompressionStream; + } - internal override Stream GetRawStream() + internal override Stream GetRawStream() + { + if (!Header.HasData) { - if (!Header.HasData) - { - return Stream.Null; - } - return CreateBaseStream(); + return Stream.Null; } + return CreateBaseStream(); + } - protected abstract Stream CreateBaseStream(); + protected abstract Stream CreateBaseStream(); - protected bool LeaveStreamOpen => - FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor) || Header.IsZip64; + protected bool LeaveStreamOpen => + FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor) || Header.IsZip64; - protected Stream CreateDecompressionStream(Stream stream, ZipCompressionMethod method) + protected Stream CreateDecompressionStream(Stream stream, ZipCompressionMethod method) + { + switch (method) { - switch (method) + case ZipCompressionMethod.None: { - case ZipCompressionMethod.None: - { - return stream; - } - case ZipCompressionMethod.Deflate: - { - return new DeflateStream(stream, CompressionMode.Decompress); - } - case ZipCompressionMethod.Deflate64: - { - return new Deflate64Stream(stream, CompressionMode.Decompress); - } - case ZipCompressionMethod.BZip2: - { - return new BZip2Stream(stream, CompressionMode.Decompress, false); - } - case ZipCompressionMethod.LZMA: - { - if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted)) - { - throw new NotSupportedException("LZMA with pkware encryption."); - } - var reader = new BinaryReader(stream); - reader.ReadUInt16(); //LZMA version - var props = new byte[reader.ReadUInt16()]; - reader.Read(props, 0, props.Length); - return new LzmaStream( - props, - stream, - Header.CompressedSize > 0 ? Header.CompressedSize - 4 - props.Length : -1, - FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1) - ? -1 - : (long)Header.UncompressedSize - ); - } - case ZipCompressionMethod.PPMd: - { - Span props = stackalloc byte[2]; - stream.ReadFully(props); - return new PpmdStream(new PpmdProperties(props), stream, false); - } - case ZipCompressionMethod.WinzipAes: - { - ExtraData? data = Header.Extra.SingleOrDefault( - x => x.Type == ExtraDataType.WinZipAes - ); - if (data is null) - { - throw new InvalidFormatException("No Winzip AES extra data found."); - } - if (data.Length != 7) - { - throw new InvalidFormatException("Winzip data length is not 7."); - } - ushort compressedMethod = BinaryPrimitives.ReadUInt16LittleEndian( - data.DataBytes - ); - - if (compressedMethod != 0x01 && compressedMethod != 0x02) - { - throw new InvalidFormatException( - "Unexpected vendor version number for WinZip AES metadata" - ); - } - - ushort vendorId = BinaryPrimitives.ReadUInt16LittleEndian( - data.DataBytes.AsSpan(2) - ); - if (vendorId != 0x4541) - { - throw new InvalidFormatException( - "Unexpected vendor ID for WinZip AES metadata" - ); - } - return CreateDecompressionStream( - stream, - (ZipCompressionMethod) - BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes.AsSpan(5)) - ); - } - default: - { - throw new NotSupportedException( - "CompressionMethod: " + Header.CompressionMethod - ); - } + return stream; } - } - - protected Stream GetCryptoStream(Stream plainStream) - { - bool isFileEncrypted = FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted); - - if (Header.CompressedSize == 0 && isFileEncrypted) + case ZipCompressionMethod.Deflate: { - throw new NotSupportedException("Cannot encrypt file with unknown size at start."); + return new DeflateStream(stream, CompressionMode.Decompress); } - - if ( - ( - Header.CompressedSize == 0 - && FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor) - ) || Header.IsZip64 - ) + case ZipCompressionMethod.Deflate64: { - plainStream = NonDisposingStream.Create(plainStream); //make sure AES doesn't close + return new Deflate64Stream(stream, CompressionMode.Decompress); } - else + case ZipCompressionMethod.BZip2: { - plainStream = new ReadOnlySubStream(plainStream, Header.CompressedSize); //make sure AES doesn't close + return new BZip2Stream(stream, CompressionMode.Decompress, false); } - - if (isFileEncrypted) + case ZipCompressionMethod.LZMA: { - switch (Header.CompressionMethod) + if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted)) { - case ZipCompressionMethod.None: - case ZipCompressionMethod.Deflate: - case ZipCompressionMethod.Deflate64: - case ZipCompressionMethod.BZip2: - case ZipCompressionMethod.LZMA: - case ZipCompressionMethod.PPMd: - { - return new PkwareTraditionalCryptoStream( - plainStream, - Header.ComposeEncryptionData(plainStream), - CryptoMode.Decrypt - ); - } - - case ZipCompressionMethod.WinzipAes: - { - if (Header.WinzipAesEncryptionData != null) - { - return new WinzipAesCryptoStream( - plainStream, - Header.WinzipAesEncryptionData, - Header.CompressedSize - 10 - ); - } - return plainStream; - } - - default: - { - throw new InvalidOperationException("Header.CompressionMethod is invalid"); - } + throw new NotSupportedException("LZMA with pkware encryption."); } + var reader = new BinaryReader(stream); + reader.ReadUInt16(); //LZMA version + var props = new byte[reader.ReadUInt16()]; + reader.Read(props, 0, props.Length); + return new LzmaStream( + props, + stream, + Header.CompressedSize > 0 ? Header.CompressedSize - 4 - props.Length : -1, + FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1) + ? -1 + : Header.UncompressedSize + ); + } + case ZipCompressionMethod.PPMd: + { + Span props = stackalloc byte[2]; + stream.ReadFully(props); + return new PpmdStream(new PpmdProperties(props), stream, false); + } + case ZipCompressionMethod.WinzipAes: + { + var data = Header.Extra.SingleOrDefault( + x => x.Type == ExtraDataType.WinZipAes + ); + if (data is null) + { + throw new InvalidFormatException("No Winzip AES extra data found."); + } + if (data.Length != 7) + { + throw new InvalidFormatException("Winzip data length is not 7."); + } + var compressedMethod = BinaryPrimitives.ReadUInt16LittleEndian( + data.DataBytes + ); + + if (compressedMethod != 0x01 && compressedMethod != 0x02) + { + throw new InvalidFormatException( + "Unexpected vendor version number for WinZip AES metadata" + ); + } + + var vendorId = BinaryPrimitives.ReadUInt16LittleEndian( + data.DataBytes.AsSpan(2) + ); + if (vendorId != 0x4541) + { + throw new InvalidFormatException( + "Unexpected vendor ID for WinZip AES metadata" + ); + } + return CreateDecompressionStream( + stream, + (ZipCompressionMethod) + BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes.AsSpan(5)) + ); + } + default: + { + throw new NotSupportedException( + "CompressionMethod: " + Header.CompressionMethod + ); } - return plainStream; } } + + protected Stream GetCryptoStream(Stream plainStream) + { + var isFileEncrypted = FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted); + + if (Header.CompressedSize == 0 && isFileEncrypted) + { + throw new NotSupportedException("Cannot encrypt file with unknown size at start."); + } + + if ( + ( + Header.CompressedSize == 0 + && FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor) + ) || Header.IsZip64 + ) + { + plainStream = NonDisposingStream.Create(plainStream); //make sure AES doesn't close + } + else + { + plainStream = new ReadOnlySubStream(plainStream, Header.CompressedSize); //make sure AES doesn't close + } + + if (isFileEncrypted) + { + switch (Header.CompressionMethod) + { + case ZipCompressionMethod.None: + case ZipCompressionMethod.Deflate: + case ZipCompressionMethod.Deflate64: + case ZipCompressionMethod.BZip2: + case ZipCompressionMethod.LZMA: + case ZipCompressionMethod.PPMd: + { + return new PkwareTraditionalCryptoStream( + plainStream, + Header.ComposeEncryptionData(plainStream), + CryptoMode.Decrypt + ); + } + + case ZipCompressionMethod.WinzipAes: + { + if (Header.WinzipAesEncryptionData != null) + { + return new WinzipAesCryptoStream( + plainStream, + Header.WinzipAesEncryptionData, + Header.CompressedSize - 10 + ); + } + return plainStream; + } + + default: + { + throw new InvalidOperationException("Header.CompressionMethod is invalid"); + } + } + } + return plainStream; + } } diff --git a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs index bc490c7e..e7737cec 100644 --- a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs @@ -1,208 +1,207 @@ -using System; +using System; using System.IO; using System.Linq; using SharpCompress.Common.Zip.Headers; using SharpCompress.IO; -namespace SharpCompress.Common.Zip +namespace SharpCompress.Common.Zip; + +internal class ZipHeaderFactory { - internal class ZipHeaderFactory + internal const uint ENTRY_HEADER_BYTES = 0x04034b50; + internal const uint POST_DATA_DESCRIPTOR = 0x08074b50; + internal const uint DIRECTORY_START_HEADER_BYTES = 0x02014b50; + internal const uint DIRECTORY_END_HEADER_BYTES = 0x06054b50; + internal const uint DIGITAL_SIGNATURE = 0x05054b50; + internal const uint SPLIT_ARCHIVE_HEADER_BYTES = 0x30304b50; + + internal const uint ZIP64_END_OF_CENTRAL_DIRECTORY = 0x06064b50; + internal const uint ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR = 0x07064b50; + + protected LocalEntryHeader? _lastEntryHeader; + private readonly string? _password; + private readonly StreamingMode _mode; + private readonly ArchiveEncoding _archiveEncoding; + + protected ZipHeaderFactory( + StreamingMode mode, + string? password, + ArchiveEncoding archiveEncoding + ) { - internal const uint ENTRY_HEADER_BYTES = 0x04034b50; - internal const uint POST_DATA_DESCRIPTOR = 0x08074b50; - internal const uint DIRECTORY_START_HEADER_BYTES = 0x02014b50; - internal const uint DIRECTORY_END_HEADER_BYTES = 0x06054b50; - internal const uint DIGITAL_SIGNATURE = 0x05054b50; - internal const uint SPLIT_ARCHIVE_HEADER_BYTES = 0x30304b50; + _mode = mode; + _password = password; + _archiveEncoding = archiveEncoding; + } - internal const uint ZIP64_END_OF_CENTRAL_DIRECTORY = 0x06064b50; - internal const uint ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR = 0x07064b50; - - protected LocalEntryHeader? _lastEntryHeader; - private readonly string? _password; - private readonly StreamingMode _mode; - private readonly ArchiveEncoding _archiveEncoding; - - protected ZipHeaderFactory( - StreamingMode mode, - string? password, - ArchiveEncoding archiveEncoding - ) + protected ZipHeader? ReadHeader(uint headerBytes, BinaryReader reader, bool zip64 = false) + { + switch (headerBytes) { - this._mode = mode; - this._password = password; - this._archiveEncoding = archiveEncoding; - } - - protected ZipHeader? ReadHeader(uint headerBytes, BinaryReader reader, bool zip64 = false) - { - switch (headerBytes) + case ENTRY_HEADER_BYTES: { - case ENTRY_HEADER_BYTES: - { - var entryHeader = new LocalEntryHeader(_archiveEncoding); - entryHeader.Read(reader); - LoadHeader(entryHeader, reader.BaseStream); + var entryHeader = new LocalEntryHeader(_archiveEncoding); + entryHeader.Read(reader); + LoadHeader(entryHeader, reader.BaseStream); - _lastEntryHeader = entryHeader; - return entryHeader; - } - case DIRECTORY_START_HEADER_BYTES: - { - var entry = new DirectoryEntryHeader(_archiveEncoding); - entry.Read(reader); - return entry; - } - case POST_DATA_DESCRIPTOR: - { - if ( - FlagUtility.HasFlag( - _lastEntryHeader!.Flags, - HeaderFlags.UsePostDataDescriptor - ) - ) - { - _lastEntryHeader.Crc = reader.ReadUInt32(); - _lastEntryHeader.CompressedSize = zip64 - ? (long)reader.ReadUInt64() - : reader.ReadUInt32(); - _lastEntryHeader.UncompressedSize = zip64 - ? (long)reader.ReadUInt64() - : reader.ReadUInt32(); - } - else - { - reader.ReadBytes(zip64 ? 20 : 12); - } - return null; - } - case DIGITAL_SIGNATURE: - return null; - case DIRECTORY_END_HEADER_BYTES: - { - var entry = new DirectoryEndHeader(); - entry.Read(reader); - return entry; - } - case SPLIT_ARCHIVE_HEADER_BYTES: - { - return new SplitHeader(); - } - case ZIP64_END_OF_CENTRAL_DIRECTORY: - { - var entry = new Zip64DirectoryEndHeader(); - entry.Read(reader); - return entry; - } - case ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR: - { - var entry = new Zip64DirectoryEndLocatorHeader(); - entry.Read(reader); - return entry; - } - default: - return null; + _lastEntryHeader = entryHeader; + return entryHeader; } - } - - internal static bool IsHeader(uint headerBytes) - { - switch (headerBytes) + case DIRECTORY_START_HEADER_BYTES: { - case ENTRY_HEADER_BYTES: - case DIRECTORY_START_HEADER_BYTES: - case POST_DATA_DESCRIPTOR: - case DIGITAL_SIGNATURE: - case DIRECTORY_END_HEADER_BYTES: - case SPLIT_ARCHIVE_HEADER_BYTES: - case ZIP64_END_OF_CENTRAL_DIRECTORY: - case ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR: - return true; - default: - return false; + var entry = new DirectoryEntryHeader(_archiveEncoding); + entry.Read(reader); + return entry; } - } - - private void LoadHeader(ZipFileEntry entryHeader, Stream stream) - { - if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.Encrypted)) + case POST_DATA_DESCRIPTOR: { if ( - !entryHeader.IsDirectory - && entryHeader.CompressedSize == 0 - && FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.UsePostDataDescriptor) + FlagUtility.HasFlag( + _lastEntryHeader!.Flags, + HeaderFlags.UsePostDataDescriptor + ) ) { - throw new NotSupportedException( - "SharpCompress cannot currently read non-seekable Zip Streams with encrypted data that has been written in a non-seekable manner." - ); + _lastEntryHeader.Crc = reader.ReadUInt32(); + _lastEntryHeader.CompressedSize = zip64 + ? (long)reader.ReadUInt64() + : reader.ReadUInt32(); + _lastEntryHeader.UncompressedSize = zip64 + ? (long)reader.ReadUInt64() + : reader.ReadUInt32(); } - - if (_password is null) + else { - throw new CryptographicException("No password supplied for encrypted zip."); - } - - entryHeader.Password = _password; - - if (entryHeader.CompressionMethod == ZipCompressionMethod.WinzipAes) - { - ExtraData? data = entryHeader.Extra.SingleOrDefault( - x => x.Type == ExtraDataType.WinZipAes - ); - if (data != null) - { - var keySize = (WinzipAesKeySize)data.DataBytes[4]; - - var salt = new byte[WinzipAesEncryptionData.KeyLengthInBytes(keySize) / 2]; - var passwordVerifyValue = new byte[2]; - stream.Read(salt, 0, salt.Length); - stream.Read(passwordVerifyValue, 0, 2); - entryHeader.WinzipAesEncryptionData = new WinzipAesEncryptionData( - keySize, - salt, - passwordVerifyValue, - _password - ); - - entryHeader.CompressedSize -= (uint)(salt.Length + 2); - } + reader.ReadBytes(zip64 ? 20 : 12); } + return null; } - - if (entryHeader.IsDirectory) + case DIGITAL_SIGNATURE: + return null; + case DIRECTORY_END_HEADER_BYTES: { - return; + var entry = new DirectoryEndHeader(); + entry.Read(reader); + return entry; } - - //if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.UsePostDataDescriptor)) - //{ - // entryHeader.PackedStream = new ReadOnlySubStream(stream); - //} - //else - //{ - switch (_mode) + case SPLIT_ARCHIVE_HEADER_BYTES: { - case StreamingMode.Seekable: - { - entryHeader.DataStartPosition = stream.Position; - stream.Position += entryHeader.CompressedSize; - break; - } - - case StreamingMode.Streaming: - { - entryHeader.PackedStream = stream; - break; - } - - default: - { - throw new InvalidFormatException("Invalid StreamingMode"); - } + return new SplitHeader(); } - - //} + case ZIP64_END_OF_CENTRAL_DIRECTORY: + { + var entry = new Zip64DirectoryEndHeader(); + entry.Read(reader); + return entry; + } + case ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR: + { + var entry = new Zip64DirectoryEndLocatorHeader(); + entry.Read(reader); + return entry; + } + default: + return null; } } + + internal static bool IsHeader(uint headerBytes) + { + switch (headerBytes) + { + case ENTRY_HEADER_BYTES: + case DIRECTORY_START_HEADER_BYTES: + case POST_DATA_DESCRIPTOR: + case DIGITAL_SIGNATURE: + case DIRECTORY_END_HEADER_BYTES: + case SPLIT_ARCHIVE_HEADER_BYTES: + case ZIP64_END_OF_CENTRAL_DIRECTORY: + case ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR: + return true; + default: + return false; + } + } + + private void LoadHeader(ZipFileEntry entryHeader, Stream stream) + { + if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.Encrypted)) + { + if ( + !entryHeader.IsDirectory + && entryHeader.CompressedSize == 0 + && FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.UsePostDataDescriptor) + ) + { + throw new NotSupportedException( + "SharpCompress cannot currently read non-seekable Zip Streams with encrypted data that has been written in a non-seekable manner." + ); + } + + if (_password is null) + { + throw new CryptographicException("No password supplied for encrypted zip."); + } + + entryHeader.Password = _password; + + if (entryHeader.CompressionMethod == ZipCompressionMethod.WinzipAes) + { + var data = entryHeader.Extra.SingleOrDefault( + x => x.Type == ExtraDataType.WinZipAes + ); + if (data != null) + { + var keySize = (WinzipAesKeySize)data.DataBytes[4]; + + var salt = new byte[WinzipAesEncryptionData.KeyLengthInBytes(keySize) / 2]; + var passwordVerifyValue = new byte[2]; + stream.Read(salt, 0, salt.Length); + stream.Read(passwordVerifyValue, 0, 2); + entryHeader.WinzipAesEncryptionData = new WinzipAesEncryptionData( + keySize, + salt, + passwordVerifyValue, + _password + ); + + entryHeader.CompressedSize -= (uint)(salt.Length + 2); + } + } + } + + if (entryHeader.IsDirectory) + { + return; + } + + //if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.UsePostDataDescriptor)) + //{ + // entryHeader.PackedStream = new ReadOnlySubStream(stream); + //} + //else + //{ + switch (_mode) + { + case StreamingMode.Seekable: + { + entryHeader.DataStartPosition = stream.Position; + stream.Position += entryHeader.CompressedSize; + break; + } + + case StreamingMode.Streaming: + { + entryHeader.PackedStream = stream; + break; + } + + default: + { + throw new InvalidFormatException("Invalid StreamingMode"); + } + } + + //} + } } diff --git a/src/SharpCompress/Common/Zip/ZipVolume.cs b/src/SharpCompress/Common/Zip/ZipVolume.cs index f8843305..26d471fe 100644 --- a/src/SharpCompress/Common/Zip/ZipVolume.cs +++ b/src/SharpCompress/Common/Zip/ZipVolume.cs @@ -1,13 +1,12 @@ using System.IO; using SharpCompress.Readers; -namespace SharpCompress.Common.Zip -{ - public class ZipVolume : Volume - { - public ZipVolume(Stream stream, ReaderOptions readerOptions, int index = 0) - : base(stream, readerOptions, index) { } +namespace SharpCompress.Common.Zip; - public string? Comment { get; internal set; } - } +public class ZipVolume : Volume +{ + public ZipVolume(Stream stream, ReaderOptions readerOptions, int index = 0) + : base(stream, readerOptions, index) { } + + public string? Comment { get; internal set; } } diff --git a/src/SharpCompress/Compressors/ADC/ADCBase.cs b/src/SharpCompress/Compressors/ADC/ADCBase.cs index f8241b2f..0ee2f1bb 100644 --- a/src/SharpCompress/Compressors/ADC/ADCBase.cs +++ b/src/SharpCompress/Compressors/ADC/ADCBase.cs @@ -1,4 +1,4 @@ -// +// // ADC.cs // // Author: @@ -26,185 +26,184 @@ using System; using System.IO; -namespace SharpCompress.Compressors.ADC +namespace SharpCompress.Compressors.ADC; + +/// +/// Provides static methods for decompressing Apple Data Compression data +/// +public static class ADCBase { - /// - /// Provides static methods for decompressing Apple Data Compression data - /// - public static class ADCBase + private const int PLAIN = 1; + private const int TWO_BYTE = 2; + private const int THREE_BYTE = 3; + + private static int GetChunkType(byte byt) { - private const int PLAIN = 1; - private const int TWO_BYTE = 2; - private const int THREE_BYTE = 3; - - private static int GetChunkType(byte byt) + if ((byt & 0x80) == 0x80) { - if ((byt & 0x80) == 0x80) - { - return PLAIN; - } - if ((byt & 0x40) == 0x40) - { - return THREE_BYTE; - } - return TWO_BYTE; + return PLAIN; + } + if ((byt & 0x40) == 0x40) + { + return THREE_BYTE; + } + return TWO_BYTE; + } + + private static int GetChunkSize(byte byt) + { + return GetChunkType(byt) switch + { + PLAIN => (byt & 0x7F) + 1, + TWO_BYTE => ((byt & 0x3F) >> 2) + 3, + THREE_BYTE => (byt & 0x3F) + 4, + _ => -1, + }; + } + + private static int GetOffset(ReadOnlySpan chunk) + { + return GetChunkType(chunk[0]) switch + { + PLAIN => 0, + TWO_BYTE => ((chunk[0] & 0x03) << 8) + chunk[1], + THREE_BYTE => (chunk[1] << 8) + chunk[2], + _ => -1, + }; + } + + /// + /// Decompresses a byte buffer that's compressed with ADC + /// + /// Compressed buffer + /// Buffer to hold decompressed data + /// Max size for decompressed data + /// How many bytes are stored on + public static int Decompress(byte[] input, out byte[]? output, int bufferSize = 262144) + { + return Decompress(new MemoryStream(input), out output, bufferSize); + } + + /// + /// Decompresses a stream that's compressed with ADC + /// + /// Stream containing compressed data + /// Buffer to hold decompressed data + /// Max size for decompressed data + /// How many bytes are stored on + public static int Decompress(Stream input, out byte[]? output, int bufferSize = 262144) + { + output = null; + + if (input is null || input.Length == 0) + { + return 0; } - private static int GetChunkSize(byte byt) + var start = (int)input.Position; + var position = (int)input.Position; + int chunkSize; + int offset; + int chunkType; + var buffer = new byte[bufferSize]; + var outPosition = 0; + var full = false; + Span temp = stackalloc byte[3]; + + while (position < input.Length) { - return GetChunkType(byt) switch + var readByte = input.ReadByte(); + if (readByte == -1) { - PLAIN => (byt & 0x7F) + 1, - TWO_BYTE => ((byt & 0x3F) >> 2) + 3, - THREE_BYTE => (byt & 0x3F) + 4, - _ => -1, - }; - } - - private static int GetOffset(ReadOnlySpan chunk) - { - return GetChunkType(chunk[0]) switch - { - PLAIN => 0, - TWO_BYTE => ((chunk[0] & 0x03) << 8) + chunk[1], - THREE_BYTE => (chunk[1] << 8) + chunk[2], - _ => -1, - }; - } - - /// - /// Decompresses a byte buffer that's compressed with ADC - /// - /// Compressed buffer - /// Buffer to hold decompressed data - /// Max size for decompressed data - /// How many bytes are stored on - public static int Decompress(byte[] input, out byte[]? output, int bufferSize = 262144) - { - return Decompress(new MemoryStream(input), out output, bufferSize); - } - - /// - /// Decompresses a stream that's compressed with ADC - /// - /// Stream containing compressed data - /// Buffer to hold decompressed data - /// Max size for decompressed data - /// How many bytes are stored on - public static int Decompress(Stream input, out byte[]? output, int bufferSize = 262144) - { - output = null; - - if (input is null || input.Length == 0) - { - return 0; + break; } - int start = (int)input.Position; - int position = (int)input.Position; - int chunkSize; - int offset; - int chunkType; - byte[] buffer = new byte[bufferSize]; - int outPosition = 0; - bool full = false; - Span temp = stackalloc byte[3]; + chunkType = GetChunkType((byte)readByte); - while (position < input.Length) + switch (chunkType) { - int readByte = input.ReadByte(); - if (readByte == -1) - { + case PLAIN: + chunkSize = GetChunkSize((byte)readByte); + if (outPosition + chunkSize > bufferSize) + { + full = true; + break; + } + input.Read(buffer, outPosition, chunkSize); + outPosition += chunkSize; + position += chunkSize + 1; break; - } - - chunkType = GetChunkType((byte)readByte); - - switch (chunkType) - { - case PLAIN: - chunkSize = GetChunkSize((byte)readByte); - if (outPosition + chunkSize > bufferSize) - { - full = true; - break; - } - input.Read(buffer, outPosition, chunkSize); - outPosition += chunkSize; - position += chunkSize + 1; + case TWO_BYTE: + chunkSize = GetChunkSize((byte)readByte); + temp[0] = (byte)readByte; + temp[1] = (byte)input.ReadByte(); + offset = GetOffset(temp); + if (outPosition + chunkSize > bufferSize) + { + full = true; break; - case TWO_BYTE: - chunkSize = GetChunkSize((byte)readByte); - temp[0] = (byte)readByte; - temp[1] = (byte)input.ReadByte(); - offset = GetOffset(temp); - if (outPosition + chunkSize > bufferSize) + } + if (offset == 0) + { + var lastByte = buffer[outPosition - 1]; + for (var i = 0; i < chunkSize; i++) { - full = true; - break; + buffer[outPosition] = lastByte; + outPosition++; } - if (offset == 0) + position += 2; + } + else + { + for (var i = 0; i < chunkSize; i++) { - byte lastByte = buffer[outPosition - 1]; - for (int i = 0; i < chunkSize; i++) - { - buffer[outPosition] = lastByte; - outPosition++; - } - position += 2; + buffer[outPosition] = buffer[outPosition - offset - 1]; + outPosition++; } - else - { - for (int i = 0; i < chunkSize; i++) - { - buffer[outPosition] = buffer[outPosition - offset - 1]; - outPosition++; - } - position += 2; - } - break; - case THREE_BYTE: - chunkSize = GetChunkSize((byte)readByte); - temp[0] = (byte)readByte; - temp[1] = (byte)input.ReadByte(); - temp[2] = (byte)input.ReadByte(); - offset = GetOffset(temp); - if (outPosition + chunkSize > bufferSize) - { - full = true; - break; - } - if (offset == 0) - { - byte lastByte = buffer[outPosition - 1]; - for (int i = 0; i < chunkSize; i++) - { - buffer[outPosition] = lastByte; - outPosition++; - } - position += 3; - } - else - { - for (int i = 0; i < chunkSize; i++) - { - buffer[outPosition] = buffer[outPosition - offset - 1]; - outPosition++; - } - position += 3; - } - break; - } - - if (full) - { + position += 2; + } + break; + case THREE_BYTE: + chunkSize = GetChunkSize((byte)readByte); + temp[0] = (byte)readByte; + temp[1] = (byte)input.ReadByte(); + temp[2] = (byte)input.ReadByte(); + offset = GetOffset(temp); + if (outPosition + chunkSize > bufferSize) + { + full = true; + break; + } + if (offset == 0) + { + var lastByte = buffer[outPosition - 1]; + for (var i = 0; i < chunkSize; i++) + { + buffer[outPosition] = lastByte; + outPosition++; + } + position += 3; + } + else + { + for (var i = 0; i < chunkSize; i++) + { + buffer[outPosition] = buffer[outPosition - offset - 1]; + outPosition++; + } + position += 3; + } break; - } } - output = new byte[outPosition]; - Array.Copy(buffer, output, outPosition); - return position - start; + if (full) + { + break; + } } + + output = new byte[outPosition]; + Array.Copy(buffer, output, outPosition); + return position - start; } } diff --git a/src/SharpCompress/Compressors/ADC/ADCStream.cs b/src/SharpCompress/Compressors/ADC/ADCStream.cs index 02337d8f..9d57d1d7 100644 --- a/src/SharpCompress/Compressors/ADC/ADCStream.cs +++ b/src/SharpCompress/Compressors/ADC/ADCStream.cs @@ -1,4 +1,4 @@ -// +// // ADC.cs // // Author: @@ -29,153 +29,152 @@ using System; using System.IO; -namespace SharpCompress.Compressors.ADC +namespace SharpCompress.Compressors.ADC; + +/// +/// Provides a forward readable only stream that decompresses ADC data +/// +public sealed class ADCStream : Stream { /// - /// Provides a forward readable only stream that decompresses ADC data + /// This stream holds the compressed data /// - public sealed class ADCStream : Stream + private readonly Stream _stream; + + /// + /// Is this instance disposed? + /// + private bool _isDisposed; + + /// + /// Position in decompressed data + /// + private long _position; + + /// + /// Buffer with currently used chunk of decompressed data + /// + private byte[] _outBuffer; + + /// + /// Position in buffer of decompressed data + /// + private int _outPosition; + + /// + /// Initializates a stream that decompresses ADC data on the fly + /// + /// Stream that contains the compressed data + /// Must be set to because compression is not implemented + public ADCStream( + Stream stream, + CompressionMode compressionMode = CompressionMode.Decompress + ) { - /// - /// This stream holds the compressed data - /// - private readonly Stream _stream; - - /// - /// Is this instance disposed? - /// - private bool _isDisposed; - - /// - /// Position in decompressed data - /// - private long _position; - - /// - /// Buffer with currently used chunk of decompressed data - /// - private byte[] _outBuffer; - - /// - /// Position in buffer of decompressed data - /// - private int _outPosition; - - /// - /// Initializates a stream that decompresses ADC data on the fly - /// - /// Stream that contains the compressed data - /// Must be set to because compression is not implemented - public ADCStream( - Stream stream, - CompressionMode compressionMode = CompressionMode.Decompress - ) - { - if (compressionMode == CompressionMode.Compress) - { - throw new NotSupportedException(); - } - - _stream = stream; - } - - public override bool CanRead => _stream.CanRead; - - public override bool CanSeek => false; - - public override bool CanWrite => false; - - public override long Length => throw new NotSupportedException(); - - public override long Position - { - get => _position; - set => throw new NotSupportedException(); - } - - public override void Flush() { } - - protected override void Dispose(bool disposing) - { - if (_isDisposed) - { - return; - } - _isDisposed = true; - base.Dispose(disposing); - } - - public override int Read(byte[] buffer, int offset, int count) - { - if (count == 0) - { - return 0; - } - if (buffer is null) - { - throw new ArgumentNullException(nameof(buffer)); - } - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - if (offset < buffer.GetLowerBound(0)) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - if ((offset + count) > buffer.GetLength(0)) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - int size = -1; - - if (_outBuffer is null) - { - size = ADCBase.Decompress(_stream, out _outBuffer); - _outPosition = 0; - } - - int inPosition = offset; - int toCopy = count; - int copied = 0; - - while (_outPosition + toCopy >= _outBuffer.Length) - { - int piece = _outBuffer.Length - _outPosition; - Array.Copy(_outBuffer, _outPosition, buffer, inPosition, piece); - inPosition += piece; - copied += piece; - _position += piece; - toCopy -= piece; - size = ADCBase.Decompress(_stream, out _outBuffer); - _outPosition = 0; - if (size == 0 || _outBuffer is null || _outBuffer.Length == 0) - { - return copied; - } - } - - Array.Copy(_outBuffer, _outPosition, buffer, inPosition, toCopy); - _outPosition += toCopy; - _position += toCopy; - copied += toCopy; - return copied; - } - - public override long Seek(long offset, SeekOrigin origin) + if (compressionMode == CompressionMode.Compress) { throw new NotSupportedException(); } - public override void SetLength(long value) + _stream = stream; + } + + public override bool CanRead => _stream.CanRead; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => _position; + set => throw new NotSupportedException(); + } + + public override void Flush() { } + + protected override void Dispose(bool disposing) + { + if (_isDisposed) { - throw new NotSupportedException(); + return; + } + _isDisposed = true; + base.Dispose(disposing); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count == 0) + { + return 0; + } + if (buffer is null) + { + throw new ArgumentNullException(nameof(buffer)); + } + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + if (offset < buffer.GetLowerBound(0)) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + if ((offset + count) > buffer.GetLength(0)) + { + throw new ArgumentOutOfRangeException(nameof(count)); } - public override void Write(byte[] buffer, int offset, int count) + var size = -1; + + if (_outBuffer is null) { - throw new NotSupportedException(); + size = ADCBase.Decompress(_stream, out _outBuffer); + _outPosition = 0; } + + var inPosition = offset; + var toCopy = count; + var copied = 0; + + while (_outPosition + toCopy >= _outBuffer.Length) + { + var piece = _outBuffer.Length - _outPosition; + Array.Copy(_outBuffer, _outPosition, buffer, inPosition, piece); + inPosition += piece; + copied += piece; + _position += piece; + toCopy -= piece; + size = ADCBase.Decompress(_stream, out _outBuffer); + _outPosition = 0; + if (size == 0 || _outBuffer is null || _outBuffer.Length == 0) + { + return copied; + } + } + + Array.Copy(_outBuffer, _outPosition, buffer, inPosition, toCopy); + _outPosition += toCopy; + _position += toCopy; + copied += toCopy; + return copied; + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); } } diff --git a/src/SharpCompress/Compressors/BZip2/BZip2Constants.cs b/src/SharpCompress/Compressors/BZip2/BZip2Constants.cs index 6644c278..cfeb02e4 100644 --- a/src/SharpCompress/Compressors/BZip2/BZip2Constants.cs +++ b/src/SharpCompress/Compressors/BZip2/BZip2Constants.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright 2001,2004-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,542 +20,541 @@ * great code. */ -namespace SharpCompress.Compressors.BZip2 +namespace SharpCompress.Compressors.BZip2; + +/** +* Base class for both the compress and decompress classes. +* Holds common arrays, and static data. +* +* @author Keiron Liddle +*/ + +internal class BZip2Constants { - /** - * Base class for both the compress and decompress classes. - * Holds common arrays, and static data. - * - * @author Keiron Liddle - */ + public const int baseBlockSize = 100000; + public const int MAX_ALPHA_SIZE = 258; + public const int MAX_CODE_LEN = 23; + public const int RUNA = 0; + public const int RUNB = 1; + public const int N_GROUPS = 6; + public const int G_SIZE = 50; + public const int N_ITERS = 4; + public const int MAX_SELECTORS = (2 + (900000 / G_SIZE)); + public const int NUM_OVERSHOOT_BYTES = 20; - internal class BZip2Constants + public static int[] rNums = { - public const int baseBlockSize = 100000; - public const int MAX_ALPHA_SIZE = 258; - public const int MAX_CODE_LEN = 23; - public const int RUNA = 0; - public const int RUNB = 1; - public const int N_GROUPS = 6; - public const int G_SIZE = 50; - public const int N_ITERS = 4; - public const int MAX_SELECTORS = (2 + (900000 / G_SIZE)); - public const int NUM_OVERSHOOT_BYTES = 20; - - public static int[] rNums = - { - 619, - 720, - 127, - 481, - 931, - 816, - 813, - 233, - 566, - 247, - 985, - 724, - 205, - 454, - 863, - 491, - 741, - 242, - 949, - 214, - 733, - 859, - 335, - 708, - 621, - 574, - 73, - 654, - 730, - 472, - 419, - 436, - 278, - 496, - 867, - 210, - 399, - 680, - 480, - 51, - 878, - 465, - 811, - 169, - 869, - 675, - 611, - 697, - 867, - 561, - 862, - 687, - 507, - 283, - 482, - 129, - 807, - 591, - 733, - 623, - 150, - 238, - 59, - 379, - 684, - 877, - 625, - 169, - 643, - 105, - 170, - 607, - 520, - 932, - 727, - 476, - 693, - 425, - 174, - 647, - 73, - 122, - 335, - 530, - 442, - 853, - 695, - 249, - 445, - 515, - 909, - 545, - 703, - 919, - 874, - 474, - 882, - 500, - 594, - 612, - 641, - 801, - 220, - 162, - 819, - 984, - 589, - 513, - 495, - 799, - 161, - 604, - 958, - 533, - 221, - 400, - 386, - 867, - 600, - 782, - 382, - 596, - 414, - 171, - 516, - 375, - 682, - 485, - 911, - 276, - 98, - 553, - 163, - 354, - 666, - 933, - 424, - 341, - 533, - 870, - 227, - 730, - 475, - 186, - 263, - 647, - 537, - 686, - 600, - 224, - 469, - 68, - 770, - 919, - 190, - 373, - 294, - 822, - 808, - 206, - 184, - 943, - 795, - 384, - 383, - 461, - 404, - 758, - 839, - 887, - 715, - 67, - 618, - 276, - 204, - 918, - 873, - 777, - 604, - 560, - 951, - 160, - 578, - 722, - 79, - 804, - 96, - 409, - 713, - 940, - 652, - 934, - 970, - 447, - 318, - 353, - 859, - 672, - 112, - 785, - 645, - 863, - 803, - 350, - 139, - 93, - 354, - 99, - 820, - 908, - 609, - 772, - 154, - 274, - 580, - 184, - 79, - 626, - 630, - 742, - 653, - 282, - 762, - 623, - 680, - 81, - 927, - 626, - 789, - 125, - 411, - 521, - 938, - 300, - 821, - 78, - 343, - 175, - 128, - 250, - 170, - 774, - 972, - 275, - 999, - 639, - 495, - 78, - 352, - 126, - 857, - 956, - 358, - 619, - 580, - 124, - 737, - 594, - 701, - 612, - 669, - 112, - 134, - 694, - 363, - 992, - 809, - 743, - 168, - 974, - 944, - 375, - 748, - 52, - 600, - 747, - 642, - 182, - 862, - 81, - 344, - 805, - 988, - 739, - 511, - 655, - 814, - 334, - 249, - 515, - 897, - 955, - 664, - 981, - 649, - 113, - 974, - 459, - 893, - 228, - 433, - 837, - 553, - 268, - 926, - 240, - 102, - 654, - 459, - 51, - 686, - 754, - 806, - 760, - 493, - 403, - 415, - 394, - 687, - 700, - 946, - 670, - 656, - 610, - 738, - 392, - 760, - 799, - 887, - 653, - 978, - 321, - 576, - 617, - 626, - 502, - 894, - 679, - 243, - 440, - 680, - 879, - 194, - 572, - 640, - 724, - 926, - 56, - 204, - 700, - 707, - 151, - 457, - 449, - 797, - 195, - 791, - 558, - 945, - 679, - 297, - 59, - 87, - 824, - 713, - 663, - 412, - 693, - 342, - 606, - 134, - 108, - 571, - 364, - 631, - 212, - 174, - 643, - 304, - 329, - 343, - 97, - 430, - 751, - 497, - 314, - 983, - 374, - 822, - 928, - 140, - 206, - 73, - 263, - 980, - 736, - 876, - 478, - 430, - 305, - 170, - 514, - 364, - 692, - 829, - 82, - 855, - 953, - 676, - 246, - 369, - 970, - 294, - 750, - 807, - 827, - 150, - 790, - 288, - 923, - 804, - 378, - 215, - 828, - 592, - 281, - 565, - 555, - 710, - 82, - 896, - 831, - 547, - 261, - 524, - 462, - 293, - 465, - 502, - 56, - 661, - 821, - 976, - 991, - 658, - 869, - 905, - 758, - 745, - 193, - 768, - 550, - 608, - 933, - 378, - 286, - 215, - 979, - 792, - 961, - 61, - 688, - 793, - 644, - 986, - 403, - 106, - 366, - 905, - 644, - 372, - 567, - 466, - 434, - 645, - 210, - 389, - 550, - 919, - 135, - 780, - 773, - 635, - 389, - 707, - 100, - 626, - 958, - 165, - 504, - 920, - 176, - 193, - 713, - 857, - 265, - 203, - 50, - 668, - 108, - 645, - 990, - 626, - 197, - 510, - 357, - 358, - 850, - 858, - 364, - 936, - 638 - }; - } + 619, + 720, + 127, + 481, + 931, + 816, + 813, + 233, + 566, + 247, + 985, + 724, + 205, + 454, + 863, + 491, + 741, + 242, + 949, + 214, + 733, + 859, + 335, + 708, + 621, + 574, + 73, + 654, + 730, + 472, + 419, + 436, + 278, + 496, + 867, + 210, + 399, + 680, + 480, + 51, + 878, + 465, + 811, + 169, + 869, + 675, + 611, + 697, + 867, + 561, + 862, + 687, + 507, + 283, + 482, + 129, + 807, + 591, + 733, + 623, + 150, + 238, + 59, + 379, + 684, + 877, + 625, + 169, + 643, + 105, + 170, + 607, + 520, + 932, + 727, + 476, + 693, + 425, + 174, + 647, + 73, + 122, + 335, + 530, + 442, + 853, + 695, + 249, + 445, + 515, + 909, + 545, + 703, + 919, + 874, + 474, + 882, + 500, + 594, + 612, + 641, + 801, + 220, + 162, + 819, + 984, + 589, + 513, + 495, + 799, + 161, + 604, + 958, + 533, + 221, + 400, + 386, + 867, + 600, + 782, + 382, + 596, + 414, + 171, + 516, + 375, + 682, + 485, + 911, + 276, + 98, + 553, + 163, + 354, + 666, + 933, + 424, + 341, + 533, + 870, + 227, + 730, + 475, + 186, + 263, + 647, + 537, + 686, + 600, + 224, + 469, + 68, + 770, + 919, + 190, + 373, + 294, + 822, + 808, + 206, + 184, + 943, + 795, + 384, + 383, + 461, + 404, + 758, + 839, + 887, + 715, + 67, + 618, + 276, + 204, + 918, + 873, + 777, + 604, + 560, + 951, + 160, + 578, + 722, + 79, + 804, + 96, + 409, + 713, + 940, + 652, + 934, + 970, + 447, + 318, + 353, + 859, + 672, + 112, + 785, + 645, + 863, + 803, + 350, + 139, + 93, + 354, + 99, + 820, + 908, + 609, + 772, + 154, + 274, + 580, + 184, + 79, + 626, + 630, + 742, + 653, + 282, + 762, + 623, + 680, + 81, + 927, + 626, + 789, + 125, + 411, + 521, + 938, + 300, + 821, + 78, + 343, + 175, + 128, + 250, + 170, + 774, + 972, + 275, + 999, + 639, + 495, + 78, + 352, + 126, + 857, + 956, + 358, + 619, + 580, + 124, + 737, + 594, + 701, + 612, + 669, + 112, + 134, + 694, + 363, + 992, + 809, + 743, + 168, + 974, + 944, + 375, + 748, + 52, + 600, + 747, + 642, + 182, + 862, + 81, + 344, + 805, + 988, + 739, + 511, + 655, + 814, + 334, + 249, + 515, + 897, + 955, + 664, + 981, + 649, + 113, + 974, + 459, + 893, + 228, + 433, + 837, + 553, + 268, + 926, + 240, + 102, + 654, + 459, + 51, + 686, + 754, + 806, + 760, + 493, + 403, + 415, + 394, + 687, + 700, + 946, + 670, + 656, + 610, + 738, + 392, + 760, + 799, + 887, + 653, + 978, + 321, + 576, + 617, + 626, + 502, + 894, + 679, + 243, + 440, + 680, + 879, + 194, + 572, + 640, + 724, + 926, + 56, + 204, + 700, + 707, + 151, + 457, + 449, + 797, + 195, + 791, + 558, + 945, + 679, + 297, + 59, + 87, + 824, + 713, + 663, + 412, + 693, + 342, + 606, + 134, + 108, + 571, + 364, + 631, + 212, + 174, + 643, + 304, + 329, + 343, + 97, + 430, + 751, + 497, + 314, + 983, + 374, + 822, + 928, + 140, + 206, + 73, + 263, + 980, + 736, + 876, + 478, + 430, + 305, + 170, + 514, + 364, + 692, + 829, + 82, + 855, + 953, + 676, + 246, + 369, + 970, + 294, + 750, + 807, + 827, + 150, + 790, + 288, + 923, + 804, + 378, + 215, + 828, + 592, + 281, + 565, + 555, + 710, + 82, + 896, + 831, + 547, + 261, + 524, + 462, + 293, + 465, + 502, + 56, + 661, + 821, + 976, + 991, + 658, + 869, + 905, + 758, + 745, + 193, + 768, + 550, + 608, + 933, + 378, + 286, + 215, + 979, + 792, + 961, + 61, + 688, + 793, + 644, + 986, + 403, + 106, + 366, + 905, + 644, + 372, + 567, + 466, + 434, + 645, + 210, + 389, + 550, + 919, + 135, + 780, + 773, + 635, + 389, + 707, + 100, + 626, + 958, + 165, + 504, + 920, + 176, + 193, + 713, + 857, + 265, + 203, + 50, + 668, + 108, + 645, + 990, + 626, + 197, + 510, + 357, + 358, + 850, + 858, + 364, + 936, + 638 + }; } diff --git a/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs b/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs index ea54e7e9..1c7aaeb4 100644 --- a/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs +++ b/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs @@ -1,132 +1,131 @@ -using System; +using System; using System.IO; -namespace SharpCompress.Compressors.BZip2 +namespace SharpCompress.Compressors.BZip2; + +public sealed class BZip2Stream : Stream { - public sealed class BZip2Stream : Stream + private readonly Stream stream; + private bool isDisposed; + + /// + /// Create a BZip2Stream + /// + /// The stream to read from + /// Compression Mode + /// Decompress Concatenated + public BZip2Stream( + Stream stream, + CompressionMode compressionMode, + bool decompressConcatenated + ) { - private readonly Stream stream; - private bool isDisposed; - - /// - /// Create a BZip2Stream - /// - /// The stream to read from - /// Compression Mode - /// Decompress Concatenated - public BZip2Stream( - Stream stream, - CompressionMode compressionMode, - bool decompressConcatenated - ) + Mode = compressionMode; + if (Mode == CompressionMode.Compress) { - Mode = compressionMode; - if (Mode == CompressionMode.Compress) - { - this.stream = new CBZip2OutputStream(stream); - } - else - { - this.stream = new CBZip2InputStream(stream, decompressConcatenated); - } + this.stream = new CBZip2OutputStream(stream); } - - public void Finish() + else { - (stream as CBZip2OutputStream)?.Finish(); + this.stream = new CBZip2InputStream(stream, decompressConcatenated); } + } - protected override void Dispose(bool disposing) + public void Finish() + { + (stream as CBZip2OutputStream)?.Finish(); + } + + protected override void Dispose(bool disposing) + { + if (isDisposed) { - if (isDisposed) - { - return; - } - isDisposed = true; - if (disposing) - { - stream.Dispose(); - } + return; } - - public CompressionMode Mode { get; } - - public override bool CanRead => stream.CanRead; - - public override bool CanSeek => stream.CanSeek; - - public override bool CanWrite => stream.CanWrite; - - public override void Flush() + isDisposed = true; + if (disposing) { - stream.Flush(); + stream.Dispose(); } + } - public override long Length => stream.Length; + public CompressionMode Mode { get; } - public override long Position - { - get => stream.Position; - set => stream.Position = value; - } + public override bool CanRead => stream.CanRead; - public override int Read(byte[] buffer, int offset, int count) - { - return stream.Read(buffer, offset, count); - } + public override bool CanSeek => stream.CanSeek; - public override int ReadByte() - { - return stream.ReadByte(); - } + public override bool CanWrite => stream.CanWrite; - public override long Seek(long offset, SeekOrigin origin) - { - return stream.Seek(offset, origin); - } + public override void Flush() + { + stream.Flush(); + } - public override void SetLength(long value) - { - stream.SetLength(value); - } + public override long Length => stream.Length; + + public override long Position + { + get => stream.Position; + set => stream.Position = value; + } + + public override int Read(byte[] buffer, int offset, int count) + { + return stream.Read(buffer, offset, count); + } + + public override int ReadByte() + { + return stream.ReadByte(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return stream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + stream.SetLength(value); + } #if !NETFRAMEWORK && !NETSTANDARD2_0 - public override int Read(Span buffer) - { - return stream.Read(buffer); - } + public override int Read(Span buffer) + { + return stream.Read(buffer); + } - public override void Write(ReadOnlySpan buffer) - { - stream.Write(buffer); - } + public override void Write(ReadOnlySpan buffer) + { + stream.Write(buffer); + } #endif - public override void Write(byte[] buffer, int offset, int count) - { - stream.Write(buffer, offset, count); - } + public override void Write(byte[] buffer, int offset, int count) + { + stream.Write(buffer, offset, count); + } - public override void WriteByte(byte value) - { - stream.WriteByte(value); - } + public override void WriteByte(byte value) + { + stream.WriteByte(value); + } - /// - /// Consumes two bytes to test if there is a BZip2 header - /// - /// - /// - public static bool IsBZip2(Stream stream) + /// + /// Consumes two bytes to test if there is a BZip2 header + /// + /// + /// + public static bool IsBZip2(Stream stream) + { + var br = new BinaryReader(stream); + var chars = br.ReadBytes(2); + if (chars.Length < 2 || chars[0] != 'B' || chars[1] != 'Z') { - BinaryReader br = new BinaryReader(stream); - byte[] chars = br.ReadBytes(2); - if (chars.Length < 2 || chars[0] != 'B' || chars[1] != 'Z') - { - return false; - } - return true; + return false; } + return true; } } diff --git a/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs b/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs index 1b77ed84..5f58b363 100644 --- a/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs +++ b/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable using System; using System.IO; @@ -25,835 +25,733 @@ using System.IO; * great code. */ -namespace SharpCompress.Compressors.BZip2 +namespace SharpCompress.Compressors.BZip2; + +/** + * An input stream that decompresses from the BZip2 format (with the file + * header chars) to be read as any other stream. + * + * @author Keiron Liddle + * + * NB: note this class has been modified to read the leading BZ from the + * start of the BZIP2 stream to make it compatible with other PGP programs. + */ + +internal class CBZip2InputStream : Stream { - /** - * An input stream that decompresses from the BZip2 format (with the file - * header chars) to be read as any other stream. - * - * @author Keiron Liddle - * - * NB: note this class has been modified to read the leading BZ from the - * start of the BZIP2 stream to make it compatible with other PGP programs. - */ - - internal class CBZip2InputStream : Stream + private static void Cadvise() { - private static void Cadvise() - { - //System.out.Println("CRC Error"); - //throw new CCoruptionError(); - } + //System.out.Println("CRC Error"); + //throw new CCoruptionError(); + } - private static void BadBGLengths() - { - Cadvise(); - } + private static void BadBGLengths() + { + Cadvise(); + } - private static void BitStreamEOF() - { - Cadvise(); - } + private static void BitStreamEOF() + { + Cadvise(); + } - private static void CompressedStreamEOF() - { - Cadvise(); - } + private static void CompressedStreamEOF() + { + Cadvise(); + } - private void MakeMaps() + private void MakeMaps() + { + int i; + nInUse = 0; + for (i = 0; i < 256; i++) { - int i; - nInUse = 0; - for (i = 0; i < 256; i++) + if (inUse[i]) { - if (inUse[i]) - { - seqToUnseq[nInUse] = (char)i; - unseqToSeq[i] = (char)nInUse; - nInUse++; - } + seqToUnseq[nInUse] = (char)i; + unseqToSeq[i] = (char)nInUse; + nInUse++; } } + } - /* - index of the last char in the block, so - the block size == last + 1. - */ - private int last; + /* + index of the last char in the block, so + the block size == last + 1. + */ + private int last; - /* - index in zptr[] of original string after sorting. - */ - private int origPtr; + /* + index in zptr[] of original string after sorting. + */ + private int origPtr; - /* - always: in the range 0 .. 9. - The current block size is 100000 * this number. - */ - private int blockSize100k; + /* + always: in the range 0 .. 9. + The current block size is 100000 * this number. + */ + private int blockSize100k; - private bool blockRandomised; + private bool blockRandomised; - private int bsBuff; - private int bsLive; - private readonly CRC mCrc = new CRC(); + private int bsBuff; + private int bsLive; + private readonly CRC mCrc = new CRC(); - private readonly bool[] inUse = new bool[256]; - private int nInUse; + private readonly bool[] inUse = new bool[256]; + private int nInUse; - private readonly char[] seqToUnseq = new char[256]; - private readonly char[] unseqToSeq = new char[256]; + private readonly char[] seqToUnseq = new char[256]; + private readonly char[] unseqToSeq = new char[256]; - private readonly char[] selector = new char[BZip2Constants.MAX_SELECTORS]; - private readonly char[] selectorMtf = new char[BZip2Constants.MAX_SELECTORS]; + private readonly char[] selector = new char[BZip2Constants.MAX_SELECTORS]; + private readonly char[] selectorMtf = new char[BZip2Constants.MAX_SELECTORS]; - private int[] tt; - private char[] ll8; + private int[] tt; + private char[] ll8; - /* - freq table collected to save a pass over the data - during decompression. - */ - private readonly int[] unzftab = new int[256]; + /* + freq table collected to save a pass over the data + during decompression. + */ + private readonly int[] unzftab = new int[256]; - private readonly int[][] limit = InitIntArray( - BZip2Constants.N_GROUPS, - BZip2Constants.MAX_ALPHA_SIZE - ); - private readonly int[][] basev = InitIntArray( - BZip2Constants.N_GROUPS, - BZip2Constants.MAX_ALPHA_SIZE - ); - private readonly int[][] perm = InitIntArray( - BZip2Constants.N_GROUPS, - BZip2Constants.MAX_ALPHA_SIZE - ); - private readonly int[] minLens = new int[BZip2Constants.N_GROUPS]; + private readonly int[][] limit = InitIntArray( + BZip2Constants.N_GROUPS, + BZip2Constants.MAX_ALPHA_SIZE + ); + private readonly int[][] basev = InitIntArray( + BZip2Constants.N_GROUPS, + BZip2Constants.MAX_ALPHA_SIZE + ); + private readonly int[][] perm = InitIntArray( + BZip2Constants.N_GROUPS, + BZip2Constants.MAX_ALPHA_SIZE + ); + private readonly int[] minLens = new int[BZip2Constants.N_GROUPS]; - private Stream bsStream; + private Stream bsStream; - private bool streamEnd; + private bool streamEnd; - private int currentChar = -1; + private int currentChar = -1; - private const int START_BLOCK_STATE = 1; - private const int RAND_PART_A_STATE = 2; - private const int RAND_PART_B_STATE = 3; - private const int RAND_PART_C_STATE = 4; - private const int NO_RAND_PART_A_STATE = 5; - private const int NO_RAND_PART_B_STATE = 6; - private const int NO_RAND_PART_C_STATE = 7; + private const int START_BLOCK_STATE = 1; + private const int RAND_PART_A_STATE = 2; + private const int RAND_PART_B_STATE = 3; + private const int RAND_PART_C_STATE = 4; + private const int NO_RAND_PART_A_STATE = 5; + private const int NO_RAND_PART_B_STATE = 6; + private const int NO_RAND_PART_C_STATE = 7; - private int currentState = START_BLOCK_STATE; + private int currentState = START_BLOCK_STATE; - private int storedBlockCRC, - storedCombinedCRC; - private int computedBlockCRC, - computedCombinedCRC; - private readonly bool decompressConcatenated; + private int storedBlockCRC, + storedCombinedCRC; + private int computedBlockCRC, + computedCombinedCRC; + private readonly bool decompressConcatenated; - private int i2, - count, - chPrev, - ch2; - private int i, - tPos; - private int rNToGo; - private int rTPos; - private int j2; - private char z; - private bool isDisposed; + private int i2, + count, + chPrev, + ch2; + private int i, + tPos; + private int rNToGo; + private int rTPos; + private int j2; + private char z; + private bool isDisposed; - public CBZip2InputStream(Stream zStream, bool decompressConcatenated) + public CBZip2InputStream(Stream zStream, bool decompressConcatenated) + { + this.decompressConcatenated = decompressConcatenated; + ll8 = null; + tt = null; + BsSetStream(zStream); + Initialize(true); + InitBlock(); + SetupBlock(); + } + + protected override void Dispose(bool disposing) + { + if (isDisposed) { - this.decompressConcatenated = decompressConcatenated; - ll8 = null; - tt = null; - BsSetStream(zStream); - Initialize(true); - InitBlock(); - SetupBlock(); + return; + } + isDisposed = true; + base.Dispose(disposing); + bsStream?.Dispose(); + } + + internal static int[][] InitIntArray(int n1, int n2) + { + var a = new int[n1][]; + for (var k = 0; k < n1; ++k) + { + a[k] = new int[n2]; + } + return a; + } + + internal static char[][] InitCharArray(int n1, int n2) + { + var a = new char[n1][]; + for (var k = 0; k < n1; ++k) + { + a[k] = new char[n2]; + } + return a; + } + + public override int ReadByte() + { + if (streamEnd) + { + return -1; + } + var retChar = currentChar; + switch (currentState) + { + case START_BLOCK_STATE: + break; + case RAND_PART_A_STATE: + break; + case RAND_PART_B_STATE: + SetupRandPartB(); + break; + case RAND_PART_C_STATE: + SetupRandPartC(); + break; + case NO_RAND_PART_A_STATE: + break; + case NO_RAND_PART_B_STATE: + SetupNoRandPartB(); + break; + case NO_RAND_PART_C_STATE: + SetupNoRandPartC(); + break; + default: + break; + } + return retChar; + } + + private bool Initialize(bool isFirstStream) + { + var magic0 = bsStream.ReadByte(); + var magic1 = bsStream.ReadByte(); + var magic2 = bsStream.ReadByte(); + if (magic0 == -1 && !isFirstStream) + { + return false; + } + if (magic0 != 'B' || magic1 != 'Z' || magic2 != 'h') + { + throw new IOException("Not a BZIP2 marked stream"); + } + var magic3 = bsStream.ReadByte(); + if (magic3 < '1' || magic3 > '9') + { + BsFinishedWithStream(); + streamEnd = true; + return false; } - protected override void Dispose(bool disposing) + SetDecompressStructureSizes(magic3 - '0'); + bsLive = 0; + computedCombinedCRC = 0; + return true; + } + + private void InitBlock() + { + char magic1, + magic2, + magic3, + magic4; + char magic5, + magic6; + + while (true) { - if (isDisposed) - { - return; - } - isDisposed = true; - base.Dispose(disposing); - if (bsStream != null) - { - bsStream.Dispose(); - } - } - - internal static int[][] InitIntArray(int n1, int n2) - { - int[][] a = new int[n1][]; - for (int k = 0; k < n1; ++k) - { - a[k] = new int[n2]; - } - return a; - } - - internal static char[][] InitCharArray(int n1, int n2) - { - char[][] a = new char[n1][]; - for (int k = 0; k < n1; ++k) - { - a[k] = new char[n2]; - } - return a; - } - - public override int ReadByte() - { - if (streamEnd) - { - return -1; - } - int retChar = currentChar; - switch (currentState) - { - case START_BLOCK_STATE: - break; - case RAND_PART_A_STATE: - break; - case RAND_PART_B_STATE: - SetupRandPartB(); - break; - case RAND_PART_C_STATE: - SetupRandPartC(); - break; - case NO_RAND_PART_A_STATE: - break; - case NO_RAND_PART_B_STATE: - SetupNoRandPartB(); - break; - case NO_RAND_PART_C_STATE: - SetupNoRandPartC(); - break; - default: - break; - } - return retChar; - } - - private bool Initialize(bool isFirstStream) - { - int magic0 = bsStream.ReadByte(); - int magic1 = bsStream.ReadByte(); - int magic2 = bsStream.ReadByte(); - if (magic0 == -1 && !isFirstStream) - { - return false; - } - if (magic0 != 'B' || magic1 != 'Z' || magic2 != 'h') - { - throw new IOException("Not a BZIP2 marked stream"); - } - int magic3 = bsStream.ReadByte(); - if (magic3 < '1' || magic3 > '9') - { - BsFinishedWithStream(); - streamEnd = true; - return false; - } - - SetDecompressStructureSizes(magic3 - '0'); - bsLive = 0; - computedCombinedCRC = 0; - return true; - } - - private void InitBlock() - { - char magic1, - magic2, - magic3, - magic4; - char magic5, - magic6; - - while (true) - { - magic1 = BsGetUChar(); - magic2 = BsGetUChar(); - magic3 = BsGetUChar(); - magic4 = BsGetUChar(); - magic5 = BsGetUChar(); - magic6 = BsGetUChar(); - if ( - magic1 != 0x17 - || magic2 != 0x72 - || magic3 != 0x45 - || magic4 != 0x38 - || magic5 != 0x50 - || magic6 != 0x90 - ) - { - break; - } - - if (Complete()) - { - return; - } - } - + magic1 = BsGetUChar(); + magic2 = BsGetUChar(); + magic3 = BsGetUChar(); + magic4 = BsGetUChar(); + magic5 = BsGetUChar(); + magic6 = BsGetUChar(); if ( - magic1 != 0x31 - || magic2 != 0x41 - || magic3 != 0x59 - || magic4 != 0x26 - || magic5 != 0x53 - || magic6 != 0x59 + magic1 != 0x17 + || magic2 != 0x72 + || magic3 != 0x45 + || magic4 != 0x38 + || magic5 != 0x50 + || magic6 != 0x90 ) { - BadBlockHeader(); - streamEnd = true; - return; + break; } - storedBlockCRC = BsGetInt32(); + if (Complete()) + { + return; + } + } + if ( + magic1 != 0x31 + || magic2 != 0x41 + || magic3 != 0x59 + || magic4 != 0x26 + || magic5 != 0x53 + || magic6 != 0x59 + ) + { + BadBlockHeader(); + streamEnd = true; + return; + } + + storedBlockCRC = BsGetInt32(); + + if (BsR(1) == 1) + { + blockRandomised = true; + } + else + { + blockRandomised = false; + } + + // currBlockNo++; + GetAndMoveToFrontDecode(); + + mCrc.InitialiseCRC(); + currentState = START_BLOCK_STATE; + } + + private void EndBlock() + { + computedBlockCRC = mCrc.GetFinalCRC(); + /* A bad CRC is considered a fatal error. */ + if (storedBlockCRC != computedBlockCRC) + { + CrcError(); + } + + computedCombinedCRC = + (computedCombinedCRC << 1) | (int)(((uint)computedCombinedCRC) >> 31); + computedCombinedCRC ^= computedBlockCRC; + } + + private bool Complete() + { + storedCombinedCRC = BsGetInt32(); + if (storedCombinedCRC != computedCombinedCRC) + { + CrcError(); + } + + var complete = !decompressConcatenated || !Initialize(false); + if (complete) + { + BsFinishedWithStream(); + streamEnd = true; + } + + // Look for the next .bz2 stream if decompressing + // concatenated files. + return complete; + } + + private static void BlockOverrun() + { + Cadvise(); + } + + private static void BadBlockHeader() + { + Cadvise(); + } + + private static void CrcError() + { + Cadvise(); + } + + private void BsFinishedWithStream() + { + bsStream?.Dispose(); + bsStream = null; + } + + private void BsSetStream(Stream f) + { + bsStream = f; + bsLive = 0; + bsBuff = 0; + } + + private int BsR(int n) + { + int v; + while (bsLive < n) + { + int zzi; + int thech = '\0'; + try + { + thech = (char)bsStream.ReadByte(); + } + catch (IOException) + { + CompressedStreamEOF(); + } + if (thech == '\uffff') + { + CompressedStreamEOF(); + } + zzi = thech; + bsBuff = (bsBuff << 8) | (zzi & 0xff); + bsLive += 8; + } + + v = (bsBuff >> (bsLive - n)) & ((1 << n) - 1); + bsLive -= n; + return v; + } + + private char BsGetUChar() + { + return (char)BsR(8); + } + + private int BsGetint() + { + var u = 0; + u = (u << 8) | BsR(8); + u = (u << 8) | BsR(8); + u = (u << 8) | BsR(8); + u = (u << 8) | BsR(8); + return u; + } + + private int BsGetIntVS(int numBits) + { + return BsR(numBits); + } + + private int BsGetInt32() + { + return BsGetint(); + } + + private void HbCreateDecodeTables( + int[] limit, + int[] basev, + int[] perm, + char[] length, + int minLen, + int maxLen, + int alphaSize + ) + { + int pp, + i, + j, + vec; + + pp = 0; + for (i = minLen; i <= maxLen; i++) + { + for (j = 0; j < alphaSize; j++) + { + if (length[j] == i) + { + perm[pp] = j; + pp++; + } + } + } + + for (i = 0; i < BZip2Constants.MAX_CODE_LEN; i++) + { + basev[i] = 0; + } + for (i = 0; i < alphaSize; i++) + { + basev[length[i] + 1]++; + } + + for (i = 1; i < BZip2Constants.MAX_CODE_LEN; i++) + { + basev[i] += basev[i - 1]; + } + + for (i = 0; i < BZip2Constants.MAX_CODE_LEN; i++) + { + limit[i] = 0; + } + vec = 0; + + for (i = minLen; i <= maxLen; i++) + { + vec += (basev[i + 1] - basev[i]); + limit[i] = vec - 1; + vec <<= 1; + } + for (i = minLen + 1; i <= maxLen; i++) + { + basev[i] = ((limit[i - 1] + 1) << 1) - basev[i]; + } + } + + private void RecvDecodingTables() + { + var len = InitCharArray(BZip2Constants.N_GROUPS, BZip2Constants.MAX_ALPHA_SIZE); + int i, + j, + t, + nGroups, + nSelectors, + alphaSize; + int minLen, + maxLen; + var inUse16 = new bool[16]; + + /* Receive the mapping table */ + for (i = 0; i < 16; i++) + { if (BsR(1) == 1) { - blockRandomised = true; + inUse16[i] = true; } else { - blockRandomised = false; + inUse16[i] = false; } - - // currBlockNo++; - GetAndMoveToFrontDecode(); - - mCrc.InitialiseCRC(); - currentState = START_BLOCK_STATE; } - private void EndBlock() + for (i = 0; i < 256; i++) { - computedBlockCRC = mCrc.GetFinalCRC(); - /* A bad CRC is considered a fatal error. */ - if (storedBlockCRC != computedBlockCRC) + inUse[i] = false; + } + + for (i = 0; i < 16; i++) + { + if (inUse16[i]) { - CrcError(); - } - - computedCombinedCRC = - (computedCombinedCRC << 1) | (int)(((uint)computedCombinedCRC) >> 31); - computedCombinedCRC ^= computedBlockCRC; - } - - private bool Complete() - { - storedCombinedCRC = BsGetInt32(); - if (storedCombinedCRC != computedCombinedCRC) - { - CrcError(); - } - - bool complete = !decompressConcatenated || !Initialize(false); - if (complete) - { - BsFinishedWithStream(); - streamEnd = true; - } - - // Look for the next .bz2 stream if decompressing - // concatenated files. - return complete; - } - - private static void BlockOverrun() - { - Cadvise(); - } - - private static void BadBlockHeader() - { - Cadvise(); - } - - private static void CrcError() - { - Cadvise(); - } - - private void BsFinishedWithStream() - { - bsStream?.Dispose(); - bsStream = null; - } - - private void BsSetStream(Stream f) - { - bsStream = f; - bsLive = 0; - bsBuff = 0; - } - - private int BsR(int n) - { - int v; - while (bsLive < n) - { - int zzi; - int thech = '\0'; - try + for (j = 0; j < 16; j++) { - thech = (char)bsStream.ReadByte(); - } - catch (IOException) - { - CompressedStreamEOF(); - } - if (thech == '\uffff') - { - CompressedStreamEOF(); - } - zzi = thech; - bsBuff = (bsBuff << 8) | (zzi & 0xff); - bsLive += 8; - } - - v = (bsBuff >> (bsLive - n)) & ((1 << n) - 1); - bsLive -= n; - return v; - } - - private char BsGetUChar() - { - return (char)BsR(8); - } - - private int BsGetint() - { - int u = 0; - u = (u << 8) | BsR(8); - u = (u << 8) | BsR(8); - u = (u << 8) | BsR(8); - u = (u << 8) | BsR(8); - return u; - } - - private int BsGetIntVS(int numBits) - { - return BsR(numBits); - } - - private int BsGetInt32() - { - return BsGetint(); - } - - private void HbCreateDecodeTables( - int[] limit, - int[] basev, - int[] perm, - char[] length, - int minLen, - int maxLen, - int alphaSize - ) - { - int pp, - i, - j, - vec; - - pp = 0; - for (i = minLen; i <= maxLen; i++) - { - for (j = 0; j < alphaSize; j++) - { - if (length[j] == i) + if (BsR(1) == 1) { - perm[pp] = j; - pp++; + inUse[(i * 16) + j] = true; } } } - - for (i = 0; i < BZip2Constants.MAX_CODE_LEN; i++) - { - basev[i] = 0; - } - for (i = 0; i < alphaSize; i++) - { - basev[length[i] + 1]++; - } - - for (i = 1; i < BZip2Constants.MAX_CODE_LEN; i++) - { - basev[i] += basev[i - 1]; - } - - for (i = 0; i < BZip2Constants.MAX_CODE_LEN; i++) - { - limit[i] = 0; - } - vec = 0; - - for (i = minLen; i <= maxLen; i++) - { - vec += (basev[i + 1] - basev[i]); - limit[i] = vec - 1; - vec <<= 1; - } - for (i = minLen + 1; i <= maxLen; i++) - { - basev[i] = ((limit[i - 1] + 1) << 1) - basev[i]; - } } - private void RecvDecodingTables() + MakeMaps(); + alphaSize = nInUse + 2; + + /* Now the selectors */ + nGroups = BsR(3); + nSelectors = BsR(15); + for (i = 0; i < nSelectors; i++) { - char[][] len = InitCharArray(BZip2Constants.N_GROUPS, BZip2Constants.MAX_ALPHA_SIZE); - int i, - j, - t, - nGroups, - nSelectors, - alphaSize; - int minLen, - maxLen; - bool[] inUse16 = new bool[16]; - - /* Receive the mapping table */ - for (i = 0; i < 16; i++) + j = 0; + while (BsR(1) == 1) { - if (BsR(1) == 1) - { - inUse16[i] = true; - } - else - { - inUse16[i] = false; - } + j++; + } + selectorMtf[i] = (char)j; + } + + /* Undo the MTF values for the selectors. */ + { + var pos = new char[BZip2Constants.N_GROUPS]; + char tmp, + v; + for (v = '\0'; v < nGroups; v++) + { + pos[v] = v; } - for (i = 0; i < 256; i++) - { - inUse[i] = false; - } - - for (i = 0; i < 16; i++) - { - if (inUse16[i]) - { - for (j = 0; j < 16; j++) - { - if (BsR(1) == 1) - { - inUse[i * 16 + j] = true; - } - } - } - } - - MakeMaps(); - alphaSize = nInUse + 2; - - /* Now the selectors */ - nGroups = BsR(3); - nSelectors = BsR(15); for (i = 0; i < nSelectors; i++) { - j = 0; - while (BsR(1) == 1) + v = selectorMtf[i]; + tmp = pos[v]; + while (v > 0) { - j++; + pos[v] = pos[v - 1]; + v--; } - selectorMtf[i] = (char)j; - } - - /* Undo the MTF values for the selectors. */ - { - char[] pos = new char[BZip2Constants.N_GROUPS]; - char tmp, - v; - for (v = '\0'; v < nGroups; v++) - { - pos[v] = v; - } - - for (i = 0; i < nSelectors; i++) - { - v = selectorMtf[i]; - tmp = pos[v]; - while (v > 0) - { - pos[v] = pos[v - 1]; - v--; - } - pos[0] = tmp; - selector[i] = tmp; - } - } - - /* Now the coding tables */ - for (t = 0; t < nGroups; t++) - { - int curr = BsR(5); - for (i = 0; i < alphaSize; i++) - { - while (BsR(1) == 1) - { - if (BsR(1) == 0) - { - curr++; - } - else - { - curr--; - } - } - len[t][i] = (char)curr; - } - } - - /* Create the Huffman decoding tables */ - for (t = 0; t < nGroups; t++) - { - minLen = 32; - maxLen = 0; - for (i = 0; i < alphaSize; i++) - { - if (len[t][i] > maxLen) - { - maxLen = len[t][i]; - } - if (len[t][i] < minLen) - { - minLen = len[t][i]; - } - } - HbCreateDecodeTables( - limit[t], - basev[t], - perm[t], - len[t], - minLen, - maxLen, - alphaSize - ); - minLens[t] = minLen; + pos[0] = tmp; + selector[i] = tmp; } } - private void GetAndMoveToFrontDecode() + /* Now the coding tables */ + for (t = 0; t < nGroups; t++) { - char[] yy = new char[256]; - int i, - j, - nextSym, - limitLast; - int EOB, - groupNo, - groupPos; - - limitLast = BZip2Constants.baseBlockSize * blockSize100k; - origPtr = BsGetIntVS(24); - - RecvDecodingTables(); - EOB = nInUse + 1; - groupNo = -1; - groupPos = 0; - - /* - Setting up the unzftab entries here is not strictly - necessary, but it does save having to do it later - in a separate pass, and so saves a block's worth of - cache misses. - */ - for (i = 0; i <= 255; i++) + var curr = BsR(5); + for (i = 0; i < alphaSize; i++) { - unzftab[i] = 0; + while (BsR(1) == 1) + { + if (BsR(1) == 0) + { + curr++; + } + else + { + curr--; + } + } + len[t][i] = (char)curr; + } + } + + /* Create the Huffman decoding tables */ + for (t = 0; t < nGroups; t++) + { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) + { + if (len[t][i] > maxLen) + { + maxLen = len[t][i]; + } + if (len[t][i] < minLen) + { + minLen = len[t][i]; + } + } + HbCreateDecodeTables( + limit[t], + basev[t], + perm[t], + len[t], + minLen, + maxLen, + alphaSize + ); + minLens[t] = minLen; + } + } + + private void GetAndMoveToFrontDecode() + { + var yy = new char[256]; + int i, + j, + nextSym, + limitLast; + int EOB, + groupNo, + groupPos; + + limitLast = BZip2Constants.baseBlockSize * blockSize100k; + origPtr = BsGetIntVS(24); + + RecvDecodingTables(); + EOB = nInUse + 1; + groupNo = -1; + groupPos = 0; + + /* + Setting up the unzftab entries here is not strictly + necessary, but it does save having to do it later + in a separate pass, and so saves a block's worth of + cache misses. + */ + for (i = 0; i <= 255; i++) + { + unzftab[i] = 0; + } + + for (i = 0; i <= 255; i++) + { + yy[i] = (char)i; + } + + last = -1; + + { + int zt, + zn, + zvec, + zj; + if (groupPos == 0) + { + groupNo++; + groupPos = BZip2Constants.G_SIZE; + } + groupPos--; + zt = selector[groupNo]; + zn = minLens[zt]; + zvec = BsR(zn); + while (zvec > limit[zt][zn]) + { + zn++; + { + { + while (bsLive < 1) + { + int zzi; + var thech = '\0'; + try + { + thech = (char)bsStream.ReadByte(); + } + catch (IOException) + { + CompressedStreamEOF(); + } + if (thech == '\uffff') + { + CompressedStreamEOF(); + } + zzi = thech; + bsBuff = (bsBuff << 8) | (zzi & 0xff); + bsLive += 8; + } + } + zj = (bsBuff >> (bsLive - 1)) & 1; + bsLive--; + } + zvec = (zvec << 1) | zj; + } + nextSym = perm[zt][zvec - basev[zt][zn]]; + } + + while (true) + { + if (nextSym == EOB) + { + break; } - for (i = 0; i <= 255; i++) + if (nextSym == BZip2Constants.RUNA || nextSym == BZip2Constants.RUNB) { - yy[i] = (char)i; - } - - last = -1; - - { - int zt, - zn, - zvec, - zj; - if (groupPos == 0) + char ch; + var s = -1; + var N = 1; + do { - groupNo++; - groupPos = BZip2Constants.G_SIZE; - } - groupPos--; - zt = selector[groupNo]; - zn = minLens[zt]; - zvec = BsR(zn); - while (zvec > limit[zt][zn]) - { - zn++; + if (nextSym == BZip2Constants.RUNA) { - { - while (bsLive < 1) - { - int zzi; - char thech = '\0'; - try - { - thech = (char)bsStream.ReadByte(); - } - catch (IOException) - { - CompressedStreamEOF(); - } - if (thech == '\uffff') - { - CompressedStreamEOF(); - } - zzi = thech; - bsBuff = (bsBuff << 8) | (zzi & 0xff); - bsLive += 8; - } - } - zj = (bsBuff >> (bsLive - 1)) & 1; - bsLive--; + s += (0 + 1) * N; } - zvec = (zvec << 1) | zj; - } - nextSym = perm[zt][zvec - basev[zt][zn]]; - } - - while (true) - { - if (nextSym == EOB) - { - break; - } - - if (nextSym == BZip2Constants.RUNA || nextSym == BZip2Constants.RUNB) - { - char ch; - int s = -1; - int N = 1; - do + else if (nextSym == BZip2Constants.RUNB) { - if (nextSym == BZip2Constants.RUNA) - { - s = s + (0 + 1) * N; - } - else if (nextSym == BZip2Constants.RUNB) - { - s = s + (1 + 1) * N; - } - N = N * 2; - { - int zt, - zn, - zvec, - zj; - if (groupPos == 0) - { - groupNo++; - groupPos = BZip2Constants.G_SIZE; - } - groupPos--; - zt = selector[groupNo]; - zn = minLens[zt]; - zvec = BsR(zn); - while (zvec > limit[zt][zn]) - { - zn++; - { - { - while (bsLive < 1) - { - int zzi; - char thech = '\0'; - try - { - thech = (char)bsStream.ReadByte(); - } - catch (IOException) - { - CompressedStreamEOF(); - } - if (thech == '\uffff') - { - CompressedStreamEOF(); - } - zzi = thech; - bsBuff = (bsBuff << 8) | (zzi & 0xff); - bsLive += 8; - } - } - zj = (bsBuff >> (bsLive - 1)) & 1; - bsLive--; - } - zvec = (zvec << 1) | zj; - } - nextSym = perm[zt][zvec - basev[zt][zn]]; - } - } while (nextSym == BZip2Constants.RUNA || nextSym == BZip2Constants.RUNB); - - s++; - ch = seqToUnseq[yy[0]]; - unzftab[ch] += s; - - while (s > 0) - { - last++; - ll8[last] = ch; - s--; + s += (1 + 1) * N; } - - if (last >= limitLast) - { - BlockOverrun(); - } - } - else - { - char tmp; - last++; - if (last >= limitLast) - { - BlockOverrun(); - } - - tmp = yy[nextSym - 1]; - unzftab[seqToUnseq[tmp]]++; - ll8[last] = seqToUnseq[tmp]; - - /* - This loop is hammered during decompression, - hence the unrolling. - - for (j = nextSym-1; j > 0; j--) yy[j] = yy[j-1]; - */ - - j = nextSym - 1; - for (; j > 3; j -= 4) - { - yy[j] = yy[j - 1]; - yy[j - 1] = yy[j - 2]; - yy[j - 2] = yy[j - 3]; - yy[j - 3] = yy[j - 4]; - } - for (; j > 0; j--) - { - yy[j] = yy[j - 1]; - } - - yy[0] = tmp; + N *= 2; { int zt, zn, @@ -876,7 +774,7 @@ namespace SharpCompress.Compressors.BZip2 while (bsLive < 1) { int zzi; - char thech = '\0'; + var thech = '\0'; try { thech = (char)bsStream.ReadByte(); @@ -885,6 +783,10 @@ namespace SharpCompress.Compressors.BZip2 { CompressedStreamEOF(); } + if (thech == '\uffff') + { + CompressedStreamEOF(); + } zzi = thech; bsBuff = (bsBuff << 8) | (zzi & 0xff); bsLive += 8; @@ -897,56 +799,212 @@ namespace SharpCompress.Compressors.BZip2 } nextSym = perm[zt][zvec - basev[zt][zn]]; } + } while (nextSym == BZip2Constants.RUNA || nextSym == BZip2Constants.RUNB); + + s++; + ch = seqToUnseq[yy[0]]; + unzftab[ch] += s; + + while (s > 0) + { + last++; + ll8[last] = ch; + s--; } - } - } - private void SetupBlock() - { - Span cftab = stackalloc int[257]; - char ch; - - cftab[0] = 0; - for (i = 1; i <= 256; i++) - { - cftab[i] = unzftab[i - 1]; - } - for (i = 1; i <= 256; i++) - { - cftab[i] += cftab[i - 1]; - } - - for (i = 0; i <= last; i++) - { - ch = ll8[i]; - tt[cftab[ch]] = i; - cftab[ch]++; - } - - tPos = tt[origPtr]; - - count = 0; - i2 = 0; - ch2 = 256; /* not a char and not EOF */ - - if (blockRandomised) - { - rNToGo = 0; - rTPos = 0; - SetupRandPartA(); + if (last >= limitLast) + { + BlockOverrun(); + } } else { - SetupNoRandPartA(); + char tmp; + last++; + if (last >= limitLast) + { + BlockOverrun(); + } + + tmp = yy[nextSym - 1]; + unzftab[seqToUnseq[tmp]]++; + ll8[last] = seqToUnseq[tmp]; + + /* + This loop is hammered during decompression, + hence the unrolling. + + for (j = nextSym-1; j > 0; j--) yy[j] = yy[j-1]; + */ + + j = nextSym - 1; + for (; j > 3; j -= 4) + { + yy[j] = yy[j - 1]; + yy[j - 1] = yy[j - 2]; + yy[j - 2] = yy[j - 3]; + yy[j - 3] = yy[j - 4]; + } + for (; j > 0; j--) + { + yy[j] = yy[j - 1]; + } + + yy[0] = tmp; + { + int zt, + zn, + zvec, + zj; + if (groupPos == 0) + { + groupNo++; + groupPos = BZip2Constants.G_SIZE; + } + groupPos--; + zt = selector[groupNo]; + zn = minLens[zt]; + zvec = BsR(zn); + while (zvec > limit[zt][zn]) + { + zn++; + { + { + while (bsLive < 1) + { + int zzi; + var thech = '\0'; + try + { + thech = (char)bsStream.ReadByte(); + } + catch (IOException) + { + CompressedStreamEOF(); + } + zzi = thech; + bsBuff = (bsBuff << 8) | (zzi & 0xff); + bsLive += 8; + } + } + zj = (bsBuff >> (bsLive - 1)) & 1; + bsLive--; + } + zvec = (zvec << 1) | zj; + } + nextSym = perm[zt][zvec - basev[zt][zn]]; + } } } + } - private void SetupRandPartA() + private void SetupBlock() + { + Span cftab = stackalloc int[257]; + char ch; + + cftab[0] = 0; + for (i = 1; i <= 256; i++) { - if (i2 <= last) + cftab[i] = unzftab[i - 1]; + } + for (i = 1; i <= 256; i++) + { + cftab[i] += cftab[i - 1]; + } + + for (i = 0; i <= last; i++) + { + ch = ll8[i]; + tt[cftab[ch]] = i; + cftab[ch]++; + } + + tPos = tt[origPtr]; + + count = 0; + i2 = 0; + ch2 = 256; /* not a char and not EOF */ + + if (blockRandomised) + { + rNToGo = 0; + rTPos = 0; + SetupRandPartA(); + } + else + { + SetupNoRandPartA(); + } + } + + private void SetupRandPartA() + { + if (i2 <= last) + { + chPrev = ch2; + ch2 = ll8[tPos]; + tPos = tt[tPos]; + if (rNToGo == 0) { - chPrev = ch2; - ch2 = ll8[tPos]; + rNToGo = BZip2Constants.rNums[rTPos]; + rTPos++; + if (rTPos == 512) + { + rTPos = 0; + } + } + rNToGo--; + ch2 ^= (rNToGo == 1) ? 1 : 0; + i2++; + + currentChar = ch2; + currentState = RAND_PART_B_STATE; + mCrc.UpdateCRC(ch2); + } + else + { + EndBlock(); + InitBlock(); + SetupBlock(); + } + } + + private void SetupNoRandPartA() + { + if (i2 <= last) + { + chPrev = ch2; + ch2 = ll8[tPos]; + tPos = tt[tPos]; + i2++; + + currentChar = ch2; + currentState = NO_RAND_PART_B_STATE; + mCrc.UpdateCRC(ch2); + } + else + { + EndBlock(); + InitBlock(); + SetupBlock(); + } + } + + private void SetupRandPartB() + { + if (ch2 != chPrev) + { + currentState = RAND_PART_A_STATE; + count = 1; + SetupRandPartA(); + } + else + { + count++; + if (count >= 4) + { + z = ll8[tPos]; tPos = tt[tPos]; if (rNToGo == 0) { @@ -958,201 +1016,139 @@ namespace SharpCompress.Compressors.BZip2 } } rNToGo--; - ch2 ^= (rNToGo == 1) ? 1 : 0; - i2++; - - currentChar = ch2; - currentState = RAND_PART_B_STATE; - mCrc.UpdateCRC(ch2); - } - else - { - EndBlock(); - InitBlock(); - SetupBlock(); - } - } - - private void SetupNoRandPartA() - { - if (i2 <= last) - { - chPrev = ch2; - ch2 = ll8[tPos]; - tPos = tt[tPos]; - i2++; - - currentChar = ch2; - currentState = NO_RAND_PART_B_STATE; - mCrc.UpdateCRC(ch2); - } - else - { - EndBlock(); - InitBlock(); - SetupBlock(); - } - } - - private void SetupRandPartB() - { - if (ch2 != chPrev) - { - currentState = RAND_PART_A_STATE; - count = 1; - SetupRandPartA(); - } - else - { - count++; - if (count >= 4) - { - z = ll8[tPos]; - tPos = tt[tPos]; - if (rNToGo == 0) - { - rNToGo = BZip2Constants.rNums[rTPos]; - rTPos++; - if (rTPos == 512) - { - rTPos = 0; - } - } - rNToGo--; - z ^= (char)((rNToGo == 1) ? 1 : 0); - j2 = 0; - currentState = RAND_PART_C_STATE; - SetupRandPartC(); - } - else - { - currentState = RAND_PART_A_STATE; - SetupRandPartA(); - } - } - } - - private void SetupRandPartC() - { - if (j2 < z) - { - currentChar = ch2; - mCrc.UpdateCRC(ch2); - j2++; + z ^= (char)((rNToGo == 1) ? 1 : 0); + j2 = 0; + currentState = RAND_PART_C_STATE; + SetupRandPartC(); } else { currentState = RAND_PART_A_STATE; - i2++; - count = 0; SetupRandPartA(); } } - - private void SetupNoRandPartB() - { - if (ch2 != chPrev) - { - currentState = NO_RAND_PART_A_STATE; - count = 1; - SetupNoRandPartA(); - } - else - { - count++; - if (count >= 4) - { - z = ll8[tPos]; - tPos = tt[tPos]; - currentState = NO_RAND_PART_C_STATE; - j2 = 0; - SetupNoRandPartC(); - } - else - { - currentState = NO_RAND_PART_A_STATE; - SetupNoRandPartA(); - } - } - } - - private void SetupNoRandPartC() - { - if (j2 < z) - { - currentChar = ch2; - mCrc.UpdateCRC(ch2); - j2++; - } - else - { - currentState = NO_RAND_PART_A_STATE; - i2++; - count = 0; - SetupNoRandPartA(); - } - } - - private void SetDecompressStructureSizes(int newSize100k) - { - if (!(0 <= newSize100k && newSize100k <= 9 && 0 <= blockSize100k && blockSize100k <= 9)) - { - // throw new IOException("Invalid block size"); - } - - blockSize100k = newSize100k; - - if (newSize100k == 0) - { - return; - } - - int n = BZip2Constants.baseBlockSize * newSize100k; - ll8 = new char[n]; - tt = new int[n]; - } - - public override void Flush() { } - - public override int Read(byte[] buffer, int offset, int count) - { - int c = -1; - int k; - for (k = 0; k < count; ++k) - { - c = ReadByte(); - if (c == -1) - { - break; - } - buffer[k + offset] = (byte)c; - } - return k; - } - - public override long Seek(long offset, SeekOrigin origin) - { - return 0; - } - - public override void SetLength(long value) { } - - public override void Write(byte[] buffer, int offset, int count) { } - - public override void WriteByte(byte value) { } - - public override bool CanRead => true; - - public override bool CanSeek => false; - - public override bool CanWrite => false; - - public override long Length => 0; - - public override long Position - { - get { return 0; } - set { } - } + } + + private void SetupRandPartC() + { + if (j2 < z) + { + currentChar = ch2; + mCrc.UpdateCRC(ch2); + j2++; + } + else + { + currentState = RAND_PART_A_STATE; + i2++; + count = 0; + SetupRandPartA(); + } + } + + private void SetupNoRandPartB() + { + if (ch2 != chPrev) + { + currentState = NO_RAND_PART_A_STATE; + count = 1; + SetupNoRandPartA(); + } + else + { + count++; + if (count >= 4) + { + z = ll8[tPos]; + tPos = tt[tPos]; + currentState = NO_RAND_PART_C_STATE; + j2 = 0; + SetupNoRandPartC(); + } + else + { + currentState = NO_RAND_PART_A_STATE; + SetupNoRandPartA(); + } + } + } + + private void SetupNoRandPartC() + { + if (j2 < z) + { + currentChar = ch2; + mCrc.UpdateCRC(ch2); + j2++; + } + else + { + currentState = NO_RAND_PART_A_STATE; + i2++; + count = 0; + SetupNoRandPartA(); + } + } + + private void SetDecompressStructureSizes(int newSize100k) + { + if (!(0 <= newSize100k && newSize100k <= 9 && 0 <= blockSize100k && blockSize100k <= 9)) + { + // throw new IOException("Invalid block size"); + } + + blockSize100k = newSize100k; + + if (newSize100k == 0) + { + return; + } + + var n = BZip2Constants.baseBlockSize * newSize100k; + ll8 = new char[n]; + tt = new int[n]; + } + + public override void Flush() { } + + public override int Read(byte[] buffer, int offset, int count) + { + var c = -1; + int k; + for (k = 0; k < count; ++k) + { + c = ReadByte(); + if (c == -1) + { + break; + } + buffer[k + offset] = (byte)c; + } + return k; + } + + public override long Seek(long offset, SeekOrigin origin) + { + return 0; + } + + public override void SetLength(long value) { } + + public override void Write(byte[] buffer, int offset, int count) { } + + public override void WriteByte(byte value) { } + + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override long Length => 0; + + public override long Position + { + get { return 0; } + set { } } } diff --git a/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs b/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs index 2e0f8005..d2bf3448 100644 --- a/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs +++ b/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; /* @@ -25,1030 +25,772 @@ using System.IO; #nullable disable -namespace SharpCompress.Compressors.BZip2 +namespace SharpCompress.Compressors.BZip2; + +/** + * An output stream that compresses into the BZip2 format (with the file + * header chars) into another stream. + * + * @author Keiron Liddle + * + * TODO: Update to BZip2 1.0.1 + * NB: note this class has been modified to add a leading BZ to the + * start of the BZIP2 stream to make it compatible with other PGP programs. + */ + +internal sealed class CBZip2OutputStream : Stream { - /** - * An output stream that compresses into the BZip2 format (with the file - * header chars) into another stream. - * - * @author Keiron Liddle - * - * TODO: Update to BZip2 1.0.1 - * NB: note this class has been modified to add a leading BZ to the - * start of the BZIP2 stream to make it compatible with other PGP programs. - */ + private const int SETMASK = (1 << 21); + private const int CLEARMASK = (~SETMASK); + private const int GREATER_ICOST = 15; + private const int LESSER_ICOST = 0; + private const int SMALL_THRESH = 20; + private const int DEPTH_THRESH = 10; - internal sealed class CBZip2OutputStream : Stream + /* + If you are ever unlucky/improbable enough + to get a stack overflow whilst sorting, + increase the following constant and try + again. In practice I have never seen the + stack go above 27 elems, so the following + limit seems very generous. + */ + private const int QSORT_STACK_SIZE = 1000; + private bool finished; + + private static void Panic() { - private const int SETMASK = (1 << 21); - private const int CLEARMASK = (~SETMASK); - private const int GREATER_ICOST = 15; - private const int LESSER_ICOST = 0; - private const int SMALL_THRESH = 20; - private const int DEPTH_THRESH = 10; + //System.out.Println("panic"); + //throw new CError(); + } + private void MakeMaps() + { + int i; + nInUse = 0; + for (i = 0; i < 256; i++) + { + if (inUse[i]) + { + seqToUnseq[nInUse] = (char)i; + unseqToSeq[i] = (char)nInUse; + nInUse++; + } + } + } + + private static void HbMakeCodeLengths(char[] len, int[] freq, int alphaSize, int maxLen) + { /* - If you are ever unlucky/improbable enough - to get a stack overflow whilst sorting, - increase the following constant and try - again. In practice I have never seen the - stack go above 27 elems, so the following - limit seems very generous. + Nodes and heap entries run from 1. Entry 0 + for both the heap and nodes is a sentinel. */ - private const int QSORT_STACK_SIZE = 1000; - private bool finished; + int nNodes, + nHeap, + n1, + n2, + i, + j, + k; + bool tooLong; - private static void Panic() + Span heap = stackalloc int[BZip2Constants.MAX_ALPHA_SIZE + 2]; // 1040 bytes + Span weight = stackalloc int[BZip2Constants.MAX_ALPHA_SIZE * 2]; // 1040 bytes + Span parent = stackalloc int[BZip2Constants.MAX_ALPHA_SIZE * 2]; // 1040 bytes + + for (i = 0; i < alphaSize; i++) { - //System.out.Println("panic"); - //throw new CError(); + weight[i + 1] = (freq[i] == 0 ? 1 : freq[i]) << 8; } - private void MakeMaps() + while (true) { - int i; - nInUse = 0; - for (i = 0; i < 256; i++) + nNodes = alphaSize; + nHeap = 0; + + heap[0] = 0; + weight[0] = 0; + parent[0] = -2; + + for (i = 1; i <= alphaSize; i++) { - if (inUse[i]) + parent[i] = -1; + nHeap++; + heap[nHeap] = i; { - seqToUnseq[nInUse] = (char)i; - unseqToSeq[i] = (char)nInUse; - nInUse++; + int zz, + tmp; + zz = nHeap; + tmp = heap[zz]; + while (weight[tmp] < weight[heap[zz >> 1]]) + { + heap[zz] = heap[zz >> 1]; + zz >>= 1; + } + heap[zz] = tmp; } } - } - - private static void HbMakeCodeLengths(char[] len, int[] freq, int alphaSize, int maxLen) - { - /* - Nodes and heap entries run from 1. Entry 0 - for both the heap and nodes is a sentinel. - */ - int nNodes, - nHeap, - n1, - n2, - i, - j, - k; - bool tooLong; - - Span heap = stackalloc int[BZip2Constants.MAX_ALPHA_SIZE + 2]; // 1040 bytes - Span weight = stackalloc int[BZip2Constants.MAX_ALPHA_SIZE * 2]; // 1040 bytes - Span parent = stackalloc int[BZip2Constants.MAX_ALPHA_SIZE * 2]; // 1040 bytes - - for (i = 0; i < alphaSize; i++) + if (!(nHeap < (BZip2Constants.MAX_ALPHA_SIZE + 2))) { - weight[i + 1] = (freq[i] == 0 ? 1 : freq[i]) << 8; + Panic(); } - while (true) + while (nHeap > 1) { - nNodes = alphaSize; - nHeap = 0; - - heap[0] = 0; - weight[0] = 0; - parent[0] = -2; - - for (i = 1; i <= alphaSize; i++) + n1 = heap[1]; + heap[1] = heap[nHeap]; + nHeap--; { - parent[i] = -1; - nHeap++; - heap[nHeap] = i; + int zz = 0, + yy = 0, + tmp = 0; + zz = 1; + tmp = heap[zz]; + while (true) { - int zz, - tmp; - zz = nHeap; - tmp = heap[zz]; - while (weight[tmp] < weight[heap[zz >> 1]]) + yy = zz << 1; + if (yy > nHeap) { - heap[zz] = heap[zz >> 1]; - zz >>= 1; + break; } - heap[zz] = tmp; + if (yy < nHeap && weight[heap[yy + 1]] < weight[heap[yy]]) + { + yy++; + } + if (weight[tmp] < weight[heap[yy]]) + { + break; + } + heap[zz] = heap[yy]; + zz = yy; } + heap[zz] = tmp; } - if (!(nHeap < (BZip2Constants.MAX_ALPHA_SIZE + 2))) + n2 = heap[1]; + heap[1] = heap[nHeap]; + nHeap--; { - Panic(); + int zz = 0, + yy = 0, + tmp = 0; + zz = 1; + tmp = heap[zz]; + while (true) + { + yy = zz << 1; + if (yy > nHeap) + { + break; + } + if (yy < nHeap && weight[heap[yy + 1]] < weight[heap[yy]]) + { + yy++; + } + if (weight[tmp] < weight[heap[yy]]) + { + break; + } + heap[zz] = heap[yy]; + zz = yy; + } + heap[zz] = tmp; } + nNodes++; + parent[n1] = parent[n2] = nNodes; - while (nHeap > 1) - { - n1 = heap[1]; - heap[1] = heap[nHeap]; - nHeap--; - { - int zz = 0, - yy = 0, - tmp = 0; - zz = 1; - tmp = heap[zz]; - while (true) - { - yy = zz << 1; - if (yy > nHeap) - { - break; - } - if (yy < nHeap && weight[heap[yy + 1]] < weight[heap[yy]]) - { - yy++; - } - if (weight[tmp] < weight[heap[yy]]) - { - break; - } - heap[zz] = heap[yy]; - zz = yy; - } - heap[zz] = tmp; - } - n2 = heap[1]; - heap[1] = heap[nHeap]; - nHeap--; - { - int zz = 0, - yy = 0, - tmp = 0; - zz = 1; - tmp = heap[zz]; - while (true) - { - yy = zz << 1; - if (yy > nHeap) - { - break; - } - if (yy < nHeap && weight[heap[yy + 1]] < weight[heap[yy]]) - { - yy++; - } - if (weight[tmp] < weight[heap[yy]]) - { - break; - } - heap[zz] = heap[yy]; - zz = yy; - } - heap[zz] = tmp; - } - nNodes++; - parent[n1] = parent[n2] = nNodes; - - weight[nNodes] = (int)( - (uint)((weight[n1] & 0xffffff00) + (weight[n2] & 0xffffff00)) - | (uint)( - 1 - + ( - ((weight[n1] & 0x000000ff) > (weight[n2] & 0x000000ff)) - ? (weight[n1] & 0x000000ff) - : (weight[n2] & 0x000000ff) - ) + weight[nNodes] = (int)( + (uint)((weight[n1] & 0xffffff00) + (weight[n2] & 0xffffff00)) + | (uint)( + 1 + + ( + ((weight[n1] & 0x000000ff) > (weight[n2] & 0x000000ff)) + ? (weight[n1] & 0x000000ff) + : (weight[n2] & 0x000000ff) ) - ); + ) + ); - parent[nNodes] = -1; - nHeap++; - heap[nHeap] = nNodes; + parent[nNodes] = -1; + nHeap++; + heap[nHeap] = nNodes; + { + int zz = 0, + tmp = 0; + zz = nHeap; + tmp = heap[zz]; + while (weight[tmp] < weight[heap[zz >> 1]]) { - int zz = 0, - tmp = 0; - zz = nHeap; - tmp = heap[zz]; - while (weight[tmp] < weight[heap[zz >> 1]]) - { - heap[zz] = heap[zz >> 1]; - zz >>= 1; - } - heap[zz] = tmp; + heap[zz] = heap[zz >> 1]; + zz >>= 1; } + heap[zz] = tmp; } - if (!(nNodes < (BZip2Constants.MAX_ALPHA_SIZE * 2))) - { - Panic(); - } + } + if (!(nNodes < (BZip2Constants.MAX_ALPHA_SIZE * 2))) + { + Panic(); + } - tooLong = false; - for (i = 1; i <= alphaSize; i++) + tooLong = false; + for (i = 1; i <= alphaSize; i++) + { + j = 0; + k = i; + while (parent[k] >= 0) { - j = 0; - k = i; - while (parent[k] >= 0) - { - k = parent[k]; - j++; - } - len[i - 1] = (char)j; - if (j > maxLen) - { - tooLong = true; - } + k = parent[k]; + j++; } + len[i - 1] = (char)j; + if (j > maxLen) + { + tooLong = true; + } + } - if (!tooLong) - { - break; - } + if (!tooLong) + { + break; + } - for (i = 1; i < alphaSize; i++) - { - j = weight[i] >> 8; - j = 1 + (j / 2); - weight[i] = j << 8; - } + for (i = 1; i < alphaSize; i++) + { + j = weight[i] >> 8; + j = 1 + (j / 2); + weight[i] = j << 8; } } + } - /* - index of the last char in the block, so - the block size == last + 1. - */ - private int last; + /* + index of the last char in the block, so + the block size == last + 1. + */ + private int last; - /* - index in zptr[] of original string after sorting. - */ - private int origPtr; + /* + index in zptr[] of original string after sorting. + */ + private int origPtr; - /* - always: in the range 0 .. 9. - The current block size is 100000 * this number. - */ - private readonly int blockSize100k; + /* + always: in the range 0 .. 9. + The current block size is 100000 * this number. + */ + private readonly int blockSize100k; - private bool blockRandomised; + private bool blockRandomised; - private int bytesOut; - private int bsBuff; - private int bsLive; - private readonly CRC mCrc = new CRC(); + private int bytesOut; + private int bsBuff; + private int bsLive; + private readonly CRC mCrc = new CRC(); - private readonly bool[] inUse = new bool[256]; - private int nInUse; + private readonly bool[] inUse = new bool[256]; + private int nInUse; - private readonly char[] seqToUnseq = new char[256]; - private readonly char[] unseqToSeq = new char[256]; + private readonly char[] seqToUnseq = new char[256]; + private readonly char[] unseqToSeq = new char[256]; - private readonly char[] selector = new char[BZip2Constants.MAX_SELECTORS]; - private readonly char[] selectorMtf = new char[BZip2Constants.MAX_SELECTORS]; + private readonly char[] selector = new char[BZip2Constants.MAX_SELECTORS]; + private readonly char[] selectorMtf = new char[BZip2Constants.MAX_SELECTORS]; - private char[] block; - private int[] quadrant; - private int[] zptr; - private short[] szptr; - private int[] ftab; + private char[] block; + private int[] quadrant; + private int[] zptr; + private short[] szptr; + private int[] ftab; - private int nMTF; + private int nMTF; - private readonly int[] mtfFreq = new int[BZip2Constants.MAX_ALPHA_SIZE]; + private readonly int[] mtfFreq = new int[BZip2Constants.MAX_ALPHA_SIZE]; - /* - * Used when sorting. If too many long comparisons - * happen, we stop sorting, randomise the block - * slightly, and try again. - */ - private readonly int workFactor; - private int workDone; - private int workLimit; - private bool firstAttempt; - private int nBlocksRandomised; + /* + * Used when sorting. If too many long comparisons + * happen, we stop sorting, randomise the block + * slightly, and try again. + */ + private readonly int workFactor; + private int workDone; + private int workLimit; + private bool firstAttempt; + private int nBlocksRandomised; - private int currentChar = -1; - private int runLength; + private int currentChar = -1; + private int runLength; - public CBZip2OutputStream(Stream inStream) : this(inStream, 9) { } + public CBZip2OutputStream(Stream inStream) : this(inStream, 9) { } - public CBZip2OutputStream(Stream inStream, int inBlockSize) + public CBZip2OutputStream(Stream inStream, int inBlockSize) + { + block = null; + quadrant = null; + zptr = null; + ftab = null; + + inStream.WriteByte((byte)'B'); + inStream.WriteByte((byte)'Z'); + + BsSetStream(inStream); + + workFactor = 50; + if (inBlockSize > 9) { - block = null; - quadrant = null; - zptr = null; - ftab = null; - - inStream.WriteByte((byte)'B'); - inStream.WriteByte((byte)'Z'); - - BsSetStream(inStream); - - workFactor = 50; - if (inBlockSize > 9) - { - inBlockSize = 9; - } - if (inBlockSize < 1) - { - inBlockSize = 1; - } - blockSize100k = inBlockSize; - AllocateCompressStructures(); - Initialize(); - InitBlock(); + inBlockSize = 9; } - - /** - * - * modified by Oliver Merkel, 010128 - * - */ - - public override void WriteByte(byte bv) + if (inBlockSize < 1) { - int b = (256 + bv) % 256; - if (currentChar != -1) + inBlockSize = 1; + } + blockSize100k = inBlockSize; + AllocateCompressStructures(); + Initialize(); + InitBlock(); + } + + /** + * + * modified by Oliver Merkel, 010128 + * + */ + + public override void WriteByte(byte bv) + { + var b = (256 + bv) % 256; + if (currentChar != -1) + { + if (currentChar == b) { - if (currentChar == b) - { - runLength++; - if (runLength > 254) - { - WriteRun(); - currentChar = -1; - runLength = 0; - } - } - else + runLength++; + if (runLength > 254) { WriteRun(); - runLength = 1; - currentChar = b; + currentChar = -1; + runLength = 0; } } else { - currentChar = b; - runLength++; - } - } - - private void WriteRun() - { - if (last < allowableBlockSize) - { - inUse[currentChar] = true; - for (int i = 0; i < runLength; i++) - { - mCrc.UpdateCRC((char)currentChar); - } - switch (runLength) - { - case 1: - last++; - block[last + 1] = (char)currentChar; - break; - case 2: - last++; - block[last + 1] = (char)currentChar; - last++; - block[last + 1] = (char)currentChar; - break; - case 3: - last++; - block[last + 1] = (char)currentChar; - last++; - block[last + 1] = (char)currentChar; - last++; - block[last + 1] = (char)currentChar; - break; - default: - inUse[runLength - 4] = true; - last++; - block[last + 1] = (char)currentChar; - last++; - block[last + 1] = (char)currentChar; - last++; - block[last + 1] = (char)currentChar; - last++; - block[last + 1] = (char)currentChar; - last++; - block[last + 1] = (char)(runLength - 4); - break; - } - } - else - { - EndBlock(); - InitBlock(); WriteRun(); + runLength = 1; + currentChar = b; } } - - private bool disposed; - - protected override void Dispose(bool disposing) + else { - if (disposing) + currentChar = b; + runLength++; + } + } + + private void WriteRun() + { + if (last < allowableBlockSize) + { + inUse[currentChar] = true; + for (var i = 0; i < runLength; i++) { - if (disposed) - { - return; - } - - Finish(); - - disposed = true; - base.Dispose(); - bsStream?.Dispose(); - bsStream = null; + mCrc.UpdateCRC((char)currentChar); + } + switch (runLength) + { + case 1: + last++; + block[last + 1] = (char)currentChar; + break; + case 2: + last++; + block[last + 1] = (char)currentChar; + last++; + block[last + 1] = (char)currentChar; + break; + case 3: + last++; + block[last + 1] = (char)currentChar; + last++; + block[last + 1] = (char)currentChar; + last++; + block[last + 1] = (char)currentChar; + break; + default: + inUse[runLength - 4] = true; + last++; + block[last + 1] = (char)currentChar; + last++; + block[last + 1] = (char)currentChar; + last++; + block[last + 1] = (char)currentChar; + last++; + block[last + 1] = (char)currentChar; + last++; + block[last + 1] = (char)(runLength - 4); + break; } } - - public void Finish() + else { - if (finished) + EndBlock(); + InitBlock(); + WriteRun(); + } + } + + private bool disposed; + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (disposed) { return; } - if (runLength > 0) + Finish(); + + disposed = true; + Dispose(); + bsStream?.Dispose(); + bsStream = null; + } + } + + public void Finish() + { + if (finished) + { + return; + } + + if (runLength > 0) + { + WriteRun(); + } + currentChar = -1; + EndBlock(); + EndCompression(); + finished = true; + Flush(); + } + + public override void Flush() + { + bsStream.Flush(); + } + + private int blockCRC, + combinedCRC; + + private void Initialize() + { + bytesOut = 0; + nBlocksRandomised = 0; + + /* Write `magic' bytes h indicating file-format == huffmanised, + followed by a digit indicating blockSize100k. + */ + BsPutUChar('h'); + BsPutUChar('0' + blockSize100k); + + combinedCRC = 0; + } + + private int allowableBlockSize; + + private void InitBlock() + { + // blockNo++; + mCrc.InitialiseCRC(); + last = -1; + + // ch = 0; + + for (var i = 0; i < 256; i++) + { + inUse[i] = false; + } + + /* 20 is just a paranoia constant */ + allowableBlockSize = (BZip2Constants.baseBlockSize * blockSize100k) - 20; + } + + private void EndBlock() + { + blockCRC = mCrc.GetFinalCRC(); + combinedCRC = (combinedCRC << 1) | (int)(((uint)combinedCRC) >> 31); + combinedCRC ^= blockCRC; + + /* sort the block and establish posn of original string */ + DoReversibleTransformation(); + + /* + A 6-byte block header, the value chosen arbitrarily + as 0x314159265359 :-). A 32 bit value does not really + give a strong enough guarantee that the value will not + appear by chance in the compressed datastream. Worst-case + probability of this event, for a 900k block, is about + 2.0e-3 for 32 bits, 1.0e-5 for 40 bits and 4.0e-8 for 48 bits. + For a compressed file of size 100Gb -- about 100000 blocks -- + only a 48-bit marker will do. NB: normal compression/ + decompression do *not* rely on these statistical properties. + They are only important when trying to recover blocks from + damaged files. + */ + BsPutUChar(0x31); + BsPutUChar(0x41); + BsPutUChar(0x59); + BsPutUChar(0x26); + BsPutUChar(0x53); + BsPutUChar(0x59); + + /* Now the block's CRC, so it is in a known place. */ + BsPutint(blockCRC); + + /* Now a single bit indicating randomisation. */ + if (blockRandomised) + { + BsW(1, 1); + nBlocksRandomised++; + } + else + { + BsW(1, 0); + } + + /* Finally, block's contents proper. */ + MoveToFrontCodeAndSend(); + } + + private void EndCompression() + { + /* + Now another magic 48-bit number, 0x177245385090, to + indicate the end of the last block. (Sqrt(pi), if + you want to know. I did want to use e, but it contains + too much repetition -- 27 18 28 18 28 46 -- for me + to feel statistically comfortable. Call me paranoid.) + */ + BsPutUChar(0x17); + BsPutUChar(0x72); + BsPutUChar(0x45); + BsPutUChar(0x38); + BsPutUChar(0x50); + BsPutUChar(0x90); + + BsPutint(combinedCRC); + + BsFinishedWithStream(); + } + + private void HbAssignCodes(int[] code, char[] length, int minLen, int maxLen, int alphaSize) + { + int n, + vec, + i; + + vec = 0; + for (n = minLen; n <= maxLen; n++) + { + for (i = 0; i < alphaSize; i++) { - WriteRun(); - } - currentChar = -1; - EndBlock(); - EndCompression(); - finished = true; - Flush(); - } - - public override void Flush() - { - bsStream.Flush(); - } - - private int blockCRC, - combinedCRC; - - private void Initialize() - { - bytesOut = 0; - nBlocksRandomised = 0; - - /* Write `magic' bytes h indicating file-format == huffmanised, - followed by a digit indicating blockSize100k. - */ - BsPutUChar('h'); - BsPutUChar('0' + blockSize100k); - - combinedCRC = 0; - } - - private int allowableBlockSize; - - private void InitBlock() - { - // blockNo++; - mCrc.InitialiseCRC(); - last = -1; - - // ch = 0; - - for (int i = 0; i < 256; i++) - { - inUse[i] = false; - } - - /* 20 is just a paranoia constant */ - allowableBlockSize = BZip2Constants.baseBlockSize * blockSize100k - 20; - } - - private void EndBlock() - { - blockCRC = mCrc.GetFinalCRC(); - combinedCRC = (combinedCRC << 1) | (int)(((uint)combinedCRC) >> 31); - combinedCRC ^= blockCRC; - - /* sort the block and establish posn of original string */ - DoReversibleTransformation(); - - /* - A 6-byte block header, the value chosen arbitrarily - as 0x314159265359 :-). A 32 bit value does not really - give a strong enough guarantee that the value will not - appear by chance in the compressed datastream. Worst-case - probability of this event, for a 900k block, is about - 2.0e-3 for 32 bits, 1.0e-5 for 40 bits and 4.0e-8 for 48 bits. - For a compressed file of size 100Gb -- about 100000 blocks -- - only a 48-bit marker will do. NB: normal compression/ - decompression do *not* rely on these statistical properties. - They are only important when trying to recover blocks from - damaged files. - */ - BsPutUChar(0x31); - BsPutUChar(0x41); - BsPutUChar(0x59); - BsPutUChar(0x26); - BsPutUChar(0x53); - BsPutUChar(0x59); - - /* Now the block's CRC, so it is in a known place. */ - BsPutint(blockCRC); - - /* Now a single bit indicating randomisation. */ - if (blockRandomised) - { - BsW(1, 1); - nBlocksRandomised++; - } - else - { - BsW(1, 0); - } - - /* Finally, block's contents proper. */ - MoveToFrontCodeAndSend(); - } - - private void EndCompression() - { - /* - Now another magic 48-bit number, 0x177245385090, to - indicate the end of the last block. (Sqrt(pi), if - you want to know. I did want to use e, but it contains - too much repetition -- 27 18 28 18 28 46 -- for me - to feel statistically comfortable. Call me paranoid.) - */ - BsPutUChar(0x17); - BsPutUChar(0x72); - BsPutUChar(0x45); - BsPutUChar(0x38); - BsPutUChar(0x50); - BsPutUChar(0x90); - - BsPutint(combinedCRC); - - BsFinishedWithStream(); - } - - private void HbAssignCodes(int[] code, char[] length, int minLen, int maxLen, int alphaSize) - { - int n, - vec, - i; - - vec = 0; - for (n = minLen; n <= maxLen; n++) - { - for (i = 0; i < alphaSize; i++) + if (length[i] == n) { - if (length[i] == n) + code[i] = vec; + vec++; + } + } + ; + vec <<= 1; + } + } + + private void BsSetStream(Stream f) + { + bsStream = f; + bsLive = 0; + bsBuff = 0; + bytesOut = 0; + } + + private void BsFinishedWithStream() + { + while (bsLive > 0) + { + var ch = (bsBuff >> 24); + bsStream.WriteByte((byte)ch); // write 8-bit + bsBuff <<= 8; + bsLive -= 8; + bytesOut++; + } + } + + private void BsW(int n, int v) + { + while (bsLive >= 8) + { + var ch = (bsBuff >> 24); + bsStream.WriteByte((byte)ch); // write 8-bit + bsBuff <<= 8; + bsLive -= 8; + bytesOut++; + } + bsBuff |= (v << (32 - bsLive - n)); + bsLive += n; + } + + private void BsPutUChar(int c) + { + BsW(8, c); + } + + private void BsPutint(int u) + { + BsW(8, (u >> 24) & 0xff); + BsW(8, (u >> 16) & 0xff); + BsW(8, (u >> 8) & 0xff); + BsW(8, u & 0xff); + } + + private void BsPutIntVS(int numBits, int c) + { + BsW(numBits, c); + } + + private void SendMTFValues() + { + var len = CBZip2InputStream.InitCharArray( + BZip2Constants.N_GROUPS, + BZip2Constants.MAX_ALPHA_SIZE + ); + + int v, + t, + i, + j, + gs, + ge, + totc, + bt, + bc, + iter; + int nSelectors = 0, + alphaSize, + minLen, + maxLen, + selCtr; + int nGroups; //, nBytes; + + alphaSize = nInUse + 2; + for (t = 0; t < BZip2Constants.N_GROUPS; t++) + { + for (v = 0; v < alphaSize; v++) + { + len[t][v] = (char)GREATER_ICOST; + } + } + + /* Decide how many coding tables to use */ + if (nMTF <= 0) + { + Panic(); + } + + if (nMTF < 200) + { + nGroups = 2; + } + else if (nMTF < 600) + { + nGroups = 3; + } + else if (nMTF < 1200) + { + nGroups = 4; + } + else if (nMTF < 2400) + { + nGroups = 5; + } + else + { + nGroups = 6; + } + + /* Generate an initial set of coding tables */ + { + int nPart, + remF, + tFreq, + aFreq; + + nPart = nGroups; + remF = nMTF; + gs = 0; + while (nPart > 0) + { + tFreq = remF / nPart; + ge = gs - 1; + aFreq = 0; + while (aFreq < tFreq && ge < alphaSize - 1) + { + ge++; + aFreq += mtfFreq[ge]; + } + + if (ge > gs && nPart != nGroups && nPart != 1 && ((nGroups - nPart) % 2 == 1)) + { + aFreq -= mtfFreq[ge]; + ge--; + } + + for (v = 0; v < alphaSize; v++) + { + if (v >= gs && v <= ge) { - code[i] = vec; - vec++; + len[nPart - 1][v] = (char)LESSER_ICOST; + } + else + { + len[nPart - 1][v] = (char)GREATER_ICOST; } } - ; - vec <<= 1; + + nPart--; + gs = ge + 1; + remF -= aFreq; } } - private void BsSetStream(Stream f) + var rfreq = CBZip2InputStream.InitIntArray( + BZip2Constants.N_GROUPS, + BZip2Constants.MAX_ALPHA_SIZE + ); + var fave = new int[BZip2Constants.N_GROUPS]; + var cost = new short[BZip2Constants.N_GROUPS]; + /* + Iterate up to N_ITERS times to improve the tables. + */ + for (iter = 0; iter < BZip2Constants.N_ITERS; iter++) { - bsStream = f; - bsLive = 0; - bsBuff = 0; - bytesOut = 0; - } - - private void BsFinishedWithStream() - { - while (bsLive > 0) + for (t = 0; t < nGroups; t++) { - int ch = (bsBuff >> 24); - bsStream.WriteByte((byte)ch); // write 8-bit - bsBuff <<= 8; - bsLive -= 8; - bytesOut++; + fave[t] = 0; } - } - private void BsW(int n, int v) - { - while (bsLive >= 8) - { - int ch = (bsBuff >> 24); - bsStream.WriteByte((byte)ch); // write 8-bit - bsBuff <<= 8; - bsLive -= 8; - bytesOut++; - } - bsBuff |= (v << (32 - bsLive - n)); - bsLive += n; - } - - private void BsPutUChar(int c) - { - BsW(8, c); - } - - private void BsPutint(int u) - { - BsW(8, (u >> 24) & 0xff); - BsW(8, (u >> 16) & 0xff); - BsW(8, (u >> 8) & 0xff); - BsW(8, u & 0xff); - } - - private void BsPutIntVS(int numBits, int c) - { - BsW(numBits, c); - } - - private void SendMTFValues() - { - char[][] len = CBZip2InputStream.InitCharArray( - BZip2Constants.N_GROUPS, - BZip2Constants.MAX_ALPHA_SIZE - ); - - int v, - t, - i, - j, - gs, - ge, - totc, - bt, - bc, - iter; - int nSelectors = 0, - alphaSize, - minLen, - maxLen, - selCtr; - int nGroups; //, nBytes; - - alphaSize = nInUse + 2; - for (t = 0; t < BZip2Constants.N_GROUPS; t++) + for (t = 0; t < nGroups; t++) { for (v = 0; v < alphaSize; v++) { - len[t][v] = (char)GREATER_ICOST; + rfreq[t][v] = 0; } } - /* Decide how many coding tables to use */ - if (nMTF <= 0) - { - Panic(); - } - - if (nMTF < 200) - { - nGroups = 2; - } - else if (nMTF < 600) - { - nGroups = 3; - } - else if (nMTF < 1200) - { - nGroups = 4; - } - else if (nMTF < 2400) - { - nGroups = 5; - } - else - { - nGroups = 6; - } - - /* Generate an initial set of coding tables */ - { - int nPart, - remF, - tFreq, - aFreq; - - nPart = nGroups; - remF = nMTF; - gs = 0; - while (nPart > 0) - { - tFreq = remF / nPart; - ge = gs - 1; - aFreq = 0; - while (aFreq < tFreq && ge < alphaSize - 1) - { - ge++; - aFreq += mtfFreq[ge]; - } - - if (ge > gs && nPart != nGroups && nPart != 1 && ((nGroups - nPart) % 2 == 1)) - { - aFreq -= mtfFreq[ge]; - ge--; - } - - for (v = 0; v < alphaSize; v++) - { - if (v >= gs && v <= ge) - { - len[nPart - 1][v] = (char)LESSER_ICOST; - } - else - { - len[nPart - 1][v] = (char)GREATER_ICOST; - } - } - - nPart--; - gs = ge + 1; - remF -= aFreq; - } - } - - int[][] rfreq = CBZip2InputStream.InitIntArray( - BZip2Constants.N_GROUPS, - BZip2Constants.MAX_ALPHA_SIZE - ); - int[] fave = new int[BZip2Constants.N_GROUPS]; - short[] cost = new short[BZip2Constants.N_GROUPS]; - /* - Iterate up to N_ITERS times to improve the tables. - */ - for (iter = 0; iter < BZip2Constants.N_ITERS; iter++) - { - for (t = 0; t < nGroups; t++) - { - fave[t] = 0; - } - - for (t = 0; t < nGroups; t++) - { - for (v = 0; v < alphaSize; v++) - { - rfreq[t][v] = 0; - } - } - - nSelectors = 0; - totc = 0; - gs = 0; - while (true) - { - /* Set group start & end marks. */ - if (gs >= nMTF) - { - break; - } - ge = gs + BZip2Constants.G_SIZE - 1; - if (ge >= nMTF) - { - ge = nMTF - 1; - } - - /* - Calculate the cost of this group as coded - by each of the coding tables. - */ - for (t = 0; t < nGroups; t++) - { - cost[t] = 0; - } - - if (nGroups == 6) - { - short cost0, - cost1, - cost2, - cost3, - cost4, - cost5; - cost0 = cost1 = cost2 = cost3 = cost4 = cost5 = 0; - for (i = gs; i <= ge; i++) - { - short icv = szptr[i]; - cost0 += (short)len[0][icv]; - cost1 += (short)len[1][icv]; - cost2 += (short)len[2][icv]; - cost3 += (short)len[3][icv]; - cost4 += (short)len[4][icv]; - cost5 += (short)len[5][icv]; - } - cost[0] = cost0; - cost[1] = cost1; - cost[2] = cost2; - cost[3] = cost3; - cost[4] = cost4; - cost[5] = cost5; - } - else - { - for (i = gs; i <= ge; i++) - { - short icv = szptr[i]; - for (t = 0; t < nGroups; t++) - { - cost[t] += (short)len[t][icv]; - } - } - } - - /* - Find the coding table which is best for this group, - and record its identity in the selector table. - */ - bc = 999999999; - bt = -1; - for (t = 0; t < nGroups; t++) - { - if (cost[t] < bc) - { - bc = cost[t]; - bt = t; - } - } - ; - totc += bc; - fave[bt]++; - selector[nSelectors] = (char)bt; - nSelectors++; - - /* - Increment the symbol frequencies for the selected table. - */ - for (i = gs; i <= ge; i++) - { - rfreq[bt][szptr[i]]++; - } - - gs = ge + 1; - } - - /* - Recompute the tables based on the accumulated frequencies. - */ - for (t = 0; t < nGroups; t++) - { - HbMakeCodeLengths(len[t], rfreq[t], alphaSize, 20); - } - } - - rfreq = null; - fave = null; - cost = null; - - if (!(nGroups < 8)) - { - Panic(); - } - if (!(nSelectors < 32768 && nSelectors <= (2 + (900000 / BZip2Constants.G_SIZE)))) - { - Panic(); - } - - /* Compute MTF values for the selectors. */ - { - char[] pos = new char[BZip2Constants.N_GROUPS]; - char ll_i, - tmp2, - tmp; - for (i = 0; i < nGroups; i++) - { - pos[i] = (char)i; - } - for (i = 0; i < nSelectors; i++) - { - ll_i = selector[i]; - j = 0; - tmp = pos[j]; - while (ll_i != tmp) - { - j++; - tmp2 = tmp; - tmp = pos[j]; - pos[j] = tmp2; - } - pos[0] = tmp; - selectorMtf[i] = (char)j; - } - } - - int[][] code = CBZip2InputStream.InitIntArray( - BZip2Constants.N_GROUPS, - BZip2Constants.MAX_ALPHA_SIZE - ); - - /* Assign actual codes for the tables. */ - for (t = 0; t < nGroups; t++) - { - minLen = 32; - maxLen = 0; - for (i = 0; i < alphaSize; i++) - { - if (len[t][i] > maxLen) - { - maxLen = len[t][i]; - } - if (len[t][i] < minLen) - { - minLen = len[t][i]; - } - } - if (maxLen > 20) - { - Panic(); - } - if (minLen < 1) - { - Panic(); - } - HbAssignCodes(code[t], len[t], minLen, maxLen, alphaSize); - } - - /* Transmit the mapping table. */ - { - bool[] inUse16 = new bool[16]; - for (i = 0; i < 16; i++) - { - inUse16[i] = false; - for (j = 0; j < 16; j++) - { - if (inUse[i * 16 + j]) - { - inUse16[i] = true; - } - } - } - - //nBytes = bytesOut; - for (i = 0; i < 16; i++) - { - if (inUse16[i]) - { - BsW(1, 1); - } - else - { - BsW(1, 0); - } - } - - for (i = 0; i < 16; i++) - { - if (inUse16[i]) - { - for (j = 0; j < 16; j++) - { - if (inUse[i * 16 + j]) - { - BsW(1, 1); - } - else - { - BsW(1, 0); - } - } - } - } - } - - /* Now the selectors. */ - //nBytes = bytesOut; - BsW(3, nGroups); - BsW(15, nSelectors); - for (i = 0; i < nSelectors; i++) - { - for (j = 0; j < selectorMtf[i]; j++) - { - BsW(1, 1); - } - BsW(1, 0); - } - - /* Now the coding tables. */ - //nBytes = bytesOut; - - for (t = 0; t < nGroups; t++) - { - int curr = len[t][0]; - BsW(5, curr); - for (i = 0; i < alphaSize; i++) - { - while (curr < len[t][i]) - { - BsW(2, 2); - curr++; /* 10 */ - } - while (curr > len[t][i]) - { - BsW(2, 3); - curr--; /* 11 */ - } - BsW(1, 0); - } - } - - /* And finally, the block data proper */ - //nBytes = bytesOut; - selCtr = 0; + nSelectors = 0; + totc = 0; gs = 0; while (true) { + /* Set group start & end marks. */ if (gs >= nMTF) { break; @@ -1058,222 +800,480 @@ namespace SharpCompress.Compressors.BZip2 { ge = nMTF - 1; } + + /* + Calculate the cost of this group as coded + by each of the coding tables. + */ + for (t = 0; t < nGroups; t++) + { + cost[t] = 0; + } + + if (nGroups == 6) + { + short cost0, + cost1, + cost2, + cost3, + cost4, + cost5; + cost0 = cost1 = cost2 = cost3 = cost4 = cost5 = 0; + for (i = gs; i <= ge; i++) + { + var icv = szptr[i]; + cost0 += (short)len[0][icv]; + cost1 += (short)len[1][icv]; + cost2 += (short)len[2][icv]; + cost3 += (short)len[3][icv]; + cost4 += (short)len[4][icv]; + cost5 += (short)len[5][icv]; + } + cost[0] = cost0; + cost[1] = cost1; + cost[2] = cost2; + cost[3] = cost3; + cost[4] = cost4; + cost[5] = cost5; + } + else + { + for (i = gs; i <= ge; i++) + { + var icv = szptr[i]; + for (t = 0; t < nGroups; t++) + { + cost[t] += (short)len[t][icv]; + } + } + } + + /* + Find the coding table which is best for this group, + and record its identity in the selector table. + */ + bc = 999999999; + bt = -1; + for (t = 0; t < nGroups; t++) + { + if (cost[t] < bc) + { + bc = cost[t]; + bt = t; + } + } + ; + totc += bc; + fave[bt]++; + selector[nSelectors] = (char)bt; + nSelectors++; + + /* + Increment the symbol frequencies for the selected table. + */ for (i = gs; i <= ge; i++) { - BsW(len[selector[selCtr]][szptr[i]], code[selector[selCtr]][szptr[i]]); + rfreq[bt][szptr[i]]++; } gs = ge + 1; - selCtr++; } - if (!(selCtr == nSelectors)) + + /* + Recompute the tables based on the accumulated frequencies. + */ + for (t = 0; t < nGroups; t++) + { + HbMakeCodeLengths(len[t], rfreq[t], alphaSize, 20); + } + } + + rfreq = null; + fave = null; + cost = null; + + if (!(nGroups < 8)) + { + Panic(); + } + if (!(nSelectors < 32768 && nSelectors <= (2 + (900000 / BZip2Constants.G_SIZE)))) + { + Panic(); + } + + /* Compute MTF values for the selectors. */ + { + var pos = new char[BZip2Constants.N_GROUPS]; + char ll_i, + tmp2, + tmp; + for (i = 0; i < nGroups; i++) + { + pos[i] = (char)i; + } + for (i = 0; i < nSelectors; i++) + { + ll_i = selector[i]; + j = 0; + tmp = pos[j]; + while (ll_i != tmp) + { + j++; + tmp2 = tmp; + tmp = pos[j]; + pos[j] = tmp2; + } + pos[0] = tmp; + selectorMtf[i] = (char)j; + } + } + + var code = CBZip2InputStream.InitIntArray( + BZip2Constants.N_GROUPS, + BZip2Constants.MAX_ALPHA_SIZE + ); + + /* Assign actual codes for the tables. */ + for (t = 0; t < nGroups; t++) + { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) + { + if (len[t][i] > maxLen) + { + maxLen = len[t][i]; + } + if (len[t][i] < minLen) + { + minLen = len[t][i]; + } + } + if (maxLen > 20) { Panic(); } + if (minLen < 1) + { + Panic(); + } + HbAssignCodes(code[t], len[t], minLen, maxLen, alphaSize); } - private void MoveToFrontCodeAndSend() + /* Transmit the mapping table. */ { - BsPutIntVS(24, origPtr); - GenerateMTFValues(); - SendMTFValues(); - } - - private Stream bsStream; - - private void SimpleSort(int lo, int hi, int d) - { - int i, - j, - h, - bigN, - hp; - int v; - - bigN = hi - lo + 1; - if (bigN < 2) + var inUse16 = new bool[16]; + for (i = 0; i < 16; i++) { - return; - } - - hp = 0; - while (incs[hp] < bigN) - { - hp++; - } - hp--; - - for (; hp >= 0; hp--) - { - h = incs[hp]; - - i = lo + h; - while (true) + inUse16[i] = false; + for (j = 0; j < 16; j++) { - /* copy 1 */ - if (i > hi) + if (inUse[(i * 16) + j]) { - break; + inUse16[i] = true; } - v = zptr[i]; - j = i; - while (FullGtU(zptr[j - h] + d, v + d)) - { - zptr[j] = zptr[j - h]; - j = j - h; - if (j <= (lo + h - 1)) - { - break; - } - } - zptr[j] = v; - i++; + } + } - /* copy 2 */ - if (i > hi) - { - break; - } - v = zptr[i]; - j = i; - while (FullGtU(zptr[j - h] + d, v + d)) - { - zptr[j] = zptr[j - h]; - j = j - h; - if (j <= (lo + h - 1)) - { - break; - } - } - zptr[j] = v; - i++; + //nBytes = bytesOut; + for (i = 0; i < 16; i++) + { + if (inUse16[i]) + { + BsW(1, 1); + } + else + { + BsW(1, 0); + } + } - /* copy 3 */ - if (i > hi) + for (i = 0; i < 16; i++) + { + if (inUse16[i]) + { + for (j = 0; j < 16; j++) { - break; - } - v = zptr[i]; - j = i; - while (FullGtU(zptr[j - h] + d, v + d)) - { - zptr[j] = zptr[j - h]; - j = j - h; - if (j <= (lo + h - 1)) + if (inUse[(i * 16) + j]) { - break; + BsW(1, 1); + } + else + { + BsW(1, 0); } - } - zptr[j] = v; - i++; - - if (workDone > workLimit && firstAttempt) - { - return; } } } } - private void Vswap(int p1, int p2, int n) + /* Now the selectors. */ + //nBytes = bytesOut; + BsW(3, nGroups); + BsW(15, nSelectors); + for (i = 0; i < nSelectors; i++) { - int temp = 0; - while (n > 0) + for (j = 0; j < selectorMtf[i]; j++) { - temp = zptr[p1]; - zptr[p1] = zptr[p2]; - zptr[p2] = temp; - p1++; - p2++; - n--; + BsW(1, 1); } + BsW(1, 0); } - private char Med3(char a, char b, char c) + /* Now the coding tables. */ + //nBytes = bytesOut; + + for (t = 0; t < nGroups; t++) { - char t; - if (a > b) + int curr = len[t][0]; + BsW(5, curr); + for (i = 0; i < alphaSize; i++) { - t = a; - a = b; - b = t; - } - if (b > c) - { - t = b; - b = c; - c = t; - } - if (a > b) - { - b = a; - } - return b; - } - - internal class StackElem - { - internal int ll; - internal int hh; - internal int dd; - } - - private void QSort3(int loSt, int hiSt, int dSt) - { - int unLo, - unHi, - ltLo, - gtHi, - med, - n, - m; - int sp, - lo, - hi, - d; - StackElem[] stack = new StackElem[QSORT_STACK_SIZE]; - for (int count = 0; count < QSORT_STACK_SIZE; count++) - { - stack[count] = new StackElem(); - } - - sp = 0; - - stack[sp].ll = loSt; - stack[sp].hh = hiSt; - stack[sp].dd = dSt; - sp++; - - while (sp > 0) - { - if (sp >= QSORT_STACK_SIZE) + while (curr < len[t][i]) { - Panic(); + BsW(2, 2); + curr++; /* 10 */ } - - sp--; - lo = stack[sp].ll; - hi = stack[sp].hh; - d = stack[sp].dd; - - if (hi - lo < SMALL_THRESH || d > DEPTH_THRESH) + while (curr > len[t][i]) { - SimpleSort(lo, hi, d); - if (workDone > workLimit && firstAttempt) + BsW(2, 3); + curr--; /* 11 */ + } + BsW(1, 0); + } + } + + /* And finally, the block data proper */ + //nBytes = bytesOut; + selCtr = 0; + gs = 0; + while (true) + { + if (gs >= nMTF) + { + break; + } + ge = gs + BZip2Constants.G_SIZE - 1; + if (ge >= nMTF) + { + ge = nMTF - 1; + } + for (i = gs; i <= ge; i++) + { + BsW(len[selector[selCtr]][szptr[i]], code[selector[selCtr]][szptr[i]]); + } + + gs = ge + 1; + selCtr++; + } + if (!(selCtr == nSelectors)) + { + Panic(); + } + } + + private void MoveToFrontCodeAndSend() + { + BsPutIntVS(24, origPtr); + GenerateMTFValues(); + SendMTFValues(); + } + + private Stream bsStream; + + private void SimpleSort(int lo, int hi, int d) + { + int i, + j, + h, + bigN, + hp; + int v; + + bigN = hi - lo + 1; + if (bigN < 2) + { + return; + } + + hp = 0; + while (incs[hp] < bigN) + { + hp++; + } + hp--; + + for (; hp >= 0; hp--) + { + h = incs[hp]; + + i = lo + h; + while (true) + { + /* copy 1 */ + if (i > hi) + { + break; + } + v = zptr[i]; + j = i; + while (FullGtU(zptr[j - h] + d, v + d)) + { + zptr[j] = zptr[j - h]; + j -= h; + if (j <= (lo + h - 1)) { - return; + break; } - continue; } + zptr[j] = v; + i++; - med = Med3( - block[zptr[lo] + d + 1], - block[zptr[hi] + d + 1], - block[zptr[(lo + hi) >> 1] + d + 1] - ); + /* copy 2 */ + if (i > hi) + { + break; + } + v = zptr[i]; + j = i; + while (FullGtU(zptr[j - h] + d, v + d)) + { + zptr[j] = zptr[j - h]; + j -= h; + if (j <= (lo + h - 1)) + { + break; + } + } + zptr[j] = v; + i++; - unLo = ltLo = lo; - unHi = gtHi = hi; + /* copy 3 */ + if (i > hi) + { + break; + } + v = zptr[i]; + j = i; + while (FullGtU(zptr[j - h] + d, v + d)) + { + zptr[j] = zptr[j - h]; + j -= h; + if (j <= (lo + h - 1)) + { + break; + } + } + zptr[j] = v; + i++; + + if (workDone > workLimit && firstAttempt) + { + return; + } + } + } + } + + private void Vswap(int p1, int p2, int n) + { + var temp = 0; + while (n > 0) + { + temp = zptr[p1]; + zptr[p1] = zptr[p2]; + zptr[p2] = temp; + p1++; + p2++; + n--; + } + } + + private char Med3(char a, char b, char c) + { + char t; + if (a > b) + { + t = a; + a = b; + b = t; + } + if (b > c) + { + t = b; + b = c; + c = t; + } + if (a > b) + { + b = a; + } + return b; + } + + internal class StackElem + { + internal int ll; + internal int hh; + internal int dd; + } + + private void QSort3(int loSt, int hiSt, int dSt) + { + int unLo, + unHi, + ltLo, + gtHi, + med, + n, + m; + int sp, + lo, + hi, + d; + var stack = new StackElem[QSORT_STACK_SIZE]; + for (var count = 0; count < QSORT_STACK_SIZE; count++) + { + stack[count] = new StackElem(); + } + + sp = 0; + + stack[sp].ll = loSt; + stack[sp].hh = hiSt; + stack[sp].dd = dSt; + sp++; + + while (sp > 0) + { + if (sp >= QSORT_STACK_SIZE) + { + Panic(); + } + + sp--; + lo = stack[sp].ll; + hi = stack[sp].hh; + d = stack[sp].dd; + + if (hi - lo < SMALL_THRESH || d > DEPTH_THRESH) + { + SimpleSort(lo, hi, d); + if (workDone > workLimit && firstAttempt) + { + return; + } + continue; + } + + med = Med3( + block[zptr[lo] + d + 1], + block[zptr[hi] + d + 1], + block[zptr[(lo + hi) >> 1] + d + 1] + ); + + unLo = ltLo = lo; + unHi = gtHi = hi; while (true) { @@ -1284,33 +1284,33 @@ namespace SharpCompress.Compressors.BZip2 break; } n = block[zptr[unLo] + d + 1] - med; - if (n == 0) - { - int temp = 0; - temp = zptr[unLo]; - zptr[unLo] = zptr[ltLo]; - zptr[ltLo] = temp; - ltLo++; - unLo++; - continue; - } - ; - if (n > 0) - { - break; - } - unLo++; - } - while (true) + if (n == 0) { - if (unLo > unHi) - { - break; - } - n = block[zptr[unHi] + d + 1] - med; - if (n == 0) - { - int temp = 0; + var temp = 0; + temp = zptr[unLo]; + zptr[unLo] = zptr[ltLo]; + zptr[ltLo] = temp; + ltLo++; + unLo++; + continue; + } + ; + if (n > 0) + { + break; + } + unLo++; + } + while (true) + { + if (unLo > unHi) + { + break; + } + n = block[zptr[unHi] + d + 1] - med; + if (n == 0) + { + var temp = 0; temp = zptr[unHi]; zptr[unHi] = zptr[gtHi]; zptr[gtHi] = temp; @@ -1329,700 +1329,699 @@ namespace SharpCompress.Compressors.BZip2 { break; } - int tempx = zptr[unLo]; + var tempx = zptr[unLo]; zptr[unLo] = zptr[unHi]; zptr[unHi] = tempx; unLo++; unHi--; } - if (gtHi < ltLo) - { - stack[sp].ll = lo; - stack[sp].hh = hi; - stack[sp].dd = d + 1; - sp++; - continue; - } - - n = ((ltLo - lo) < (unLo - ltLo)) ? (ltLo - lo) : (unLo - ltLo); - Vswap(lo, unLo - n, n); - m = ((hi - gtHi) < (gtHi - unHi)) ? (hi - gtHi) : (gtHi - unHi); - Vswap(unLo, hi - m + 1, m); - - n = lo + unLo - ltLo - 1; - m = hi - (gtHi - unHi) + 1; - + if (gtHi < ltLo) + { stack[sp].ll = lo; - stack[sp].hh = n; - stack[sp].dd = d; - sp++; - - stack[sp].ll = n + 1; - stack[sp].hh = m - 1; + stack[sp].hh = hi; stack[sp].dd = d + 1; sp++; - - stack[sp].ll = m; - stack[sp].hh = hi; - stack[sp].dd = d; - sp++; + continue; } + + n = ((ltLo - lo) < (unLo - ltLo)) ? (ltLo - lo) : (unLo - ltLo); + Vswap(lo, unLo - n, n); + m = ((hi - gtHi) < (gtHi - unHi)) ? (hi - gtHi) : (gtHi - unHi); + Vswap(unLo, hi - m + 1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + stack[sp].ll = lo; + stack[sp].hh = n; + stack[sp].dd = d; + sp++; + + stack[sp].ll = n + 1; + stack[sp].hh = m - 1; + stack[sp].dd = d + 1; + sp++; + + stack[sp].ll = m; + stack[sp].hh = hi; + stack[sp].dd = d; + sp++; } + } - private void MainSort() - { - int i, - j, - ss, - sb; - Span runningOrder = stackalloc int[256]; - Span copy = stackalloc int[256]; - bool[] bigDone = new bool[256]; - int c1, - c2; - int numQSorted; - - /* - In the various block-sized structures, live data runs - from 0 to last+NUM_OVERSHOOT_BYTES inclusive. First, - set up the overshoot area for block. - */ - - // if (verbosity >= 4) fprintf ( stderr, " sort initialise ...\n" ); - for (i = 0; i < BZip2Constants.NUM_OVERSHOOT_BYTES; i++) - { - block[last + i + 2] = block[(i % (last + 1)) + 1]; - } - for (i = 0; i <= last + BZip2Constants.NUM_OVERSHOOT_BYTES; i++) - { - quadrant[i] = 0; - } - - block[0] = block[last + 1]; - - if (last < 4000) - { - /* - Use SimpleSort(), since the full sorting mechanism - has quite a large constant overhead. - */ - for (i = 0; i <= last; i++) - { - zptr[i] = i; - } - firstAttempt = false; - workDone = workLimit = 0; - SimpleSort(0, last, 0); - } - else - { - numQSorted = 0; - for (i = 0; i <= 255; i++) - { - bigDone[i] = false; - } - - for (i = 0; i <= 65536; i++) - { - ftab[i] = 0; - } - - c1 = block[0]; - for (i = 0; i <= last; i++) - { - c2 = block[i + 1]; - ftab[(c1 << 8) + c2]++; - c1 = c2; - } - - for (i = 1; i <= 65536; i++) - { - ftab[i] += ftab[i - 1]; - } - - c1 = block[1]; - for (i = 0; i < last; i++) - { - c2 = block[i + 2]; - j = (c1 << 8) + c2; - c1 = c2; - ftab[j]--; - zptr[ftab[j]] = i; - } - - j = ((block[last + 1]) << 8) + (block[1]); - ftab[j]--; - zptr[ftab[j]] = last; - - /* - Now ftab contains the first loc of every small bucket. - Calculate the running order, from smallest to largest - big bucket. - */ - - for (i = 0; i <= 255; i++) - { - runningOrder[i] = i; - } - - { - int vv; - int h = 1; - do - { - h = 3 * h + 1; - } while (h <= 256); - do - { - h = h / 3; - for (i = h; i <= 255; i++) - { - vv = runningOrder[i]; - j = i; - while ( - ( - ftab[((runningOrder[j - h]) + 1) << 8] - - ftab[(runningOrder[j - h]) << 8] - ) > (ftab[((vv) + 1) << 8] - ftab[(vv) << 8]) - ) - { - runningOrder[j] = runningOrder[j - h]; - j = j - h; - if (j <= (h - 1)) - { - break; - } - } - runningOrder[j] = vv; - } - } while (h != 1); - } - - /* - The main sorting loop. - */ - for (i = 0; i <= 255; i++) - { - /* - Process big buckets, starting with the least full. - */ - ss = runningOrder[i]; - - /* - Complete the big bucket [ss] by quicksorting - any unsorted small buckets [ss, j]. Hopefully - previous pointer-scanning phases have already - completed many of the small buckets [ss, j], so - we don't have to sort them at all. - */ - for (j = 0; j <= 255; j++) - { - sb = (ss << 8) + j; - if (!((ftab[sb] & SETMASK) == SETMASK)) - { - int lo = ftab[sb] & CLEARMASK; - int hi = (ftab[sb + 1] & CLEARMASK) - 1; - if (hi > lo) - { - QSort3(lo, hi, 2); - numQSorted += (hi - lo + 1); - if (workDone > workLimit && firstAttempt) - { - return; - } - } - ftab[sb] |= SETMASK; - } - } - - /* - The ss big bucket is now done. Record this fact, - and update the quadrant descriptors. Remember to - update quadrants in the overshoot area too, if - necessary. The "if (i < 255)" test merely skips - this updating for the last bucket processed, since - updating for the last bucket is pointless. - */ - bigDone[ss] = true; - - if (i < 255) - { - int bbStart = ftab[ss << 8] & CLEARMASK; - int bbSize = (ftab[(ss + 1) << 8] & CLEARMASK) - bbStart; - int shifts = 0; - - while ((bbSize >> shifts) > 65534) - { - shifts++; - } - - for (j = 0; j < bbSize; j++) - { - int a2update = zptr[bbStart + j]; - int qVal = (j >> shifts); - quadrant[a2update] = qVal; - if (a2update < BZip2Constants.NUM_OVERSHOOT_BYTES) - { - quadrant[a2update + last + 1] = qVal; - } - } - - if (!(((bbSize - 1) >> shifts) <= 65535)) - { - Panic(); - } - } - - /* - Now scan this big bucket so as to synthesise the - sorted order for small buckets [t, ss] for all t != ss. - */ - for (j = 0; j <= 255; j++) - { - copy[j] = ftab[(j << 8) + ss] & CLEARMASK; - } - - for (j = ftab[ss << 8] & CLEARMASK; j < (ftab[(ss + 1) << 8] & CLEARMASK); j++) - { - c1 = block[zptr[j]]; - if (!bigDone[c1]) - { - zptr[copy[c1]] = zptr[j] == 0 ? last : zptr[j] - 1; - copy[c1]++; - } - } - - for (j = 0; j <= 255; j++) - { - ftab[(j << 8) + ss] |= SETMASK; - } - } - } - } - - private void RandomiseBlock() - { - int i; - int rNToGo = 0; - int rTPos = 0; - for (i = 0; i < 256; i++) - { - inUse[i] = false; - } - - for (i = 0; i <= last; i++) - { - if (rNToGo == 0) - { - rNToGo = (char)BZip2Constants.rNums[rTPos]; - rTPos++; - if (rTPos == 512) - { - rTPos = 0; - } - } - rNToGo--; - block[i + 1] ^= (char)((rNToGo == 1) ? 1 : 0); - - // handle 16 bit signed numbers - block[i + 1] &= (char)0xFF; - - inUse[block[i + 1]] = true; - } - } - - private void DoReversibleTransformation() - { - int i; - - workLimit = workFactor * last; - workDone = 0; - blockRandomised = false; - firstAttempt = true; - - MainSort(); - - if (workDone > workLimit && firstAttempt) - { - RandomiseBlock(); - workLimit = workDone = 0; - blockRandomised = true; - firstAttempt = false; - MainSort(); - } - - origPtr = -1; - for (i = 0; i <= last; i++) - { - if (zptr[i] == 0) - { - origPtr = i; - break; - } - } - ; - - if (origPtr == -1) - { - Panic(); - } - } - - private bool FullGtU(int i1, int i2) - { - int k; - char c1, - c2; - int s1, - s2; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return (c1 > c2); - } - i1++; - i2++; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return (c1 > c2); - } - i1++; - i2++; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return (c1 > c2); - } - i1++; - i2++; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return (c1 > c2); - } - i1++; - i2++; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return (c1 > c2); - } - i1++; - i2++; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return (c1 > c2); - } - i1++; - i2++; - - k = last + 1; - - do - { - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return (c1 > c2); - } - s1 = quadrant[i1]; - s2 = quadrant[i2]; - if (s1 != s2) - { - return (s1 > s2); - } - i1++; - i2++; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return (c1 > c2); - } - s1 = quadrant[i1]; - s2 = quadrant[i2]; - if (s1 != s2) - { - return (s1 > s2); - } - i1++; - i2++; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return (c1 > c2); - } - s1 = quadrant[i1]; - s2 = quadrant[i2]; - if (s1 != s2) - { - return (s1 > s2); - } - i1++; - i2++; - - c1 = block[i1 + 1]; - c2 = block[i2 + 1]; - if (c1 != c2) - { - return (c1 > c2); - } - s1 = quadrant[i1]; - s2 = quadrant[i2]; - if (s1 != s2) - { - return (s1 > s2); - } - i1++; - i2++; - - if (i1 > last) - { - i1 -= last; - i1--; - } - ; - if (i2 > last) - { - i2 -= last; - i2--; - } - ; - - k -= 4; - workDone++; - } while (k >= 0); - - return false; - } + private void MainSort() + { + int i, + j, + ss, + sb; + Span runningOrder = stackalloc int[256]; + Span copy = stackalloc int[256]; + var bigDone = new bool[256]; + int c1, + c2; + int numQSorted; /* - Knuth's increments seem to work better - than Incerpi-Sedgewick here. Possibly - because the number of elems to sort is - usually small, typically <= 20. + In the various block-sized structures, live data runs + from 0 to last+NUM_OVERSHOOT_BYTES inclusive. First, + set up the overshoot area for block. */ - private readonly int[] incs = + // if (verbosity >= 4) fprintf ( stderr, " sort initialise ...\n" ); + for (i = 0; i < BZip2Constants.NUM_OVERSHOOT_BYTES; i++) { - 1, - 4, - 13, - 40, - 121, - 364, - 1093, - 3280, - 9841, - 29524, - 88573, - 265720, - 797161, - 2391484 - }; - - private void AllocateCompressStructures() + block[last + i + 2] = block[(i % (last + 1)) + 1]; + } + for (i = 0; i <= last + BZip2Constants.NUM_OVERSHOOT_BYTES; i++) { - int n = BZip2Constants.baseBlockSize * blockSize100k; - block = new char[(n + 1 + BZip2Constants.NUM_OVERSHOOT_BYTES)]; - quadrant = new int[(n + BZip2Constants.NUM_OVERSHOOT_BYTES)]; - zptr = new int[n]; - ftab = new int[65537]; - - if (block is null || quadrant is null || zptr is null || ftab is null) - { - //int totalDraw = (n + 1 + NUM_OVERSHOOT_BYTES) + (n + NUM_OVERSHOOT_BYTES) + n + 65537; - //compressOutOfMemory ( totalDraw, n ); - } - - /* - The back end needs a place to store the MTF values - whilst it calculates the coding tables. We could - put them in the zptr array. However, these values - will fit in a short, so we overlay szptr at the - start of zptr, in the hope of reducing the number - of cache misses induced by the multiple traversals - of the MTF values when calculating coding tables. - Seems to improve compression speed by about 1%. - */ - // szptr = zptr; - - szptr = new short[2 * n]; + quadrant[i] = 0; } - private void GenerateMTFValues() + block[0] = block[last + 1]; + + if (last < 4000) { - char[] yy = new char[256]; - int i, - j; - char tmp; - char tmp2; - int zPend; - int wr; - int EOB; - - MakeMaps(); - EOB = nInUse + 1; - - for (i = 0; i <= EOB; i++) - { - mtfFreq[i] = 0; - } - - wr = 0; - zPend = 0; - for (i = 0; i < nInUse; i++) - { - yy[i] = (char)i; - } - + /* + Use SimpleSort(), since the full sorting mechanism + has quite a large constant overhead. + */ for (i = 0; i <= last; i++) { - char ll_i; + zptr[i] = i; + } + firstAttempt = false; + workDone = workLimit = 0; + SimpleSort(0, last, 0); + } + else + { + numQSorted = 0; + for (i = 0; i <= 255; i++) + { + bigDone[i] = false; + } - ll_i = unseqToSeq[block[zptr[i]]]; + for (i = 0; i <= 65536; i++) + { + ftab[i] = 0; + } - j = 0; - tmp = yy[j]; - while (ll_i != tmp) - { - j++; - tmp2 = tmp; - tmp = yy[j]; - yy[j] = tmp2; - } - ; - yy[0] = tmp; + c1 = block[0]; + for (i = 0; i <= last; i++) + { + c2 = block[i + 1]; + ftab[(c1 << 8) + c2]++; + c1 = c2; + } - if (j == 0) + for (i = 1; i <= 65536; i++) + { + ftab[i] += ftab[i - 1]; + } + + c1 = block[1]; + for (i = 0; i < last; i++) + { + c2 = block[i + 2]; + j = (c1 << 8) + c2; + c1 = c2; + ftab[j]--; + zptr[ftab[j]] = i; + } + + j = ((block[last + 1]) << 8) + (block[1]); + ftab[j]--; + zptr[ftab[j]] = last; + + /* + Now ftab contains the first loc of every small bucket. + Calculate the running order, from smallest to largest + big bucket. + */ + + for (i = 0; i <= 255; i++) + { + runningOrder[i] = i; + } + + { + int vv; + var h = 1; + do { - zPend++; - } - else + h = (3 * h) + 1; + } while (h <= 256); + do { - if (zPend > 0) + h /= 3; + for (i = h; i <= 255; i++) { - zPend--; - while (true) + vv = runningOrder[i]; + j = i; + while ( + ( + ftab[((runningOrder[j - h]) + 1) << 8] + - ftab[(runningOrder[j - h]) << 8] + ) > (ftab[((vv) + 1) << 8] - ftab[(vv) << 8]) + ) { - switch (zPend % 2) - { - case 0: - szptr[wr] = BZip2Constants.RUNA; - wr++; - mtfFreq[BZip2Constants.RUNA]++; - break; - case 1: - szptr[wr] = BZip2Constants.RUNB; - wr++; - mtfFreq[BZip2Constants.RUNB]++; - break; - } - ; - if (zPend < 2) + runningOrder[j] = runningOrder[j - h]; + j -= h; + if (j <= (h - 1)) { break; } - zPend = (zPend - 2) / 2; } - ; - zPend = 0; + runningOrder[j] = vv; } - szptr[wr] = (short)(j + 1); - wr++; - mtfFreq[j + 1]++; - } + } while (h != 1); } - if (zPend > 0) + /* + The main sorting loop. + */ + for (i = 0; i <= 255; i++) { - zPend--; - while (true) + /* + Process big buckets, starting with the least full. + */ + ss = runningOrder[i]; + + /* + Complete the big bucket [ss] by quicksorting + any unsorted small buckets [ss, j]. Hopefully + previous pointer-scanning phases have already + completed many of the small buckets [ss, j], so + we don't have to sort them at all. + */ + for (j = 0; j <= 255; j++) { - switch (zPend % 2) + sb = (ss << 8) + j; + if (!((ftab[sb] & SETMASK) == SETMASK)) { - case 0: - szptr[wr] = BZip2Constants.RUNA; - wr++; - mtfFreq[BZip2Constants.RUNA]++; - break; - case 1: - szptr[wr] = BZip2Constants.RUNB; - wr++; - mtfFreq[BZip2Constants.RUNB]++; - break; + var lo = ftab[sb] & CLEARMASK; + var hi = (ftab[sb + 1] & CLEARMASK) - 1; + if (hi > lo) + { + QSort3(lo, hi, 2); + numQSorted += (hi - lo + 1); + if (workDone > workLimit && firstAttempt) + { + return; + } + } + ftab[sb] |= SETMASK; } - if (zPend < 2) + } + + /* + The ss big bucket is now done. Record this fact, + and update the quadrant descriptors. Remember to + update quadrants in the overshoot area too, if + necessary. The "if (i < 255)" test merely skips + this updating for the last bucket processed, since + updating for the last bucket is pointless. + */ + bigDone[ss] = true; + + if (i < 255) + { + var bbStart = ftab[ss << 8] & CLEARMASK; + var bbSize = (ftab[(ss + 1) << 8] & CLEARMASK) - bbStart; + var shifts = 0; + + while ((bbSize >> shifts) > 65534) { - break; + shifts++; } - zPend = (zPend - 2) / 2; + + for (j = 0; j < bbSize; j++) + { + var a2update = zptr[bbStart + j]; + var qVal = (j >> shifts); + quadrant[a2update] = qVal; + if (a2update < BZip2Constants.NUM_OVERSHOOT_BYTES) + { + quadrant[a2update + last + 1] = qVal; + } + } + + if (!(((bbSize - 1) >> shifts) <= 65535)) + { + Panic(); + } + } + + /* + Now scan this big bucket so as to synthesise the + sorted order for small buckets [t, ss] for all t != ss. + */ + for (j = 0; j <= 255; j++) + { + copy[j] = ftab[(j << 8) + ss] & CLEARMASK; + } + + for (j = ftab[ss << 8] & CLEARMASK; j < (ftab[(ss + 1) << 8] & CLEARMASK); j++) + { + c1 = block[zptr[j]]; + if (!bigDone[c1]) + { + zptr[copy[c1]] = zptr[j] == 0 ? last : zptr[j] - 1; + copy[c1]++; + } + } + + for (j = 0; j <= 255; j++) + { + ftab[(j << 8) + ss] |= SETMASK; } } - - szptr[wr] = (short)EOB; - wr++; - mtfFreq[EOB]++; - - nMTF = wr; - } - - public override int Read(byte[] buffer, int offset, int count) - { - return 0; - } - - public override int ReadByte() - { - return -1; - } - - public override long Seek(long offset, SeekOrigin origin) - { - return 0; - } - - public override void SetLength(long value) { } - - public override void Write(byte[] buffer, int offset, int count) - { - for (int k = 0; k < count; ++k) - { - WriteByte(buffer[k + offset]); - } - } - - public override bool CanRead => false; - - public override bool CanSeek => false; - - public override bool CanWrite => true; - - public override long Length => 0; - - public override long Position - { - get { return 0; } - set { } } } + + private void RandomiseBlock() + { + int i; + var rNToGo = 0; + var rTPos = 0; + for (i = 0; i < 256; i++) + { + inUse[i] = false; + } + + for (i = 0; i <= last; i++) + { + if (rNToGo == 0) + { + rNToGo = (char)BZip2Constants.rNums[rTPos]; + rTPos++; + if (rTPos == 512) + { + rTPos = 0; + } + } + rNToGo--; + block[i + 1] ^= (char)((rNToGo == 1) ? 1 : 0); + + // handle 16 bit signed numbers + block[i + 1] &= (char)0xFF; + + inUse[block[i + 1]] = true; + } + } + + private void DoReversibleTransformation() + { + int i; + + workLimit = workFactor * last; + workDone = 0; + blockRandomised = false; + firstAttempt = true; + + MainSort(); + + if (workDone > workLimit && firstAttempt) + { + RandomiseBlock(); + workLimit = workDone = 0; + blockRandomised = true; + firstAttempt = false; + MainSort(); + } + + origPtr = -1; + for (i = 0; i <= last; i++) + { + if (zptr[i] == 0) + { + origPtr = i; + break; + } + } + ; + + if (origPtr == -1) + { + Panic(); + } + } + + private bool FullGtU(int i1, int i2) + { + int k; + char c1, + c2; + int s1, + s2; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return (c1 > c2); + } + i1++; + i2++; + + k = last + 1; + + do + { + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return (c1 > c2); + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) + { + return (s1 > s2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return (c1 > c2); + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) + { + return (s1 > s2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return (c1 > c2); + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) + { + return (s1 > s2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) + { + return (c1 > c2); + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) + { + return (s1 > s2); + } + i1++; + i2++; + + if (i1 > last) + { + i1 -= last; + i1--; + } + ; + if (i2 > last) + { + i2 -= last; + i2--; + } + ; + + k -= 4; + workDone++; + } while (k >= 0); + + return false; + } + + /* + Knuth's increments seem to work better + than Incerpi-Sedgewick here. Possibly + because the number of elems to sort is + usually small, typically <= 20. + */ + + private readonly int[] incs = + { + 1, + 4, + 13, + 40, + 121, + 364, + 1093, + 3280, + 9841, + 29524, + 88573, + 265720, + 797161, + 2391484 + }; + + private void AllocateCompressStructures() + { + var n = BZip2Constants.baseBlockSize * blockSize100k; + block = new char[(n + 1 + BZip2Constants.NUM_OVERSHOOT_BYTES)]; + quadrant = new int[(n + BZip2Constants.NUM_OVERSHOOT_BYTES)]; + zptr = new int[n]; + ftab = new int[65537]; + + if (block is null || quadrant is null || zptr is null || ftab is null) + { + //int totalDraw = (n + 1 + NUM_OVERSHOOT_BYTES) + (n + NUM_OVERSHOOT_BYTES) + n + 65537; + //compressOutOfMemory ( totalDraw, n ); + } + + /* + The back end needs a place to store the MTF values + whilst it calculates the coding tables. We could + put them in the zptr array. However, these values + will fit in a short, so we overlay szptr at the + start of zptr, in the hope of reducing the number + of cache misses induced by the multiple traversals + of the MTF values when calculating coding tables. + Seems to improve compression speed by about 1%. + */ + // szptr = zptr; + + szptr = new short[2 * n]; + } + + private void GenerateMTFValues() + { + var yy = new char[256]; + int i, + j; + char tmp; + char tmp2; + int zPend; + int wr; + int EOB; + + MakeMaps(); + EOB = nInUse + 1; + + for (i = 0; i <= EOB; i++) + { + mtfFreq[i] = 0; + } + + wr = 0; + zPend = 0; + for (i = 0; i < nInUse; i++) + { + yy[i] = (char)i; + } + + for (i = 0; i <= last; i++) + { + char ll_i; + + ll_i = unseqToSeq[block[zptr[i]]]; + + j = 0; + tmp = yy[j]; + while (ll_i != tmp) + { + j++; + tmp2 = tmp; + tmp = yy[j]; + yy[j] = tmp2; + } + ; + yy[0] = tmp; + + if (j == 0) + { + zPend++; + } + else + { + if (zPend > 0) + { + zPend--; + while (true) + { + switch (zPend % 2) + { + case 0: + szptr[wr] = BZip2Constants.RUNA; + wr++; + mtfFreq[BZip2Constants.RUNA]++; + break; + case 1: + szptr[wr] = BZip2Constants.RUNB; + wr++; + mtfFreq[BZip2Constants.RUNB]++; + break; + } + ; + if (zPend < 2) + { + break; + } + zPend = (zPend - 2) / 2; + } + ; + zPend = 0; + } + szptr[wr] = (short)(j + 1); + wr++; + mtfFreq[j + 1]++; + } + } + + if (zPend > 0) + { + zPend--; + while (true) + { + switch (zPend % 2) + { + case 0: + szptr[wr] = BZip2Constants.RUNA; + wr++; + mtfFreq[BZip2Constants.RUNA]++; + break; + case 1: + szptr[wr] = BZip2Constants.RUNB; + wr++; + mtfFreq[BZip2Constants.RUNB]++; + break; + } + if (zPend < 2) + { + break; + } + zPend = (zPend - 2) / 2; + } + } + + szptr[wr] = (short)EOB; + wr++; + mtfFreq[EOB]++; + + nMTF = wr; + } + + public override int Read(byte[] buffer, int offset, int count) + { + return 0; + } + + public override int ReadByte() + { + return -1; + } + + public override long Seek(long offset, SeekOrigin origin) + { + return 0; + } + + public override void SetLength(long value) { } + + public override void Write(byte[] buffer, int offset, int count) + { + for (var k = 0; k < count; ++k) + { + WriteByte(buffer[k + offset]); + } + } + + public override bool CanRead => false; + + public override bool CanSeek => false; + + public override bool CanWrite => true; + + public override long Length => 0; + + public override long Position + { + get { return 0; } + set { } + } } diff --git a/src/SharpCompress/Compressors/BZip2/CRC.cs b/src/SharpCompress/Compressors/BZip2/CRC.cs index d9b268cb..7c208aff 100644 --- a/src/SharpCompress/Compressors/BZip2/CRC.cs +++ b/src/SharpCompress/Compressors/BZip2/CRC.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright 2001,2004-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,312 +20,311 @@ * great code. */ -namespace SharpCompress.Compressors.BZip2 +namespace SharpCompress.Compressors.BZip2; + +/** + * A simple class the hold and calculate the CRC for sanity checking + * of the data. + * + * @author Keiron Liddle + */ + +internal class CRC { - /** - * A simple class the hold and calculate the CRC for sanity checking - * of the data. - * - * @author Keiron Liddle - */ - - internal class CRC + public static int[] crc32Table = { - public static int[] crc32Table = - { - 0x00000000, - 0x04c11db7, - 0x09823b6e, - 0x0d4326d9, - 0x130476dc, - 0x17c56b6b, - 0x1a864db2, - 0x1e475005, - 0x2608edb8, - 0x22c9f00f, - 0x2f8ad6d6, - 0x2b4bcb61, - 0x350c9b64, - 0x31cd86d3, - 0x3c8ea00a, - 0x384fbdbd, - 0x4c11db70, - 0x48d0c6c7, - 0x4593e01e, - 0x4152fda9, - 0x5f15adac, - 0x5bd4b01b, - 0x569796c2, - 0x52568b75, - 0x6a1936c8, - 0x6ed82b7f, - 0x639b0da6, - 0x675a1011, - 0x791d4014, - 0x7ddc5da3, - 0x709f7b7a, - 0x745e66cd, - unchecked((int)0x9823b6e0), - unchecked((int)0x9ce2ab57), - unchecked((int)0x91a18d8e), - unchecked((int)0x95609039), - unchecked((int)0x8b27c03c), - unchecked((int)0x8fe6dd8b), - unchecked((int)0x82a5fb52), - unchecked((int)0x8664e6e5), - unchecked((int)0xbe2b5b58), - unchecked((int)0xbaea46ef), - unchecked((int)0xb7a96036), - unchecked((int)0xb3687d81), - unchecked((int)0xad2f2d84), - unchecked((int)0xa9ee3033), - unchecked((int)0xa4ad16ea), - unchecked((int)0xa06c0b5d), - unchecked((int)0xd4326d90), - unchecked((int)0xd0f37027), - unchecked((int)0xddb056fe), - unchecked((int)0xd9714b49), - unchecked((int)0xc7361b4c), - unchecked((int)0xc3f706fb), - unchecked((int)0xceb42022), - unchecked((int)0xca753d95), - unchecked((int)0xf23a8028), - unchecked((int)0xf6fb9d9f), - unchecked((int)0xfbb8bb46), - unchecked((int)0xff79a6f1), - unchecked((int)0xe13ef6f4), - unchecked((int)0xe5ffeb43), - unchecked((int)0xe8bccd9a), - unchecked((int)0xec7dd02d), - 0x34867077, - 0x30476dc0, - 0x3d044b19, - 0x39c556ae, - 0x278206ab, - 0x23431b1c, - 0x2e003dc5, - 0x2ac12072, - 0x128e9dcf, - 0x164f8078, - 0x1b0ca6a1, - 0x1fcdbb16, - 0x018aeb13, - 0x054bf6a4, - 0x0808d07d, - 0x0cc9cdca, - 0x7897ab07, - 0x7c56b6b0, - 0x71159069, - 0x75d48dde, - 0x6b93dddb, - 0x6f52c06c, - 0x6211e6b5, - 0x66d0fb02, - 0x5e9f46bf, - 0x5a5e5b08, - 0x571d7dd1, - 0x53dc6066, - 0x4d9b3063, - 0x495a2dd4, - 0x44190b0d, - 0x40d816ba, - unchecked((int)0xaca5c697), - unchecked((int)0xa864db20), - unchecked((int)0xa527fdf9), - unchecked((int)0xa1e6e04e), - unchecked((int)0xbfa1b04b), - unchecked((int)0xbb60adfc), - unchecked((int)0xb6238b25), - unchecked((int)0xb2e29692), - unchecked((int)0x8aad2b2f), - unchecked((int)0x8e6c3698), - unchecked((int)0x832f1041), - unchecked((int)0x87ee0df6), - unchecked((int)0x99a95df3), - unchecked((int)0x9d684044), - unchecked((int)0x902b669d), - unchecked((int)0x94ea7b2a), - unchecked((int)0xe0b41de7), - unchecked((int)0xe4750050), - unchecked((int)0xe9362689), - unchecked((int)0xedf73b3e), - unchecked((int)0xf3b06b3b), - unchecked((int)0xf771768c), - unchecked((int)0xfa325055), - unchecked((int)0xfef34de2), - unchecked((int)0xc6bcf05f), - unchecked((int)0xc27dede8), - unchecked((int)0xcf3ecb31), - unchecked((int)0xcbffd686), - unchecked((int)0xd5b88683), - unchecked((int)0xd1799b34), - unchecked((int)0xdc3abded), - unchecked((int)0xd8fba05a), - 0x690ce0ee, - 0x6dcdfd59, - 0x608edb80, - 0x644fc637, - 0x7a089632, - 0x7ec98b85, - 0x738aad5c, - 0x774bb0eb, - 0x4f040d56, - 0x4bc510e1, - 0x46863638, - 0x42472b8f, - 0x5c007b8a, - 0x58c1663d, - 0x558240e4, - 0x51435d53, - 0x251d3b9e, - 0x21dc2629, - 0x2c9f00f0, - 0x285e1d47, - 0x36194d42, - 0x32d850f5, - 0x3f9b762c, - 0x3b5a6b9b, - 0x0315d626, - 0x07d4cb91, - 0x0a97ed48, - 0x0e56f0ff, - 0x1011a0fa, - 0x14d0bd4d, - 0x19939b94, - 0x1d528623, - unchecked((int)0xf12f560e), - unchecked((int)0xf5ee4bb9), - unchecked((int)0xf8ad6d60), - unchecked((int)0xfc6c70d7), - unchecked((int)0xe22b20d2), - unchecked((int)0xe6ea3d65), - unchecked((int)0xeba91bbc), - unchecked((int)0xef68060b), - unchecked((int)0xd727bbb6), - unchecked((int)0xd3e6a601), - unchecked((int)0xdea580d8), - unchecked((int)0xda649d6f), - unchecked((int)0xc423cd6a), - unchecked((int)0xc0e2d0dd), - unchecked((int)0xcda1f604), - unchecked((int)0xc960ebb3), - unchecked((int)0xbd3e8d7e), - unchecked((int)0xb9ff90c9), - unchecked((int)0xb4bcb610), - unchecked((int)0xb07daba7), - unchecked((int)0xae3afba2), - unchecked((int)0xaafbe615), - unchecked((int)0xa7b8c0cc), - unchecked((int)0xa379dd7b), - unchecked((int)0x9b3660c6), - unchecked((int)0x9ff77d71), - unchecked((int)0x92b45ba8), - unchecked((int)0x9675461f), - unchecked((int)0x8832161a), - unchecked((int)0x8cf30bad), - unchecked((int)0x81b02d74), - unchecked((int)0x857130c3), - 0x5d8a9099, - 0x594b8d2e, - 0x5408abf7, - 0x50c9b640, - 0x4e8ee645, - 0x4a4ffbf2, - 0x470cdd2b, - 0x43cdc09c, - 0x7b827d21, - 0x7f436096, - 0x7200464f, - 0x76c15bf8, - 0x68860bfd, - 0x6c47164a, - 0x61043093, - 0x65c52d24, - 0x119b4be9, - 0x155a565e, - 0x18197087, - 0x1cd86d30, - 0x029f3d35, - 0x065e2082, - 0x0b1d065b, - 0x0fdc1bec, - 0x3793a651, - 0x3352bbe6, - 0x3e119d3f, - 0x3ad08088, - 0x2497d08d, - 0x2056cd3a, - 0x2d15ebe3, - 0x29d4f654, - unchecked((int)0xc5a92679), - unchecked((int)0xc1683bce), - unchecked((int)0xcc2b1d17), - unchecked((int)0xc8ea00a0), - unchecked((int)0xd6ad50a5), - unchecked((int)0xd26c4d12), - unchecked((int)0xdf2f6bcb), - unchecked((int)0xdbee767c), - unchecked((int)0xe3a1cbc1), - unchecked((int)0xe760d676), - unchecked((int)0xea23f0af), - unchecked((int)0xeee2ed18), - unchecked((int)0xf0a5bd1d), - unchecked((int)0xf464a0aa), - unchecked((int)0xf9278673), - unchecked((int)0xfde69bc4), - unchecked((int)0x89b8fd09), - unchecked((int)0x8d79e0be), - unchecked((int)0x803ac667), - unchecked((int)0x84fbdbd0), - unchecked((int)0x9abc8bd5), - unchecked((int)0x9e7d9662), - unchecked((int)0x933eb0bb), - unchecked((int)0x97ffad0c), - unchecked((int)0xafb010b1), - unchecked((int)0xab710d06), - unchecked((int)0xa6322bdf), - unchecked((int)0xa2f33668), - unchecked((int)0xbcb4666d), - unchecked((int)0xb8757bda), - unchecked((int)0xb5365d03), - unchecked((int)0xb1f740b4) - }; + 0x00000000, + 0x04c11db7, + 0x09823b6e, + 0x0d4326d9, + 0x130476dc, + 0x17c56b6b, + 0x1a864db2, + 0x1e475005, + 0x2608edb8, + 0x22c9f00f, + 0x2f8ad6d6, + 0x2b4bcb61, + 0x350c9b64, + 0x31cd86d3, + 0x3c8ea00a, + 0x384fbdbd, + 0x4c11db70, + 0x48d0c6c7, + 0x4593e01e, + 0x4152fda9, + 0x5f15adac, + 0x5bd4b01b, + 0x569796c2, + 0x52568b75, + 0x6a1936c8, + 0x6ed82b7f, + 0x639b0da6, + 0x675a1011, + 0x791d4014, + 0x7ddc5da3, + 0x709f7b7a, + 0x745e66cd, + unchecked((int)0x9823b6e0), + unchecked((int)0x9ce2ab57), + unchecked((int)0x91a18d8e), + unchecked((int)0x95609039), + unchecked((int)0x8b27c03c), + unchecked((int)0x8fe6dd8b), + unchecked((int)0x82a5fb52), + unchecked((int)0x8664e6e5), + unchecked((int)0xbe2b5b58), + unchecked((int)0xbaea46ef), + unchecked((int)0xb7a96036), + unchecked((int)0xb3687d81), + unchecked((int)0xad2f2d84), + unchecked((int)0xa9ee3033), + unchecked((int)0xa4ad16ea), + unchecked((int)0xa06c0b5d), + unchecked((int)0xd4326d90), + unchecked((int)0xd0f37027), + unchecked((int)0xddb056fe), + unchecked((int)0xd9714b49), + unchecked((int)0xc7361b4c), + unchecked((int)0xc3f706fb), + unchecked((int)0xceb42022), + unchecked((int)0xca753d95), + unchecked((int)0xf23a8028), + unchecked((int)0xf6fb9d9f), + unchecked((int)0xfbb8bb46), + unchecked((int)0xff79a6f1), + unchecked((int)0xe13ef6f4), + unchecked((int)0xe5ffeb43), + unchecked((int)0xe8bccd9a), + unchecked((int)0xec7dd02d), + 0x34867077, + 0x30476dc0, + 0x3d044b19, + 0x39c556ae, + 0x278206ab, + 0x23431b1c, + 0x2e003dc5, + 0x2ac12072, + 0x128e9dcf, + 0x164f8078, + 0x1b0ca6a1, + 0x1fcdbb16, + 0x018aeb13, + 0x054bf6a4, + 0x0808d07d, + 0x0cc9cdca, + 0x7897ab07, + 0x7c56b6b0, + 0x71159069, + 0x75d48dde, + 0x6b93dddb, + 0x6f52c06c, + 0x6211e6b5, + 0x66d0fb02, + 0x5e9f46bf, + 0x5a5e5b08, + 0x571d7dd1, + 0x53dc6066, + 0x4d9b3063, + 0x495a2dd4, + 0x44190b0d, + 0x40d816ba, + unchecked((int)0xaca5c697), + unchecked((int)0xa864db20), + unchecked((int)0xa527fdf9), + unchecked((int)0xa1e6e04e), + unchecked((int)0xbfa1b04b), + unchecked((int)0xbb60adfc), + unchecked((int)0xb6238b25), + unchecked((int)0xb2e29692), + unchecked((int)0x8aad2b2f), + unchecked((int)0x8e6c3698), + unchecked((int)0x832f1041), + unchecked((int)0x87ee0df6), + unchecked((int)0x99a95df3), + unchecked((int)0x9d684044), + unchecked((int)0x902b669d), + unchecked((int)0x94ea7b2a), + unchecked((int)0xe0b41de7), + unchecked((int)0xe4750050), + unchecked((int)0xe9362689), + unchecked((int)0xedf73b3e), + unchecked((int)0xf3b06b3b), + unchecked((int)0xf771768c), + unchecked((int)0xfa325055), + unchecked((int)0xfef34de2), + unchecked((int)0xc6bcf05f), + unchecked((int)0xc27dede8), + unchecked((int)0xcf3ecb31), + unchecked((int)0xcbffd686), + unchecked((int)0xd5b88683), + unchecked((int)0xd1799b34), + unchecked((int)0xdc3abded), + unchecked((int)0xd8fba05a), + 0x690ce0ee, + 0x6dcdfd59, + 0x608edb80, + 0x644fc637, + 0x7a089632, + 0x7ec98b85, + 0x738aad5c, + 0x774bb0eb, + 0x4f040d56, + 0x4bc510e1, + 0x46863638, + 0x42472b8f, + 0x5c007b8a, + 0x58c1663d, + 0x558240e4, + 0x51435d53, + 0x251d3b9e, + 0x21dc2629, + 0x2c9f00f0, + 0x285e1d47, + 0x36194d42, + 0x32d850f5, + 0x3f9b762c, + 0x3b5a6b9b, + 0x0315d626, + 0x07d4cb91, + 0x0a97ed48, + 0x0e56f0ff, + 0x1011a0fa, + 0x14d0bd4d, + 0x19939b94, + 0x1d528623, + unchecked((int)0xf12f560e), + unchecked((int)0xf5ee4bb9), + unchecked((int)0xf8ad6d60), + unchecked((int)0xfc6c70d7), + unchecked((int)0xe22b20d2), + unchecked((int)0xe6ea3d65), + unchecked((int)0xeba91bbc), + unchecked((int)0xef68060b), + unchecked((int)0xd727bbb6), + unchecked((int)0xd3e6a601), + unchecked((int)0xdea580d8), + unchecked((int)0xda649d6f), + unchecked((int)0xc423cd6a), + unchecked((int)0xc0e2d0dd), + unchecked((int)0xcda1f604), + unchecked((int)0xc960ebb3), + unchecked((int)0xbd3e8d7e), + unchecked((int)0xb9ff90c9), + unchecked((int)0xb4bcb610), + unchecked((int)0xb07daba7), + unchecked((int)0xae3afba2), + unchecked((int)0xaafbe615), + unchecked((int)0xa7b8c0cc), + unchecked((int)0xa379dd7b), + unchecked((int)0x9b3660c6), + unchecked((int)0x9ff77d71), + unchecked((int)0x92b45ba8), + unchecked((int)0x9675461f), + unchecked((int)0x8832161a), + unchecked((int)0x8cf30bad), + unchecked((int)0x81b02d74), + unchecked((int)0x857130c3), + 0x5d8a9099, + 0x594b8d2e, + 0x5408abf7, + 0x50c9b640, + 0x4e8ee645, + 0x4a4ffbf2, + 0x470cdd2b, + 0x43cdc09c, + 0x7b827d21, + 0x7f436096, + 0x7200464f, + 0x76c15bf8, + 0x68860bfd, + 0x6c47164a, + 0x61043093, + 0x65c52d24, + 0x119b4be9, + 0x155a565e, + 0x18197087, + 0x1cd86d30, + 0x029f3d35, + 0x065e2082, + 0x0b1d065b, + 0x0fdc1bec, + 0x3793a651, + 0x3352bbe6, + 0x3e119d3f, + 0x3ad08088, + 0x2497d08d, + 0x2056cd3a, + 0x2d15ebe3, + 0x29d4f654, + unchecked((int)0xc5a92679), + unchecked((int)0xc1683bce), + unchecked((int)0xcc2b1d17), + unchecked((int)0xc8ea00a0), + unchecked((int)0xd6ad50a5), + unchecked((int)0xd26c4d12), + unchecked((int)0xdf2f6bcb), + unchecked((int)0xdbee767c), + unchecked((int)0xe3a1cbc1), + unchecked((int)0xe760d676), + unchecked((int)0xea23f0af), + unchecked((int)0xeee2ed18), + unchecked((int)0xf0a5bd1d), + unchecked((int)0xf464a0aa), + unchecked((int)0xf9278673), + unchecked((int)0xfde69bc4), + unchecked((int)0x89b8fd09), + unchecked((int)0x8d79e0be), + unchecked((int)0x803ac667), + unchecked((int)0x84fbdbd0), + unchecked((int)0x9abc8bd5), + unchecked((int)0x9e7d9662), + unchecked((int)0x933eb0bb), + unchecked((int)0x97ffad0c), + unchecked((int)0xafb010b1), + unchecked((int)0xab710d06), + unchecked((int)0xa6322bdf), + unchecked((int)0xa2f33668), + unchecked((int)0xbcb4666d), + unchecked((int)0xb8757bda), + unchecked((int)0xb5365d03), + unchecked((int)0xb1f740b4) + }; - public CRC() - { - InitialiseCRC(); - } - - internal void InitialiseCRC() - { - globalCrc = unchecked((int)0xffffffff); - } - - internal int GetFinalCRC() - { - return ~globalCrc; - } - - internal int GetGlobalCRC() - { - return globalCrc; - } - - internal void SetGlobalCRC(int newCrc) - { - globalCrc = newCrc; - } - - internal void UpdateCRC(int inCh) - { - int temp = (globalCrc >> 24) ^ inCh; - if (temp < 0) - { - temp = 256 + temp; - } - globalCrc = (globalCrc << 8) ^ crc32Table[temp]; - } - - internal int globalCrc; + public CRC() + { + InitialiseCRC(); } + + internal void InitialiseCRC() + { + globalCrc = unchecked((int)0xffffffff); + } + + internal int GetFinalCRC() + { + return ~globalCrc; + } + + internal int GetGlobalCRC() + { + return globalCrc; + } + + internal void SetGlobalCRC(int newCrc) + { + globalCrc = newCrc; + } + + internal void UpdateCRC(int inCh) + { + var temp = (globalCrc >> 24) ^ inCh; + if (temp < 0) + { + temp = 256 + temp; + } + globalCrc = (globalCrc << 8) ^ crc32Table[temp]; + } + + internal int globalCrc; } diff --git a/src/SharpCompress/Compressors/CompressionMode.cs b/src/SharpCompress/Compressors/CompressionMode.cs index a886c441..4eba1121 100644 --- a/src/SharpCompress/Compressors/CompressionMode.cs +++ b/src/SharpCompress/Compressors/CompressionMode.cs @@ -1,8 +1,7 @@ -namespace SharpCompress.Compressors +namespace SharpCompress.Compressors; + +public enum CompressionMode { - public enum CompressionMode - { - Compress = 0, - Decompress = 1 - } + Compress = 0, + Decompress = 1 } diff --git a/src/SharpCompress/Compressors/Deflate/CRC32.cs b/src/SharpCompress/Compressors/Deflate/CRC32.cs index c1f3a0e2..01e5ba2a 100644 --- a/src/SharpCompress/Compressors/Deflate/CRC32.cs +++ b/src/SharpCompress/Compressors/Deflate/CRC32.cs @@ -35,260 +35,253 @@ using System; using System.IO; -namespace SharpCompress.Compressors.Deflate +namespace SharpCompress.Compressors.Deflate; + +/// +/// Calculates a 32bit Cyclic Redundancy Checksum (CRC) using the same polynomial +/// used by Zip. This type is used internally by DotNetZip; it is generally not used +/// directly by applications wishing to create, read, or manipulate zip archive +/// files. +/// +[CLSCompliant(false)] +public class CRC32 { - /// - /// Calculates a 32bit Cyclic Redundancy Checksum (CRC) using the same polynomial - /// used by Zip. This type is used internally by DotNetZip; it is generally not used - /// directly by applications wishing to create, read, or manipulate zip archive - /// files. - /// - [CLSCompliant(false)] - public class CRC32 + private const int BUFFER_SIZE = 8192; + private static readonly uint[] crc32Table; + private uint runningCrc32Result = 0xFFFFFFFF; + + static CRC32() { - private const int BUFFER_SIZE = 8192; - private static readonly UInt32[] crc32Table; - private UInt32 runningCrc32Result = 0xFFFFFFFF; - - static CRC32() + unchecked { - unchecked + // PKZip specifies CRC32 with a polynomial of 0xEDB88320; + // This is also the CRC-32 polynomial used bby Ethernet, FDDI, + // bzip2, gzip, and others. + // Often the polynomial is shown reversed as 0x04C11DB7. + // For more details, see http://en.wikipedia.org/wiki/Cyclic_redundancy_check + var dwPolynomial = 0xEDB88320; + uint i, + j; + + crc32Table = new uint[256]; + + uint dwCrc; + for (i = 0; i < 256; i++) { - // PKZip specifies CRC32 with a polynomial of 0xEDB88320; - // This is also the CRC-32 polynomial used bby Ethernet, FDDI, - // bzip2, gzip, and others. - // Often the polynomial is shown reversed as 0x04C11DB7. - // For more details, see http://en.wikipedia.org/wiki/Cyclic_redundancy_check - UInt32 dwPolynomial = 0xEDB88320; - UInt32 i, - j; - - crc32Table = new UInt32[256]; - - UInt32 dwCrc; - for (i = 0; i < 256; i++) + dwCrc = i; + for (j = 8; j > 0; j--) { - dwCrc = i; - for (j = 8; j > 0; j--) + if ((dwCrc & 1) == 1) { - if ((dwCrc & 1) == 1) - { - dwCrc = (dwCrc >> 1) ^ dwPolynomial; - } - else - { - dwCrc >>= 1; - } + dwCrc = (dwCrc >> 1) ^ dwPolynomial; } - crc32Table[i] = dwCrc; - } - } - } - - /// - /// indicates the total number of bytes read on the CRC stream. - /// This is used when writing the ZipDirEntry when compressing files. - /// - public Int64 TotalBytesRead { get; private set; } - - /// - /// Indicates the current CRC for all blocks slurped in. - /// - public Int32 Crc32Result => unchecked((Int32)(~runningCrc32Result)); - - /// - /// Returns the CRC32 for the specified stream. - /// - /// The stream over which to calculate the CRC32 - /// the CRC32 calculation - public UInt32 GetCrc32(Stream input) - { - return GetCrc32AndCopy(input, null); - } - - /// - /// Returns the CRC32 for the specified stream, and writes the input into the - /// output stream. - /// - /// The stream over which to calculate the CRC32 - /// The stream into which to deflate the input - /// the CRC32 calculation - public UInt32 GetCrc32AndCopy(Stream input, Stream? output) - { - if (input is null) - { - throw new ZlibException("The input stream must not be null."); - } - unchecked - { - //UInt32 crc32Result; - //crc32Result = 0xFFFFFFFF; - var buffer = new byte[BUFFER_SIZE]; - int readSize = BUFFER_SIZE; - - TotalBytesRead = 0; - int count = input.Read(buffer, 0, readSize); - if (output != null) - { - output.Write(buffer, 0, count); - } - TotalBytesRead += count; - while (count > 0) - { - SlurpBlock(buffer, 0, count); - count = input.Read(buffer, 0, readSize); - if (output != null) + else { - output.Write(buffer, 0, count); + dwCrc >>= 1; } - TotalBytesRead += count; } - - return ~runningCrc32Result; + crc32Table[i] = dwCrc; } } + } - /// - /// Get the CRC32 for the given (word,byte) combo. This is a computation - /// defined by PKzip. - /// - /// The word to start with. - /// The byte to combine it with. - /// The CRC-ized result. - public Int32 ComputeCrc32(Int32 W, byte B) + /// + /// indicates the total number of bytes read on the CRC stream. + /// This is used when writing the ZipDirEntry when compressing files. + /// + public long TotalBytesRead { get; private set; } + + /// + /// Indicates the current CRC for all blocks slurped in. + /// + public int Crc32Result => unchecked((int)(~runningCrc32Result)); + + /// + /// Returns the CRC32 for the specified stream. + /// + /// The stream over which to calculate the CRC32 + /// the CRC32 calculation + public uint GetCrc32(Stream input) + { + return GetCrc32AndCopy(input, null); + } + + /// + /// Returns the CRC32 for the specified stream, and writes the input into the + /// output stream. + /// + /// The stream over which to calculate the CRC32 + /// The stream into which to deflate the input + /// the CRC32 calculation + public uint GetCrc32AndCopy(Stream input, Stream? output) + { + if (input is null) { - return _InternalComputeCrc32((UInt32)W, B); + throw new ZlibException("The input stream must not be null."); } - - internal int _InternalComputeCrc32(UInt32 W, byte B) + unchecked { - return (Int32)(crc32Table[(W ^ B) & 0xFF] ^ (W >> 8)); - } + //UInt32 crc32Result; + //crc32Result = 0xFFFFFFFF; + var buffer = new byte[BUFFER_SIZE]; + var readSize = BUFFER_SIZE; - /// - /// Update the value for the running CRC32 using the given block of bytes. - /// This is useful when using the CRC32() class in a Stream. - /// - /// block of bytes to slurp - /// starting point in the block - /// how many bytes within the block to slurp - public void SlurpBlock(byte[] block, int offset, int count) - { - if (block is null) - { - throw new ZlibException("The data buffer must not be null."); - } - - for (int i = 0; i < count; i++) - { - int x = offset + i; - runningCrc32Result = - ((runningCrc32Result) >> 8) - ^ crc32Table[(block[x]) ^ ((runningCrc32Result) & 0x000000FF)]; - } + TotalBytesRead = 0; + var count = input.Read(buffer, 0, readSize); + output?.Write(buffer, 0, count); TotalBytesRead += count; + while (count > 0) + { + SlurpBlock(buffer, 0, count); + count = input.Read(buffer, 0, readSize); + output?.Write(buffer, 0, count); + TotalBytesRead += count; + } + + return ~runningCrc32Result; + } + } + + /// + /// Get the CRC32 for the given (word,byte) combo. This is a computation + /// defined by PKzip. + /// + /// The word to start with. + /// The byte to combine it with. + /// The CRC-ized result. + public int ComputeCrc32(int W, byte B) + { + return _InternalComputeCrc32((uint)W, B); + } + + internal int _InternalComputeCrc32(uint W, byte B) + { + return (int)(crc32Table[(W ^ B) & 0xFF] ^ (W >> 8)); + } + + /// + /// Update the value for the running CRC32 using the given block of bytes. + /// This is useful when using the CRC32() class in a Stream. + /// + /// block of bytes to slurp + /// starting point in the block + /// how many bytes within the block to slurp + public void SlurpBlock(byte[] block, int offset, int count) + { + if (block is null) + { + throw new ZlibException("The data buffer must not be null."); } - // pre-initialize the crc table for speed of lookup. - - private uint gf2_matrix_times(ReadOnlySpan matrix, uint vec) + for (var i = 0; i < count; i++) { - uint sum = 0; - int i = 0; - while (vec != 0) + var x = offset + i; + runningCrc32Result = + ((runningCrc32Result) >> 8) + ^ crc32Table[(block[x]) ^ ((runningCrc32Result) & 0x000000FF)]; + } + TotalBytesRead += count; + } + + // pre-initialize the crc table for speed of lookup. + + private uint gf2_matrix_times(ReadOnlySpan matrix, uint vec) + { + uint sum = 0; + var i = 0; + while (vec != 0) + { + if ((vec & 0x01) == 0x01) { - if ((vec & 0x01) == 0x01) - { - sum ^= matrix[i]; - } - vec >>= 1; - i++; + sum ^= matrix[i]; } - return sum; + vec >>= 1; + i++; + } + return sum; + } + + private void gf2_matrix_square(Span square, Span mat) + { + for (var i = 0; i < 32; i++) + { + square[i] = gf2_matrix_times(mat, mat[i]); + } + } + + /// + /// Combines the given CRC32 value with the current running total. + /// + /// + /// This is useful when using a divide-and-conquer approach to calculating a CRC. + /// Multiple threads can each calculate a CRC32 on a segment of the data, and then + /// combine the individual CRC32 values at the end. + /// + /// the crc value to be combined with this one + /// the length of data the CRC value was calculated on + public void Combine(int crc, int length) + { + Span even = stackalloc uint[32]; // even-power-of-two zeros operator + Span odd = stackalloc uint[32]; // odd-power-of-two zeros operator + + if (length == 0) + { + return; } - private void gf2_matrix_square(Span square, Span mat) + var crc1 = ~runningCrc32Result; + var crc2 = (uint)crc; + + // put operator for one zero bit in odd + odd[0] = 0xEDB88320; // the CRC-32 polynomial + uint row = 1; + for (var i = 1; i < 32; i++) { - for (int i = 0; i < 32; i++) - { - square[i] = gf2_matrix_times(mat, mat[i]); - } + odd[i] = row; + row <<= 1; } - /// - /// Combines the given CRC32 value with the current running total. - /// - /// - /// This is useful when using a divide-and-conquer approach to calculating a CRC. - /// Multiple threads can each calculate a CRC32 on a segment of the data, and then - /// combine the individual CRC32 values at the end. - /// - /// the crc value to be combined with this one - /// the length of data the CRC value was calculated on - public void Combine(int crc, int length) + // put operator for two zero bits in even + gf2_matrix_square(even, odd); + + // put operator for four zero bits in odd + gf2_matrix_square(odd, even); + + var len2 = (uint)length; + + // apply len2 zeros to crc1 (first square will put the operator for one + // zero byte, eight zero bits, in even) + do { - Span even = stackalloc uint[32]; // even-power-of-two zeros operator - Span odd = stackalloc uint[32]; // odd-power-of-two zeros operator - - if (length == 0) - { - return; - } - - uint crc1 = ~runningCrc32Result; - var crc2 = (uint)crc; - - // put operator for one zero bit in odd - odd[0] = 0xEDB88320; // the CRC-32 polynomial - uint row = 1; - for (int i = 1; i < 32; i++) - { - odd[i] = row; - row <<= 1; - } - - // put operator for two zero bits in even + // apply zeros operator for this bit of len2 gf2_matrix_square(even, odd); - // put operator for four zero bits in odd - gf2_matrix_square(odd, even); - - var len2 = (uint)length; - - // apply len2 zeros to crc1 (first square will put the operator for one - // zero byte, eight zero bits, in even) - do + if ((len2 & 1) == 1) { - // apply zeros operator for this bit of len2 - gf2_matrix_square(even, odd); + crc1 = gf2_matrix_times(even, crc1); + } + len2 >>= 1; - if ((len2 & 1) == 1) - { - crc1 = gf2_matrix_times(even, crc1); - } - len2 >>= 1; + if (len2 == 0) + { + break; + } - if (len2 == 0) - { - break; - } + // another iteration of the loop with odd and even swapped + gf2_matrix_square(odd, even); + if ((len2 & 1) == 1) + { + crc1 = gf2_matrix_times(odd, crc1); + } + len2 >>= 1; + } while (len2 != 0); - // another iteration of the loop with odd and even swapped - gf2_matrix_square(odd, even); - if ((len2 & 1) == 1) - { - crc1 = gf2_matrix_times(odd, crc1); - } - len2 >>= 1; - } while (len2 != 0); + crc1 ^= crc2; - crc1 ^= crc2; + runningCrc32Result = ~crc1; - runningCrc32Result = ~crc1; - - //return (int) crc1; - } - - // private member vars + //return (int) crc1; } + + // private member vars } diff --git a/src/SharpCompress/Compressors/Deflate/DeflateManager.cs b/src/SharpCompress/Compressors/Deflate/DeflateManager.cs index e6dd441d..f424b1a0 100644 --- a/src/SharpCompress/Compressors/Deflate/DeflateManager.cs +++ b/src/SharpCompress/Compressors/Deflate/DeflateManager.cs @@ -72,1322 +72,1464 @@ using System; using SharpCompress.Algorithms; -namespace SharpCompress.Compressors.Deflate +namespace SharpCompress.Compressors.Deflate; + +internal sealed partial class DeflateManager { - internal sealed partial class DeflateManager + // extra bits for each length code + internal static readonly int[] ExtraLengthBits = { - // extra bits for each length code - internal static readonly int[] ExtraLengthBits = - { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 4, - 4, - 4, - 4, - 5, - 5, - 5, - 5, - 0 - }; + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 0 + }; - // extra bits for each distance code - internal static readonly int[] ExtraDistanceBits = - { - 0, - 0, - 0, - 0, - 1, - 1, - 2, - 2, - 3, - 3, - 4, - 4, - 5, - 5, - 6, - 6, - 7, - 7, - 8, - 8, - 9, - 9, - 10, - 10, - 11, - 11, - 12, - 12, - 13, - 13 - }; + // extra bits for each distance code + internal static readonly int[] ExtraDistanceBits = + { + 0, + 0, + 0, + 0, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 5, + 6, + 6, + 7, + 7, + 8, + 8, + 9, + 9, + 10, + 10, + 11, + 11, + 12, + 12, + 13, + 13 + }; - internal enum BlockState + internal enum BlockState + { + NeedMore = 0, // block not completed, need more input or more output + BlockDone, // block flush performed + FinishStarted, // finish started, need only more output at next deflate + FinishDone // finish done, accept no more input or output + } + + internal enum DeflateFlavor + { + Store, + Fast, + Slow + } + + private const int MEM_LEVEL_MAX = 9; + private const int MEM_LEVEL_DEFAULT = 8; + + internal delegate BlockState CompressFunc(FlushType flush); + + internal class Config + { + // Use a faster search when the previous match is longer than this + internal int GoodLength; // reduce lazy search above this match length + + // Attempt to find a better match only when the current match is + // strictly smaller than this value. This mechanism is used only for + // compression levels >= 4. For levels 1,2,3: MaxLazy is actually + // MaxInsertLength. (See DeflateFast) + + internal int MaxLazy; // do not perform lazy search above this match length + + internal int NiceLength; // quit search above this match length + + // To speed up deflation, hash chains are never searched beyond this + // length. A higher limit improves compression ratio but degrades the speed. + + internal int MaxChainLength; + + internal DeflateFlavor Flavor; + + private Config( + int goodLength, + int maxLazy, + int niceLength, + int maxChainLength, + DeflateFlavor flavor + ) { - NeedMore = 0, // block not completed, need more input or more output - BlockDone, // block flush performed - FinishStarted, // finish started, need only more output at next deflate - FinishDone // finish done, accept no more input or output + GoodLength = goodLength; + MaxLazy = maxLazy; + NiceLength = niceLength; + MaxChainLength = maxChainLength; + Flavor = flavor; } - internal enum DeflateFlavor + public static Config Lookup(CompressionLevel level) { - Store, - Fast, - Slow + return Table[(int)level]; } - private const int MEM_LEVEL_MAX = 9; - private const int MEM_LEVEL_DEFAULT = 8; - - internal delegate BlockState CompressFunc(FlushType flush); - - internal class Config + static Config() { - // Use a faster search when the previous match is longer than this - internal int GoodLength; // reduce lazy search above this match length - - // Attempt to find a better match only when the current match is - // strictly smaller than this value. This mechanism is used only for - // compression levels >= 4. For levels 1,2,3: MaxLazy is actually - // MaxInsertLength. (See DeflateFast) - - internal int MaxLazy; // do not perform lazy search above this match length - - internal int NiceLength; // quit search above this match length - - // To speed up deflation, hash chains are never searched beyond this - // length. A higher limit improves compression ratio but degrades the speed. - - internal int MaxChainLength; - - internal DeflateFlavor Flavor; - - private Config( - int goodLength, - int maxLazy, - int niceLength, - int maxChainLength, - DeflateFlavor flavor - ) + Table = new[] { - GoodLength = goodLength; - MaxLazy = maxLazy; - NiceLength = niceLength; - MaxChainLength = maxChainLength; - Flavor = flavor; + new Config(0, 0, 0, 0, DeflateFlavor.Store), + new Config(4, 4, 8, 4, DeflateFlavor.Fast), + new Config(4, 5, 16, 8, DeflateFlavor.Fast), + new Config(4, 6, 32, 32, DeflateFlavor.Fast), + new Config(4, 4, 16, 16, DeflateFlavor.Slow), + new Config(8, 16, 32, 32, DeflateFlavor.Slow), + new Config(8, 16, 128, 128, DeflateFlavor.Slow), + new Config(8, 32, 128, 256, DeflateFlavor.Slow), + new Config(32, 128, 258, 1024, DeflateFlavor.Slow), + new Config(32, 258, 258, 4096, DeflateFlavor.Slow) + }; + } + + private static readonly Config[] Table; + } + + private CompressFunc DeflateFunction; + + private static readonly string[] _ErrorMessage = + { + "need dictionary", + "stream end", + "", + "file error", + "stream error", + "data error", + "insufficient memory", + "buffer error", + "incompatible version", + "" + }; + + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int INIT_STATE = 42; + private const int BUSY_STATE = 113; + private const int FINISH_STATE = 666; + + // The deflate compression method + private const int Z_DEFLATED = 8; + + private const int STORED_BLOCK = 0; + private const int STATIC_TREES = 1; + private const int DYN_TREES = 2; + + // The three kinds of block type + private const int Z_BINARY = 0; + private const int Z_ASCII = 1; + private const int Z_UNKNOWN = 2; + + private const int Buf_size = 8 * 2; + + private const int MIN_MATCH = 3; + private const int MAX_MATCH = 258; + + private const int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); + + private static readonly int HEAP_SIZE = ((2 * InternalConstants.L_CODES) + 1); + + private const int END_BLOCK = 256; + + internal ZlibCodec _codec; // the zlib encoder/decoder + internal int status; // as the name implies + internal byte[] pending; // output still pending - waiting to be compressed + internal int nextPending; // index of next pending byte to output to the stream + internal int pendingCount; // number of bytes in the pending buffer + + internal sbyte data_type; // UNKNOWN, BINARY or ASCII + internal int last_flush; // value of flush param for previous deflate call + + internal int w_size; // LZ77 window size (32K by default) + internal int w_bits; // log2(w_size) (8..16) + internal int w_mask; // w_size - 1 + + //internal byte[] dictionary; + internal byte[] window; + + // Sliding window. Input bytes are read into the second half of the window, + // and move to the first half later to keep a dictionary of at least wSize + // bytes. With this organization, matches are limited to a distance of + // wSize-MAX_MATCH bytes, but this ensures that IO is always + // performed with a length multiple of the block size. + // + // To do: use the user input buffer as sliding window. + + internal int window_size; + + // Actual size of window: 2*wSize, except when the user input buffer + // is directly used as sliding window. + + internal short[] prev; + + // Link to older string with same hash index. To limit the size of this + // array to 64K, this link is maintained only for the last 32K strings. + // An index in this array is thus a window index modulo 32K. + + private short[] head; // Heads of the hash chains or NIL. + + private int ins_h; // hash index of string to be inserted + private int hash_size; // number of elements in hash table + private int hash_bits; // log2(hash_size) + private int hash_mask; // hash_size-1 + + // Number of bits by which ins_h must be shifted at each input + // step. It must be such that after MIN_MATCH steps, the oldest + // byte no longer takes part in the hash key, that is: + // hash_shift * MIN_MATCH >= hash_bits + private int hash_shift; + + // Window position at the beginning of the current output block. Gets + // negative when the window is moved backwards. + + private int blockStart; + + private Config config; + private int match_length; // length of best match + private int prev_match; // previous match + private int match_available; // set if previous match exists + private int strstart; // start of string to insert into.....???? + private int match_start; // start of matching string + private int lookahead; // number of valid bytes ahead in window + + // Length of the best match at previous step. Matches not greater than this + // are discarded. This is used in the lazy match evaluation. + private int prev_length; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + private CompressionLevel compressionLevel; // compression level (1..9) + private CompressionStrategy compressionStrategy; // favor or force Huffman coding + + private readonly short[] dyn_ltree; // literal and length tree + private readonly short[] dyn_dtree; // distance tree + private readonly short[] bl_tree; // Huffman tree for bit lengths + + private readonly Tree treeLiterals = new Tree(); // desc for literal tree + private readonly Tree treeDistances = new Tree(); // desc for distance tree + private readonly Tree treeBitLengths = new Tree(); // desc for bit length tree + + // number of codes at each bit length for an optimal tree + private readonly short[] bl_count = new short[InternalConstants.MAX_BITS + 1]; + + // heap used to build the Huffman trees + private readonly int[] heap = new int[(2 * InternalConstants.L_CODES) + 1]; + + private int heap_len; // number of elements in the heap + private int heap_max; // element of largest frequency + + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + // Depth of each subtree used as tie breaker for trees of equal frequency + private readonly sbyte[] depth = new sbyte[(2 * InternalConstants.L_CODES) + 1]; + + private int _lengthOffset; // index for literals or lengths + + // Size of match buffer for literals/lengths. There are 4 reasons for + // limiting lit_bufsize to 64K: + // - frequencies can be kept in 16 bit counters + // - if compression is not successful for the first block, all input + // data is still in the window so we can still emit a stored block even + // when input comes from standard input. (This can also be done for + // all blocks if lit_bufsize is not greater than 32K.) + // - if compression is not successful for a file smaller than 64K, we can + // even emit a stored file instead of a stored block (saving 5 bytes). + // This is applicable only for zip (not gzip or zlib). + // - creating new Huffman trees less frequently may not provide fast + // adaptation to changes in the input data statistics. (Take for + // example a binary file with poorly compressible code followed by + // a highly compressible string table.) Smaller buffer sizes give + // fast adaptation but have of course the overhead of transmitting + // trees more frequently. + + internal int lit_bufsize; + + internal int last_lit; // running index in l_buf + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + internal int _distanceOffset; // index into pending; points to distance data?? + + internal int opt_len; // bit length of current block with optimal trees + internal int static_len; // bit length of current block with static trees + internal int matches; // number of string matches in current block + internal int last_eob_len; // bit length of EOB code for last block + + // Output buffer. bits are inserted starting at the bottom (least + // significant bits). + internal short bi_buf; + + // Number of valid bits in bi_buf. All bits above the last valid bit + // are always zero. + internal int bi_valid; + + internal DeflateManager() + { + dyn_ltree = new short[HEAP_SIZE * 2]; + dyn_dtree = new short[((2 * InternalConstants.D_CODES) + 1) * 2]; // distance tree + bl_tree = new short[((2 * InternalConstants.BL_CODES) + 1) * 2]; // Huffman tree for bit lengths + } + + // lm_init + private void _InitializeLazyMatch() + { + window_size = 2 * w_size; + + // clear the hash - workitem 9063 + Array.Clear(head, 0, hash_size); + + //for (int i = 0; i < hash_size; i++) head[i] = 0; + + config = Config.Lookup(compressionLevel); + SetDeflater(); + + strstart = 0; + blockStart = 0; + lookahead = 0; + match_length = prev_length = MIN_MATCH - 1; + match_available = 0; + ins_h = 0; + } + + // Initialize the tree data structures for a new zlib stream. + private void _InitializeTreeData() + { + treeLiterals.dyn_tree = dyn_ltree; + treeLiterals.staticTree = StaticTree.Literals; + + treeDistances.dyn_tree = dyn_dtree; + treeDistances.staticTree = StaticTree.Distances; + + treeBitLengths.dyn_tree = bl_tree; + treeBitLengths.staticTree = StaticTree.BitLengths; + + bi_buf = 0; + bi_valid = 0; + last_eob_len = 8; // enough lookahead for inflate + + // Initialize the first block of the first file: + _InitializeBlocks(); + } + + internal void _InitializeBlocks() + { + // Initialize the trees. + for (var i = 0; i < InternalConstants.L_CODES; i++) + { + dyn_ltree[i * 2] = 0; + } + for (var i = 0; i < InternalConstants.D_CODES; i++) + { + dyn_dtree[i * 2] = 0; + } + for (var i = 0; i < InternalConstants.BL_CODES; i++) + { + bl_tree[i * 2] = 0; + } + + dyn_ltree[END_BLOCK * 2] = 1; + opt_len = static_len = 0; + last_lit = matches = 0; + } + + // Restore the heap property by moving down the tree starting at node k, + // exchanging a node with the smallest of its two sons if necessary, stopping + // when the heap property is re-established (each father smaller than its + // two sons). + internal void pqdownheap(short[] tree, int k) + { + var v = heap[k]; + var j = k << 1; // left son of k + while (j <= heap_len) + { + // Set j to the smallest of the two sons: + if (j < heap_len && IsSmaller(tree, heap[j + 1], heap[j], depth)) + { + j++; } - public static Config Lookup(CompressionLevel level) + // Exit if v is smaller than both sons + if (IsSmaller(tree, v, heap[j], depth)) { - return Table[(int)level]; + break; } - static Config() + // Exchange v with the smallest son + heap[k] = heap[j]; + k = j; + + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + heap[k] = v; + } + + internal static bool IsSmaller(short[] tree, int n, int m, sbyte[] depth) + { + var tn2 = tree[n * 2]; + var tm2 = tree[m * 2]; + return (tn2 < tm2 || (tn2 == tm2 && depth[n] <= depth[m])); + } + + // Scan a literal or distance tree to determine the frequencies of the codes + // in the bit length tree. + internal void ScanTree(short[] tree, int maxCode) + { + int n; // iterates over all tree elements + var prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[(0 * 2) + 1]; // length of next code + var count = 0; // repeat count of the current code + var max_count = 7; // max repeat count + var min_count = 4; // min repeat count + + if (nextlen == 0) + { + max_count = 138; + min_count = 3; + } + tree[((maxCode + 1) * 2) + 1] = 0x7fff; // guard //?? + + for (n = 0; n <= maxCode; n++) + { + curlen = nextlen; + nextlen = tree[((n + 1) * 2) + 1]; + if (++count < max_count && curlen == nextlen) { - Table = new[] - { - new Config(0, 0, 0, 0, DeflateFlavor.Store), - new Config(4, 4, 8, 4, DeflateFlavor.Fast), - new Config(4, 5, 16, 8, DeflateFlavor.Fast), - new Config(4, 6, 32, 32, DeflateFlavor.Fast), - new Config(4, 4, 16, 16, DeflateFlavor.Slow), - new Config(8, 16, 32, 32, DeflateFlavor.Slow), - new Config(8, 16, 128, 128, DeflateFlavor.Slow), - new Config(8, 32, 128, 256, DeflateFlavor.Slow), - new Config(32, 128, 258, 1024, DeflateFlavor.Slow), - new Config(32, 258, 258, 4096, DeflateFlavor.Slow) - }; + continue; } - - private static readonly Config[] Table; - } - - private CompressFunc DeflateFunction; - - private static readonly String[] _ErrorMessage = - { - "need dictionary", - "stream end", - "", - "file error", - "stream error", - "data error", - "insufficient memory", - "buffer error", - "incompatible version", - "" - }; - - // preset dictionary flag in zlib header - private const int PRESET_DICT = 0x20; - - private const int INIT_STATE = 42; - private const int BUSY_STATE = 113; - private const int FINISH_STATE = 666; - - // The deflate compression method - private const int Z_DEFLATED = 8; - - private const int STORED_BLOCK = 0; - private const int STATIC_TREES = 1; - private const int DYN_TREES = 2; - - // The three kinds of block type - private const int Z_BINARY = 0; - private const int Z_ASCII = 1; - private const int Z_UNKNOWN = 2; - - private const int Buf_size = 8 * 2; - - private const int MIN_MATCH = 3; - private const int MAX_MATCH = 258; - - private const int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); - - private static readonly int HEAP_SIZE = (2 * InternalConstants.L_CODES + 1); - - private const int END_BLOCK = 256; - - internal ZlibCodec _codec; // the zlib encoder/decoder - internal int status; // as the name implies - internal byte[] pending; // output still pending - waiting to be compressed - internal int nextPending; // index of next pending byte to output to the stream - internal int pendingCount; // number of bytes in the pending buffer - - internal sbyte data_type; // UNKNOWN, BINARY or ASCII - internal int last_flush; // value of flush param for previous deflate call - - internal int w_size; // LZ77 window size (32K by default) - internal int w_bits; // log2(w_size) (8..16) - internal int w_mask; // w_size - 1 - - //internal byte[] dictionary; - internal byte[] window; - - // Sliding window. Input bytes are read into the second half of the window, - // and move to the first half later to keep a dictionary of at least wSize - // bytes. With this organization, matches are limited to a distance of - // wSize-MAX_MATCH bytes, but this ensures that IO is always - // performed with a length multiple of the block size. - // - // To do: use the user input buffer as sliding window. - - internal int window_size; - - // Actual size of window: 2*wSize, except when the user input buffer - // is directly used as sliding window. - - internal short[] prev; - - // Link to older string with same hash index. To limit the size of this - // array to 64K, this link is maintained only for the last 32K strings. - // An index in this array is thus a window index modulo 32K. - - private short[] head; // Heads of the hash chains or NIL. - - private int ins_h; // hash index of string to be inserted - private int hash_size; // number of elements in hash table - private int hash_bits; // log2(hash_size) - private int hash_mask; // hash_size-1 - - // Number of bits by which ins_h must be shifted at each input - // step. It must be such that after MIN_MATCH steps, the oldest - // byte no longer takes part in the hash key, that is: - // hash_shift * MIN_MATCH >= hash_bits - private int hash_shift; - - // Window position at the beginning of the current output block. Gets - // negative when the window is moved backwards. - - private int blockStart; - - private Config config; - private int match_length; // length of best match - private int prev_match; // previous match - private int match_available; // set if previous match exists - private int strstart; // start of string to insert into.....???? - private int match_start; // start of matching string - private int lookahead; // number of valid bytes ahead in window - - // Length of the best match at previous step. Matches not greater than this - // are discarded. This is used in the lazy match evaluation. - private int prev_length; - - // Insert new strings in the hash table only if the match length is not - // greater than this length. This saves time but degrades compression. - // max_insert_length is used only for compression levels <= 3. - - private CompressionLevel compressionLevel; // compression level (1..9) - private CompressionStrategy compressionStrategy; // favor or force Huffman coding - - private readonly short[] dyn_ltree; // literal and length tree - private readonly short[] dyn_dtree; // distance tree - private readonly short[] bl_tree; // Huffman tree for bit lengths - - private readonly Tree treeLiterals = new Tree(); // desc for literal tree - private readonly Tree treeDistances = new Tree(); // desc for distance tree - private readonly Tree treeBitLengths = new Tree(); // desc for bit length tree - - // number of codes at each bit length for an optimal tree - private readonly short[] bl_count = new short[InternalConstants.MAX_BITS + 1]; - - // heap used to build the Huffman trees - private readonly int[] heap = new int[2 * InternalConstants.L_CODES + 1]; - - private int heap_len; // number of elements in the heap - private int heap_max; // element of largest frequency - - // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. - // The same heap array is used to build all trees. - - // Depth of each subtree used as tie breaker for trees of equal frequency - private readonly sbyte[] depth = new sbyte[2 * InternalConstants.L_CODES + 1]; - - private int _lengthOffset; // index for literals or lengths - - // Size of match buffer for literals/lengths. There are 4 reasons for - // limiting lit_bufsize to 64K: - // - frequencies can be kept in 16 bit counters - // - if compression is not successful for the first block, all input - // data is still in the window so we can still emit a stored block even - // when input comes from standard input. (This can also be done for - // all blocks if lit_bufsize is not greater than 32K.) - // - if compression is not successful for a file smaller than 64K, we can - // even emit a stored file instead of a stored block (saving 5 bytes). - // This is applicable only for zip (not gzip or zlib). - // - creating new Huffman trees less frequently may not provide fast - // adaptation to changes in the input data statistics. (Take for - // example a binary file with poorly compressible code followed by - // a highly compressible string table.) Smaller buffer sizes give - // fast adaptation but have of course the overhead of transmitting - // trees more frequently. - - internal int lit_bufsize; - - internal int last_lit; // running index in l_buf - - // Buffer for distances. To simplify the code, d_buf and l_buf have - // the same number of elements. To use different lengths, an extra flag - // array would be necessary. - - internal int _distanceOffset; // index into pending; points to distance data?? - - internal int opt_len; // bit length of current block with optimal trees - internal int static_len; // bit length of current block with static trees - internal int matches; // number of string matches in current block - internal int last_eob_len; // bit length of EOB code for last block - - // Output buffer. bits are inserted starting at the bottom (least - // significant bits). - internal short bi_buf; - - // Number of valid bits in bi_buf. All bits above the last valid bit - // are always zero. - internal int bi_valid; - - internal DeflateManager() - { - dyn_ltree = new short[HEAP_SIZE * 2]; - dyn_dtree = new short[(2 * InternalConstants.D_CODES + 1) * 2]; // distance tree - bl_tree = new short[(2 * InternalConstants.BL_CODES + 1) * 2]; // Huffman tree for bit lengths - } - - // lm_init - private void _InitializeLazyMatch() - { - window_size = 2 * w_size; - - // clear the hash - workitem 9063 - Array.Clear(head, 0, hash_size); - - //for (int i = 0; i < hash_size; i++) head[i] = 0; - - config = Config.Lookup(compressionLevel); - SetDeflater(); - - strstart = 0; - blockStart = 0; - lookahead = 0; - match_length = prev_length = MIN_MATCH - 1; - match_available = 0; - ins_h = 0; - } - - // Initialize the tree data structures for a new zlib stream. - private void _InitializeTreeData() - { - treeLiterals.dyn_tree = dyn_ltree; - treeLiterals.staticTree = StaticTree.Literals; - - treeDistances.dyn_tree = dyn_dtree; - treeDistances.staticTree = StaticTree.Distances; - - treeBitLengths.dyn_tree = bl_tree; - treeBitLengths.staticTree = StaticTree.BitLengths; - - bi_buf = 0; - bi_valid = 0; - last_eob_len = 8; // enough lookahead for inflate - - // Initialize the first block of the first file: - _InitializeBlocks(); - } - - internal void _InitializeBlocks() - { - // Initialize the trees. - for (int i = 0; i < InternalConstants.L_CODES; i++) + if (count < min_count) { - dyn_ltree[i * 2] = 0; + bl_tree[curlen * 2] = (short)(bl_tree[curlen * 2] + count); } - for (int i = 0; i < InternalConstants.D_CODES; i++) + else if (curlen != 0) { - dyn_dtree[i * 2] = 0; + if (curlen != prevlen) + { + bl_tree[curlen * 2]++; + } + bl_tree[InternalConstants.REP_3_6 * 2]++; } - for (int i = 0; i < InternalConstants.BL_CODES; i++) + else if (count <= 10) { - bl_tree[i * 2] = 0; - } - - dyn_ltree[END_BLOCK * 2] = 1; - opt_len = static_len = 0; - last_lit = matches = 0; - } - - // Restore the heap property by moving down the tree starting at node k, - // exchanging a node with the smallest of its two sons if necessary, stopping - // when the heap property is re-established (each father smaller than its - // two sons). - internal void pqdownheap(short[] tree, int k) - { - int v = heap[k]; - int j = k << 1; // left son of k - while (j <= heap_len) - { - // Set j to the smallest of the two sons: - if (j < heap_len && IsSmaller(tree, heap[j + 1], heap[j], depth)) - { - j++; - } - - // Exit if v is smaller than both sons - if (IsSmaller(tree, v, heap[j], depth)) - { - break; - } - - // Exchange v with the smallest son - heap[k] = heap[j]; - k = j; - - // And continue down the tree, setting j to the left son of k - j <<= 1; - } - heap[k] = v; - } - - internal static bool IsSmaller(short[] tree, int n, int m, sbyte[] depth) - { - short tn2 = tree[n * 2]; - short tm2 = tree[m * 2]; - return (tn2 < tm2 || (tn2 == tm2 && depth[n] <= depth[m])); - } - - // Scan a literal or distance tree to determine the frequencies of the codes - // in the bit length tree. - internal void ScanTree(short[] tree, int maxCode) - { - int n; // iterates over all tree elements - int prevlen = -1; // last emitted length - int curlen; // length of current code - int nextlen = tree[0 * 2 + 1]; // length of next code - int count = 0; // repeat count of the current code - int max_count = 7; // max repeat count - int min_count = 4; // min repeat count - - if (nextlen == 0) - { - max_count = 138; - min_count = 3; - } - tree[(maxCode + 1) * 2 + 1] = 0x7fff; // guard //?? - - for (n = 0; n <= maxCode; n++) - { - curlen = nextlen; - nextlen = tree[(n + 1) * 2 + 1]; - if (++count < max_count && curlen == nextlen) - { - continue; - } - if (count < min_count) - { - bl_tree[curlen * 2] = (short)(bl_tree[curlen * 2] + count); - } - else if (curlen != 0) - { - if (curlen != prevlen) - { - bl_tree[curlen * 2]++; - } - bl_tree[InternalConstants.REP_3_6 * 2]++; - } - else if (count <= 10) - { - bl_tree[InternalConstants.REPZ_3_10 * 2]++; - } - else - { - bl_tree[InternalConstants.REPZ_11_138 * 2]++; - } - count = 0; - prevlen = curlen; - if (nextlen == 0) - { - max_count = 138; - min_count = 3; - } - else if (curlen == nextlen) - { - max_count = 6; - min_count = 3; - } - else - { - max_count = 7; - min_count = 4; - } - } - } - - // Construct the Huffman tree for the bit lengths and return the index in - // bl_order of the last bit length code to send. - internal int BuildBlTree() - { - int max_blindex; // index of last bit length code of non zero freq - - // Determine the bit length frequencies for literal and distance trees - ScanTree(dyn_ltree, treeLiterals.max_code); - ScanTree(dyn_dtree, treeDistances.max_code); - - // Build the bit length tree: - treeBitLengths.build_tree(this); - - // opt_len now includes the length of the tree representations, except - // the lengths of the bit lengths codes and the 5+5+4 bits for the counts. - - // Determine the number of bit length codes to send. The pkzip format - // requires that at least 4 bit length codes be sent. (appnote.txt says - // 3 but the actual value used is 4.) - for (max_blindex = InternalConstants.BL_CODES - 1; max_blindex >= 3; max_blindex--) - { - if (bl_tree[Tree.bl_order[max_blindex] * 2 + 1] != 0) - { - break; - } - } - - // Update opt_len to include the bit length tree and counts - opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; - - return max_blindex; - } - - // Send the header for a block using dynamic Huffman trees: the counts, the - // lengths of the bit length codes, the literal tree and the distance tree. - // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. - internal void send_all_trees(int lcodes, int dcodes, int blcodes) - { - int rank; // index in bl_order - - send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt - send_bits(dcodes - 1, 5); - send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt - for (rank = 0; rank < blcodes; rank++) - { - send_bits(bl_tree[Tree.bl_order[rank] * 2 + 1], 3); - } - send_tree(dyn_ltree, lcodes - 1); // literal tree - send_tree(dyn_dtree, dcodes - 1); // distance tree - } - - // Send a literal or distance tree in compressed form, using the codes in - // bl_tree. - internal void send_tree(short[] tree, int max_code) - { - int n; // iterates over all tree elements - int prevlen = -1; // last emitted length - int curlen; // length of current code - int nextlen = tree[0 * 2 + 1]; // length of next code - int count = 0; // repeat count of the current code - int max_count = 7; // max repeat count - int min_count = 4; // min repeat count - - if (nextlen == 0) - { - max_count = 138; - min_count = 3; - } - - for (n = 0; n <= max_code; n++) - { - curlen = nextlen; - nextlen = tree[(n + 1) * 2 + 1]; - if (++count < max_count && curlen == nextlen) - { - continue; - } - if (count < min_count) - { - do - { - send_code(curlen, bl_tree); - } while (--count != 0); - } - else if (curlen != 0) - { - if (curlen != prevlen) - { - send_code(curlen, bl_tree); - count--; - } - send_code(InternalConstants.REP_3_6, bl_tree); - send_bits(count - 3, 2); - } - else if (count <= 10) - { - send_code(InternalConstants.REPZ_3_10, bl_tree); - send_bits(count - 3, 3); - } - else - { - send_code(InternalConstants.REPZ_11_138, bl_tree); - send_bits(count - 11, 7); - } - count = 0; - prevlen = curlen; - if (nextlen == 0) - { - max_count = 138; - min_count = 3; - } - else if (curlen == nextlen) - { - max_count = 6; - min_count = 3; - } - else - { - max_count = 7; - min_count = 4; - } - } - } - - // Output a block of bytes on the stream. - // IN assertion: there is enough room in pending_buf. - private void put_bytes(byte[] p, int start, int len) - { - Array.Copy(p, start, pending, pendingCount, len); - pendingCount += len; - } - -#if NOTNEEDED - private void put_byte(byte c) - { - pending[pendingCount++] = c; - } - - internal void put_short(int b) - { - unchecked - { - pending[pendingCount++] = (byte)b; - pending[pendingCount++] = (byte)(b >> 8); - } - } - - internal void putShortMSB(int b) - { - unchecked - { - pending[pendingCount++] = (byte)(b >> 8); - pending[pendingCount++] = (byte)b; - } - } -#endif - - internal void send_code(int c, short[] tree) - { - int c2 = c * 2; - send_bits((tree[c2] & 0xffff), (tree[c2 + 1] & 0xffff)); - } - -#pragma warning disable 675 // workaround for Visual Studio 2015 compiler bug: https://github.com/dotnet/roslyn/issues/4027 - internal void send_bits(int value, int length) - { - int len = length; - unchecked - { - if (bi_valid > Buf_size - len) - { - //int val = value; - // bi_buf |= (val << bi_valid); - - bi_buf |= (short)((value << bi_valid) & 0xffff); - - //put_short(bi_buf); - pending[pendingCount++] = (byte)bi_buf; - pending[pendingCount++] = (byte)(bi_buf >> 8); - - bi_buf = (short)((uint)value >> (Buf_size - bi_valid)); - bi_valid += len - Buf_size; - } - else - { - // bi_buf |= (value) << bi_valid; - bi_buf |= (short)((value << bi_valid) & 0xffff); - bi_valid += len; - } - } - } -#pragma warning restore 675 - - // Send one empty static block to give enough lookahead for inflate. - // This takes 10 bits, of which 7 may remain in the bit buffer. - // The current inflate code requires 9 bits of lookahead. If the - // last two codes for the previous block (real code plus EOB) were coded - // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode - // the last real code. In this case we send two empty static blocks instead - // of one. (There are no problems if the previous block is stored or fixed.) - // To simplify the code, we assume the worst case of last real code encoded - // on one bit only. - internal void _tr_align() - { - send_bits(STATIC_TREES << 1, 3); - send_code(END_BLOCK, StaticTree.lengthAndLiteralsTreeCodes); - - bi_flush(); - - // Of the 10 bits for the empty block, we have already sent - // (10 - bi_valid) bits. The lookahead for the last real code (before - // the EOB of the previous block) was thus at least one plus the length - // of the EOB plus what we have just sent of the empty static block. - if (1 + last_eob_len + 10 - bi_valid < 9) - { - send_bits(STATIC_TREES << 1, 3); - send_code(END_BLOCK, StaticTree.lengthAndLiteralsTreeCodes); - bi_flush(); - } - last_eob_len = 7; - } - - // Save the match info and tally the frequency counts. Return true if - // the current block must be flushed. - internal bool _tr_tally(int dist, int lc) - { - pending[_distanceOffset + last_lit * 2] = unchecked((byte)((uint)dist >> 8)); - pending[_distanceOffset + last_lit * 2 + 1] = unchecked((byte)dist); - pending[_lengthOffset + last_lit] = unchecked((byte)lc); - last_lit++; - - if (dist == 0) - { - // lc is the unmatched char - dyn_ltree[lc * 2]++; + bl_tree[InternalConstants.REPZ_3_10 * 2]++; } else { - matches++; - - // Here, lc is the match length - MIN_MATCH - dist--; // dist = match distance - 1 - dyn_ltree[(Tree.LengthCode[lc] + InternalConstants.LITERALS + 1) * 2]++; - dyn_dtree[Tree.DistanceCode(dist) * 2]++; + bl_tree[InternalConstants.REPZ_11_138 * 2]++; } - - if ((last_lit & 0x1fff) == 0 && (int)compressionLevel > 2) + count = 0; + prevlen = curlen; + if (nextlen == 0) { - // Compute an upper bound for the compressed length - int out_length = last_lit << 3; - int in_length = strstart - blockStart; - int dcode; - for (dcode = 0; dcode < InternalConstants.D_CODES; dcode++) - { - out_length = (int)( - out_length + dyn_dtree[dcode * 2] * (5L + ExtraDistanceBits[dcode]) - ); - } - out_length >>= 3; - if ((matches < (last_lit / 2)) && out_length < in_length / 2) - { - return true; - } + max_count = 138; + min_count = 3; } + else if (curlen == nextlen) + { + max_count = 6; + min_count = 3; + } + else + { + max_count = 7; + min_count = 4; + } + } + } - return (last_lit == lit_bufsize - 1) || (last_lit == lit_bufsize); + // Construct the Huffman tree for the bit lengths and return the index in + // bl_order of the last bit length code to send. + internal int BuildBlTree() + { + int max_blindex; // index of last bit length code of non zero freq - // dinoch - wraparound? - // We avoid equality with lit_bufsize because of wraparound at 64K - // on 16 bit machines and because stored blocks are restricted to - // 64K-1 bytes. + // Determine the bit length frequencies for literal and distance trees + ScanTree(dyn_ltree, treeLiterals.max_code); + ScanTree(dyn_dtree, treeDistances.max_code); + + // Build the bit length tree: + treeBitLengths.build_tree(this); + + // opt_len now includes the length of the tree representations, except + // the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + + // Determine the number of bit length codes to send. The pkzip format + // requires that at least 4 bit length codes be sent. (appnote.txt says + // 3 but the actual value used is 4.) + for (max_blindex = InternalConstants.BL_CODES - 1; max_blindex >= 3; max_blindex--) + { + if (bl_tree[(Tree.bl_order[max_blindex] * 2) + 1] != 0) + { + break; + } } - // Send the block data compressed using the given Huffman trees - internal void send_compressed_block(short[] ltree, short[] dtree) - { - int distance; // distance of matched string - int lc; // match length or unmatched char (if dist == 0) - int lx = 0; // running index in l_buf - int code; // the code to send - int extra; // number of extra bits to send + // Update opt_len to include the bit length tree and counts + opt_len += (3 * (max_blindex + 1)) + 5 + 5 + 4; - if (last_lit != 0) + return max_blindex; + } + + // Send the header for a block using dynamic Huffman trees: the counts, the + // lengths of the bit length codes, the literal tree and the distance tree. + // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + internal void send_all_trees(int lcodes, int dcodes, int blcodes) + { + int rank; // index in bl_order + + send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes - 1, 5); + send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) + { + send_bits(bl_tree[(Tree.bl_order[rank] * 2) + 1], 3); + } + send_tree(dyn_ltree, lcodes - 1); // literal tree + send_tree(dyn_dtree, dcodes - 1); // distance tree + } + + // Send a literal or distance tree in compressed form, using the codes in + // bl_tree. + internal void send_tree(short[] tree, int max_code) + { + int n; // iterates over all tree elements + var prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[(0 * 2) + 1]; // length of next code + var count = 0; // repeat count of the current code + var max_count = 7; // max repeat count + var min_count = 4; // min repeat count + + if (nextlen == 0) + { + max_count = 138; + min_count = 3; + } + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; + nextlen = tree[((n + 1) * 2) + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + if (count < min_count) { do { - int ix = _distanceOffset + lx * 2; - distance = ((pending[ix] << 8) & 0xff00) | (pending[ix + 1] & 0xff); - lc = (pending[_lengthOffset + lx]) & 0xff; - lx++; - - if (distance == 0) - { - send_code(lc, ltree); // send a literal byte - } - else - { - // literal or match pair - // Here, lc is the match length - MIN_MATCH - code = Tree.LengthCode[lc]; - - // send the length code - send_code(code + InternalConstants.LITERALS + 1, ltree); - extra = ExtraLengthBits[code]; - if (extra != 0) - { - // send the extra length bits - lc -= Tree.LengthBase[code]; - send_bits(lc, extra); - } - distance--; // dist is now the match distance - 1 - code = Tree.DistanceCode(distance); - - // send the distance code - send_code(code, dtree); - - extra = ExtraDistanceBits[code]; - if (extra != 0) - { - // send the extra distance bits - distance -= Tree.DistanceBase[code]; - send_bits(distance, extra); - } - } - - // Check that the overlay between pending and d_buf+l_buf is ok: - } while (lx < last_lit); + send_code(curlen, bl_tree); + } while (--count != 0); } - - send_code(END_BLOCK, ltree); - last_eob_len = ltree[END_BLOCK * 2 + 1]; - } - - // Set the data type to ASCII or BINARY, using a crude approximation: - // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. - // IN assertion: the fields freq of dyn_ltree are set and the total of all - // frequencies does not exceed 64K (to fit in an int on 16 bit machines). - internal void set_data_type() - { - int n = 0; - int ascii_freq = 0; - int bin_freq = 0; - while (n < 7) + else if (curlen != 0) { - bin_freq += dyn_ltree[n * 2]; - n++; - } - while (n < 128) - { - ascii_freq += dyn_ltree[n * 2]; - n++; - } - while (n < InternalConstants.LITERALS) - { - bin_freq += dyn_ltree[n * 2]; - n++; - } - data_type = (sbyte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII); - } - - // Flush the bit buffer, keeping at most 7 bits in it. - internal void bi_flush() - { - if (bi_valid == 16) - { - pending[pendingCount++] = (byte)bi_buf; - pending[pendingCount++] = (byte)(bi_buf >> 8); - bi_buf = 0; - bi_valid = 0; - } - else if (bi_valid >= 8) - { - //put_byte((byte)bi_buf); - pending[pendingCount++] = (byte)bi_buf; - bi_buf >>= 8; - bi_valid -= 8; - } - } - - // Flush the bit buffer and align the output on a byte boundary - internal void bi_windup() - { - if (bi_valid > 8) - { - pending[pendingCount++] = (byte)bi_buf; - pending[pendingCount++] = (byte)(bi_buf >> 8); - } - else if (bi_valid > 0) - { - //put_byte((byte)bi_buf); - pending[pendingCount++] = (byte)bi_buf; - } - bi_buf = 0; - bi_valid = 0; - } - - // Copy a stored block, storing first the length and its - // one's complement if requested. - internal void copy_block(int buf, int len, bool header) - { - bi_windup(); // align on byte boundary - last_eob_len = 8; // enough lookahead for inflate - - if (header) - { - unchecked + if (curlen != prevlen) { - //put_short((short)len); - pending[pendingCount++] = (byte)len; - pending[pendingCount++] = (byte)(len >> 8); - - //put_short((short)~len); - pending[pendingCount++] = (byte)~len; - pending[pendingCount++] = (byte)(~len >> 8); + send_code(curlen, bl_tree); + count--; } + send_code(InternalConstants.REP_3_6, bl_tree); + send_bits(count - 3, 2); } - - put_bytes(window, buf, len); - } - - internal void flush_block_only(bool eof) - { - _tr_flush_block(blockStart >= 0 ? blockStart : -1, strstart - blockStart, eof); - blockStart = strstart; - _codec.flush_pending(); - } - - // Copy without compression as much as possible from the input stream, return - // the current block state. - // This function does not insert new strings in the dictionary since - // uncompressible data is probably not useful. This function is used - // only for the level=0 compression option. - // NOTE: this function should be optimized to avoid extra copying from - // window to pending_buf. - internal BlockState DeflateNone(FlushType flush) - { - // Stored blocks are limited to 0xffff bytes, pending is limited - // to pending_buf_size, and each stored block has a 5 byte header: - - int max_block_size = 0xffff; - int max_start; - - if (max_block_size > pending.Length - 5) + else if (count <= 10) { - max_block_size = pending.Length - 5; - } - - // Copy as much as possible from input to output: - while (true) - { - // Fill the window as much as possible: - if (lookahead <= 1) - { - _fillWindow(); - if (lookahead == 0 && flush == FlushType.None) - { - return BlockState.NeedMore; - } - if (lookahead == 0) - { - break; // flush the current block - } - } - - strstart += lookahead; - lookahead = 0; - - // Emit a stored block if pending will be full: - max_start = blockStart + max_block_size; - if (strstart == 0 || strstart >= max_start) - { - // strstart == 0 is possible when wraparound on 16-bit machine - lookahead = strstart - max_start; - strstart = max_start; - - flush_block_only(false); - if (_codec.AvailableBytesOut == 0) - { - return BlockState.NeedMore; - } - } - - // Flush if we may have to slide, otherwise block_start may become - // negative and the data will be gone: - if (strstart - blockStart >= w_size - MIN_LOOKAHEAD) - { - flush_block_only(false); - if (_codec.AvailableBytesOut == 0) - { - return BlockState.NeedMore; - } - } - } - - flush_block_only(flush == FlushType.Finish); - if (_codec.AvailableBytesOut == 0) - { - return (flush == FlushType.Finish) ? BlockState.FinishStarted : BlockState.NeedMore; - } - - return flush == FlushType.Finish ? BlockState.FinishDone : BlockState.BlockDone; - } - - // Send a stored block - internal void _tr_stored_block(int buf, int stored_len, bool eof) - { - send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type - copy_block(buf, stored_len, true); // with header - } - - // Determine the best encoding for the current block: dynamic trees, static - // trees or store, and output the encoded block to the zip file. - internal void _tr_flush_block(int buf, int stored_len, bool eof) - { - int opt_lenb, - static_lenb; // opt_len and static_len in bytes - int max_blindex = 0; // index of last bit length code of non zero freq - - // Build the Huffman trees unless a stored block is forced - if (compressionLevel > 0) - { - // Check if the file is ascii or binary - if (data_type == Z_UNKNOWN) - { - set_data_type(); - } - - // Construct the literal and distance trees - treeLiterals.build_tree(this); - - treeDistances.build_tree(this); - - // At this point, opt_len and static_len are the total bit lengths of - // the compressed block data, excluding the tree representations. - - // Build the bit length tree for the above two trees, and get the index - // in bl_order of the last bit length code to send. - max_blindex = BuildBlTree(); - - // Determine the best encoding. Compute first the block length in bytes - opt_lenb = (opt_len + 3 + 7) >> 3; - static_lenb = (static_len + 3 + 7) >> 3; - - if (static_lenb <= opt_lenb) - { - opt_lenb = static_lenb; - } + send_code(InternalConstants.REPZ_3_10, bl_tree); + send_bits(count - 3, 3); } else { - opt_lenb = static_lenb = stored_len + 5; // force a stored block + send_code(InternalConstants.REPZ_11_138, bl_tree); + send_bits(count - 11, 7); } - - if (stored_len + 4 <= opt_lenb && buf != -1) + count = 0; + prevlen = curlen; + if (nextlen == 0) { - // 4: two words for the lengths - // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. - // Otherwise we can't have processed more than WSIZE input bytes since - // the last block flush, because compression would have been - // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to - // transform a block into a stored block. - _tr_stored_block(buf, stored_len, eof); + max_count = 138; + min_count = 3; } - else if (static_lenb == opt_lenb) + else if (curlen == nextlen) { - send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); - send_compressed_block( - StaticTree.lengthAndLiteralsTreeCodes, - StaticTree.distTreeCodes - ); + max_count = 6; + min_count = 3; } else { - send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3); - send_all_trees( - treeLiterals.max_code + 1, - treeDistances.max_code + 1, - max_blindex + 1 - ); - send_compressed_block(dyn_ltree, dyn_dtree); + max_count = 7; + min_count = 4; } + } + } - // The above check is made mod 2^32, for files larger than 512 MB - // and uLong implemented on 32 bits. + // Output a block of bytes on the stream. + // IN assertion: there is enough room in pending_buf. + private void put_bytes(byte[] p, int start, int len) + { + Array.Copy(p, start, pending, pendingCount, len); + pendingCount += len; + } - _InitializeBlocks(); +#if NOTNEEDED + private void put_byte(byte c) + { + pending[pendingCount++] = c; + } - if (eof) + internal void put_short(int b) + { + unchecked + { + pending[pendingCount++] = (byte)b; + pending[pendingCount++] = (byte)(b >> 8); + } + } + + internal void putShortMSB(int b) + { + unchecked + { + pending[pendingCount++] = (byte)(b >> 8); + pending[pendingCount++] = (byte)b; + } + } +#endif + + internal void send_code(int c, short[] tree) + { + var c2 = c * 2; + send_bits((tree[c2] & 0xffff), (tree[c2 + 1] & 0xffff)); + } + +#pragma warning disable 675 // workaround for Visual Studio 2015 compiler bug: https://github.com/dotnet/roslyn/issues/4027 + internal void send_bits(int value, int length) + { + var len = length; + unchecked + { + if (bi_valid > Buf_size - len) { - bi_windup(); + //int val = value; + // bi_buf |= (val << bi_valid); + + bi_buf |= (short)((value << bi_valid) & 0xffff); + + //put_short(bi_buf); + pending[pendingCount++] = (byte)bi_buf; + pending[pendingCount++] = (byte)(bi_buf >> 8); + + bi_buf = (short)((uint)value >> (Buf_size - bi_valid)); + bi_valid += len - Buf_size; + } + else + { + // bi_buf |= (value) << bi_valid; + bi_buf |= (short)((value << bi_valid) & 0xffff); + bi_valid += len; + } + } + } +#pragma warning restore 675 + + // Send one empty static block to give enough lookahead for inflate. + // This takes 10 bits, of which 7 may remain in the bit buffer. + // The current inflate code requires 9 bits of lookahead. If the + // last two codes for the previous block (real code plus EOB) were coded + // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + // the last real code. In this case we send two empty static blocks instead + // of one. (There are no problems if the previous block is stored or fixed.) + // To simplify the code, we assume the worst case of last real code encoded + // on one bit only. + internal void _tr_align() + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, StaticTree.lengthAndLiteralsTreeCodes); + + bi_flush(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, StaticTree.lengthAndLiteralsTreeCodes); + bi_flush(); + } + last_eob_len = 7; + } + + // Save the match info and tally the frequency counts. Return true if + // the current block must be flushed. + internal bool _tr_tally(int dist, int lc) + { + pending[_distanceOffset + (last_lit * 2)] = unchecked((byte)((uint)dist >> 8)); + pending[_distanceOffset + (last_lit * 2) + 1] = unchecked((byte)dist); + pending[_lengthOffset + last_lit] = unchecked((byte)lc); + last_lit++; + + if (dist == 0) + { + // lc is the unmatched char + dyn_ltree[lc * 2]++; + } + else + { + matches++; + + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(Tree.LengthCode[lc] + InternalConstants.LITERALS + 1) * 2]++; + dyn_dtree[Tree.DistanceCode(dist) * 2]++; + } + + if ((last_lit & 0x1fff) == 0 && (int)compressionLevel > 2) + { + // Compute an upper bound for the compressed length + var out_length = last_lit << 3; + var in_length = strstart - blockStart; + int dcode; + for (dcode = 0; dcode < InternalConstants.D_CODES; dcode++) + { + out_length = (int)( + out_length + (dyn_dtree[dcode * 2] * (5L + ExtraDistanceBits[dcode])) + ); + } + out_length >>= 3; + if ((matches < (last_lit / 2)) && out_length < in_length / 2) + { + return true; } } - // Fill the window when the lookahead becomes insufficient. - // Updates strstart and lookahead. - // - // IN assertion: lookahead < MIN_LOOKAHEAD - // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD - // At least one byte has been read, or avail_in == 0; reads are - // performed for at least two bytes (required for the zip translate_eol - // option -- not supported here). - private void _fillWindow() - { - int n, - m; - int p; - int more; // Amount of free space at the end of the window. + return (last_lit == lit_bufsize - 1) || (last_lit == lit_bufsize); + // dinoch - wraparound? + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + // Send the block data compressed using the given Huffman trees + internal void send_compressed_block(short[] ltree, short[] dtree) + { + int distance; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + var lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0) + { do { - more = (window_size - lookahead - strstart); + var ix = _distanceOffset + (lx * 2); + distance = ((pending[ix] << 8) & 0xff00) | (pending[ix + 1] & 0xff); + lc = (pending[_lengthOffset + lx]) & 0xff; + lx++; - // Deal with !@#$% 64K limit: - if (more == 0 && strstart == 0 && lookahead == 0) + if (distance == 0) { - more = w_size; - } - else if (more == -1) - { - // Very unlikely, but possible on 16 bit machine if strstart == 0 - // and lookahead == 1 (input done one byte at time) - more--; - - // If the window is almost full and there is insufficient lookahead, - // move the upper half to the lower one to make room in the upper half. - } - else if (strstart >= w_size + w_size - MIN_LOOKAHEAD) - { - Array.Copy(window, w_size, window, 0, w_size); - match_start -= w_size; - strstart -= w_size; // we now have strstart >= MAX_DIST - blockStart -= w_size; - - // Slide the hash table (could be avoided with 32 bit values - // at the expense of memory usage). We slide even when level == 0 - // to keep the hash table consistent if we switch back to level > 0 - // later. (Using level 0 permanently is not an optimal usage of - // zlib, so we don't care about this pathological case.) - - n = hash_size; - p = n; - do - { - m = (head[--p] & 0xffff); - head[p] = (short)((m >= w_size) ? (m - w_size) : 0); - } while (--n != 0); - - n = w_size; - p = n; - do - { - m = (prev[--p] & 0xffff); - prev[p] = (short)((m >= w_size) ? (m - w_size) : 0); - - // If n is not on any hash chain, prev[n] is garbage but - // its value will never be used. - } while (--n != 0); - more += w_size; - } - - if (_codec.AvailableBytesIn == 0) - { - return; - } - - // If there was no sliding: - // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && - // more == window_size - lookahead - strstart - // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) - // => more >= window_size - 2*WSIZE + 2 - // In the BIG_MEM or MMAP case (not yet supported), - // window_size == input_size + MIN_LOOKAHEAD && - // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. - // Otherwise, window_size == 2*WSIZE so more >= 2. - // If there was sliding, more >= WSIZE. So in all cases, more >= 2. - - n = _codec.read_buf(window, strstart + lookahead, more); - lookahead += n; - - // Initialize the hash value now that we have some input: - if (lookahead >= MIN_MATCH) - { - ins_h = window[strstart] & 0xff; - ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask; - } - - // If the whole input has less than MIN_MATCH bytes, ins_h is garbage, - // but this is not important since only literal bytes will be emitted. - } while (lookahead < MIN_LOOKAHEAD && _codec.AvailableBytesIn != 0); - } - - // Compress as much as possible from the input stream, return the current - // block state. - // This function does not perform lazy evaluation of matches and inserts - // new strings in the dictionary only for unmatched strings or for short - // matches. It is used only for the fast compression options. - internal BlockState DeflateFast(FlushType flush) - { - // short hash_head = 0; // head of the hash chain - int hash_head = 0; // head of the hash chain - bool bflush; // set if current block must be flushed - - while (true) - { - // Make sure that we always have enough lookahead, except - // at the end of the input file. We need MAX_MATCH bytes - // for the next match, plus MIN_MATCH bytes to insert the - // string following the next match. - if (lookahead < MIN_LOOKAHEAD) - { - _fillWindow(); - if (lookahead < MIN_LOOKAHEAD && flush == FlushType.None) - { - return BlockState.NeedMore; - } - if (lookahead == 0) - { - break; // flush the current block - } - } - - // Insert the string window[strstart .. strstart+2] in the - // dictionary, and set hash_head to the head of the hash chain: - if (lookahead >= MIN_MATCH) - { - ins_h = - (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) - & hash_mask; - - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = (head[ins_h] & 0xffff); - prev[strstart & w_mask] = head[ins_h]; - head[ins_h] = unchecked((short)strstart); - } - - // Find the longest match, discarding those <= prev_length. - // At this point we have always match_length < MIN_MATCH - - if (hash_head != 0L && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) - { - // To simplify the code, we prevent matches with the string - // of window index 0 (in particular we have to avoid a match - // of the string with itself at the start of the input file). - if (compressionStrategy != CompressionStrategy.HuffmanOnly) - { - match_length = longest_match(hash_head); - } - - // longest_match() sets match_start - } - if (match_length >= MIN_MATCH) - { - // check_match(strstart, match_start, match_length); - - bflush = _tr_tally(strstart - match_start, match_length - MIN_MATCH); - - lookahead -= match_length; - - // Insert new strings in the hash table only if the match length - // is not too large. This saves time but degrades compression. - if (match_length <= config.MaxLazy && lookahead >= MIN_MATCH) - { - match_length--; // string at strstart already in hash table - do - { - strstart++; - - ins_h = - ( - (ins_h << hash_shift) - ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff) - ) & hash_mask; - - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = (head[ins_h] & 0xffff); - prev[strstart & w_mask] = head[ins_h]; - head[ins_h] = unchecked((short)strstart); - - // strstart never exceeds WSIZE-MAX_MATCH, so there are - // always MIN_MATCH bytes ahead. - } while (--match_length != 0); - strstart++; - } - else - { - strstart += match_length; - match_length = 0; - ins_h = window[strstart] & 0xff; - - ins_h = - (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask; - - // If lookahead < MIN_MATCH, ins_h is garbage, but it does not - // matter since it will be recomputed at next deflate call. - } + send_code(lc, ltree); // send a literal byte } else { - // No match, output a literal byte + // literal or match pair + // Here, lc is the match length - MIN_MATCH + code = Tree.LengthCode[lc]; - bflush = _tr_tally(0, window[strstart] & 0xff); - lookahead--; + // send the length code + send_code(code + InternalConstants.LITERALS + 1, ltree); + extra = ExtraLengthBits[code]; + if (extra != 0) + { + // send the extra length bits + lc -= Tree.LengthBase[code]; + send_bits(lc, extra); + } + distance--; // dist is now the match distance - 1 + code = Tree.DistanceCode(distance); + + // send the distance code + send_code(code, dtree); + + extra = ExtraDistanceBits[code]; + if (extra != 0) + { + // send the extra distance bits + distance -= Tree.DistanceBase[code]; + send_bits(distance, extra); + } + } + + // Check that the overlay between pending and d_buf+l_buf is ok: + } while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); + last_eob_len = ltree[(END_BLOCK * 2) + 1]; + } + + // Set the data type to ASCII or BINARY, using a crude approximation: + // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + // IN assertion: the fields freq of dyn_ltree are set and the total of all + // frequencies does not exceed 64K (to fit in an int on 16 bit machines). + internal void set_data_type() + { + var n = 0; + var ascii_freq = 0; + var bin_freq = 0; + while (n < 7) + { + bin_freq += dyn_ltree[n * 2]; + n++; + } + while (n < 128) + { + ascii_freq += dyn_ltree[n * 2]; + n++; + } + while (n < InternalConstants.LITERALS) + { + bin_freq += dyn_ltree[n * 2]; + n++; + } + data_type = (sbyte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII); + } + + // Flush the bit buffer, keeping at most 7 bits in it. + internal void bi_flush() + { + if (bi_valid == 16) + { + pending[pendingCount++] = (byte)bi_buf; + pending[pendingCount++] = (byte)(bi_buf >> 8); + bi_buf = 0; + bi_valid = 0; + } + else if (bi_valid >= 8) + { + //put_byte((byte)bi_buf); + pending[pendingCount++] = (byte)bi_buf; + bi_buf >>= 8; + bi_valid -= 8; + } + } + + // Flush the bit buffer and align the output on a byte boundary + internal void bi_windup() + { + if (bi_valid > 8) + { + pending[pendingCount++] = (byte)bi_buf; + pending[pendingCount++] = (byte)(bi_buf >> 8); + } + else if (bi_valid > 0) + { + //put_byte((byte)bi_buf); + pending[pendingCount++] = (byte)bi_buf; + } + bi_buf = 0; + bi_valid = 0; + } + + // Copy a stored block, storing first the length and its + // one's complement if requested. + internal void copy_block(int buf, int len, bool header) + { + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) + { + unchecked + { + //put_short((short)len); + pending[pendingCount++] = (byte)len; + pending[pendingCount++] = (byte)(len >> 8); + + //put_short((short)~len); + pending[pendingCount++] = (byte)~len; + pending[pendingCount++] = (byte)(~len >> 8); + } + } + + put_bytes(window, buf, len); + } + + internal void flush_block_only(bool eof) + { + _tr_flush_block(blockStart >= 0 ? blockStart : -1, strstart - blockStart, eof); + blockStart = strstart; + _codec.flush_pending(); + } + + // Copy without compression as much as possible from the input stream, return + // the current block state. + // This function does not insert new strings in the dictionary since + // uncompressible data is probably not useful. This function is used + // only for the level=0 compression option. + // NOTE: this function should be optimized to avoid extra copying from + // window to pending_buf. + internal BlockState DeflateNone(FlushType flush) + { + // Stored blocks are limited to 0xffff bytes, pending is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + var max_block_size = 0xffff; + int max_start; + + if (max_block_size > pending.Length - 5) + { + max_block_size = pending.Length - 5; + } + + // Copy as much as possible from input to output: + while (true) + { + // Fill the window as much as possible: + if (lookahead <= 1) + { + _fillWindow(); + if (lookahead == 0 && flush == FlushType.None) + { + return BlockState.NeedMore; + } + if (lookahead == 0) + { + break; // flush the current block + } + } + + strstart += lookahead; + lookahead = 0; + + // Emit a stored block if pending will be full: + max_start = blockStart + max_block_size; + if (strstart == 0 || strstart >= max_start) + { + // strstart == 0 is possible when wraparound on 16-bit machine + lookahead = strstart - max_start; + strstart = max_start; + + flush_block_only(false); + if (_codec.AvailableBytesOut == 0) + { + return BlockState.NeedMore; + } + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if (strstart - blockStart >= w_size - MIN_LOOKAHEAD) + { + flush_block_only(false); + if (_codec.AvailableBytesOut == 0) + { + return BlockState.NeedMore; + } + } + } + + flush_block_only(flush == FlushType.Finish); + if (_codec.AvailableBytesOut == 0) + { + return (flush == FlushType.Finish) ? BlockState.FinishStarted : BlockState.NeedMore; + } + + return flush == FlushType.Finish ? BlockState.FinishDone : BlockState.BlockDone; + } + + // Send a stored block + internal void _tr_stored_block(int buf, int stored_len, bool eof) + { + send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type + copy_block(buf, stored_len, true); // with header + } + + // Determine the best encoding for the current block: dynamic trees, static + // trees or store, and output the encoded block to the zip file. + internal void _tr_flush_block(int buf, int stored_len, bool eof) + { + int opt_lenb, + static_lenb; // opt_len and static_len in bytes + var max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if (compressionLevel > 0) + { + // Check if the file is ascii or binary + if (data_type == Z_UNKNOWN) + { + set_data_type(); + } + + // Construct the literal and distance trees + treeLiterals.build_tree(this); + + treeDistances.build_tree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex = BuildBlTree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb = (opt_len + 3 + 7) >> 3; + static_lenb = (static_len + 3 + 7) >> 3; + + if (static_lenb <= opt_lenb) + { + opt_lenb = static_lenb; + } + } + else + { + opt_lenb = static_lenb = stored_len + 5; // force a stored block + } + + if (stored_len + 4 <= opt_lenb && buf != -1) + { + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + _tr_stored_block(buf, stored_len, eof); + } + else if (static_lenb == opt_lenb) + { + send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); + send_compressed_block( + StaticTree.lengthAndLiteralsTreeCodes, + StaticTree.distTreeCodes + ); + } + else + { + send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3); + send_all_trees( + treeLiterals.max_code + 1, + treeDistances.max_code + 1, + max_blindex + 1 + ); + send_compressed_block(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + _InitializeBlocks(); + + if (eof) + { + bi_windup(); + } + } + + // Fill the window when the lookahead becomes insufficient. + // Updates strstart and lookahead. + // + // IN assertion: lookahead < MIN_LOOKAHEAD + // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + // At least one byte has been read, or avail_in == 0; reads are + // performed for at least two bytes (required for the zip translate_eol + // option -- not supported here). + private void _fillWindow() + { + int n, + m; + int p; + int more; // Amount of free space at the end of the window. + + do + { + more = (window_size - lookahead - strstart); + + // Deal with !@#$% 64K limit: + if (more == 0 && strstart == 0 && lookahead == 0) + { + more = w_size; + } + else if (more == -1) + { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } + else if (strstart >= w_size + w_size - MIN_LOOKAHEAD) + { + Array.Copy(window, w_size, window, 0, w_size); + match_start -= w_size; + strstart -= w_size; // we now have strstart >= MAX_DIST + blockStart -= w_size; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = hash_size; + p = n; + do + { + m = (head[--p] & 0xffff); + head[p] = (short)((m >= w_size) ? (m - w_size) : 0); + } while (--n != 0); + + n = w_size; + p = n; + do + { + m = (prev[--p] & 0xffff); + prev[p] = (short)((m >= w_size) ? (m - w_size) : 0); + + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } while (--n != 0); + more += w_size; + } + + if (_codec.AvailableBytesIn == 0) + { + return; + } + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = _codec.read_buf(window, strstart + lookahead, more); + lookahead += n; + + // Initialize the hash value now that we have some input: + if (lookahead >= MIN_MATCH) + { + ins_h = window[strstart] & 0xff; + ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask; + } + + // If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + // but this is not important since only literal bytes will be emitted. + } while (lookahead < MIN_LOOKAHEAD && _codec.AvailableBytesIn != 0); + } + + // Compress as much as possible from the input stream, return the current + // block state. + // This function does not perform lazy evaluation of matches and inserts + // new strings in the dictionary only for unmatched strings or for short + // matches. It is used only for the fast compression options. + internal BlockState DeflateFast(FlushType flush) + { + // short hash_head = 0; // head of the hash chain + var hash_head = 0; // head of the hash chain + bool bflush; // set if current block must be flushed + + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + if (lookahead < MIN_LOOKAHEAD) + { + _fillWindow(); + if (lookahead < MIN_LOOKAHEAD && flush == FlushType.None) + { + return BlockState.NeedMore; + } + if (lookahead == 0) + { + break; // flush the current block + } + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + if (lookahead >= MIN_MATCH) + { + ins_h = + (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) + & hash_mask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (head[ins_h] & 0xffff); + prev[strstart & w_mask] = head[ins_h]; + head[ins_h] = unchecked((short)strstart); + } + + // Find the longest match, discarding those <= prev_length. + // At this point we have always match_length < MIN_MATCH + + if (hash_head != 0L && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + if (compressionStrategy != CompressionStrategy.HuffmanOnly) + { + match_length = longest_match(hash_head); + } + + // longest_match() sets match_start + } + if (match_length >= MIN_MATCH) + { + // check_match(strstart, match_start, match_length); + + bflush = _tr_tally(strstart - match_start, match_length - MIN_MATCH); + + lookahead -= match_length; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if (match_length <= config.MaxLazy && lookahead >= MIN_MATCH) + { + match_length--; // string at strstart already in hash table + do + { + strstart++; + + ins_h = + ( + (ins_h << hash_shift) + ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff) + ) & hash_mask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (head[ins_h] & 0xffff); + prev[strstart & w_mask] = head[ins_h]; + head[ins_h] = unchecked((short)strstart); + + // strstart never exceeds WSIZE-MAX_MATCH, so there are + // always MIN_MATCH bytes ahead. + } while (--match_length != 0); strstart++; } + else + { + strstart += match_length; + match_length = 0; + ins_h = window[strstart] & 0xff; + + ins_h = + (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask; + + // If lookahead < MIN_MATCH, ins_h is garbage, but it does not + // matter since it will be recomputed at next deflate call. + } + } + else + { + // No match, output a literal byte + + bflush = _tr_tally(0, window[strstart] & 0xff); + lookahead--; + strstart++; + } + if (bflush) + { + flush_block_only(false); + if (_codec.AvailableBytesOut == 0) + { + return BlockState.NeedMore; + } + } + } + + flush_block_only(flush == FlushType.Finish); + if (_codec.AvailableBytesOut == 0) + { + if (flush == FlushType.Finish) + { + return BlockState.FinishStarted; + } + return BlockState.NeedMore; + } + return flush == FlushType.Finish ? BlockState.FinishDone : BlockState.BlockDone; + } + + // Same as above, but achieves better compression. We use a lazy + // evaluation for matches: a match is finally adopted only if there is + // no better match at the next window position. + internal BlockState DeflateSlow(FlushType flush) + { + // short hash_head = 0; // head of hash chain + var hash_head = 0; // head of hash chain + bool bflush; // set if current block must be flushed + + // Process the input block. + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + + if (lookahead < MIN_LOOKAHEAD) + { + _fillWindow(); + if (lookahead < MIN_LOOKAHEAD && flush == FlushType.None) + { + return BlockState.NeedMore; + } + + if (lookahead == 0) + { + break; // flush the current block + } + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + + if (lookahead >= MIN_MATCH) + { + ins_h = + (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) + & hash_mask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (head[ins_h] & 0xffff); + prev[strstart & w_mask] = head[ins_h]; + head[ins_h] = unchecked((short)strstart); + } + + // Find the longest match, discarding those <= prev_length. + prev_length = match_length; + prev_match = match_start; + match_length = MIN_MATCH - 1; + + if ( + hash_head != 0 + && prev_length < config.MaxLazy + && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + + if (compressionStrategy != CompressionStrategy.HuffmanOnly) + { + match_length = longest_match(hash_head); + } + + // longest_match() sets match_start + + if ( + match_length <= 5 + && ( + compressionStrategy == CompressionStrategy.Filtered + || (match_length == MIN_MATCH && strstart - match_start > 4096) + ) + ) + { + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + match_length = MIN_MATCH - 1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if (prev_length >= MIN_MATCH && match_length <= prev_length) + { + var max_insert = strstart + lookahead - MIN_MATCH; + + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush = _tr_tally(strstart - 1 - prev_match, prev_length - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + lookahead -= (prev_length - 1); + prev_length -= 2; + do + { + if (++strstart <= max_insert) + { + ins_h = + ( + ((ins_h) << hash_shift) + ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff) + ) & hash_mask; + + //prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (head[ins_h] & 0xffff); + prev[strstart & w_mask] = head[ins_h]; + head[ins_h] = unchecked((short)strstart); + } + } while (--prev_length != 0); + match_available = 0; + match_length = MIN_MATCH - 1; + strstart++; + if (bflush) { flush_block_only(false); @@ -1397,707 +1539,565 @@ namespace SharpCompress.Compressors.Deflate } } } - - flush_block_only(flush == FlushType.Finish); - if (_codec.AvailableBytesOut == 0) + else if (match_available != 0) { - if (flush == FlushType.Finish) + // If there was no match at the previous position, output a + // single literal. If there was a match but the current match + // is longer, truncate the previous match to a single literal. + + bflush = _tr_tally(0, window[strstart - 1] & 0xff); + + if (bflush) { - return BlockState.FinishStarted; + flush_block_only(false); + } + strstart++; + lookahead--; + if (_codec.AvailableBytesOut == 0) + { + return BlockState.NeedMore; } - return BlockState.NeedMore; } - return flush == FlushType.Finish ? BlockState.FinishDone : BlockState.BlockDone; + else + { + // There is no previous match to compare with, wait for + // the next step to decide. + + match_available = 1; + strstart++; + lookahead--; + } } - // Same as above, but achieves better compression. We use a lazy - // evaluation for matches: a match is finally adopted only if there is - // no better match at the next window position. - internal BlockState DeflateSlow(FlushType flush) + if (match_available != 0) { - // short hash_head = 0; // head of hash chain - int hash_head = 0; // head of hash chain - bool bflush; // set if current block must be flushed + bflush = _tr_tally(0, window[strstart - 1] & 0xff); + match_available = 0; + } + flush_block_only(flush == FlushType.Finish); - // Process the input block. - while (true) + if (_codec.AvailableBytesOut == 0) + { + if (flush == FlushType.Finish) { - // Make sure that we always have enough lookahead, except - // at the end of the input file. We need MAX_MATCH bytes - // for the next match, plus MIN_MATCH bytes to insert the - // string following the next match. + return BlockState.FinishStarted; + } + return BlockState.NeedMore; + } - if (lookahead < MIN_LOOKAHEAD) + return flush == FlushType.Finish ? BlockState.FinishDone : BlockState.BlockDone; + } + + internal int longest_match(int cur_match) + { + var chain_length = config.MaxChainLength; // max hash chain length + var scan = strstart; // current string + int match; // matched string + int len; // length of current match + var best_len = prev_length; // best match length so far + var limit = + strstart > (w_size - MIN_LOOKAHEAD) ? strstart - (w_size - MIN_LOOKAHEAD) : 0; + + var niceLength = config.NiceLength; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + var wmask = w_mask; + + var strend = strstart + MAX_MATCH; + var scan_end1 = window[scan + best_len - 1]; + var scan_end = window[scan + best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (prev_length >= config.GoodLength) + { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (niceLength > lookahead) + { + niceLength = lookahead; + } + + do + { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if ( + window[match + best_len] != scan_end + || window[match + best_len - 1] != scan_end1 + || window[match] != window[scan] + || window[++match] != window[scan + 1] + ) + { + continue; + } + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; + match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do + { } while ( + window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && scan < strend + ); + + len = MAX_MATCH - (strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) + { + match_start = cur_match; + best_len = len; + if (len >= niceLength) { - _fillWindow(); - if (lookahead < MIN_LOOKAHEAD && flush == FlushType.None) - { - return BlockState.NeedMore; - } - - if (lookahead == 0) - { - break; // flush the current block - } + break; } + scan_end1 = window[scan + best_len - 1]; + scan_end = window[scan + best_len]; + } + } while ( + (cur_match = (prev[cur_match & wmask] & 0xffff)) > limit && --chain_length != 0 + ); - // Insert the string window[strstart .. strstart+2] in the - // dictionary, and set hash_head to the head of the hash chain: + if (best_len <= lookahead) + { + return best_len; + } + return lookahead; + } - if (lookahead >= MIN_MATCH) + private bool Rfc1950BytesEmitted; + + internal bool WantRfc1950HeaderBytes { get; set; } = true; + + internal int Initialize(ZlibCodec codec, CompressionLevel level) + { + return Initialize(codec, level, ZlibConstants.WindowBitsMax); + } + + internal int Initialize(ZlibCodec codec, CompressionLevel level, int bits) + { + return Initialize(codec, level, bits, MEM_LEVEL_DEFAULT, CompressionStrategy.Default); + } + + internal int Initialize( + ZlibCodec codec, + CompressionLevel level, + int bits, + CompressionStrategy compressionStrategy + ) + { + return Initialize(codec, level, bits, MEM_LEVEL_DEFAULT, compressionStrategy); + } + + internal int Initialize( + ZlibCodec codec, + CompressionLevel level, + int windowBits, + int memLevel, + CompressionStrategy strategy + ) + { + _codec = codec; + _codec.Message = null; + + // validation + if (windowBits < 9 || windowBits > 15) + { + throw new ZlibException("windowBits must be in the range 9..15."); + } + + if (memLevel < 1 || memLevel > MEM_LEVEL_MAX) + { + throw new ZlibException( + string.Format("memLevel must be in the range 1.. {0}", MEM_LEVEL_MAX) + ); + } + + _codec.dstate = this; + + w_bits = windowBits; + w_size = 1 << w_bits; + w_mask = w_size - 1; + + hash_bits = memLevel + 7; + hash_size = 1 << hash_bits; + hash_mask = hash_size - 1; + hash_shift = ((hash_bits + MIN_MATCH - 1) / MIN_MATCH); + + window = new byte[w_size * 2]; + prev = new short[w_size]; + head = new short[hash_size]; + + // for memLevel==8, this will be 16384, 16k + lit_bufsize = 1 << (memLevel + 6); + + // Use a single array as the buffer for data pending compression, + // the output distance codes, and the output length codes (aka tree). + // orig comment: This works just fine since the average + // output size for (length,distance) codes is <= 24 bits. + pending = new byte[lit_bufsize * 4]; + _distanceOffset = lit_bufsize; + _lengthOffset = (1 + 2) * lit_bufsize; + + // So, for memLevel 8, the length of the pending buffer is 65536. 64k. + // The first 16k are pending bytes. + // The middle slice, of 32k, is used for distance codes. + // The final 16k are length codes. + + compressionLevel = level; + compressionStrategy = strategy; + + Reset(); + return ZlibConstants.Z_OK; + } + + internal void Reset() + { + _codec.TotalBytesIn = _codec.TotalBytesOut = 0; + _codec.Message = null; + + //strm.data_type = Z_UNKNOWN; + + pendingCount = 0; + nextPending = 0; + + Rfc1950BytesEmitted = false; + + status = (WantRfc1950HeaderBytes) ? INIT_STATE : BUSY_STATE; + _codec._adler32 = 1; + + last_flush = (int)FlushType.None; + + _InitializeTreeData(); + _InitializeLazyMatch(); + } + + internal int End() + { + if (status != INIT_STATE && status != BUSY_STATE && status != FINISH_STATE) + { + return ZlibConstants.Z_STREAM_ERROR; + } + + // Deallocate in reverse order of allocations: + pending = null; + head = null; + prev = null; + window = null; + + // free + // dstate=null; + return status == BUSY_STATE ? ZlibConstants.Z_DATA_ERROR : ZlibConstants.Z_OK; + } + + private void SetDeflater() + { + switch (config.Flavor) + { + case DeflateFlavor.Store: + DeflateFunction = DeflateNone; + break; + case DeflateFlavor.Fast: + DeflateFunction = DeflateFast; + break; + case DeflateFlavor.Slow: + DeflateFunction = DeflateSlow; + break; + } + } + + internal int SetParams(CompressionLevel level, CompressionStrategy strategy) + { + var result = ZlibConstants.Z_OK; + + if (compressionLevel != level) + { + var newConfig = Config.Lookup(level); + + // change in the deflate flavor (Fast vs slow vs none)? + if (newConfig.Flavor != config.Flavor && _codec.TotalBytesIn != 0) + { + // Flush the last buffer: + result = _codec.Deflate(FlushType.Partial); + } + + compressionLevel = level; + config = newConfig; + SetDeflater(); + } + + // no need to flush with change in strategy? Really? + compressionStrategy = strategy; + + return result; + } + + internal int SetDictionary(byte[] dictionary) + { + var length = dictionary.Length; + var index = 0; + + if (dictionary is null || status != INIT_STATE) + { + throw new ZlibException("Stream error."); + } + + _codec._adler32 = Adler32.Calculate(_codec._adler32, dictionary); + + if (length < MIN_MATCH) + { + return ZlibConstants.Z_OK; + } + if (length > w_size - MIN_LOOKAHEAD) + { + length = w_size - MIN_LOOKAHEAD; + index = dictionary.Length - length; // use the tail of the dictionary + } + Array.Copy(dictionary, index, window, 0, length); + strstart = length; + blockStart = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + ins_h = window[0] & 0xff; + ins_h = (((ins_h) << hash_shift) ^ (window[1] & 0xff)) & hash_mask; + + for (var n = 0; n <= length - MIN_MATCH; n++) + { + ins_h = + (((ins_h) << hash_shift) ^ (window[(n) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; + prev[n & w_mask] = head[ins_h]; + head[ins_h] = (short)n; + } + return ZlibConstants.Z_OK; + } + + internal int Deflate(FlushType flush) + { + int old_flush; + + if ( + _codec.OutputBuffer is null + || (_codec.InputBuffer is null && _codec.AvailableBytesIn != 0) + || (status == FINISH_STATE && flush != FlushType.Finish) + ) + { + _codec.Message = _ErrorMessage[ + ZlibConstants.Z_NEED_DICT - (ZlibConstants.Z_STREAM_ERROR) + ]; + throw new ZlibException(string.Format("Something is fishy. [{0}]", _codec.Message)); + + //return ZlibConstants.Z_STREAM_ERROR; + } + if (_codec.AvailableBytesOut == 0) + { + _codec.Message = _ErrorMessage[ + ZlibConstants.Z_NEED_DICT - (ZlibConstants.Z_BUF_ERROR) + ]; + throw new ZlibException("OutputBuffer is full (AvailableBytesOut == 0)"); + + //return ZlibConstants.Z_BUF_ERROR; + } + + old_flush = last_flush; + last_flush = (int)flush; + + // Write the zlib (rfc1950) header bytes + if (status == INIT_STATE) + { + var header = (Z_DEFLATED + ((w_bits - 8) << 4)) << 8; + var level_flags = (((int)compressionLevel - 1) & 0xff) >> 1; + + if (level_flags > 3) + { + level_flags = 3; + } + header |= (level_flags << 6); + if (strstart != 0) + { + header |= PRESET_DICT; + } + header += 31 - (header % 31); + + status = BUSY_STATE; + //putShortMSB(header); + unchecked + { + pending[pendingCount++] = (byte)(header >> 8); + pending[pendingCount++] = (byte)header; + } + + // Save the adler32 of the preset dictionary: + if (strstart != 0) + { + ////putShortMSB((int)(SharedUtils.URShift(_codec._Adler32, 16))); + //putShortMSB((int)((UInt64)_codec._Adler32 >> 16)); + //putShortMSB((int)(_codec._Adler32 & 0xffff)); + pending[pendingCount++] = (byte)((_codec._adler32 & 0xFF000000) >> 24); + pending[pendingCount++] = (byte)((_codec._adler32 & 0x00FF0000) >> 16); + pending[pendingCount++] = (byte)((_codec._adler32 & 0x0000FF00) >> 8); + pending[pendingCount++] = (byte)(_codec._adler32 & 0x000000FF); + } + _codec._adler32 = 1; + } + + // Flush as much pending output as possible + if (pendingCount != 0) + { + _codec.flush_pending(); + if (_codec.AvailableBytesOut == 0) + { + //System.out.println(" avail_out==0"); + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = -1; + return ZlibConstants.Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with Z_FINISH, we keep + // returning Z_STREAM_END instead of Z_BUFF_ERROR. + } + else if ( + _codec.AvailableBytesIn == 0 && (int)flush <= old_flush && flush != FlushType.Finish + ) + { + // workitem 8557 + // Not sure why this needs to be an error. + // pendingCount == 0, which means there's nothing to deflate. + // And the caller has not asked for a FlushType.Finish, but... + // that seems very non-fatal. We can just say "OK" and do nthing. + + // _codec.Message = z_errmsg[ZlibConstants.Z_NEED_DICT - (ZlibConstants.Z_BUF_ERROR)]; + // throw new ZlibException("AvailableBytesIn == 0 && flush<=old_flush && flush != FlushType.Finish"); + + return ZlibConstants.Z_OK; + } + + // User must not provide more input after the first FINISH: + if (status == FINISH_STATE && _codec.AvailableBytesIn != 0) + { + _codec.Message = _ErrorMessage[ + ZlibConstants.Z_NEED_DICT - (ZlibConstants.Z_BUF_ERROR) + ]; + throw new ZlibException("status == FINISH_STATE && _codec.AvailableBytesIn != 0"); + } + + // Start a new block or continue the current one. + if ( + _codec.AvailableBytesIn != 0 + || lookahead != 0 + || (flush != FlushType.None && status != FINISH_STATE) + ) + { + var bstate = DeflateFunction(flush); + + if (bstate == BlockState.FinishStarted || bstate == BlockState.FinishDone) + { + status = FINISH_STATE; + } + if (bstate == BlockState.NeedMore || bstate == BlockState.FinishStarted) + { + if (_codec.AvailableBytesOut == 0) { - ins_h = - (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) - & hash_mask; - - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = (head[ins_h] & 0xffff); - prev[strstart & w_mask] = head[ins_h]; - head[ins_h] = unchecked((short)strstart); + last_flush = -1; // avoid BUF_ERROR next call, see above } + return ZlibConstants.Z_OK; - // Find the longest match, discarding those <= prev_length. - prev_length = match_length; - prev_match = match_start; - match_length = MIN_MATCH - 1; + // If flush != Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } - if ( - hash_head != 0 - && prev_length < config.MaxLazy - && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD - ) + if (bstate == BlockState.BlockDone) + { + if (flush == FlushType.Partial) { - // To simplify the code, we prevent matches with the string - // of window index 0 (in particular we have to avoid a match - // of the string with itself at the start of the input file). - - if (compressionStrategy != CompressionStrategy.HuffmanOnly) - { - match_length = longest_match(hash_head); - } - - // longest_match() sets match_start - - if ( - match_length <= 5 - && ( - compressionStrategy == CompressionStrategy.Filtered - || (match_length == MIN_MATCH && strstart - match_start > 4096) - ) - ) - { - // If prev_match is also MIN_MATCH, match_start is garbage - // but we will ignore the current match anyway. - match_length = MIN_MATCH - 1; - } - } - - // If there was a match at the previous step and the current - // match is not better, output the previous match: - if (prev_length >= MIN_MATCH && match_length <= prev_length) - { - int max_insert = strstart + lookahead - MIN_MATCH; - - // Do not insert strings in hash table beyond this. - - // check_match(strstart-1, prev_match, prev_length); - - bflush = _tr_tally(strstart - 1 - prev_match, prev_length - MIN_MATCH); - - // Insert in hash table all strings up to the end of the match. - // strstart-1 and strstart are already inserted. If there is not - // enough lookahead, the last two strings are not inserted in - // the hash table. - lookahead -= (prev_length - 1); - prev_length -= 2; - do - { - if (++strstart <= max_insert) - { - ins_h = - ( - ((ins_h) << hash_shift) - ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff) - ) & hash_mask; - - //prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = (head[ins_h] & 0xffff); - prev[strstart & w_mask] = head[ins_h]; - head[ins_h] = unchecked((short)strstart); - } - } while (--prev_length != 0); - match_available = 0; - match_length = MIN_MATCH - 1; - strstart++; - - if (bflush) - { - flush_block_only(false); - if (_codec.AvailableBytesOut == 0) - { - return BlockState.NeedMore; - } - } - } - else if (match_available != 0) - { - // If there was no match at the previous position, output a - // single literal. If there was a match but the current match - // is longer, truncate the previous match to a single literal. - - bflush = _tr_tally(0, window[strstart - 1] & 0xff); - - if (bflush) - { - flush_block_only(false); - } - strstart++; - lookahead--; - if (_codec.AvailableBytesOut == 0) - { - return BlockState.NeedMore; - } + _tr_align(); } else { - // There is no previous match to compare with, wait for - // the next step to decide. + // FlushType.Full or FlushType.Sync + _tr_stored_block(0, 0, false); - match_available = 1; - strstart++; - lookahead--; - } - } - - if (match_available != 0) - { - bflush = _tr_tally(0, window[strstart - 1] & 0xff); - match_available = 0; - } - flush_block_only(flush == FlushType.Finish); - - if (_codec.AvailableBytesOut == 0) - { - if (flush == FlushType.Finish) - { - return BlockState.FinishStarted; - } - return BlockState.NeedMore; - } - - return flush == FlushType.Finish ? BlockState.FinishDone : BlockState.BlockDone; - } - - internal int longest_match(int cur_match) - { - int chain_length = config.MaxChainLength; // max hash chain length - int scan = strstart; // current string - int match; // matched string - int len; // length of current match - int best_len = prev_length; // best match length so far - int limit = - strstart > (w_size - MIN_LOOKAHEAD) ? strstart - (w_size - MIN_LOOKAHEAD) : 0; - - int niceLength = config.NiceLength; - - // Stop when cur_match becomes <= limit. To simplify the code, - // we prevent matches with the string of window index 0. - - int wmask = w_mask; - - int strend = strstart + MAX_MATCH; - byte scan_end1 = window[scan + best_len - 1]; - byte scan_end = window[scan + best_len]; - - // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. - // It is easy to get rid of this optimization if necessary. - - // Do not waste too much time if we already have a good match: - if (prev_length >= config.GoodLength) - { - chain_length >>= 2; - } - - // Do not look for matches beyond the end of the input. This is necessary - // to make deflate deterministic. - if (niceLength > lookahead) - { - niceLength = lookahead; - } - - do - { - match = cur_match; - - // Skip to next match if the match length cannot increase - // or if the match length is less than 2: - if ( - window[match + best_len] != scan_end - || window[match + best_len - 1] != scan_end1 - || window[match] != window[scan] - || window[++match] != window[scan + 1] - ) - { - continue; - } - - // The check at best_len-1 can be removed because it will be made - // again later. (This heuristic is not always a win.) - // It is not necessary to compare scan[2] and match[2] since they - // are always equal when the other bytes match, given that - // the hash keys are equal and that HASH_BITS >= 8. - scan += 2; - match++; - - // We check for insufficient lookahead only every 8th comparison; - // the 256th check will be made at strstart+258. - do { } while ( - window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && window[++scan] == window[++match] - && scan < strend - ); - - len = MAX_MATCH - (strend - scan); - scan = strend - MAX_MATCH; - - if (len > best_len) - { - match_start = cur_match; - best_len = len; - if (len >= niceLength) + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if (flush == FlushType.Full) { - break; + // clear hash (forget the history) + for (var i = 0; i < hash_size; i++) + { + head[i] = 0; + } } - scan_end1 = window[scan + best_len - 1]; - scan_end = window[scan + best_len]; } - } while ( - (cur_match = (prev[cur_match & wmask] & 0xffff)) > limit && --chain_length != 0 - ); - - if (best_len <= lookahead) - { - return best_len; - } - return lookahead; - } - - private bool Rfc1950BytesEmitted; - - internal bool WantRfc1950HeaderBytes { get; set; } = true; - - internal int Initialize(ZlibCodec codec, CompressionLevel level) - { - return Initialize(codec, level, ZlibConstants.WindowBitsMax); - } - - internal int Initialize(ZlibCodec codec, CompressionLevel level, int bits) - { - return Initialize(codec, level, bits, MEM_LEVEL_DEFAULT, CompressionStrategy.Default); - } - - internal int Initialize( - ZlibCodec codec, - CompressionLevel level, - int bits, - CompressionStrategy compressionStrategy - ) - { - return Initialize(codec, level, bits, MEM_LEVEL_DEFAULT, compressionStrategy); - } - - internal int Initialize( - ZlibCodec codec, - CompressionLevel level, - int windowBits, - int memLevel, - CompressionStrategy strategy - ) - { - _codec = codec; - _codec.Message = null; - - // validation - if (windowBits < 9 || windowBits > 15) - { - throw new ZlibException("windowBits must be in the range 9..15."); - } - - if (memLevel < 1 || memLevel > MEM_LEVEL_MAX) - { - throw new ZlibException( - String.Format("memLevel must be in the range 1.. {0}", MEM_LEVEL_MAX) - ); - } - - _codec.dstate = this; - - w_bits = windowBits; - w_size = 1 << w_bits; - w_mask = w_size - 1; - - hash_bits = memLevel + 7; - hash_size = 1 << hash_bits; - hash_mask = hash_size - 1; - hash_shift = ((hash_bits + MIN_MATCH - 1) / MIN_MATCH); - - window = new byte[w_size * 2]; - prev = new short[w_size]; - head = new short[hash_size]; - - // for memLevel==8, this will be 16384, 16k - lit_bufsize = 1 << (memLevel + 6); - - // Use a single array as the buffer for data pending compression, - // the output distance codes, and the output length codes (aka tree). - // orig comment: This works just fine since the average - // output size for (length,distance) codes is <= 24 bits. - pending = new byte[lit_bufsize * 4]; - _distanceOffset = lit_bufsize; - _lengthOffset = (1 + 2) * lit_bufsize; - - // So, for memLevel 8, the length of the pending buffer is 65536. 64k. - // The first 16k are pending bytes. - // The middle slice, of 32k, is used for distance codes. - // The final 16k are length codes. - - compressionLevel = level; - compressionStrategy = strategy; - - Reset(); - return ZlibConstants.Z_OK; - } - - internal void Reset() - { - _codec.TotalBytesIn = _codec.TotalBytesOut = 0; - _codec.Message = null; - - //strm.data_type = Z_UNKNOWN; - - pendingCount = 0; - nextPending = 0; - - Rfc1950BytesEmitted = false; - - status = (WantRfc1950HeaderBytes) ? INIT_STATE : BUSY_STATE; - _codec._adler32 = 1; - - last_flush = (int)FlushType.None; - - _InitializeTreeData(); - _InitializeLazyMatch(); - } - - internal int End() - { - if (status != INIT_STATE && status != BUSY_STATE && status != FINISH_STATE) - { - return ZlibConstants.Z_STREAM_ERROR; - } - - // Deallocate in reverse order of allocations: - pending = null; - head = null; - prev = null; - window = null; - - // free - // dstate=null; - return status == BUSY_STATE ? ZlibConstants.Z_DATA_ERROR : ZlibConstants.Z_OK; - } - - private void SetDeflater() - { - switch (config.Flavor) - { - case DeflateFlavor.Store: - DeflateFunction = DeflateNone; - break; - case DeflateFlavor.Fast: - DeflateFunction = DeflateFast; - break; - case DeflateFlavor.Slow: - DeflateFunction = DeflateSlow; - break; - } - } - - internal int SetParams(CompressionLevel level, CompressionStrategy strategy) - { - int result = ZlibConstants.Z_OK; - - if (compressionLevel != level) - { - Config newConfig = Config.Lookup(level); - - // change in the deflate flavor (Fast vs slow vs none)? - if (newConfig.Flavor != config.Flavor && _codec.TotalBytesIn != 0) - { - // Flush the last buffer: - result = _codec.Deflate(FlushType.Partial); - } - - compressionLevel = level; - config = newConfig; - SetDeflater(); - } - - // no need to flush with change in strategy? Really? - compressionStrategy = strategy; - - return result; - } - - internal int SetDictionary(byte[] dictionary) - { - int length = dictionary.Length; - int index = 0; - - if (dictionary is null || status != INIT_STATE) - { - throw new ZlibException("Stream error."); - } - - _codec._adler32 = Adler32.Calculate(_codec._adler32, dictionary); - - if (length < MIN_MATCH) - { - return ZlibConstants.Z_OK; - } - if (length > w_size - MIN_LOOKAHEAD) - { - length = w_size - MIN_LOOKAHEAD; - index = dictionary.Length - length; // use the tail of the dictionary - } - Array.Copy(dictionary, index, window, 0, length); - strstart = length; - blockStart = length; - - // Insert all strings in the hash table (except for the last two bytes). - // s->lookahead stays null, so s->ins_h will be recomputed at the next - // call of fill_window. - - ins_h = window[0] & 0xff; - ins_h = (((ins_h) << hash_shift) ^ (window[1] & 0xff)) & hash_mask; - - for (int n = 0; n <= length - MIN_MATCH; n++) - { - ins_h = - (((ins_h) << hash_shift) ^ (window[(n) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; - prev[n & w_mask] = head[ins_h]; - head[ins_h] = (short)n; - } - return ZlibConstants.Z_OK; - } - - internal int Deflate(FlushType flush) - { - int old_flush; - - if ( - _codec.OutputBuffer is null - || (_codec.InputBuffer is null && _codec.AvailableBytesIn != 0) - || (status == FINISH_STATE && flush != FlushType.Finish) - ) - { - _codec.Message = _ErrorMessage[ - ZlibConstants.Z_NEED_DICT - (ZlibConstants.Z_STREAM_ERROR) - ]; - throw new ZlibException(String.Format("Something is fishy. [{0}]", _codec.Message)); - - //return ZlibConstants.Z_STREAM_ERROR; - } - if (_codec.AvailableBytesOut == 0) - { - _codec.Message = _ErrorMessage[ - ZlibConstants.Z_NEED_DICT - (ZlibConstants.Z_BUF_ERROR) - ]; - throw new ZlibException("OutputBuffer is full (AvailableBytesOut == 0)"); - - //return ZlibConstants.Z_BUF_ERROR; - } - - old_flush = last_flush; - last_flush = (int)flush; - - // Write the zlib (rfc1950) header bytes - if (status == INIT_STATE) - { - int header = (Z_DEFLATED + ((w_bits - 8) << 4)) << 8; - int level_flags = (((int)compressionLevel - 1) & 0xff) >> 1; - - if (level_flags > 3) - { - level_flags = 3; - } - header |= (level_flags << 6); - if (strstart != 0) - { - header |= PRESET_DICT; - } - header += 31 - (header % 31); - - status = BUSY_STATE; - //putShortMSB(header); - unchecked - { - pending[pendingCount++] = (byte)(header >> 8); - pending[pendingCount++] = (byte)header; - } - - // Save the adler32 of the preset dictionary: - if (strstart != 0) - { - ////putShortMSB((int)(SharedUtils.URShift(_codec._Adler32, 16))); - //putShortMSB((int)((UInt64)_codec._Adler32 >> 16)); - //putShortMSB((int)(_codec._Adler32 & 0xffff)); - pending[pendingCount++] = (byte)((_codec._adler32 & 0xFF000000) >> 24); - pending[pendingCount++] = (byte)((_codec._adler32 & 0x00FF0000) >> 16); - pending[pendingCount++] = (byte)((_codec._adler32 & 0x0000FF00) >> 8); - pending[pendingCount++] = (byte)(_codec._adler32 & 0x000000FF); - } - _codec._adler32 = 1; - } - - // Flush as much pending output as possible - if (pendingCount != 0) - { _codec.flush_pending(); if (_codec.AvailableBytesOut == 0) { - //System.out.println(" avail_out==0"); - // Since avail_out is 0, deflate will be called again with - // more output space, but possibly with both pending and - // avail_in equal to zero. There won't be anything to do, - // but this is not an error situation so make sure we - // return OK instead of BUF_ERROR at next call of deflate: - last_flush = -1; + last_flush = -1; // avoid BUF_ERROR at next call, see above return ZlibConstants.Z_OK; } - - // Make sure there is something to do and avoid duplicate consecutive - // flushes. For repeated and useless calls with Z_FINISH, we keep - // returning Z_STREAM_END instead of Z_BUFF_ERROR. } - else if ( - _codec.AvailableBytesIn == 0 && (int)flush <= old_flush && flush != FlushType.Finish - ) - { - // workitem 8557 - // Not sure why this needs to be an error. - // pendingCount == 0, which means there's nothing to deflate. - // And the caller has not asked for a FlushType.Finish, but... - // that seems very non-fatal. We can just say "OK" and do nthing. - - // _codec.Message = z_errmsg[ZlibConstants.Z_NEED_DICT - (ZlibConstants.Z_BUF_ERROR)]; - // throw new ZlibException("AvailableBytesIn == 0 && flush<=old_flush && flush != FlushType.Finish"); - - return ZlibConstants.Z_OK; - } - - // User must not provide more input after the first FINISH: - if (status == FINISH_STATE && _codec.AvailableBytesIn != 0) - { - _codec.Message = _ErrorMessage[ - ZlibConstants.Z_NEED_DICT - (ZlibConstants.Z_BUF_ERROR) - ]; - throw new ZlibException("status == FINISH_STATE && _codec.AvailableBytesIn != 0"); - } - - // Start a new block or continue the current one. - if ( - _codec.AvailableBytesIn != 0 - || lookahead != 0 - || (flush != FlushType.None && status != FINISH_STATE) - ) - { - BlockState bstate = DeflateFunction(flush); - - if (bstate == BlockState.FinishStarted || bstate == BlockState.FinishDone) - { - status = FINISH_STATE; - } - if (bstate == BlockState.NeedMore || bstate == BlockState.FinishStarted) - { - if (_codec.AvailableBytesOut == 0) - { - last_flush = -1; // avoid BUF_ERROR next call, see above - } - return ZlibConstants.Z_OK; - - // If flush != Z_NO_FLUSH && avail_out == 0, the next call - // of deflate should use the same flush parameter to make sure - // that the flush is complete. So we don't have to output an - // empty block here, this will be done at next call. This also - // ensures that for a very small output buffer, we emit at most - // one empty block. - } - - if (bstate == BlockState.BlockDone) - { - if (flush == FlushType.Partial) - { - _tr_align(); - } - else - { - // FlushType.Full or FlushType.Sync - _tr_stored_block(0, 0, false); - - // For a full flush, this empty block will be recognized - // as a special marker by inflate_sync(). - if (flush == FlushType.Full) - { - // clear hash (forget the history) - for (int i = 0; i < hash_size; i++) - { - head[i] = 0; - } - } - } - _codec.flush_pending(); - if (_codec.AvailableBytesOut == 0) - { - last_flush = -1; // avoid BUF_ERROR at next call, see above - return ZlibConstants.Z_OK; - } - } - } - - if (flush != FlushType.Finish) - { - return ZlibConstants.Z_OK; - } - - if (!WantRfc1950HeaderBytes || Rfc1950BytesEmitted) - { - return ZlibConstants.Z_STREAM_END; - } - - // Write the zlib trailer (adler32) - pending[pendingCount++] = (byte)((_codec._adler32 & 0xFF000000) >> 24); - pending[pendingCount++] = (byte)((_codec._adler32 & 0x00FF0000) >> 16); - pending[pendingCount++] = (byte)((_codec._adler32 & 0x0000FF00) >> 8); - pending[pendingCount++] = (byte)(_codec._adler32 & 0x000000FF); - - //putShortMSB((int)(SharedUtils.URShift(_codec._Adler32, 16))); - //putShortMSB((int)(_codec._Adler32 & 0xffff)); - - _codec.flush_pending(); - - // If avail_out is zero, the application will call deflate again - // to flush the rest. - - Rfc1950BytesEmitted = true; // write the trailer only once! - - return pendingCount != 0 ? ZlibConstants.Z_OK : ZlibConstants.Z_STREAM_END; } + + if (flush != FlushType.Finish) + { + return ZlibConstants.Z_OK; + } + + if (!WantRfc1950HeaderBytes || Rfc1950BytesEmitted) + { + return ZlibConstants.Z_STREAM_END; + } + + // Write the zlib trailer (adler32) + pending[pendingCount++] = (byte)((_codec._adler32 & 0xFF000000) >> 24); + pending[pendingCount++] = (byte)((_codec._adler32 & 0x00FF0000) >> 16); + pending[pendingCount++] = (byte)((_codec._adler32 & 0x0000FF00) >> 8); + pending[pendingCount++] = (byte)(_codec._adler32 & 0x000000FF); + + //putShortMSB((int)(SharedUtils.URShift(_codec._Adler32, 16))); + //putShortMSB((int)(_codec._Adler32 & 0xffff)); + + _codec.flush_pending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + + Rfc1950BytesEmitted = true; // write the trailer only once! + + return pendingCount != 0 ? ZlibConstants.Z_OK : ZlibConstants.Z_STREAM_END; } } diff --git a/src/SharpCompress/Compressors/Deflate/DeflateStream.cs b/src/SharpCompress/Compressors/Deflate/DeflateStream.cs index c34b8282..cb7512a6 100644 --- a/src/SharpCompress/Compressors/Deflate/DeflateStream.cs +++ b/src/SharpCompress/Compressors/Deflate/DeflateStream.cs @@ -28,356 +28,355 @@ using System; using System.IO; using System.Text; -namespace SharpCompress.Compressors.Deflate +namespace SharpCompress.Compressors.Deflate; + +public class DeflateStream : Stream { - public class DeflateStream : Stream + private readonly ZlibBaseStream _baseStream; + private bool _disposed; + + public DeflateStream( + Stream stream, + CompressionMode mode, + CompressionLevel level = CompressionLevel.Default, + Encoding? forceEncoding = null + ) { - private readonly ZlibBaseStream _baseStream; - private bool _disposed; - - public DeflateStream( - Stream stream, - CompressionMode mode, - CompressionLevel level = CompressionLevel.Default, - Encoding? forceEncoding = null - ) - { - _baseStream = new ZlibBaseStream( - stream, - mode, - level, - ZlibStreamFlavor.DEFLATE, - forceEncoding - ); - } - - #region Zlib properties - - /// - /// This property sets the flush behavior on the stream. - /// - /// See the ZLIB documentation for the meaning of the flush behavior. - /// - public virtual FlushType FlushMode - { - get => (_baseStream._flushMode); - set - { - if (_disposed) - { - throw new ObjectDisposedException("DeflateStream"); - } - _baseStream._flushMode = value; - } - } - - /// - /// The size of the working buffer for the compression codec. - /// - /// - /// - /// - /// The working buffer is used for all stream operations. The default size is - /// 1024 bytes. The minimum size is 128 bytes. You may get better performance - /// with a larger buffer. Then again, you might not. You would have to test - /// it. - /// - /// - /// - /// Set this before the first call to Read() or Write() on the - /// stream. If you try to set it afterwards, it will throw. - /// - /// - public int BufferSize - { - get => _baseStream._bufferSize; - set - { - if (_disposed) - { - throw new ObjectDisposedException("DeflateStream"); - } - if (_baseStream._workingBuffer != null) - { - throw new ZlibException("The working buffer is already set."); - } - if (value < ZlibConstants.WorkingBufferSizeMin) - { - throw new ZlibException( - String.Format( - "Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", - value, - ZlibConstants.WorkingBufferSizeMin - ) - ); - } - _baseStream._bufferSize = value; - } - } - - /// - /// The ZLIB strategy to be used during compression. - /// - /// - /// - /// By tweaking this parameter, you may be able to optimize the compression for - /// data with particular characteristics. - /// - public CompressionStrategy Strategy - { - get => _baseStream.Strategy; - set - { - if (_disposed) - { - throw new ObjectDisposedException("DeflateStream"); - } - _baseStream.Strategy = value; - } - } - - /// Returns the total number of bytes input so far. - public virtual long TotalIn => _baseStream._z.TotalBytesIn; - - /// Returns the total number of bytes output so far. - public virtual long TotalOut => _baseStream._z.TotalBytesOut; - - #endregion - - #region System.IO.Stream methods - - /// - /// Indicates whether the stream can be read. - /// - /// - /// The return value depends on whether the captive stream supports reading. - /// - public override bool CanRead - { - get - { - if (_disposed) - { - throw new ObjectDisposedException("DeflateStream"); - } - return _baseStream._stream.CanRead; - } - } - - /// - /// Indicates whether the stream supports Seek operations. - /// - /// - /// Always returns false. - /// - public override bool CanSeek => false; - - /// - /// Indicates whether the stream can be written. - /// - /// - /// The return value depends on whether the captive stream supports writing. - /// - public override bool CanWrite - { - get - { - if (_disposed) - { - throw new ObjectDisposedException("DeflateStream"); - } - return _baseStream._stream.CanWrite; - } - } - - /// - /// Reading this property always throws a . - /// - public override long Length => throw new NotSupportedException(); - - /// - /// The position of the stream pointer. - /// - /// - /// - /// Setting this property always throws a . Reading will return the total bytes - /// written out, if used in writing, or the total bytes read in, if used in - /// reading. The count may refer to compressed bytes or uncompressed bytes, - /// depending on how you've used the stream. - /// - public override long Position - { - get - { - if (_baseStream._streamMode == ZlibBaseStream.StreamMode.Writer) - { - return _baseStream._z.TotalBytesOut; - } - if (_baseStream._streamMode == ZlibBaseStream.StreamMode.Reader) - { - return _baseStream._z.TotalBytesIn; - } - return 0; - } - set => throw new NotSupportedException(); - } - - /// - /// Dispose the stream. - /// - /// - /// This may or may not result in a Close() call on the captive stream. - /// - protected override void Dispose(bool disposing) - { - try - { - if (!_disposed) - { - if (disposing) - { - _baseStream?.Dispose(); - } - _disposed = true; - } - } - finally - { - base.Dispose(disposing); - } - } - - /// - /// Flush the stream. - /// - public override void Flush() - { - if (_disposed) - { - throw new ObjectDisposedException("DeflateStream"); - } - _baseStream.Flush(); - } - - /// - /// Read data from the stream. - /// - /// - /// - /// - /// If you wish to use the DeflateStream to compress data while - /// reading, you can create a DeflateStream with - /// CompressionMode.Compress, providing an uncompressed data stream. - /// Then call Read() on that DeflateStream, and the data read will be - /// compressed as you read. If you wish to use the DeflateStream to - /// decompress data while reading, you can create a DeflateStream with - /// CompressionMode.Decompress, providing a readable compressed data - /// stream. Then call Read() on that DeflateStream, and the data read - /// will be decompressed as you read. - /// - /// - /// - /// A DeflateStream can be used for Read() or Write(), but not both. - /// - /// - /// - /// The buffer into which the read data should be placed. - /// the offset within that data array to put the first byte read. - /// the number of bytes to read. - /// the number of bytes actually read - public override int Read(byte[] buffer, int offset, int count) - { - if (_disposed) - { - throw new ObjectDisposedException("DeflateStream"); - } - return _baseStream.Read(buffer, offset, count); - } - - public override int ReadByte() - { - if (_disposed) - { - throw new ObjectDisposedException("DeflateStream"); - } - return _baseStream.ReadByte(); - } - - /// - /// Calling this method always throws a . - /// - /// this is irrelevant, since it will always throw! - /// this is irrelevant, since it will always throw! - /// irrelevant! - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - /// - /// Calling this method always throws a . - /// - /// this is irrelevant, since it will always throw! - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - /// - /// Write data to the stream. - /// - /// - /// - /// - /// If you wish to use the DeflateStream to compress data while - /// writing, you can create a DeflateStream with - /// CompressionMode.Compress, and a writable output stream. Then call - /// Write() on that DeflateStream, providing uncompressed data - /// as input. The data sent to the output stream will be the compressed form - /// of the data written. If you wish to use the DeflateStream to - /// decompress data while writing, you can create a DeflateStream with - /// CompressionMode.Decompress, and a writable output stream. Then - /// call Write() on that stream, providing previously compressed - /// data. The data sent to the output stream will be the decompressed form of - /// the data written. - /// - /// - /// - /// A DeflateStream can be used for Read() or Write(), - /// but not both. - /// - /// - /// - /// - /// The buffer holding data to write to the stream. - /// the offset within that data array to find the first byte to write. - /// the number of bytes to write. - public override void Write(byte[] buffer, int offset, int count) - { - if (_disposed) - { - throw new ObjectDisposedException("DeflateStream"); - } - _baseStream.Write(buffer, offset, count); - } - - public override void WriteByte(byte value) - { - if (_disposed) - { - throw new ObjectDisposedException("DeflateStream"); - } - _baseStream.WriteByte(value); - } - - #endregion - - public MemoryStream InputBuffer => - new MemoryStream( - _baseStream._z.InputBuffer, - _baseStream._z.NextIn, - _baseStream._z.AvailableBytesIn - ); + _baseStream = new ZlibBaseStream( + stream, + mode, + level, + ZlibStreamFlavor.DEFLATE, + forceEncoding + ); } + + #region Zlib properties + + /// + /// This property sets the flush behavior on the stream. + /// + /// See the ZLIB documentation for the meaning of the flush behavior. + /// + public virtual FlushType FlushMode + { + get => (_baseStream._flushMode); + set + { + if (_disposed) + { + throw new ObjectDisposedException("DeflateStream"); + } + _baseStream._flushMode = value; + } + } + + /// + /// The size of the working buffer for the compression codec. + /// + /// + /// + /// + /// The working buffer is used for all stream operations. The default size is + /// 1024 bytes. The minimum size is 128 bytes. You may get better performance + /// with a larger buffer. Then again, you might not. You would have to test + /// it. + /// + /// + /// + /// Set this before the first call to Read() or Write() on the + /// stream. If you try to set it afterwards, it will throw. + /// + /// + public int BufferSize + { + get => _baseStream._bufferSize; + set + { + if (_disposed) + { + throw new ObjectDisposedException("DeflateStream"); + } + if (_baseStream._workingBuffer != null) + { + throw new ZlibException("The working buffer is already set."); + } + if (value < ZlibConstants.WorkingBufferSizeMin) + { + throw new ZlibException( + string.Format( + "Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", + value, + ZlibConstants.WorkingBufferSizeMin + ) + ); + } + _baseStream._bufferSize = value; + } + } + + /// + /// The ZLIB strategy to be used during compression. + /// + /// + /// + /// By tweaking this parameter, you may be able to optimize the compression for + /// data with particular characteristics. + /// + public CompressionStrategy Strategy + { + get => _baseStream.Strategy; + set + { + if (_disposed) + { + throw new ObjectDisposedException("DeflateStream"); + } + _baseStream.Strategy = value; + } + } + + /// Returns the total number of bytes input so far. + public virtual long TotalIn => _baseStream._z.TotalBytesIn; + + /// Returns the total number of bytes output so far. + public virtual long TotalOut => _baseStream._z.TotalBytesOut; + + #endregion + + #region System.IO.Stream methods + + /// + /// Indicates whether the stream can be read. + /// + /// + /// The return value depends on whether the captive stream supports reading. + /// + public override bool CanRead + { + get + { + if (_disposed) + { + throw new ObjectDisposedException("DeflateStream"); + } + return _baseStream._stream.CanRead; + } + } + + /// + /// Indicates whether the stream supports Seek operations. + /// + /// + /// Always returns false. + /// + public override bool CanSeek => false; + + /// + /// Indicates whether the stream can be written. + /// + /// + /// The return value depends on whether the captive stream supports writing. + /// + public override bool CanWrite + { + get + { + if (_disposed) + { + throw new ObjectDisposedException("DeflateStream"); + } + return _baseStream._stream.CanWrite; + } + } + + /// + /// Reading this property always throws a . + /// + public override long Length => throw new NotSupportedException(); + + /// + /// The position of the stream pointer. + /// + /// + /// + /// Setting this property always throws a . Reading will return the total bytes + /// written out, if used in writing, or the total bytes read in, if used in + /// reading. The count may refer to compressed bytes or uncompressed bytes, + /// depending on how you've used the stream. + /// + public override long Position + { + get + { + if (_baseStream._streamMode == ZlibBaseStream.StreamMode.Writer) + { + return _baseStream._z.TotalBytesOut; + } + if (_baseStream._streamMode == ZlibBaseStream.StreamMode.Reader) + { + return _baseStream._z.TotalBytesIn; + } + return 0; + } + set => throw new NotSupportedException(); + } + + /// + /// Dispose the stream. + /// + /// + /// This may or may not result in a Close() call on the captive stream. + /// + protected override void Dispose(bool disposing) + { + try + { + if (!_disposed) + { + if (disposing) + { + _baseStream?.Dispose(); + } + _disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + + /// + /// Flush the stream. + /// + public override void Flush() + { + if (_disposed) + { + throw new ObjectDisposedException("DeflateStream"); + } + _baseStream.Flush(); + } + + /// + /// Read data from the stream. + /// + /// + /// + /// + /// If you wish to use the DeflateStream to compress data while + /// reading, you can create a DeflateStream with + /// CompressionMode.Compress, providing an uncompressed data stream. + /// Then call Read() on that DeflateStream, and the data read will be + /// compressed as you read. If you wish to use the DeflateStream to + /// decompress data while reading, you can create a DeflateStream with + /// CompressionMode.Decompress, providing a readable compressed data + /// stream. Then call Read() on that DeflateStream, and the data read + /// will be decompressed as you read. + /// + /// + /// + /// A DeflateStream can be used for Read() or Write(), but not both. + /// + /// + /// + /// The buffer into which the read data should be placed. + /// the offset within that data array to put the first byte read. + /// the number of bytes to read. + /// the number of bytes actually read + public override int Read(byte[] buffer, int offset, int count) + { + if (_disposed) + { + throw new ObjectDisposedException("DeflateStream"); + } + return _baseStream.Read(buffer, offset, count); + } + + public override int ReadByte() + { + if (_disposed) + { + throw new ObjectDisposedException("DeflateStream"); + } + return _baseStream.ReadByte(); + } + + /// + /// Calling this method always throws a . + /// + /// this is irrelevant, since it will always throw! + /// this is irrelevant, since it will always throw! + /// irrelevant! + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + /// + /// Calling this method always throws a . + /// + /// this is irrelevant, since it will always throw! + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + /// + /// Write data to the stream. + /// + /// + /// + /// + /// If you wish to use the DeflateStream to compress data while + /// writing, you can create a DeflateStream with + /// CompressionMode.Compress, and a writable output stream. Then call + /// Write() on that DeflateStream, providing uncompressed data + /// as input. The data sent to the output stream will be the compressed form + /// of the data written. If you wish to use the DeflateStream to + /// decompress data while writing, you can create a DeflateStream with + /// CompressionMode.Decompress, and a writable output stream. Then + /// call Write() on that stream, providing previously compressed + /// data. The data sent to the output stream will be the decompressed form of + /// the data written. + /// + /// + /// + /// A DeflateStream can be used for Read() or Write(), + /// but not both. + /// + /// + /// + /// + /// The buffer holding data to write to the stream. + /// the offset within that data array to find the first byte to write. + /// the number of bytes to write. + public override void Write(byte[] buffer, int offset, int count) + { + if (_disposed) + { + throw new ObjectDisposedException("DeflateStream"); + } + _baseStream.Write(buffer, offset, count); + } + + public override void WriteByte(byte value) + { + if (_disposed) + { + throw new ObjectDisposedException("DeflateStream"); + } + _baseStream.WriteByte(value); + } + + #endregion + + public MemoryStream InputBuffer => + new MemoryStream( + _baseStream._z.InputBuffer, + _baseStream._z.NextIn, + _baseStream._z.AvailableBytesIn + ); } diff --git a/src/SharpCompress/Compressors/Deflate/FlushType.cs b/src/SharpCompress/Compressors/Deflate/FlushType.cs index 1cb4eb06..bcf7fe10 100644 --- a/src/SharpCompress/Compressors/Deflate/FlushType.cs +++ b/src/SharpCompress/Compressors/Deflate/FlushType.cs @@ -1,44 +1,43 @@ -namespace SharpCompress.Compressors.Deflate +namespace SharpCompress.Compressors.Deflate; + +/// +/// Describes how to flush the current deflate operation. +/// +/// +/// The different FlushType values are useful when using a Deflate in a streaming application. +/// +public enum FlushType { - /// - /// Describes how to flush the current deflate operation. + /// No flush at all. + None = 0, + + /// Closes the current block, but doesn't flush it to + /// the output. Used internally only in hypothetical + /// scenarios. This was supposed to be removed by Zlib, but it is + /// still in use in some edge cases. /// - /// - /// The different FlushType values are useful when using a Deflate in a streaming application. - /// - public enum FlushType - { - /// No flush at all. - None = 0, + Partial, - /// Closes the current block, but doesn't flush it to - /// the output. Used internally only in hypothetical - /// scenarios. This was supposed to be removed by Zlib, but it is - /// still in use in some edge cases. - /// - Partial, + /// + /// Use this during compression to specify that all pending output should be + /// flushed to the output buffer and the output should be aligned on a byte + /// boundary. You might use this in a streaming communication scenario, so that + /// the decompressor can get all input data available so far. When using this + /// with a ZlibCodec, AvailableBytesIn will be zero after the call if + /// enough output space has been provided before the call. Flushing will + /// degrade compression and so it should be used only when necessary. + /// + Sync, - /// - /// Use this during compression to specify that all pending output should be - /// flushed to the output buffer and the output should be aligned on a byte - /// boundary. You might use this in a streaming communication scenario, so that - /// the decompressor can get all input data available so far. When using this - /// with a ZlibCodec, AvailableBytesIn will be zero after the call if - /// enough output space has been provided before the call. Flushing will - /// degrade compression and so it should be used only when necessary. - /// - Sync, + /// + /// Use this during compression to specify that all output should be flushed, as + /// with FlushType.Sync, but also, the compression state should be reset + /// so that decompression can restart from this point if previous compressed + /// data has been damaged or if random access is desired. Using + /// FlushType.Full too often can significantly degrade the compression. + /// + Full, - /// - /// Use this during compression to specify that all output should be flushed, as - /// with FlushType.Sync, but also, the compression state should be reset - /// so that decompression can restart from this point if previous compressed - /// data has been damaged or if random access is desired. Using - /// FlushType.Full too often can significantly degrade the compression. - /// - Full, - - /// Signals the end of the compression/decompression stream. - Finish - } + /// Signals the end of the compression/decompression stream. + Finish } diff --git a/src/SharpCompress/Compressors/Deflate/GZipStream.cs b/src/SharpCompress/Compressors/Deflate/GZipStream.cs index 572f677e..9c99cd26 100644 --- a/src/SharpCompress/Compressors/Deflate/GZipStream.cs +++ b/src/SharpCompress/Compressors/Deflate/GZipStream.cs @@ -31,461 +31,457 @@ using System.Buffers.Binary; using System.IO; using System.Text; -namespace SharpCompress.Compressors.Deflate +namespace SharpCompress.Compressors.Deflate; + +public class GZipStream : Stream { - public class GZipStream : Stream + internal static readonly DateTime UNIX_EPOCH = new DateTime( + 1970, + 1, + 1, + 0, + 0, + 0, + DateTimeKind.Utc + ); + + private string? _comment; + private string? _fileName; + private DateTime? _lastModified; + + internal ZlibBaseStream BaseStream; + private bool _disposed; + private bool _firstReadDone; + private int _headerByteCount; + + private readonly Encoding _encoding; + + public GZipStream(Stream stream, CompressionMode mode) + : this(stream, mode, CompressionLevel.Default, Encoding.UTF8) { } + + public GZipStream(Stream stream, CompressionMode mode, CompressionLevel level) + : this(stream, mode, level, Encoding.UTF8) { } + + public GZipStream( + Stream stream, + CompressionMode mode, + CompressionLevel level, + Encoding encoding + ) { - internal static readonly DateTime UNIX_EPOCH = new DateTime( - 1970, - 1, - 1, - 0, - 0, - 0, - DateTimeKind.Utc - ); + BaseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.GZIP, encoding); + _encoding = encoding; + } - private string? _comment; - private string? _fileName; - private DateTime? _lastModified; + #region Zlib properties - internal ZlibBaseStream BaseStream; - private bool _disposed; - private bool _firstReadDone; - private int _headerByteCount; - - private readonly Encoding _encoding; - - public GZipStream(Stream stream, CompressionMode mode) - : this(stream, mode, CompressionLevel.Default, Encoding.UTF8) { } - - public GZipStream(Stream stream, CompressionMode mode, CompressionLevel level) - : this(stream, mode, level, Encoding.UTF8) { } - - public GZipStream( - Stream stream, - CompressionMode mode, - CompressionLevel level, - Encoding encoding - ) - { - BaseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.GZIP, encoding); - _encoding = encoding; - } - - #region Zlib properties - - public virtual FlushType FlushMode - { - get => (BaseStream._flushMode); - set - { - if (_disposed) - { - throw new ObjectDisposedException("GZipStream"); - } - BaseStream._flushMode = value; - } - } - - public int BufferSize - { - get => BaseStream._bufferSize; - set - { - if (_disposed) - { - throw new ObjectDisposedException("GZipStream"); - } - if (BaseStream._workingBuffer != null) - { - throw new ZlibException("The working buffer is already set."); - } - if (value < ZlibConstants.WorkingBufferSizeMin) - { - throw new ZlibException( - String.Format( - "Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", - value, - ZlibConstants.WorkingBufferSizeMin - ) - ); - } - BaseStream._bufferSize = value; - } - } - - internal virtual long TotalIn => BaseStream._z.TotalBytesIn; - - internal virtual long TotalOut => BaseStream._z.TotalBytesOut; - - #endregion - - #region Stream methods - - /// - /// Indicates whether the stream can be read. - /// - /// - /// The return value depends on whether the captive stream supports reading. - /// - public override bool CanRead - { - get - { - if (_disposed) - { - throw new ObjectDisposedException("GZipStream"); - } - return BaseStream._stream.CanRead; - } - } - - /// - /// Indicates whether the stream supports Seek operations. - /// - /// - /// Always returns false. - /// - public override bool CanSeek => false; - - /// - /// Indicates whether the stream can be written. - /// - /// - /// The return value depends on whether the captive stream supports writing. - /// - public override bool CanWrite - { - get - { - if (_disposed) - { - throw new ObjectDisposedException("GZipStream"); - } - return BaseStream._stream.CanWrite; - } - } - - /// - /// Reading this property always throws a . - /// - public override long Length => throw new NotSupportedException(); - - /// - /// The position of the stream pointer. - /// - /// - /// - /// Setting this property always throws a . Reading will return the total bytes - /// written out, if used in writing, or the total bytes read in, if used in - /// reading. The count may refer to compressed bytes or uncompressed bytes, - /// depending on how you've used the stream. - /// - public override long Position - { - get - { - if (BaseStream._streamMode == ZlibBaseStream.StreamMode.Writer) - { - return BaseStream._z.TotalBytesOut + _headerByteCount; - } - if (BaseStream._streamMode == ZlibBaseStream.StreamMode.Reader) - { - return BaseStream._z.TotalBytesIn + BaseStream._gzipHeaderByteCount; - } - return 0; - } - set => throw new NotSupportedException(); - } - - /// - /// Dispose the stream. - /// - /// - /// This may or may not result in a Close() call on the captive stream. - /// - protected override void Dispose(bool disposing) - { - try - { - if (!_disposed) - { - if (disposing && (BaseStream != null)) - { - BaseStream.Dispose(); - Crc32 = BaseStream.Crc32; - } - _disposed = true; - } - } - finally - { - base.Dispose(disposing); - } - } - - /// - /// Flush the stream. - /// - public override void Flush() + public virtual FlushType FlushMode + { + get => (BaseStream._flushMode); + set { if (_disposed) { throw new ObjectDisposedException("GZipStream"); } - BaseStream.Flush(); - } - - /// - /// Read and decompress data from the source stream. - /// - /// - /// - /// With a GZipStream, decompression is done through reading. - /// - /// - /// - /// - /// byte[] working = new byte[WORKING_BUFFER_SIZE]; - /// using (System.IO.Stream input = System.IO.File.OpenRead(_CompressedFile)) - /// { - /// using (Stream decompressor= new Ionic.Zlib.GZipStream(input, CompressionMode.Decompress, true)) - /// { - /// using (var output = System.IO.File.Create(_DecompressedFile)) - /// { - /// int n; - /// while ((n= decompressor.Read(working, 0, working.Length)) !=0) - /// { - /// output.Write(working, 0, n); - /// } - /// } - /// } - /// } - /// - /// - /// The buffer into which the decompressed data should be placed. - /// the offset within that data array to put the first byte read. - /// the number of bytes to read. - /// the number of bytes actually read - public override int Read(byte[] buffer, int offset, int count) - { - if (_disposed) - { - throw new ObjectDisposedException("GZipStream"); - } - int n = BaseStream.Read(buffer, offset, count); - - // Console.WriteLine("GZipStream::Read(buffer, off({0}), c({1}) = {2}", offset, count, n); - // Console.WriteLine( Util.FormatByteArray(buffer, offset, n) ); - - if (!_firstReadDone) - { - _firstReadDone = true; - FileName = BaseStream._GzipFileName; - Comment = BaseStream._GzipComment; - LastModified = BaseStream._GzipMtime; - } - return n; - } - - /// - /// Calling this method always throws a . - /// - /// irrelevant; it will always throw! - /// irrelevant; it will always throw! - /// irrelevant! - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - /// - /// Calling this method always throws a . - /// - /// irrelevant; this method will always throw! - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - /// - /// Write data to the stream. - /// - /// - /// - /// - /// If you wish to use the GZipStream to compress data while writing, - /// you can create a GZipStream with CompressionMode.Compress, and a - /// writable output stream. Then call Write() on that GZipStream, - /// providing uncompressed data as input. The data sent to the output stream - /// will be the compressed form of the data written. - /// - /// - /// - /// A GZipStream can be used for Read() or Write(), but not - /// both. Writing implies compression. Reading implies decompression. - /// - /// - /// - /// The buffer holding data to write to the stream. - /// the offset within that data array to find the first byte to write. - /// the number of bytes to write. - public override void Write(byte[] buffer, int offset, int count) - { - if (_disposed) - { - throw new ObjectDisposedException("GZipStream"); - } - if (BaseStream._streamMode == ZlibBaseStream.StreamMode.Undefined) - { - //Console.WriteLine("GZipStream: First write"); - if (BaseStream._wantCompress) - { - // first write in compression, therefore, emit the GZIP header - _headerByteCount = EmitHeader(); - } - else - { - throw new InvalidOperationException(); - } - } - - BaseStream.Write(buffer, offset, count); - } - - #endregion Stream methods - - public string? Comment - { - get => _comment; - set - { - if (_disposed) - { - throw new ObjectDisposedException(nameof(GZipStream)); - } - _comment = value; - } - } - - public DateTime? LastModified - { - get => _lastModified; - set - { - if (_disposed) - { - throw new ObjectDisposedException(nameof(GZipStream)); - } - _lastModified = value; - } - } - - public string? FileName - { - get => _fileName; - set - { - if (_disposed) - { - throw new ObjectDisposedException(nameof(GZipStream)); - } - _fileName = value; - if (_fileName is null) - { - return; - } - if (_fileName.Contains('/')) - { - _fileName = _fileName.Replace('/', '\\'); - } - if (_fileName.EndsWith('\\')) - { - throw new InvalidOperationException("Illegal filename"); - } - - if (_fileName.Contains('\\')) - { - // trim any leading path - _fileName = Path.GetFileName(_fileName); - } - } - } - - public int Crc32 { get; private set; } - - private int EmitHeader() - { - byte[]? commentBytes = (Comment is null) ? null : _encoding.GetBytes(Comment); - byte[]? filenameBytes = (FileName is null) ? null : _encoding.GetBytes(FileName); - - int cbLength = commentBytes?.Length + 1 ?? 0; - int fnLength = filenameBytes?.Length + 1 ?? 0; - - int bufferLength = 10 + cbLength + fnLength; - var header = new byte[bufferLength]; - int i = 0; - - // ID - header[i++] = 0x1F; - header[i++] = 0x8B; - - // compression method - header[i++] = 8; - byte flag = 0; - if (Comment != null) - { - flag ^= 0x10; - } - if (FileName != null) - { - flag ^= 0x8; - } - - // flag - header[i++] = flag; - - // mtime - if (LastModified is null) - { - LastModified = DateTime.Now; - } - TimeSpan delta = LastModified.Value - UNIX_EPOCH; - var timet = (int)delta.TotalSeconds; - BinaryPrimitives.WriteInt32LittleEndian(header.AsSpan(i), timet); - i += 4; - - // xflg - header[i++] = 0; // this field is totally useless - - // OS - header[i++] = 0xFF; // 0xFF == unspecified - - // extra field length - only if FEXTRA is set, which it is not. - //header[i++]= 0; - //header[i++]= 0; - - // filename - if (fnLength != 0) - { - Array.Copy(filenameBytes!, 0, header, i, fnLength - 1); - i += fnLength - 1; - header[i++] = 0; // terminate - } - - // comment - if (cbLength != 0) - { - Array.Copy(commentBytes!, 0, header, i, cbLength - 1); - i += cbLength - 1; - header[i++] = 0; // terminate - } - - BaseStream._stream.Write(header, 0, header.Length); - - return header.Length; // bytes written + BaseStream._flushMode = value; } } + + public int BufferSize + { + get => BaseStream._bufferSize; + set + { + if (_disposed) + { + throw new ObjectDisposedException("GZipStream"); + } + if (BaseStream._workingBuffer != null) + { + throw new ZlibException("The working buffer is already set."); + } + if (value < ZlibConstants.WorkingBufferSizeMin) + { + throw new ZlibException( + string.Format( + "Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", + value, + ZlibConstants.WorkingBufferSizeMin + ) + ); + } + BaseStream._bufferSize = value; + } + } + + internal virtual long TotalIn => BaseStream._z.TotalBytesIn; + + internal virtual long TotalOut => BaseStream._z.TotalBytesOut; + + #endregion + + #region Stream methods + + /// + /// Indicates whether the stream can be read. + /// + /// + /// The return value depends on whether the captive stream supports reading. + /// + public override bool CanRead + { + get + { + if (_disposed) + { + throw new ObjectDisposedException("GZipStream"); + } + return BaseStream._stream.CanRead; + } + } + + /// + /// Indicates whether the stream supports Seek operations. + /// + /// + /// Always returns false. + /// + public override bool CanSeek => false; + + /// + /// Indicates whether the stream can be written. + /// + /// + /// The return value depends on whether the captive stream supports writing. + /// + public override bool CanWrite + { + get + { + if (_disposed) + { + throw new ObjectDisposedException("GZipStream"); + } + return BaseStream._stream.CanWrite; + } + } + + /// + /// Reading this property always throws a . + /// + public override long Length => throw new NotSupportedException(); + + /// + /// The position of the stream pointer. + /// + /// + /// + /// Setting this property always throws a . Reading will return the total bytes + /// written out, if used in writing, or the total bytes read in, if used in + /// reading. The count may refer to compressed bytes or uncompressed bytes, + /// depending on how you've used the stream. + /// + public override long Position + { + get + { + if (BaseStream._streamMode == ZlibBaseStream.StreamMode.Writer) + { + return BaseStream._z.TotalBytesOut + _headerByteCount; + } + if (BaseStream._streamMode == ZlibBaseStream.StreamMode.Reader) + { + return BaseStream._z.TotalBytesIn + BaseStream._gzipHeaderByteCount; + } + return 0; + } + set => throw new NotSupportedException(); + } + + /// + /// Dispose the stream. + /// + /// + /// This may or may not result in a Close() call on the captive stream. + /// + protected override void Dispose(bool disposing) + { + try + { + if (!_disposed) + { + if (disposing && (BaseStream != null)) + { + BaseStream.Dispose(); + Crc32 = BaseStream.Crc32; + } + _disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + + /// + /// Flush the stream. + /// + public override void Flush() + { + if (_disposed) + { + throw new ObjectDisposedException("GZipStream"); + } + BaseStream.Flush(); + } + + /// + /// Read and decompress data from the source stream. + /// + /// + /// + /// With a GZipStream, decompression is done through reading. + /// + /// + /// + /// + /// byte[] working = new byte[WORKING_BUFFER_SIZE]; + /// using (System.IO.Stream input = System.IO.File.OpenRead(_CompressedFile)) + /// { + /// using (Stream decompressor= new Ionic.Zlib.GZipStream(input, CompressionMode.Decompress, true)) + /// { + /// using (var output = System.IO.File.Create(_DecompressedFile)) + /// { + /// int n; + /// while ((n= decompressor.Read(working, 0, working.Length)) !=0) + /// { + /// output.Write(working, 0, n); + /// } + /// } + /// } + /// } + /// + /// + /// The buffer into which the decompressed data should be placed. + /// the offset within that data array to put the first byte read. + /// the number of bytes to read. + /// the number of bytes actually read + public override int Read(byte[] buffer, int offset, int count) + { + if (_disposed) + { + throw new ObjectDisposedException("GZipStream"); + } + var n = BaseStream.Read(buffer, offset, count); + + // Console.WriteLine("GZipStream::Read(buffer, off({0}), c({1}) = {2}", offset, count, n); + // Console.WriteLine( Util.FormatByteArray(buffer, offset, n) ); + + if (!_firstReadDone) + { + _firstReadDone = true; + FileName = BaseStream._GzipFileName; + Comment = BaseStream._GzipComment; + LastModified = BaseStream._GzipMtime; + } + return n; + } + + /// + /// Calling this method always throws a . + /// + /// irrelevant; it will always throw! + /// irrelevant; it will always throw! + /// irrelevant! + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + /// + /// Calling this method always throws a . + /// + /// irrelevant; this method will always throw! + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + /// + /// Write data to the stream. + /// + /// + /// + /// + /// If you wish to use the GZipStream to compress data while writing, + /// you can create a GZipStream with CompressionMode.Compress, and a + /// writable output stream. Then call Write() on that GZipStream, + /// providing uncompressed data as input. The data sent to the output stream + /// will be the compressed form of the data written. + /// + /// + /// + /// A GZipStream can be used for Read() or Write(), but not + /// both. Writing implies compression. Reading implies decompression. + /// + /// + /// + /// The buffer holding data to write to the stream. + /// the offset within that data array to find the first byte to write. + /// the number of bytes to write. + public override void Write(byte[] buffer, int offset, int count) + { + if (_disposed) + { + throw new ObjectDisposedException("GZipStream"); + } + if (BaseStream._streamMode == ZlibBaseStream.StreamMode.Undefined) + { + //Console.WriteLine("GZipStream: First write"); + if (BaseStream._wantCompress) + { + // first write in compression, therefore, emit the GZIP header + _headerByteCount = EmitHeader(); + } + else + { + throw new InvalidOperationException(); + } + } + + BaseStream.Write(buffer, offset, count); + } + + #endregion Stream methods + + public string? Comment + { + get => _comment; + set + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(GZipStream)); + } + _comment = value; + } + } + + public DateTime? LastModified + { + get => _lastModified; + set + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(GZipStream)); + } + _lastModified = value; + } + } + + public string? FileName + { + get => _fileName; + set + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(GZipStream)); + } + _fileName = value; + if (_fileName is null) + { + return; + } + if (_fileName.Contains('/')) + { + _fileName = _fileName.Replace('/', '\\'); + } + if (_fileName.EndsWith('\\')) + { + throw new InvalidOperationException("Illegal filename"); + } + + if (_fileName.Contains('\\')) + { + // trim any leading path + _fileName = Path.GetFileName(_fileName); + } + } + } + + public int Crc32 { get; private set; } + + private int EmitHeader() + { + var commentBytes = (Comment is null) ? null : _encoding.GetBytes(Comment); + var filenameBytes = (FileName is null) ? null : _encoding.GetBytes(FileName); + + var cbLength = commentBytes?.Length + 1 ?? 0; + var fnLength = filenameBytes?.Length + 1 ?? 0; + + var bufferLength = 10 + cbLength + fnLength; + var header = new byte[bufferLength]; + var i = 0; + + // ID + header[i++] = 0x1F; + header[i++] = 0x8B; + + // compression method + header[i++] = 8; + byte flag = 0; + if (Comment != null) + { + flag ^= 0x10; + } + if (FileName != null) + { + flag ^= 0x8; + } + + // flag + header[i++] = flag; + + // mtime + LastModified ??= DateTime.Now; + var delta = LastModified.Value - UNIX_EPOCH; + var timet = (int)delta.TotalSeconds; + BinaryPrimitives.WriteInt32LittleEndian(header.AsSpan(i), timet); + i += 4; + + // xflg + header[i++] = 0; // this field is totally useless + + // OS + header[i++] = 0xFF; // 0xFF == unspecified + + // extra field length - only if FEXTRA is set, which it is not. + //header[i++]= 0; + //header[i++]= 0; + + // filename + if (fnLength != 0) + { + Array.Copy(filenameBytes!, 0, header, i, fnLength - 1); + i += fnLength - 1; + header[i++] = 0; // terminate + } + + // comment + if (cbLength != 0) + { + Array.Copy(commentBytes!, 0, header, i, cbLength - 1); + i += cbLength - 1; + header[i++] = 0; // terminate + } + + BaseStream._stream.Write(header, 0, header.Length); + + return header.Length; // bytes written + } } diff --git a/src/SharpCompress/Compressors/Deflate/InfTree.cs b/src/SharpCompress/Compressors/Deflate/InfTree.cs index 9d8e4fe6..89f02d7a 100644 --- a/src/SharpCompress/Compressors/Deflate/InfTree.cs +++ b/src/SharpCompress/Compressors/Deflate/InfTree.cs @@ -63,2182 +63,2175 @@ using System; -namespace SharpCompress.Compressors.Deflate +namespace SharpCompress.Compressors.Deflate; + +internal sealed class InfTree { - internal sealed class InfTree + private const int MANY = 1440; + + private const int Z_OK = 0; + private const int Z_DATA_ERROR = -3; + private const int Z_MEM_ERROR = -4; + private const int Z_BUF_ERROR = -5; + internal const int fixed_bl = 9; + internal const int fixed_bd = 5; + internal const int BMAX = 15; // maximum bit length of any code + + //UPGRADE_NOTE: Final was removed from the declaration of 'fixed_tl'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[] fixed_tl = { - private const int MANY = 1440; + 96, + 7, + 256, + 0, + 8, + 80, + 0, + 8, + 16, + 84, + 8, + 115, + 82, + 7, + 31, + 0, + 8, + 112, + 0, + 8, + 48, + 0, + 9, + 192, + 80, + 7, + 10, + 0, + 8, + 96, + 0, + 8, + 32, + 0, + 9, + 160, + 0, + 8, + 0, + 0, + 8, + 128, + 0, + 8, + 64, + 0, + 9, + 224, + 80, + 7, + 6, + 0, + 8, + 88, + 0, + 8, + 24, + 0, + 9, + 144, + 83, + 7, + 59, + 0, + 8, + 120, + 0, + 8, + 56, + 0, + 9, + 208, + 81, + 7, + 17, + 0, + 8, + 104, + 0, + 8, + 40, + 0, + 9, + 176, + 0, + 8, + 8, + 0, + 8, + 136, + 0, + 8, + 72, + 0, + 9, + 240, + 80, + 7, + 4, + 0, + 8, + 84, + 0, + 8, + 20, + 85, + 8, + 227, + 83, + 7, + 43, + 0, + 8, + 116, + 0, + 8, + 52, + 0, + 9, + 200, + 81, + 7, + 13, + 0, + 8, + 100, + 0, + 8, + 36, + 0, + 9, + 168, + 0, + 8, + 4, + 0, + 8, + 132, + 0, + 8, + 68, + 0, + 9, + 232, + 80, + 7, + 8, + 0, + 8, + 92, + 0, + 8, + 28, + 0, + 9, + 152, + 84, + 7, + 83, + 0, + 8, + 124, + 0, + 8, + 60, + 0, + 9, + 216, + 82, + 7, + 23, + 0, + 8, + 108, + 0, + 8, + 44, + 0, + 9, + 184, + 0, + 8, + 12, + 0, + 8, + 140, + 0, + 8, + 76, + 0, + 9, + 248, + 80, + 7, + 3, + 0, + 8, + 82, + 0, + 8, + 18, + 85, + 8, + 163, + 83, + 7, + 35, + 0, + 8, + 114, + 0, + 8, + 50, + 0, + 9, + 196, + 81, + 7, + 11, + 0, + 8, + 98, + 0, + 8, + 34, + 0, + 9, + 164, + 0, + 8, + 2, + 0, + 8, + 130, + 0, + 8, + 66, + 0, + 9, + 228, + 80, + 7, + 7, + 0, + 8, + 90, + 0, + 8, + 26, + 0, + 9, + 148, + 84, + 7, + 67, + 0, + 8, + 122, + 0, + 8, + 58, + 0, + 9, + 212, + 82, + 7, + 19, + 0, + 8, + 106, + 0, + 8, + 42, + 0, + 9, + 180, + 0, + 8, + 10, + 0, + 8, + 138, + 0, + 8, + 74, + 0, + 9, + 244, + 80, + 7, + 5, + 0, + 8, + 86, + 0, + 8, + 22, + 192, + 8, + 0, + 83, + 7, + 51, + 0, + 8, + 118, + 0, + 8, + 54, + 0, + 9, + 204, + 81, + 7, + 15, + 0, + 8, + 102, + 0, + 8, + 38, + 0, + 9, + 172, + 0, + 8, + 6, + 0, + 8, + 134, + 0, + 8, + 70, + 0, + 9, + 236, + 80, + 7, + 9, + 0, + 8, + 94, + 0, + 8, + 30, + 0, + 9, + 156, + 84, + 7, + 99, + 0, + 8, + 126, + 0, + 8, + 62, + 0, + 9, + 220, + 82, + 7, + 27, + 0, + 8, + 110, + 0, + 8, + 46, + 0, + 9, + 188, + 0, + 8, + 14, + 0, + 8, + 142, + 0, + 8, + 78, + 0, + 9, + 252, + 96, + 7, + 256, + 0, + 8, + 81, + 0, + 8, + 17, + 85, + 8, + 131, + 82, + 7, + 31, + 0, + 8, + 113, + 0, + 8, + 49, + 0, + 9, + 194, + 80, + 7, + 10, + 0, + 8, + 97, + 0, + 8, + 33, + 0, + 9, + 162, + 0, + 8, + 1, + 0, + 8, + 129, + 0, + 8, + 65, + 0, + 9, + 226, + 80, + 7, + 6, + 0, + 8, + 89, + 0, + 8, + 25, + 0, + 9, + 146, + 83, + 7, + 59, + 0, + 8, + 121, + 0, + 8, + 57, + 0, + 9, + 210, + 81, + 7, + 17, + 0, + 8, + 105, + 0, + 8, + 41, + 0, + 9, + 178, + 0, + 8, + 9, + 0, + 8, + 137, + 0, + 8, + 73, + 0, + 9, + 242, + 80, + 7, + 4, + 0, + 8, + 85, + 0, + 8, + 21, + 80, + 8, + 258, + 83, + 7, + 43, + 0, + 8, + 117, + 0, + 8, + 53, + 0, + 9, + 202, + 81, + 7, + 13, + 0, + 8, + 101, + 0, + 8, + 37, + 0, + 9, + 170, + 0, + 8, + 5, + 0, + 8, + 133, + 0, + 8, + 69, + 0, + 9, + 234, + 80, + 7, + 8, + 0, + 8, + 93, + 0, + 8, + 29, + 0, + 9, + 154, + 84, + 7, + 83, + 0, + 8, + 125, + 0, + 8, + 61, + 0, + 9, + 218, + 82, + 7, + 23, + 0, + 8, + 109, + 0, + 8, + 45, + 0, + 9, + 186, + 0, + 8, + 13, + 0, + 8, + 141, + 0, + 8, + 77, + 0, + 9, + 250, + 80, + 7, + 3, + 0, + 8, + 83, + 0, + 8, + 19, + 85, + 8, + 195, + 83, + 7, + 35, + 0, + 8, + 115, + 0, + 8, + 51, + 0, + 9, + 198, + 81, + 7, + 11, + 0, + 8, + 99, + 0, + 8, + 35, + 0, + 9, + 166, + 0, + 8, + 3, + 0, + 8, + 131, + 0, + 8, + 67, + 0, + 9, + 230, + 80, + 7, + 7, + 0, + 8, + 91, + 0, + 8, + 27, + 0, + 9, + 150, + 84, + 7, + 67, + 0, + 8, + 123, + 0, + 8, + 59, + 0, + 9, + 214, + 82, + 7, + 19, + 0, + 8, + 107, + 0, + 8, + 43, + 0, + 9, + 182, + 0, + 8, + 11, + 0, + 8, + 139, + 0, + 8, + 75, + 0, + 9, + 246, + 80, + 7, + 5, + 0, + 8, + 87, + 0, + 8, + 23, + 192, + 8, + 0, + 83, + 7, + 51, + 0, + 8, + 119, + 0, + 8, + 55, + 0, + 9, + 206, + 81, + 7, + 15, + 0, + 8, + 103, + 0, + 8, + 39, + 0, + 9, + 174, + 0, + 8, + 7, + 0, + 8, + 135, + 0, + 8, + 71, + 0, + 9, + 238, + 80, + 7, + 9, + 0, + 8, + 95, + 0, + 8, + 31, + 0, + 9, + 158, + 84, + 7, + 99, + 0, + 8, + 127, + 0, + 8, + 63, + 0, + 9, + 222, + 82, + 7, + 27, + 0, + 8, + 111, + 0, + 8, + 47, + 0, + 9, + 190, + 0, + 8, + 15, + 0, + 8, + 143, + 0, + 8, + 79, + 0, + 9, + 254, + 96, + 7, + 256, + 0, + 8, + 80, + 0, + 8, + 16, + 84, + 8, + 115, + 82, + 7, + 31, + 0, + 8, + 112, + 0, + 8, + 48, + 0, + 9, + 193, + 80, + 7, + 10, + 0, + 8, + 96, + 0, + 8, + 32, + 0, + 9, + 161, + 0, + 8, + 0, + 0, + 8, + 128, + 0, + 8, + 64, + 0, + 9, + 225, + 80, + 7, + 6, + 0, + 8, + 88, + 0, + 8, + 24, + 0, + 9, + 145, + 83, + 7, + 59, + 0, + 8, + 120, + 0, + 8, + 56, + 0, + 9, + 209, + 81, + 7, + 17, + 0, + 8, + 104, + 0, + 8, + 40, + 0, + 9, + 177, + 0, + 8, + 8, + 0, + 8, + 136, + 0, + 8, + 72, + 0, + 9, + 241, + 80, + 7, + 4, + 0, + 8, + 84, + 0, + 8, + 20, + 85, + 8, + 227, + 83, + 7, + 43, + 0, + 8, + 116, + 0, + 8, + 52, + 0, + 9, + 201, + 81, + 7, + 13, + 0, + 8, + 100, + 0, + 8, + 36, + 0, + 9, + 169, + 0, + 8, + 4, + 0, + 8, + 132, + 0, + 8, + 68, + 0, + 9, + 233, + 80, + 7, + 8, + 0, + 8, + 92, + 0, + 8, + 28, + 0, + 9, + 153, + 84, + 7, + 83, + 0, + 8, + 124, + 0, + 8, + 60, + 0, + 9, + 217, + 82, + 7, + 23, + 0, + 8, + 108, + 0, + 8, + 44, + 0, + 9, + 185, + 0, + 8, + 12, + 0, + 8, + 140, + 0, + 8, + 76, + 0, + 9, + 249, + 80, + 7, + 3, + 0, + 8, + 82, + 0, + 8, + 18, + 85, + 8, + 163, + 83, + 7, + 35, + 0, + 8, + 114, + 0, + 8, + 50, + 0, + 9, + 197, + 81, + 7, + 11, + 0, + 8, + 98, + 0, + 8, + 34, + 0, + 9, + 165, + 0, + 8, + 2, + 0, + 8, + 130, + 0, + 8, + 66, + 0, + 9, + 229, + 80, + 7, + 7, + 0, + 8, + 90, + 0, + 8, + 26, + 0, + 9, + 149, + 84, + 7, + 67, + 0, + 8, + 122, + 0, + 8, + 58, + 0, + 9, + 213, + 82, + 7, + 19, + 0, + 8, + 106, + 0, + 8, + 42, + 0, + 9, + 181, + 0, + 8, + 10, + 0, + 8, + 138, + 0, + 8, + 74, + 0, + 9, + 245, + 80, + 7, + 5, + 0, + 8, + 86, + 0, + 8, + 22, + 192, + 8, + 0, + 83, + 7, + 51, + 0, + 8, + 118, + 0, + 8, + 54, + 0, + 9, + 205, + 81, + 7, + 15, + 0, + 8, + 102, + 0, + 8, + 38, + 0, + 9, + 173, + 0, + 8, + 6, + 0, + 8, + 134, + 0, + 8, + 70, + 0, + 9, + 237, + 80, + 7, + 9, + 0, + 8, + 94, + 0, + 8, + 30, + 0, + 9, + 157, + 84, + 7, + 99, + 0, + 8, + 126, + 0, + 8, + 62, + 0, + 9, + 221, + 82, + 7, + 27, + 0, + 8, + 110, + 0, + 8, + 46, + 0, + 9, + 189, + 0, + 8, + 14, + 0, + 8, + 142, + 0, + 8, + 78, + 0, + 9, + 253, + 96, + 7, + 256, + 0, + 8, + 81, + 0, + 8, + 17, + 85, + 8, + 131, + 82, + 7, + 31, + 0, + 8, + 113, + 0, + 8, + 49, + 0, + 9, + 195, + 80, + 7, + 10, + 0, + 8, + 97, + 0, + 8, + 33, + 0, + 9, + 163, + 0, + 8, + 1, + 0, + 8, + 129, + 0, + 8, + 65, + 0, + 9, + 227, + 80, + 7, + 6, + 0, + 8, + 89, + 0, + 8, + 25, + 0, + 9, + 147, + 83, + 7, + 59, + 0, + 8, + 121, + 0, + 8, + 57, + 0, + 9, + 211, + 81, + 7, + 17, + 0, + 8, + 105, + 0, + 8, + 41, + 0, + 9, + 179, + 0, + 8, + 9, + 0, + 8, + 137, + 0, + 8, + 73, + 0, + 9, + 243, + 80, + 7, + 4, + 0, + 8, + 85, + 0, + 8, + 21, + 80, + 8, + 258, + 83, + 7, + 43, + 0, + 8, + 117, + 0, + 8, + 53, + 0, + 9, + 203, + 81, + 7, + 13, + 0, + 8, + 101, + 0, + 8, + 37, + 0, + 9, + 171, + 0, + 8, + 5, + 0, + 8, + 133, + 0, + 8, + 69, + 0, + 9, + 235, + 80, + 7, + 8, + 0, + 8, + 93, + 0, + 8, + 29, + 0, + 9, + 155, + 84, + 7, + 83, + 0, + 8, + 125, + 0, + 8, + 61, + 0, + 9, + 219, + 82, + 7, + 23, + 0, + 8, + 109, + 0, + 8, + 45, + 0, + 9, + 187, + 0, + 8, + 13, + 0, + 8, + 141, + 0, + 8, + 77, + 0, + 9, + 251, + 80, + 7, + 3, + 0, + 8, + 83, + 0, + 8, + 19, + 85, + 8, + 195, + 83, + 7, + 35, + 0, + 8, + 115, + 0, + 8, + 51, + 0, + 9, + 199, + 81, + 7, + 11, + 0, + 8, + 99, + 0, + 8, + 35, + 0, + 9, + 167, + 0, + 8, + 3, + 0, + 8, + 131, + 0, + 8, + 67, + 0, + 9, + 231, + 80, + 7, + 7, + 0, + 8, + 91, + 0, + 8, + 27, + 0, + 9, + 151, + 84, + 7, + 67, + 0, + 8, + 123, + 0, + 8, + 59, + 0, + 9, + 215, + 82, + 7, + 19, + 0, + 8, + 107, + 0, + 8, + 43, + 0, + 9, + 183, + 0, + 8, + 11, + 0, + 8, + 139, + 0, + 8, + 75, + 0, + 9, + 247, + 80, + 7, + 5, + 0, + 8, + 87, + 0, + 8, + 23, + 192, + 8, + 0, + 83, + 7, + 51, + 0, + 8, + 119, + 0, + 8, + 55, + 0, + 9, + 207, + 81, + 7, + 15, + 0, + 8, + 103, + 0, + 8, + 39, + 0, + 9, + 175, + 0, + 8, + 7, + 0, + 8, + 135, + 0, + 8, + 71, + 0, + 9, + 239, + 80, + 7, + 9, + 0, + 8, + 95, + 0, + 8, + 31, + 0, + 9, + 159, + 84, + 7, + 99, + 0, + 8, + 127, + 0, + 8, + 63, + 0, + 9, + 223, + 82, + 7, + 27, + 0, + 8, + 111, + 0, + 8, + 47, + 0, + 9, + 191, + 0, + 8, + 15, + 0, + 8, + 143, + 0, + 8, + 79, + 0, + 9, + 255 + }; - private const int Z_OK = 0; - private const int Z_STREAM_END = 1; - private const int Z_NEED_DICT = 2; - private const int Z_ERRNO = -1; - private const int Z_STREAM_ERROR = -2; - private const int Z_DATA_ERROR = -3; - private const int Z_MEM_ERROR = -4; - private const int Z_BUF_ERROR = -5; - private const int Z_VERSION_ERROR = -6; + //UPGRADE_NOTE: Final was removed from the declaration of 'fixed_td'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[] fixed_td = + { + 80, + 5, + 1, + 87, + 5, + 257, + 83, + 5, + 17, + 91, + 5, + 4097, + 81, + 5, + 5, + 89, + 5, + 1025, + 85, + 5, + 65, + 93, + 5, + 16385, + 80, + 5, + 3, + 88, + 5, + 513, + 84, + 5, + 33, + 92, + 5, + 8193, + 82, + 5, + 9, + 90, + 5, + 2049, + 86, + 5, + 129, + 192, + 5, + 24577, + 80, + 5, + 2, + 87, + 5, + 385, + 83, + 5, + 25, + 91, + 5, + 6145, + 81, + 5, + 7, + 89, + 5, + 1537, + 85, + 5, + 97, + 93, + 5, + 24577, + 80, + 5, + 4, + 88, + 5, + 769, + 84, + 5, + 49, + 92, + 5, + 12289, + 82, + 5, + 13, + 90, + 5, + 3073, + 86, + 5, + 193, + 192, + 5, + 24577 + }; - internal const int fixed_bl = 9; - internal const int fixed_bd = 5; - internal const int BMAX = 15; // maximum bit length of any code + // Tables for deflate from PKZIP's appnote.txt. + //UPGRADE_NOTE: Final was removed from the declaration of 'cplens'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[] cplens = + { + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 13, + 15, + 17, + 19, + 23, + 27, + 31, + 35, + 43, + 51, + 59, + 67, + 83, + 99, + 115, + 131, + 163, + 195, + 227, + 258, + 0, + 0 + }; - //UPGRADE_NOTE: Final was removed from the declaration of 'fixed_tl'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - internal static readonly int[] fixed_tl = + // see note #13 above about 258 + //UPGRADE_NOTE: Final was removed from the declaration of 'cplext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[] cplext = + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 0, + 112, + 112 + }; + + //UPGRADE_NOTE: Final was removed from the declaration of 'cpdist'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[] cpdist = + { + 1, + 2, + 3, + 4, + 5, + 7, + 9, + 13, + 17, + 25, + 33, + 49, + 65, + 97, + 129, + 193, + 257, + 385, + 513, + 769, + 1025, + 1537, + 2049, + 3073, + 4097, + 6145, + 8193, + 12289, + 16385, + 24577 + }; + + //UPGRADE_NOTE: Final was removed from the declaration of 'cpdext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[] cpdext = + { + 0, + 0, + 0, + 0, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 5, + 6, + 6, + 7, + 7, + 8, + 8, + 9, + 9, + 10, + 10, + 11, + 11, + 12, + 12, + 13, + 13 + }; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + internal int[] c; // bit length count table + internal int[] hn; // hufts used in space + internal int[] r; // table entry for structure assignment + internal int[] u; // table stack + internal int[] v; // work area for huft_build + internal int[] x; // bit offsets, then code stack + + private int huft_build( + int[] b, + int bindex, + int n, + int s, + int[] d, + int[] e, + int[] t, + int[] m, + int[] hp, + int[] hn, + int[] v + ) + { + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; + i = n; + do { - 96, - 7, - 256, - 0, - 8, - 80, - 0, - 8, - 16, - 84, - 8, - 115, - 82, - 7, - 31, - 0, - 8, - 112, - 0, - 8, - 48, - 0, - 9, - 192, - 80, - 7, - 10, - 0, - 8, - 96, - 0, - 8, - 32, - 0, - 9, - 160, - 0, - 8, - 0, - 0, - 8, - 128, - 0, - 8, - 64, - 0, - 9, - 224, - 80, - 7, - 6, - 0, - 8, - 88, - 0, - 8, - 24, - 0, - 9, - 144, - 83, - 7, - 59, - 0, - 8, - 120, - 0, - 8, - 56, - 0, - 9, - 208, - 81, - 7, - 17, - 0, - 8, - 104, - 0, - 8, - 40, - 0, - 9, - 176, - 0, - 8, - 8, - 0, - 8, - 136, - 0, - 8, - 72, - 0, - 9, - 240, - 80, - 7, - 4, - 0, - 8, - 84, - 0, - 8, - 20, - 85, - 8, - 227, - 83, - 7, - 43, - 0, - 8, - 116, - 0, - 8, - 52, - 0, - 9, - 200, - 81, - 7, - 13, - 0, - 8, - 100, - 0, - 8, - 36, - 0, - 9, - 168, - 0, - 8, - 4, - 0, - 8, - 132, - 0, - 8, - 68, - 0, - 9, - 232, - 80, - 7, - 8, - 0, - 8, - 92, - 0, - 8, - 28, - 0, - 9, - 152, - 84, - 7, - 83, - 0, - 8, - 124, - 0, - 8, - 60, - 0, - 9, - 216, - 82, - 7, - 23, - 0, - 8, - 108, - 0, - 8, - 44, - 0, - 9, - 184, - 0, - 8, - 12, - 0, - 8, - 140, - 0, - 8, - 76, - 0, - 9, - 248, - 80, - 7, - 3, - 0, - 8, - 82, - 0, - 8, - 18, - 85, - 8, - 163, - 83, - 7, - 35, - 0, - 8, - 114, - 0, - 8, - 50, - 0, - 9, - 196, - 81, - 7, - 11, - 0, - 8, - 98, - 0, - 8, - 34, - 0, - 9, - 164, - 0, - 8, - 2, - 0, - 8, - 130, - 0, - 8, - 66, - 0, - 9, - 228, - 80, - 7, - 7, - 0, - 8, - 90, - 0, - 8, - 26, - 0, - 9, - 148, - 84, - 7, - 67, - 0, - 8, - 122, - 0, - 8, - 58, - 0, - 9, - 212, - 82, - 7, - 19, - 0, - 8, - 106, - 0, - 8, - 42, - 0, - 9, - 180, - 0, - 8, - 10, - 0, - 8, - 138, - 0, - 8, - 74, - 0, - 9, - 244, - 80, - 7, - 5, - 0, - 8, - 86, - 0, - 8, - 22, - 192, - 8, - 0, - 83, - 7, - 51, - 0, - 8, - 118, - 0, - 8, - 54, - 0, - 9, - 204, - 81, - 7, - 15, - 0, - 8, - 102, - 0, - 8, - 38, - 0, - 9, - 172, - 0, - 8, - 6, - 0, - 8, - 134, - 0, - 8, - 70, - 0, - 9, - 236, - 80, - 7, - 9, - 0, - 8, - 94, - 0, - 8, - 30, - 0, - 9, - 156, - 84, - 7, - 99, - 0, - 8, - 126, - 0, - 8, - 62, - 0, - 9, - 220, - 82, - 7, - 27, - 0, - 8, - 110, - 0, - 8, - 46, - 0, - 9, - 188, - 0, - 8, - 14, - 0, - 8, - 142, - 0, - 8, - 78, - 0, - 9, - 252, - 96, - 7, - 256, - 0, - 8, - 81, - 0, - 8, - 17, - 85, - 8, - 131, - 82, - 7, - 31, - 0, - 8, - 113, - 0, - 8, - 49, - 0, - 9, - 194, - 80, - 7, - 10, - 0, - 8, - 97, - 0, - 8, - 33, - 0, - 9, - 162, - 0, - 8, - 1, - 0, - 8, - 129, - 0, - 8, - 65, - 0, - 9, - 226, - 80, - 7, - 6, - 0, - 8, - 89, - 0, - 8, - 25, - 0, - 9, - 146, - 83, - 7, - 59, - 0, - 8, - 121, - 0, - 8, - 57, - 0, - 9, - 210, - 81, - 7, - 17, - 0, - 8, - 105, - 0, - 8, - 41, - 0, - 9, - 178, - 0, - 8, - 9, - 0, - 8, - 137, - 0, - 8, - 73, - 0, - 9, - 242, - 80, - 7, - 4, - 0, - 8, - 85, - 0, - 8, - 21, - 80, - 8, - 258, - 83, - 7, - 43, - 0, - 8, - 117, - 0, - 8, - 53, - 0, - 9, - 202, - 81, - 7, - 13, - 0, - 8, - 101, - 0, - 8, - 37, - 0, - 9, - 170, - 0, - 8, - 5, - 0, - 8, - 133, - 0, - 8, - 69, - 0, - 9, - 234, - 80, - 7, - 8, - 0, - 8, - 93, - 0, - 8, - 29, - 0, - 9, - 154, - 84, - 7, - 83, - 0, - 8, - 125, - 0, - 8, - 61, - 0, - 9, - 218, - 82, - 7, - 23, - 0, - 8, - 109, - 0, - 8, - 45, - 0, - 9, - 186, - 0, - 8, - 13, - 0, - 8, - 141, - 0, - 8, - 77, - 0, - 9, - 250, - 80, - 7, - 3, - 0, - 8, - 83, - 0, - 8, - 19, - 85, - 8, - 195, - 83, - 7, - 35, - 0, - 8, - 115, - 0, - 8, - 51, - 0, - 9, - 198, - 81, - 7, - 11, - 0, - 8, - 99, - 0, - 8, - 35, - 0, - 9, - 166, - 0, - 8, - 3, - 0, - 8, - 131, - 0, - 8, - 67, - 0, - 9, - 230, - 80, - 7, - 7, - 0, - 8, - 91, - 0, - 8, - 27, - 0, - 9, - 150, - 84, - 7, - 67, - 0, - 8, - 123, - 0, - 8, - 59, - 0, - 9, - 214, - 82, - 7, - 19, - 0, - 8, - 107, - 0, - 8, - 43, - 0, - 9, - 182, - 0, - 8, - 11, - 0, - 8, - 139, - 0, - 8, - 75, - 0, - 9, - 246, - 80, - 7, - 5, - 0, - 8, - 87, - 0, - 8, - 23, - 192, - 8, - 0, - 83, - 7, - 51, - 0, - 8, - 119, - 0, - 8, - 55, - 0, - 9, - 206, - 81, - 7, - 15, - 0, - 8, - 103, - 0, - 8, - 39, - 0, - 9, - 174, - 0, - 8, - 7, - 0, - 8, - 135, - 0, - 8, - 71, - 0, - 9, - 238, - 80, - 7, - 9, - 0, - 8, - 95, - 0, - 8, - 31, - 0, - 9, - 158, - 84, - 7, - 99, - 0, - 8, - 127, - 0, - 8, - 63, - 0, - 9, - 222, - 82, - 7, - 27, - 0, - 8, - 111, - 0, - 8, - 47, - 0, - 9, - 190, - 0, - 8, - 15, - 0, - 8, - 143, - 0, - 8, - 79, - 0, - 9, - 254, - 96, - 7, - 256, - 0, - 8, - 80, - 0, - 8, - 16, - 84, - 8, - 115, - 82, - 7, - 31, - 0, - 8, - 112, - 0, - 8, - 48, - 0, - 9, - 193, - 80, - 7, - 10, - 0, - 8, - 96, - 0, - 8, - 32, - 0, - 9, - 161, - 0, - 8, - 0, - 0, - 8, - 128, - 0, - 8, - 64, - 0, - 9, - 225, - 80, - 7, - 6, - 0, - 8, - 88, - 0, - 8, - 24, - 0, - 9, - 145, - 83, - 7, - 59, - 0, - 8, - 120, - 0, - 8, - 56, - 0, - 9, - 209, - 81, - 7, - 17, - 0, - 8, - 104, - 0, - 8, - 40, - 0, - 9, - 177, - 0, - 8, - 8, - 0, - 8, - 136, - 0, - 8, - 72, - 0, - 9, - 241, - 80, - 7, - 4, - 0, - 8, - 84, - 0, - 8, - 20, - 85, - 8, - 227, - 83, - 7, - 43, - 0, - 8, - 116, - 0, - 8, - 52, - 0, - 9, - 201, - 81, - 7, - 13, - 0, - 8, - 100, - 0, - 8, - 36, - 0, - 9, - 169, - 0, - 8, - 4, - 0, - 8, - 132, - 0, - 8, - 68, - 0, - 9, - 233, - 80, - 7, - 8, - 0, - 8, - 92, - 0, - 8, - 28, - 0, - 9, - 153, - 84, - 7, - 83, - 0, - 8, - 124, - 0, - 8, - 60, - 0, - 9, - 217, - 82, - 7, - 23, - 0, - 8, - 108, - 0, - 8, - 44, - 0, - 9, - 185, - 0, - 8, - 12, - 0, - 8, - 140, - 0, - 8, - 76, - 0, - 9, - 249, - 80, - 7, - 3, - 0, - 8, - 82, - 0, - 8, - 18, - 85, - 8, - 163, - 83, - 7, - 35, - 0, - 8, - 114, - 0, - 8, - 50, - 0, - 9, - 197, - 81, - 7, - 11, - 0, - 8, - 98, - 0, - 8, - 34, - 0, - 9, - 165, - 0, - 8, - 2, - 0, - 8, - 130, - 0, - 8, - 66, - 0, - 9, - 229, - 80, - 7, - 7, - 0, - 8, - 90, - 0, - 8, - 26, - 0, - 9, - 149, - 84, - 7, - 67, - 0, - 8, - 122, - 0, - 8, - 58, - 0, - 9, - 213, - 82, - 7, - 19, - 0, - 8, - 106, - 0, - 8, - 42, - 0, - 9, - 181, - 0, - 8, - 10, - 0, - 8, - 138, - 0, - 8, - 74, - 0, - 9, - 245, - 80, - 7, - 5, - 0, - 8, - 86, - 0, - 8, - 22, - 192, - 8, - 0, - 83, - 7, - 51, - 0, - 8, - 118, - 0, - 8, - 54, - 0, - 9, - 205, - 81, - 7, - 15, - 0, - 8, - 102, - 0, - 8, - 38, - 0, - 9, - 173, - 0, - 8, - 6, - 0, - 8, - 134, - 0, - 8, - 70, - 0, - 9, - 237, - 80, - 7, - 9, - 0, - 8, - 94, - 0, - 8, - 30, - 0, - 9, - 157, - 84, - 7, - 99, - 0, - 8, - 126, - 0, - 8, - 62, - 0, - 9, - 221, - 82, - 7, - 27, - 0, - 8, - 110, - 0, - 8, - 46, - 0, - 9, - 189, - 0, - 8, - 14, - 0, - 8, - 142, - 0, - 8, - 78, - 0, - 9, - 253, - 96, - 7, - 256, - 0, - 8, - 81, - 0, - 8, - 17, - 85, - 8, - 131, - 82, - 7, - 31, - 0, - 8, - 113, - 0, - 8, - 49, - 0, - 9, - 195, - 80, - 7, - 10, - 0, - 8, - 97, - 0, - 8, - 33, - 0, - 9, - 163, - 0, - 8, - 1, - 0, - 8, - 129, - 0, - 8, - 65, - 0, - 9, - 227, - 80, - 7, - 6, - 0, - 8, - 89, - 0, - 8, - 25, - 0, - 9, - 147, - 83, - 7, - 59, - 0, - 8, - 121, - 0, - 8, - 57, - 0, - 9, - 211, - 81, - 7, - 17, - 0, - 8, - 105, - 0, - 8, - 41, - 0, - 9, - 179, - 0, - 8, - 9, - 0, - 8, - 137, - 0, - 8, - 73, - 0, - 9, - 243, - 80, - 7, - 4, - 0, - 8, - 85, - 0, - 8, - 21, - 80, - 8, - 258, - 83, - 7, - 43, - 0, - 8, - 117, - 0, - 8, - 53, - 0, - 9, - 203, - 81, - 7, - 13, - 0, - 8, - 101, - 0, - 8, - 37, - 0, - 9, - 171, - 0, - 8, - 5, - 0, - 8, - 133, - 0, - 8, - 69, - 0, - 9, - 235, - 80, - 7, - 8, - 0, - 8, - 93, - 0, - 8, - 29, - 0, - 9, - 155, - 84, - 7, - 83, - 0, - 8, - 125, - 0, - 8, - 61, - 0, - 9, - 219, - 82, - 7, - 23, - 0, - 8, - 109, - 0, - 8, - 45, - 0, - 9, - 187, - 0, - 8, - 13, - 0, - 8, - 141, - 0, - 8, - 77, - 0, - 9, - 251, - 80, - 7, - 3, - 0, - 8, - 83, - 0, - 8, - 19, - 85, - 8, - 195, - 83, - 7, - 35, - 0, - 8, - 115, - 0, - 8, - 51, - 0, - 9, - 199, - 81, - 7, - 11, - 0, - 8, - 99, - 0, - 8, - 35, - 0, - 9, - 167, - 0, - 8, - 3, - 0, - 8, - 131, - 0, - 8, - 67, - 0, - 9, - 231, - 80, - 7, - 7, - 0, - 8, - 91, - 0, - 8, - 27, - 0, - 9, - 151, - 84, - 7, - 67, - 0, - 8, - 123, - 0, - 8, - 59, - 0, - 9, - 215, - 82, - 7, - 19, - 0, - 8, - 107, - 0, - 8, - 43, - 0, - 9, - 183, - 0, - 8, - 11, - 0, - 8, - 139, - 0, - 8, - 75, - 0, - 9, - 247, - 80, - 7, - 5, - 0, - 8, - 87, - 0, - 8, - 23, - 192, - 8, - 0, - 83, - 7, - 51, - 0, - 8, - 119, - 0, - 8, - 55, - 0, - 9, - 207, - 81, - 7, - 15, - 0, - 8, - 103, - 0, - 8, - 39, - 0, - 9, - 175, - 0, - 8, - 7, - 0, - 8, - 135, - 0, - 8, - 71, - 0, - 9, - 239, - 80, - 7, - 9, - 0, - 8, - 95, - 0, - 8, - 31, - 0, - 9, - 159, - 84, - 7, - 99, - 0, - 8, - 127, - 0, - 8, - 63, - 0, - 9, - 223, - 82, - 7, - 27, - 0, - 8, - 111, - 0, - 8, - 47, - 0, - 9, - 191, - 0, - 8, - 15, - 0, - 8, - 143, - 0, - 8, - 79, - 0, - 9, - 255 - }; + c[b[bindex + p]]++; + p++; + i--; // assume all entries <= BMAX + } while (i != 0); - //UPGRADE_NOTE: Final was removed from the declaration of 'fixed_td'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - internal static readonly int[] fixed_td = + if (c[0] == n) { - 80, - 5, - 1, - 87, - 5, - 257, - 83, - 5, - 17, - 91, - 5, - 4097, - 81, - 5, - 5, - 89, - 5, - 1025, - 85, - 5, - 65, - 93, - 5, - 16385, - 80, - 5, - 3, - 88, - 5, - 513, - 84, - 5, - 33, - 92, - 5, - 8193, - 82, - 5, - 9, - 90, - 5, - 2049, - 86, - 5, - 129, - 192, - 5, - 24577, - 80, - 5, - 2, - 87, - 5, - 385, - 83, - 5, - 25, - 91, - 5, - 6145, - 81, - 5, - 7, - 89, - 5, - 1537, - 85, - 5, - 97, - 93, - 5, - 24577, - 80, - 5, - 4, - 88, - 5, - 769, - 84, - 5, - 49, - 92, - 5, - 12289, - 82, - 5, - 13, - 90, - 5, - 3073, - 86, - 5, - 193, - 192, - 5, - 24577 - }; + // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return Z_OK; + } - // Tables for deflate from PKZIP's appnote.txt. - //UPGRADE_NOTE: Final was removed from the declaration of 'cplens'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - internal static readonly int[] cplens = + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) { - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 13, - 15, - 17, - 19, - 23, - 27, - 31, - 35, - 43, - 51, - 59, - 67, - 83, - 99, - 115, - 131, - 163, - 195, - 227, - 258, - 0, - 0 - }; - - // see note #13 above about 258 - //UPGRADE_NOTE: Final was removed from the declaration of 'cplext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - internal static readonly int[] cplext = - { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 4, - 4, - 4, - 4, - 5, - 5, - 5, - 5, - 0, - 112, - 112 - }; - - //UPGRADE_NOTE: Final was removed from the declaration of 'cpdist'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - internal static readonly int[] cpdist = - { - 1, - 2, - 3, - 4, - 5, - 7, - 9, - 13, - 17, - 25, - 33, - 49, - 65, - 97, - 129, - 193, - 257, - 385, - 513, - 769, - 1025, - 1537, - 2049, - 3073, - 4097, - 6145, - 8193, - 12289, - 16385, - 24577 - }; - - //UPGRADE_NOTE: Final was removed from the declaration of 'cpdext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - internal static readonly int[] cpdext = - { - 0, - 0, - 0, - 0, - 1, - 1, - 2, - 2, - 3, - 3, - 4, - 4, - 5, - 5, - 6, - 6, - 7, - 7, - 8, - 8, - 9, - 9, - 10, - 10, - 11, - 11, - 12, - 12, - 13, - 13 - }; - - // If BMAX needs to be larger than 16, then h and x[] should be uLong. - internal int[] c; // bit length count table - internal int[] hn; // hufts used in space - internal int[] r; // table entry for structure assignment - internal int[] u; // table stack - internal int[] v; // work area for huft_build - internal int[] x; // bit offsets, then code stack - - private int huft_build( - int[] b, - int bindex, - int n, - int s, - int[] d, - int[] e, - int[] t, - int[] m, - int[] hp, - int[] hn, - int[] v - ) - { - // Given a list of code lengths and a maximum table size, make a set of - // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR - // if the given code set is incomplete (the tables are still built in this - // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of - // lengths), or Z_MEM_ERROR if not enough memory. - - int a; // counter for codes of length k - int f; // i repeats in table every f entries - int g; // maximum code length - int h; // table level - int i; // counter, current code - int j; // counter - int k; // number of bits in current code - int l; // bits per table (returned in m) - int mask; // (1 << w) - 1, to avoid cc -O bug on HP - int p; // pointer into c[], b[], or v[] - int q; // points to current table - int w; // bits before this table == (l * h) - int xp; // pointer into x - int y; // number of dummy codes added - int z; // number of entries in current table - - // Generate counts for each bit length - - p = 0; - i = n; - do + if (c[j] != 0) { - c[b[bindex + p]]++; - p++; - i--; // assume all entries <= BMAX - } while (i != 0); - - if (c[0] == n) - { - // null input--all zero length codes - t[0] = -1; - m[0] = 0; - return Z_OK; + break; } + } + k = j; // minimum code length + if (l < j) + { + l = j; + } + for (i = BMAX; i != 0; i--) + { + if (c[i] != 0) + { + break; + } + } + g = i; // maximum code length + if (l > i) + { + l = i; + } + m[0] = l; - // Find minimum and maximum length, bound *m by those - l = m[0]; - for (j = 1; j <= BMAX; j++) - { - if (c[j] != 0) - { - break; - } - } - k = j; // minimum code length - if (l < j) - { - l = j; - } - for (i = BMAX; i != 0; i--) - { - if (c[i] != 0) - { - break; - } - } - g = i; // maximum code length - if (l > i) - { - l = i; - } - m[0] = l; - - // Adjust last length count to fill out codes, if needed - for (y = 1 << j; j < i; j++, y <<= 1) - { - if ((y -= c[j]) < 0) - { - return Z_DATA_ERROR; - } - } - if ((y -= c[i]) < 0) + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + { + if ((y -= c[j]) < 0) { return Z_DATA_ERROR; } - c[i] += y; + } + if ((y -= c[i]) < 0) + { + return Z_DATA_ERROR; + } + c[i] += y; - // Generate starting offsets into the value table for each length - x[1] = j = 0; - p = 1; - xp = 2; - while (--i != 0) + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; + xp = 2; + while (--i != 0) + { + // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; + p = 0; + do + { + if ((j = b[bindex + p]) != 0) { - // note that i == g from above - x[xp] = (j += c[p]); - xp++; - p++; + v[x[j]++] = i; } + p++; + } while (++i < n); + n = x[g]; // set n to length of v - // Make a table of values in order of bit lengths - i = 0; - p = 0; - do + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a-- != 0) { - if ((j = b[bindex + p]) != 0) + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) { - v[x[j]++] = i; - } - p++; - } while (++i < n); - n = x[g]; // set n to length of v + h++; + w += l; // previous table always l bits - // Generate the Huffman codes and for each, make the table entries - x[0] = i = 0; // first Huffman code is zero - p = 0; // grab values in bit order - h = -1; // no tables yet--level -1 - w = -l; // bits decoded == (l * h) - u[0] = 0; // just to keep compilers happy - q = 0; // ditto - z = 0; // ditto - - // go through the bit lengths (k already is bits in shortest code) - for (; k <= g; k++) - { - a = c[k]; - while (a-- != 0) - { - // here i is the Huffman code of length k bits for value *p - // make tables up to required level - while (k > w + l) + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) { - h++; - w += l; // previous table always l bits - - // compute minimum size table less than or equal to l bits - z = g - w; - z = (z > l) ? l : z; // table size upper limit - if ((f = 1 << (j = k - w)) > a + 1) + // try a k-w bit table + // too few codes for k-w bit table + f -= (a + 1); // deduct codes from patterns left + xp = k; + if (j < z) { - // try a k-w bit table - // too few codes for k-w bit table - f -= (a + 1); // deduct codes from patterns left - xp = k; - if (j < z) + while (++j < z) { - while (++j < z) + // try smaller tables up to z bits + if ((f <<= 1) <= c[++xp]) { - // try smaller tables up to z bits - if ((f <<= 1) <= c[++xp]) - { - break; // enough codes to use up j bits - } - f -= c[xp]; // else deduct codes from patterns + break; // enough codes to use up j bits } + f -= c[xp]; // else deduct codes from patterns } } - z = 1 << j; // table entries for j-bit table - - // allocate new table - if (hn[0] + z > MANY) - { - // (note: doesn't matter for fixed) - return Z_DATA_ERROR; // overflow of MANY - } - u[h] = q = hn[0]; // DEBUG - hn[0] += z; - - // connect to last table, if there is one - if (h != 0) - { - x[h] = i; // save pattern for backing up - r[0] = (sbyte)j; // bits in this table - r[1] = (sbyte)l; // bits to dump before this table - j = SharedUtils.URShift(i, (w - l)); - r[2] = (q - u[h - 1] - j); // offset to this table - Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table - } - else - { - t[0] = q; // first table is returned result - } } + z = 1 << j; // table entries for j-bit table - // set up table entry in r - r[1] = (sbyte)(k - w); - if (p >= n) + // allocate new table + if (hn[0] + z > MANY) { - r[0] = 128 + 64; // out of values--invalid code + // (note: doesn't matter for fixed) + return Z_DATA_ERROR; // overflow of MANY } - else if (v[p] < s) + u[h] = q = hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if (h != 0) { - r[0] = (sbyte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block - r[2] = v[p++]; // simple code is just the value + x[h] = i; // save pattern for backing up + r[0] = (sbyte)j; // bits in this table + r[1] = (sbyte)l; // bits to dump before this table + j = SharedUtils.URShift(i, (w - l)); + r[2] = (q - u[h - 1] - j); // offset to this table + Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table } else { - r[0] = (sbyte)(e[v[p] - s] + 16 + 64); // non-simple--look up in lists - r[2] = d[v[p++] - s]; - } - - // fill code-like entries with r - f = 1 << (k - w); - for (j = SharedUtils.URShift(i, w); j < z; j += f) - { - Array.Copy(r, 0, hp, (q + j) * 3, 3); - } - - // backwards increment the k-bit code i - for (j = 1 << (k - 1); (i & j) != 0; j = SharedUtils.URShift(j, 1)) - { - i ^= j; - } - i ^= j; - - // backup over finished tables - mask = (1 << w) - 1; // needed on HP, cc -O bug - while ((i & mask) != x[h]) - { - h--; // don't need to update q - w -= l; - mask = (1 << w) - 1; + t[0] = q; // first table is returned result } } - } - // Return Z_BUF_ERROR if we were given an incomplete table - return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; + // set up table entry in r + r[1] = (sbyte)(k - w); + if (p >= n) + { + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s) + { + r[0] = (sbyte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else + { + r[0] = (sbyte)(e[v[p] - s] + 16 + 64); // non-simple--look up in lists + r[2] = d[v[p++] - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = SharedUtils.URShift(i, w); j < z; j += f) + { + Array.Copy(r, 0, hp, (q + j) * 3, 3); + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); (i & j) != 0; j = SharedUtils.URShift(j, 1)) + { + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } } - internal int inflate_trees_bits(int[] c, int[] bb, int[] tb, int[] hp, ZlibCodec z) - { - int result; - initWorkArea(19); - hn[0] = 0; - result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; + } + internal int inflate_trees_bits(int[] c, int[] bb, int[] tb, int[] hp, ZlibCodec z) + { + int result; + initWorkArea(19); + hn[0] = 0; + result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if (result == Z_DATA_ERROR) + { + z.Message = "oversubscribed dynamic bit lengths tree"; + } + else if (result == Z_BUF_ERROR || bb[0] == 0) + { + z.Message = "incomplete dynamic bit lengths tree"; + result = Z_DATA_ERROR; + } + return result; + } + + internal int inflate_trees_dynamic( + int nl, + int nd, + int[] c, + int[] bl, + int[] bd, + int[] tl, + int[] td, + int[] hp, + ZlibCodec z + ) + { + int result; + + // build literal/length tree + initWorkArea(288); + hn[0] = 0; + result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != Z_OK || bl[0] == 0) + { if (result == Z_DATA_ERROR) { - z.Message = "oversubscribed dynamic bit lengths tree"; + z.Message = "oversubscribed literal/length tree"; } - else if (result == Z_BUF_ERROR || bb[0] == 0) + else if (result != Z_MEM_ERROR) { - z.Message = "incomplete dynamic bit lengths tree"; + z.Message = "incomplete literal/length tree"; result = Z_DATA_ERROR; } return result; } - internal int inflate_trees_dynamic( - int nl, - int nd, - int[] c, - int[] bl, - int[] bd, - int[] tl, - int[] td, - int[] hp, - ZlibCodec z - ) + // build distance tree + initWorkArea(288); + result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != Z_OK || (bd[0] == 0 && nl > 257)) { - int result; - - // build literal/length tree - initWorkArea(288); - hn[0] = 0; - result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); - if (result != Z_OK || bl[0] == 0) + if (result == Z_DATA_ERROR) { - if (result == Z_DATA_ERROR) - { - z.Message = "oversubscribed literal/length tree"; - } - else if (result != Z_MEM_ERROR) - { - z.Message = "incomplete literal/length tree"; - result = Z_DATA_ERROR; - } - return result; + z.Message = "oversubscribed distance tree"; } - - // build distance tree - initWorkArea(288); - result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); - - if (result != Z_OK || (bd[0] == 0 && nl > 257)) + else if (result == Z_BUF_ERROR) { - if (result == Z_DATA_ERROR) - { - z.Message = "oversubscribed distance tree"; - } - else if (result == Z_BUF_ERROR) - { - z.Message = "incomplete distance tree"; - result = Z_DATA_ERROR; - } - else if (result != Z_MEM_ERROR) - { - z.Message = "empty distance tree with lengths"; - result = Z_DATA_ERROR; - } - return result; + z.Message = "incomplete distance tree"; + result = Z_DATA_ERROR; } - - return Z_OK; + else if (result != Z_MEM_ERROR) + { + z.Message = "empty distance tree with lengths"; + result = Z_DATA_ERROR; + } + return result; } - internal static int inflate_trees_fixed( - int[] bl, - int[] bd, - int[][] tl, - int[][] td, - ZlibCodec z - ) - { - bl[0] = fixed_bl; - bd[0] = fixed_bd; - tl[0] = fixed_tl; - td[0] = fixed_td; - return Z_OK; - } + return Z_OK; + } - private void initWorkArea(int vsize) + internal static int inflate_trees_fixed( + int[] bl, + int[] bd, + int[][] tl, + int[][] td, + ZlibCodec z + ) + { + bl[0] = fixed_bl; + bd[0] = fixed_bd; + tl[0] = fixed_tl; + td[0] = fixed_td; + return Z_OK; + } + + private void initWorkArea(int vsize) + { + if (hn is null) { - if (hn is null) + hn = new int[1]; + v = new int[vsize]; + c = new int[BMAX + 1]; + r = new int[3]; + u = new int[BMAX]; + x = new int[BMAX + 1]; + } + else + { + if (v.Length < vsize) { - hn = new int[1]; v = new int[vsize]; - c = new int[BMAX + 1]; - r = new int[3]; - u = new int[BMAX]; - x = new int[BMAX + 1]; } - else - { - if (v.Length < vsize) - { - v = new int[vsize]; - } - Array.Clear(v, 0, vsize); - Array.Clear(c, 0, BMAX + 1); - r[0] = 0; - r[1] = 0; - r[2] = 0; + Array.Clear(v, 0, vsize); + Array.Clear(c, 0, BMAX + 1); + r[0] = 0; + r[1] = 0; + r[2] = 0; - // for(int i=0; i> 1) + { + case 0: // stored + b >>= 3; + k -= (3); + t = k & 7; // go to byte boundary + b >>= t; + k -= t; + mode = InflateBlockMode.LENS; // get length of stored block + break; - q = writeAt; - m = (q < readAt ? readAt - q - 1 : end - q); + case 1: // fixed + var bl = new int[1]; + var bd = new int[1]; + var tl = new int[1][]; + var td = new int[1][]; + InfTree.inflate_trees_fixed(bl, bd, tl, td, _codec); + codes.Init(bl[0], bd[0], tl[0], 0, td[0], 0); + b >>= 3; + k -= 3; + mode = InflateBlockMode.CODES; + break; - // process input based on current state - while (true) - { - switch (mode) - { - case InflateBlockMode.TYPE: + case 2: // dynamic + b >>= 3; + k -= 3; + mode = InflateBlockMode.TABLE; + break; + case 3: // illegal + b >>= 3; + k -= 3; + mode = InflateBlockMode.BAD; + _codec.Message = "invalid block type"; + r = ZlibConstants.Z_DATA_ERROR; + bitb = b; + bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + break; + + case InflateBlockMode.LENS: + + while (k < (32)) + { + if (n != 0) + { + r = ZlibConstants.Z_OK; + } + else + { + bitb = b; + bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + ; + n--; + b |= (_codec.InputBuffer[p++] & 0xff) << k; + k += 8; + } + + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + mode = InflateBlockMode.BAD; + _codec.Message = "invalid stored block lengths"; + r = ZlibConstants.Z_DATA_ERROR; + + bitb = b; + bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = + left != 0 + ? InflateBlockMode.STORED + : (last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE); + break; + + case InflateBlockMode.STORED: + if (n == 0) + { + bitb = b; + bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + + if (m == 0) + { + if (q == end && readAt != 0) + { + q = 0; + m = (q < readAt ? readAt - q - 1 : end - q); + } + if (m == 0) + { + writeAt = q; + r = Flush(r); + q = writeAt; + m = (q < readAt ? readAt - q - 1 : end - q); + if (q == end && readAt != 0) + { + q = 0; + m = (q < readAt ? readAt - q - 1 : end - q); + } + if (m == 0) + { + bitb = b; + bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + } + } + r = ZlibConstants.Z_OK; + + t = left; + if (t > n) + { + t = n; + } + if (t > m) + { + t = m; + } + Array.Copy(_codec.InputBuffer, p, window, q, t); + p += t; + n -= t; + q += t; + m -= t; + if ((left -= t) != 0) + { + break; + } + mode = last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE; + break; + + case InflateBlockMode.TABLE: + + while (k < (14)) + { + if (n != 0) + { + r = ZlibConstants.Z_OK; + } + else + { + bitb = b; + bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + + n--; + b |= (_codec.InputBuffer[p++] & 0xff) << k; + k += 8; + } + + table = t = (b & 0x3fff); + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + mode = InflateBlockMode.BAD; + _codec.Message = "too many length or distance symbols"; + r = ZlibConstants.Z_DATA_ERROR; + + bitb = b; + bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (blens is null || blens.Length < t) + { + blens = new int[t]; + } + else + { + Array.Clear(blens, 0, t); + + // for (int i = 0; i < t; i++) + // { + // blens[i] = 0; + // } + } + + b >>= 14; + k -= 14; + + index = 0; + mode = InflateBlockMode.BTREE; + goto case InflateBlockMode.BTREE; + + case InflateBlockMode.BTREE: + while (index < 4 + (table >> 10)) + { while (k < (3)) { if (n != 0) @@ -194,168 +438,58 @@ namespace SharpCompress.Compressors.Deflate b |= (_codec.InputBuffer[p++] & 0xff) << k; k += 8; } - t = (b & 7); - last = t & 1; - switch ((uint)t >> 1) - { - case 0: // stored - b >>= 3; - k -= (3); - t = k & 7; // go to byte boundary - b >>= t; - k -= t; - mode = InflateBlockMode.LENS; // get length of stored block - break; - - case 1: // fixed - var bl = new int[1]; - var bd = new int[1]; - var tl = new int[1][]; - var td = new int[1][]; - InfTree.inflate_trees_fixed(bl, bd, tl, td, _codec); - codes.Init(bl[0], bd[0], tl[0], 0, td[0], 0); - b >>= 3; - k -= 3; - mode = InflateBlockMode.CODES; - break; - - case 2: // dynamic - b >>= 3; - k -= 3; - mode = InflateBlockMode.TABLE; - break; - - case 3: // illegal - b >>= 3; - k -= 3; - mode = InflateBlockMode.BAD; - _codec.Message = "invalid block type"; - r = ZlibConstants.Z_DATA_ERROR; - bitb = b; - bitk = k; - _codec.AvailableBytesIn = n; - _codec.TotalBytesIn += p - _codec.NextIn; - _codec.NextIn = p; - writeAt = q; - return Flush(r); - } - break; - - case InflateBlockMode.LENS: - - while (k < (32)) - { - if (n != 0) - { - r = ZlibConstants.Z_OK; - } - else - { - bitb = b; - bitk = k; - _codec.AvailableBytesIn = n; - _codec.TotalBytesIn += p - _codec.NextIn; - _codec.NextIn = p; - writeAt = q; - return Flush(r); - } - ; - n--; - b |= (_codec.InputBuffer[p++] & 0xff) << k; - k += 8; - } - - if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + blens[border[index++]] = b & 7; + + b >>= 3; + k -= 3; + } + + while (index < 19) + { + blens[border[index++]] = 0; + } + + bb[0] = 7; + t = inftree.inflate_trees_bits(blens, bb, tb, hufts, _codec); + if (t != ZlibConstants.Z_OK) + { + r = t; + if (r == ZlibConstants.Z_DATA_ERROR) { + blens = null; mode = InflateBlockMode.BAD; - _codec.Message = "invalid stored block lengths"; - r = ZlibConstants.Z_DATA_ERROR; - - bitb = b; - bitk = k; - _codec.AvailableBytesIn = n; - _codec.TotalBytesIn += p - _codec.NextIn; - _codec.NextIn = p; - writeAt = q; - return Flush(r); - } - left = (b & 0xffff); - b = k = 0; // dump bits - mode = - left != 0 - ? InflateBlockMode.STORED - : (last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE); - break; - - case InflateBlockMode.STORED: - if (n == 0) - { - bitb = b; - bitk = k; - _codec.AvailableBytesIn = n; - _codec.TotalBytesIn += p - _codec.NextIn; - _codec.NextIn = p; - writeAt = q; - return Flush(r); } - if (m == 0) - { - if (q == end && readAt != 0) - { - q = 0; - m = (q < readAt ? readAt - q - 1 : end - q); - } - if (m == 0) - { - writeAt = q; - r = Flush(r); - q = writeAt; - m = (q < readAt ? readAt - q - 1 : end - q); - if (q == end && readAt != 0) - { - q = 0; - m = (q < readAt ? readAt - q - 1 : end - q); - } - if (m == 0) - { - bitb = b; - bitk = k; - _codec.AvailableBytesIn = n; - _codec.TotalBytesIn += p - _codec.NextIn; - _codec.NextIn = p; - writeAt = q; - return Flush(r); - } - } - } - r = ZlibConstants.Z_OK; + bitb = b; + bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } - t = left; - if (t > n) - { - t = n; - } - if (t > m) - { - t = m; - } - Array.Copy(_codec.InputBuffer, p, window, q, t); - p += t; - n -= t; - q += t; - m -= t; - if ((left -= t) != 0) + index = 0; + mode = InflateBlockMode.DTREE; + goto case InflateBlockMode.DTREE; + + case InflateBlockMode.DTREE: + while (true) + { + t = table; + if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) { break; } - mode = last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE; - break; - case InflateBlockMode.TABLE: + int i, + j, + c; - while (k < (14)) + t = bb[0]; + + while (k < t) { if (n != 0) { @@ -377,47 +511,26 @@ namespace SharpCompress.Compressors.Deflate k += 8; } - table = t = (b & 0x3fff); - if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) - { - mode = InflateBlockMode.BAD; - _codec.Message = "too many length or distance symbols"; - r = ZlibConstants.Z_DATA_ERROR; + t = hufts[ + ((tb[0] + (b & InternalInflateConstants.InflateMask[t])) * 3) + 1 + ]; + c = hufts[ + ((tb[0] + (b & InternalInflateConstants.InflateMask[t])) * 3) + 2 + ]; - bitb = b; - bitk = k; - _codec.AvailableBytesIn = n; - _codec.TotalBytesIn += p - _codec.NextIn; - _codec.NextIn = p; - writeAt = q; - return Flush(r); - } - t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); - if (blens is null || blens.Length < t) + if (c < 16) { - blens = new int[t]; + b >>= t; + k -= t; + blens[index++] = c; } else { - Array.Clear(blens, 0, t); + // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; - // for (int i = 0; i < t; i++) - // { - // blens[i] = 0; - // } - } - - b >>= 14; - k -= 14; - - index = 0; - mode = InflateBlockMode.BTREE; - goto case InflateBlockMode.BTREE; - - case InflateBlockMode.BTREE: - while (index < 4 + (table >> 10)) - { - while (k < (3)) + while (k < (t + i)) { if (n != 0) { @@ -439,27 +552,73 @@ namespace SharpCompress.Compressors.Deflate k += 8; } - blens[border[index++]] = b & 7; + b >>= t; + k -= t; - b >>= 3; - k -= 3; + j += (b & InternalInflateConstants.InflateMask[i]); + + b >>= i; + k -= i; + + i = index; + t = table; + if ( + i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) + || (c == 16 && i < 1) + ) + { + blens = null; + mode = InflateBlockMode.BAD; + _codec.Message = "invalid bit length repeat"; + r = ZlibConstants.Z_DATA_ERROR; + + bitb = b; + bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + + c = (c == 16) ? blens[i - 1] : 0; + do + { + blens[i++] = c; + } while (--j != 0); + index = i; } + } - while (index < 19) - { - blens[border[index++]] = 0; - } + tb[0] = -1; + + { + var bl = new[] { 9 }; // must be <= 9 for lookahead assumptions + var bd = new[] { 6 }; // must be <= 9 for lookahead assumptions + var tl = new int[1]; + var td = new int[1]; + + t = table; + t = inftree.inflate_trees_dynamic( + 257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, + bl, + bd, + tl, + td, + hufts, + _codec + ); - bb[0] = 7; - t = inftree.inflate_trees_bits(blens, bb, tb, hufts, _codec); if (t != ZlibConstants.Z_OK) { - r = t; - if (r == ZlibConstants.Z_DATA_ERROR) + if (t == ZlibConstants.Z_DATA_ERROR) { blens = null; mode = InflateBlockMode.BAD; } + r = t; bitb = b; bitk = k; @@ -469,469 +628,438 @@ namespace SharpCompress.Compressors.Deflate writeAt = q; return Flush(r); } + codes.Init(bl[0], bd[0], hufts, tl[0], hufts, td[0]); + } + mode = InflateBlockMode.CODES; + goto case InflateBlockMode.CODES; - index = 0; - mode = InflateBlockMode.DTREE; - goto case InflateBlockMode.DTREE; + case InflateBlockMode.CODES: + bitb = b; + bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; - case InflateBlockMode.DTREE: - while (true) + r = codes.Process(this, r); + if (r != ZlibConstants.Z_STREAM_END) + { + return Flush(r); + } + + r = ZlibConstants.Z_OK; + p = _codec.NextIn; + n = _codec.AvailableBytesIn; + b = bitb; + k = bitk; + q = writeAt; + m = (q < readAt ? readAt - q - 1 : end - q); + + if (last == 0) + { + mode = InflateBlockMode.TYPE; + break; + } + mode = InflateBlockMode.DRY; + goto case InflateBlockMode.DRY; + + case InflateBlockMode.DRY: + writeAt = q; + r = Flush(r); + q = writeAt; + m = (q < readAt ? readAt - q - 1 : end - q); + if (readAt != writeAt) + { + bitb = b; + bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + mode = InflateBlockMode.DONE; + goto case InflateBlockMode.DONE; + + case InflateBlockMode.DONE: + r = ZlibConstants.Z_STREAM_END; + bitb = b; + bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + + case InflateBlockMode.BAD: + r = ZlibConstants.Z_DATA_ERROR; + + bitb = b; + bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + + default: + r = ZlibConstants.Z_STREAM_ERROR; + + bitb = b; + bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + } + } + + internal void Free() + { + Reset(); + window = null; + hufts = null; + } + + internal void SetDictionary(byte[] d, int start, int n) + { + Array.Copy(d, start, window, 0, n); + readAt = writeAt = n; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or Z_FULL_FLUSH. + internal int SyncPoint() + { + return mode == InflateBlockMode.LENS ? 1 : 0; + } + + // copy as much as possible from the sliding window to the output area + internal int Flush(int r) + { + int nBytes; + + for (var pass = 0; pass < 2; pass++) + { + if (pass == 0) + { + // compute number of bytes to copy as far as end of window + nBytes = ((readAt <= writeAt ? writeAt : end) - readAt); + } + else + { + // compute bytes to copy + nBytes = writeAt - readAt; + } + + // workitem 8870 + if (nBytes == 0) + { + if (r == ZlibConstants.Z_BUF_ERROR) + { + r = ZlibConstants.Z_OK; + } + return r; + } + + if (nBytes > _codec.AvailableBytesOut) + { + nBytes = _codec.AvailableBytesOut; + } + + if (nBytes != 0 && r == ZlibConstants.Z_BUF_ERROR) + { + r = ZlibConstants.Z_OK; + } + + // update counters + _codec.AvailableBytesOut -= nBytes; + _codec.TotalBytesOut += nBytes; + + // update check information + if (checkfn != null) + { + _codec._adler32 = check = Adler32.Calculate( + check, + window.AsSpan(readAt, nBytes) + ); + } + + // copy as far as end of window + Array.Copy(window, readAt, _codec.OutputBuffer, _codec.NextOut, nBytes); + _codec.NextOut += nBytes; + readAt += nBytes; + + // see if more to copy at beginning of window + if (readAt == end && pass == 0) + { + // wrap pointers + readAt = 0; + if (writeAt == end) + { + writeAt = 0; + } + } + else + { + pass++; + } + } + + // done + return r; + } + + #region Nested type: InflateBlockMode + + private enum InflateBlockMode + { + TYPE = 0, // get type bits (3, including end bit) + LENS = 1, // get lengths for stored + STORED = 2, // processing stored block + TABLE = 3, // get table lengths + BTREE = 4, // get bit lengths tree for a dynamic block + DTREE = 5, // get length, distance trees for a dynamic block + CODES = 6, // processing fixed or dynamic block + DRY = 7, // output remaining window bytes + DONE = 8, // finished last block, done + BAD = 9 // ot a data error--stuck here + } + + #endregion +} + +internal static class InternalInflateConstants +{ + // And'ing with mask[n] masks the lower n bits + internal static readonly int[] InflateMask = + { + 0x00000000, + 0x00000001, + 0x00000003, + 0x00000007, + 0x0000000f, + 0x0000001f, + 0x0000003f, + 0x0000007f, + 0x000000ff, + 0x000001ff, + 0x000003ff, + 0x000007ff, + 0x00000fff, + 0x00001fff, + 0x00003fff, + 0x00007fff, + 0x0000ffff + }; +} + +internal sealed class InflateCodes +{ + // waiting for "i:"=input, + // "o:"=output, + // "x:"=nothing + private const int START = 0; // x: set up for LEN + private const int LEN = 1; // i: get length/literal/eob next + private const int LENEXT = 2; // i: getting length extra (have base) + private const int DIST = 3; // i: get distance next + private const int DISTEXT = 4; // i: getting distance extra + private const int COPY = 5; // o: copying bytes in window, waiting for space + private const int LIT = 6; // o: got literal, waiting for output space + private const int WASH = 7; // o: got eob, possibly still output waiting + private const int END = 8; // x: got eob and all data flushed + private const int BADCODE = 9; // x: got error + + // if EXT or COPY, where and how much + internal int bitsToGet; // bits to get for extra + internal byte dbits; // dtree bits decoder per branch + internal int dist; // distance back to copy from + internal int[] dtree; // distance tree + internal int dtree_index; // distance tree + + internal byte lbits; // ltree bits decoded per branch + internal int len; + internal int lit; + internal int[] ltree; // literal/length/eob tree + internal int ltree_index; // literal/length/eob tree + internal int mode; // current inflate_codes mode + internal int need; // bits needed + internal int[] tree; // pointer into tree + internal int tree_index; + + internal void Init(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index) + { + mode = START; + lbits = (byte)bl; + dbits = (byte)bd; + ltree = tl; + ltree_index = tl_index; + dtree = td; + dtree_index = td_index; + tree = null; + } + + internal int Process(InflateBlocks blocks, int r) + { + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + var b = 0; // bit buffer + var k = 0; // bits in bit buffer + var p = 0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + var z = blocks._codec; + + // copy input/output information to locals (UPDATE macro restores) + p = z.NextIn; + n = z.AvailableBytesIn; + b = blocks.bitb; + k = blocks.bitk; + q = blocks.writeAt; + m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; + + // process input and output based on current state + while (true) + { + switch (mode) + { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case START: // x: set up for LEN + if (m >= 258 && n >= 10) + { + blocks.bitb = b; + blocks.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + blocks.writeAt = q; + r = InflateFast( + lbits, + dbits, + ltree, + ltree_index, + dtree, + dtree_index, + blocks, + z + ); + + p = z.NextIn; + n = z.AvailableBytesIn; + b = blocks.bitb; + k = blocks.bitk; + q = blocks.writeAt; + m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; + + if (r != ZlibConstants.Z_OK) { - t = table; - if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) + mode = (r == ZlibConstants.Z_STREAM_END) ? WASH : BADCODE; + break; + } + } + need = lbits; + tree = ltree; + tree_index = ltree_index; + + mode = LEN; + goto case LEN; + + case LEN: // i: get length/literal/eob next + j = need; + + while (k < j) + { + if (n != 0) + { + r = ZlibConstants.Z_OK; + } + else + { + // Handling missing trailing bit(s) + var tmp_tindex = + (tree_index + (b & InternalInflateConstants.InflateMask[k])) + * 3; + if (k >= tree[tmp_tindex + 1]) { break; } - int i, - j, - c; - - t = bb[0]; - - while (k < t) - { - if (n != 0) - { - r = ZlibConstants.Z_OK; - } - else - { - bitb = b; - bitk = k; - _codec.AvailableBytesIn = n; - _codec.TotalBytesIn += p - _codec.NextIn; - _codec.NextIn = p; - writeAt = q; - return Flush(r); - } - - n--; - b |= (_codec.InputBuffer[p++] & 0xff) << k; - k += 8; - } - - t = hufts[ - (tb[0] + (b & InternalInflateConstants.InflateMask[t])) * 3 + 1 - ]; - c = hufts[ - (tb[0] + (b & InternalInflateConstants.InflateMask[t])) * 3 + 2 - ]; - - if (c < 16) - { - b >>= t; - k -= t; - blens[index++] = c; - } - else - { - // c == 16..18 - i = c == 18 ? 7 : c - 14; - j = c == 18 ? 11 : 3; - - while (k < (t + i)) - { - if (n != 0) - { - r = ZlibConstants.Z_OK; - } - else - { - bitb = b; - bitk = k; - _codec.AvailableBytesIn = n; - _codec.TotalBytesIn += p - _codec.NextIn; - _codec.NextIn = p; - writeAt = q; - return Flush(r); - } - - n--; - b |= (_codec.InputBuffer[p++] & 0xff) << k; - k += 8; - } - - b >>= t; - k -= t; - - j += (b & InternalInflateConstants.InflateMask[i]); - - b >>= i; - k -= i; - - i = index; - t = table; - if ( - i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) - || (c == 16 && i < 1) - ) - { - blens = null; - mode = InflateBlockMode.BAD; - _codec.Message = "invalid bit length repeat"; - r = ZlibConstants.Z_DATA_ERROR; - - bitb = b; - bitk = k; - _codec.AvailableBytesIn = n; - _codec.TotalBytesIn += p - _codec.NextIn; - _codec.NextIn = p; - writeAt = q; - return Flush(r); - } - - c = (c == 16) ? blens[i - 1] : 0; - do - { - blens[i++] = c; - } while (--j != 0); - index = i; - } + blocks.bitb = b; + blocks.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); } - - tb[0] = -1; - - { - var bl = new[] { 9 }; // must be <= 9 for lookahead assumptions - var bd = new[] { 6 }; // must be <= 9 for lookahead assumptions - var tl = new int[1]; - var td = new int[1]; - - t = table; - t = inftree.inflate_trees_dynamic( - 257 + (t & 0x1f), - 1 + ((t >> 5) & 0x1f), - blens, - bl, - bd, - tl, - td, - hufts, - _codec - ); - - if (t != ZlibConstants.Z_OK) - { - if (t == ZlibConstants.Z_DATA_ERROR) - { - blens = null; - mode = InflateBlockMode.BAD; - } - r = t; - - bitb = b; - bitk = k; - _codec.AvailableBytesIn = n; - _codec.TotalBytesIn += p - _codec.NextIn; - _codec.NextIn = p; - writeAt = q; - return Flush(r); - } - codes.Init(bl[0], bd[0], hufts, tl[0], hufts, td[0]); - } - mode = InflateBlockMode.CODES; - goto case InflateBlockMode.CODES; - - case InflateBlockMode.CODES: - bitb = b; - bitk = k; - _codec.AvailableBytesIn = n; - _codec.TotalBytesIn += p - _codec.NextIn; - _codec.NextIn = p; - writeAt = q; - - r = codes.Process(this, r); - if (r != ZlibConstants.Z_STREAM_END) - { - return Flush(r); - } - - r = ZlibConstants.Z_OK; - p = _codec.NextIn; - n = _codec.AvailableBytesIn; - b = bitb; - k = bitk; - q = writeAt; - m = (q < readAt ? readAt - q - 1 : end - q); - - if (last == 0) - { - mode = InflateBlockMode.TYPE; - break; - } - mode = InflateBlockMode.DRY; - goto case InflateBlockMode.DRY; - - case InflateBlockMode.DRY: - writeAt = q; - r = Flush(r); - q = writeAt; - m = (q < readAt ? readAt - q - 1 : end - q); - if (readAt != writeAt) - { - bitb = b; - bitk = k; - _codec.AvailableBytesIn = n; - _codec.TotalBytesIn += p - _codec.NextIn; - _codec.NextIn = p; - writeAt = q; - return Flush(r); - } - mode = InflateBlockMode.DONE; - goto case InflateBlockMode.DONE; - - case InflateBlockMode.DONE: - r = ZlibConstants.Z_STREAM_END; - bitb = b; - bitk = k; - _codec.AvailableBytesIn = n; - _codec.TotalBytesIn += p - _codec.NextIn; - _codec.NextIn = p; - writeAt = q; - return Flush(r); - - case InflateBlockMode.BAD: - r = ZlibConstants.Z_DATA_ERROR; - - bitb = b; - bitk = k; - _codec.AvailableBytesIn = n; - _codec.TotalBytesIn += p - _codec.NextIn; - _codec.NextIn = p; - writeAt = q; - return Flush(r); - - default: - r = ZlibConstants.Z_STREAM_ERROR; - - bitb = b; - bitk = k; - _codec.AvailableBytesIn = n; - _codec.TotalBytesIn += p - _codec.NextIn; - _codec.NextIn = p; - writeAt = q; - return Flush(r); - } - } - } - - internal void Free() - { - Reset(); - window = null; - hufts = null; - } - - internal void SetDictionary(byte[] d, int start, int n) - { - Array.Copy(d, start, window, 0, n); - readAt = writeAt = n; - } - - // Returns true if inflate is currently at the end of a block generated - // by Z_SYNC_FLUSH or Z_FULL_FLUSH. - internal int SyncPoint() - { - return mode == InflateBlockMode.LENS ? 1 : 0; - } - - // copy as much as possible from the sliding window to the output area - internal int Flush(int r) - { - int nBytes; - - for (int pass = 0; pass < 2; pass++) - { - if (pass == 0) - { - // compute number of bytes to copy as far as end of window - nBytes = ((readAt <= writeAt ? writeAt : end) - readAt); - } - else - { - // compute bytes to copy - nBytes = writeAt - readAt; - } - - // workitem 8870 - if (nBytes == 0) - { - if (r == ZlibConstants.Z_BUF_ERROR) - { - r = ZlibConstants.Z_OK; + n--; + b |= (z.InputBuffer[p++] & 0xff) << k; + k += 8; } - return r; - } - if (nBytes > _codec.AvailableBytesOut) - { - nBytes = _codec.AvailableBytesOut; - } + tindex = (tree_index + (b & InternalInflateConstants.InflateMask[j])) * 3; - if (nBytes != 0 && r == ZlibConstants.Z_BUF_ERROR) - { - r = ZlibConstants.Z_OK; - } + b >>= (tree[tindex + 1]); + k -= (tree[tindex + 1]); - // update counters - _codec.AvailableBytesOut -= nBytes; - _codec.TotalBytesOut += nBytes; + e = tree[tindex]; - // update check information - if (checkfn != null) - { - _codec._adler32 = check = Adler32.Calculate( - check, - window.AsSpan(readAt, nBytes) - ); - } - - // copy as far as end of window - Array.Copy(window, readAt, _codec.OutputBuffer, _codec.NextOut, nBytes); - _codec.NextOut += nBytes; - readAt += nBytes; - - // see if more to copy at beginning of window - if (readAt == end && pass == 0) - { - // wrap pointers - readAt = 0; - if (writeAt == end) + if (e == 0) { - writeAt = 0; + // literal + lit = tree[tindex + 2]; + mode = LIT; + break; } - } - else - { - pass++; - } - } + if ((e & 16) != 0) + { + // length + bitsToGet = e & 15; + len = tree[tindex + 2]; + mode = LENEXT; + break; + } + if ((e & 64) == 0) + { + // next table + need = e; + tree_index = (tindex / 3) + tree[tindex + 2]; + break; + } + if ((e & 32) != 0) + { + // end of block + mode = WASH; + break; + } + mode = BADCODE; // invalid code + z.Message = "invalid literal/length code"; + r = ZlibConstants.Z_DATA_ERROR; - // done - return r; - } + blocks.bitb = b; + blocks.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); - #region Nested type: InflateBlockMode + case LENEXT: // i: getting length extra (have base) + j = bitsToGet; - private enum InflateBlockMode - { - TYPE = 0, // get type bits (3, including end bit) - LENS = 1, // get lengths for stored - STORED = 2, // processing stored block - TABLE = 3, // get table lengths - BTREE = 4, // get bit lengths tree for a dynamic block - DTREE = 5, // get length, distance trees for a dynamic block - CODES = 6, // processing fixed or dynamic block - DRY = 7, // output remaining window bytes - DONE = 8, // finished last block, done - BAD = 9 // ot a data error--stuck here - } - - #endregion - } - - internal static class InternalInflateConstants - { - // And'ing with mask[n] masks the lower n bits - internal static readonly int[] InflateMask = - { - 0x00000000, - 0x00000001, - 0x00000003, - 0x00000007, - 0x0000000f, - 0x0000001f, - 0x0000003f, - 0x0000007f, - 0x000000ff, - 0x000001ff, - 0x000003ff, - 0x000007ff, - 0x00000fff, - 0x00001fff, - 0x00003fff, - 0x00007fff, - 0x0000ffff - }; - } - - internal sealed class InflateCodes - { - // waiting for "i:"=input, - // "o:"=output, - // "x:"=nothing - private const int START = 0; // x: set up for LEN - private const int LEN = 1; // i: get length/literal/eob next - private const int LENEXT = 2; // i: getting length extra (have base) - private const int DIST = 3; // i: get distance next - private const int DISTEXT = 4; // i: getting distance extra - private const int COPY = 5; // o: copying bytes in window, waiting for space - private const int LIT = 6; // o: got literal, waiting for output space - private const int WASH = 7; // o: got eob, possibly still output waiting - private const int END = 8; // x: got eob and all data flushed - private const int BADCODE = 9; // x: got error - - // if EXT or COPY, where and how much - internal int bitsToGet; // bits to get for extra - internal byte dbits; // dtree bits decoder per branch - internal int dist; // distance back to copy from - internal int[] dtree; // distance tree - internal int dtree_index; // distance tree - - internal byte lbits; // ltree bits decoded per branch - internal int len; - internal int lit; - internal int[] ltree; // literal/length/eob tree - internal int ltree_index; // literal/length/eob tree - internal int mode; // current inflate_codes mode - internal int need; // bits needed - internal int[] tree; // pointer into tree - internal int tree_index; - - internal void Init(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index) - { - mode = START; - lbits = (byte)bl; - dbits = (byte)bd; - ltree = tl; - ltree_index = tl_index; - dtree = td; - dtree_index = td_index; - tree = null; - } - - internal int Process(InflateBlocks blocks, int r) - { - int j; // temporary storage - int tindex; // temporary pointer - int e; // extra bits or operation - int b = 0; // bit buffer - int k = 0; // bits in bit buffer - int p = 0; // input data pointer - int n; // bytes available there - int q; // output window write pointer - int m; // bytes to end of window or read pointer - int f; // pointer to copy strings from - - ZlibCodec z = blocks._codec; - - // copy input/output information to locals (UPDATE macro restores) - p = z.NextIn; - n = z.AvailableBytesIn; - b = blocks.bitb; - k = blocks.bitk; - q = blocks.writeAt; - m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; - - // process input and output based on current state - while (true) - { - switch (mode) - { - // waiting for "i:"=input, "o:"=output, "x:"=nothing - case START: // x: set up for LEN - if (m >= 258 && n >= 10) + while (k < j) + { + if (n != 0) + { + r = ZlibConstants.Z_OK; + } + else { blocks.bitb = b; blocks.bitk = k; @@ -939,308 +1067,131 @@ namespace SharpCompress.Compressors.Deflate z.TotalBytesIn += p - z.NextIn; z.NextIn = p; blocks.writeAt = q; - r = InflateFast( - lbits, - dbits, - ltree, - ltree_index, - dtree, - dtree_index, - blocks, - z - ); + return blocks.Flush(r); + } + n--; + b |= (z.InputBuffer[p++] & 0xff) << k; + k += 8; + } - p = z.NextIn; - n = z.AvailableBytesIn; - b = blocks.bitb; - k = blocks.bitk; - q = blocks.writeAt; - m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; + len += (b & InternalInflateConstants.InflateMask[j]); - if (r != ZlibConstants.Z_OK) + b >>= j; + k -= j; + + need = dbits; + tree = dtree; + tree_index = dtree_index; + mode = DIST; + goto case DIST; + + case DIST: // i: get distance next + j = need; + + while (k < j) + { + if (n != 0) + { + r = ZlibConstants.Z_OK; + } + else + { + // Handling missing trailing bit(s) + var tmp_tindex = + (tree_index + (b & InternalInflateConstants.InflateMask[k])) + * 3; + if (k >= tree[tmp_tindex + 1]) { - mode = (r == ZlibConstants.Z_STREAM_END) ? WASH : BADCODE; break; } + + blocks.bitb = b; + blocks.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); } - need = lbits; - tree = ltree; - tree_index = ltree_index; + n--; + b |= (z.InputBuffer[p++] & 0xff) << k; + k += 8; + } - mode = LEN; - goto case LEN; + tindex = (tree_index + (b & InternalInflateConstants.InflateMask[j])) * 3; - case LEN: // i: get length/literal/eob next - j = need; + b >>= tree[tindex + 1]; + k -= tree[tindex + 1]; - while (k < j) - { - if (n != 0) - { - r = ZlibConstants.Z_OK; - } - else - { - // Handling missing trailing bit(s) - var tmp_tindex = - (tree_index + (b & InternalInflateConstants.InflateMask[k])) - * 3; - if (k >= tree[tmp_tindex + 1]) - { - break; - } - - blocks.bitb = b; - blocks.bitk = k; - z.AvailableBytesIn = n; - z.TotalBytesIn += p - z.NextIn; - z.NextIn = p; - blocks.writeAt = q; - return blocks.Flush(r); - } - n--; - b |= (z.InputBuffer[p++] & 0xff) << k; - k += 8; - } - - tindex = (tree_index + (b & InternalInflateConstants.InflateMask[j])) * 3; - - b >>= (tree[tindex + 1]); - k -= (tree[tindex + 1]); - - e = tree[tindex]; - - if (e == 0) - { - // literal - lit = tree[tindex + 2]; - mode = LIT; - break; - } - if ((e & 16) != 0) - { - // length - bitsToGet = e & 15; - len = tree[tindex + 2]; - mode = LENEXT; - break; - } - if ((e & 64) == 0) - { - // next table - need = e; - tree_index = tindex / 3 + tree[tindex + 2]; - break; - } - if ((e & 32) != 0) - { - // end of block - mode = WASH; - break; - } - mode = BADCODE; // invalid code - z.Message = "invalid literal/length code"; - r = ZlibConstants.Z_DATA_ERROR; - - blocks.bitb = b; - blocks.bitk = k; - z.AvailableBytesIn = n; - z.TotalBytesIn += p - z.NextIn; - z.NextIn = p; - blocks.writeAt = q; - return blocks.Flush(r); - - case LENEXT: // i: getting length extra (have base) - j = bitsToGet; - - while (k < j) - { - if (n != 0) - { - r = ZlibConstants.Z_OK; - } - else - { - blocks.bitb = b; - blocks.bitk = k; - z.AvailableBytesIn = n; - z.TotalBytesIn += p - z.NextIn; - z.NextIn = p; - blocks.writeAt = q; - return blocks.Flush(r); - } - n--; - b |= (z.InputBuffer[p++] & 0xff) << k; - k += 8; - } - - len += (b & InternalInflateConstants.InflateMask[j]); - - b >>= j; - k -= j; - - need = dbits; - tree = dtree; - tree_index = dtree_index; - mode = DIST; - goto case DIST; - - case DIST: // i: get distance next - j = need; - - while (k < j) - { - if (n != 0) - { - r = ZlibConstants.Z_OK; - } - else - { - // Handling missing trailing bit(s) - var tmp_tindex = - (tree_index + (b & InternalInflateConstants.InflateMask[k])) - * 3; - if (k >= tree[tmp_tindex + 1]) - { - break; - } - - blocks.bitb = b; - blocks.bitk = k; - z.AvailableBytesIn = n; - z.TotalBytesIn += p - z.NextIn; - z.NextIn = p; - blocks.writeAt = q; - return blocks.Flush(r); - } - n--; - b |= (z.InputBuffer[p++] & 0xff) << k; - k += 8; - } - - tindex = (tree_index + (b & InternalInflateConstants.InflateMask[j])) * 3; - - b >>= tree[tindex + 1]; - k -= tree[tindex + 1]; - - e = (tree[tindex]); - if ((e & 0x10) != 0) - { - // distance - bitsToGet = e & 15; - dist = tree[tindex + 2]; - mode = DISTEXT; - break; - } - if ((e & 64) == 0) - { - // next table - need = e; - tree_index = tindex / 3 + tree[tindex + 2]; - break; - } - mode = BADCODE; // invalid code - z.Message = "invalid distance code"; - r = ZlibConstants.Z_DATA_ERROR; - - blocks.bitb = b; - blocks.bitk = k; - z.AvailableBytesIn = n; - z.TotalBytesIn += p - z.NextIn; - z.NextIn = p; - blocks.writeAt = q; - return blocks.Flush(r); - - case DISTEXT: // i: getting distance extra - j = bitsToGet; - - while (k < j) - { - if (n != 0) - { - r = ZlibConstants.Z_OK; - } - else - { - blocks.bitb = b; - blocks.bitk = k; - z.AvailableBytesIn = n; - z.TotalBytesIn += p - z.NextIn; - z.NextIn = p; - blocks.writeAt = q; - return blocks.Flush(r); - } - n--; - b |= (z.InputBuffer[p++] & 0xff) << k; - k += 8; - } - - dist += (b & InternalInflateConstants.InflateMask[j]); - - b >>= j; - k -= j; - - mode = COPY; - goto case COPY; - - case COPY: // o: copying bytes in window, waiting for space - f = q - dist; - while (f < 0) - { - // modulo window size-"while" instead - f += blocks.end; // of "if" handles invalid distances - } - while (len != 0) - { - if (m == 0) - { - if (q == blocks.end && blocks.readAt != 0) - { - q = 0; - m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; - } - if (m == 0) - { - blocks.writeAt = q; - r = blocks.Flush(r); - q = blocks.writeAt; - m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; - - if (q == blocks.end && blocks.readAt != 0) - { - q = 0; - m = - q < blocks.readAt - ? blocks.readAt - q - 1 - : blocks.end - q; - } - - if (m == 0) - { - blocks.bitb = b; - blocks.bitk = k; - z.AvailableBytesIn = n; - z.TotalBytesIn += p - z.NextIn; - z.NextIn = p; - blocks.writeAt = q; - return blocks.Flush(r); - } - } - } - - blocks.window[q++] = blocks.window[f++]; - m--; - - if (f == blocks.end) - { - f = 0; - } - len--; - } - mode = START; + e = (tree[tindex]); + if ((e & 0x10) != 0) + { + // distance + bitsToGet = e & 15; + dist = tree[tindex + 2]; + mode = DISTEXT; break; + } + if ((e & 64) == 0) + { + // next table + need = e; + tree_index = (tindex / 3) + tree[tindex + 2]; + break; + } + mode = BADCODE; // invalid code + z.Message = "invalid distance code"; + r = ZlibConstants.Z_DATA_ERROR; - case LIT: // o: got literal, waiting for output space + blocks.bitb = b; + blocks.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); + + case DISTEXT: // i: getting distance extra + j = bitsToGet; + + while (k < j) + { + if (n != 0) + { + r = ZlibConstants.Z_OK; + } + else + { + blocks.bitb = b; + blocks.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); + } + n--; + b |= (z.InputBuffer[p++] & 0xff) << k; + k += 8; + } + + dist += (b & InternalInflateConstants.InflateMask[j]); + + b >>= j; + k -= j; + + mode = COPY; + goto case COPY; + + case COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while (f < 0) + { + // modulo window size-"while" instead + f += blocks.end; // of "if" handles invalid distances + } + while (len != 0) + { if (m == 0) { if (q == blocks.end && blocks.readAt != 0) @@ -1258,8 +1209,12 @@ namespace SharpCompress.Compressors.Deflate if (q == blocks.end && blocks.readAt != 0) { q = 0; - m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; + m = + q < blocks.readAt + ? blocks.readAt - q - 1 + : blocks.end - q; } + if (m == 0) { blocks.bitb = b; @@ -1272,814 +1227,855 @@ namespace SharpCompress.Compressors.Deflate } } } - r = ZlibConstants.Z_OK; - blocks.window[q++] = (byte)lit; + blocks.window[q++] = blocks.window[f++]; m--; - mode = START; - break; - - case WASH: // o: got eob, possibly more output - if (k > 7) + if (f == blocks.end) { - // return unused byte, if any - k -= 8; - n++; - p--; // can always return one + f = 0; } + len--; + } + mode = START; + break; - blocks.writeAt = q; - r = blocks.Flush(r); - q = blocks.writeAt; - m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; - - if (blocks.readAt != blocks.writeAt) - { - blocks.bitb = b; - blocks.bitk = k; - z.AvailableBytesIn = n; - z.TotalBytesIn += p - z.NextIn; - z.NextIn = p; - blocks.writeAt = q; - return blocks.Flush(r); - } - mode = END; - goto case END; - - case END: - r = ZlibConstants.Z_STREAM_END; - blocks.bitb = b; - blocks.bitk = k; - z.AvailableBytesIn = n; - z.TotalBytesIn += p - z.NextIn; - z.NextIn = p; - blocks.writeAt = q; - return blocks.Flush(r); - - case BADCODE: // x: got error - - r = ZlibConstants.Z_DATA_ERROR; - - blocks.bitb = b; - blocks.bitk = k; - z.AvailableBytesIn = n; - z.TotalBytesIn += p - z.NextIn; - z.NextIn = p; - blocks.writeAt = q; - return blocks.Flush(r); - - default: - r = ZlibConstants.Z_STREAM_ERROR; - - blocks.bitb = b; - blocks.bitk = k; - z.AvailableBytesIn = n; - z.TotalBytesIn += p - z.NextIn; - z.NextIn = p; - blocks.writeAt = q; - return blocks.Flush(r); - } - } - } - - // Called with number of bytes left to write in window at least 258 - // (the maximum string length) and number of input bytes available - // at least ten. The ten bytes are six bytes for the longest length/ - // distance pair plus four bytes for overloading the bit buffer. - - internal int InflateFast( - int bl, - int bd, - int[] tl, - int tl_index, - int[] td, - int td_index, - InflateBlocks s, - ZlibCodec z - ) - { - int t; // temporary pointer - int[] tp; // temporary pointer - int tp_index; // temporary pointer - int e; // extra bits or operation - int b; // bit buffer - int k; // bits in bit buffer - int p; // input data pointer - int n; // bytes available there - int q; // output window write pointer - int m; // bytes to end of window or read pointer - int ml; // mask for literal/length tree - int md; // mask for distance tree - int c; // bytes to copy - int d; // distance back to copy from - int r; // copy source pointer - - int tp_index_t_3; // (tp_index+t)*3 - - // load input, output, bit values - p = z.NextIn; - n = z.AvailableBytesIn; - b = s.bitb; - k = s.bitk; - q = s.writeAt; - m = q < s.readAt ? s.readAt - q - 1 : s.end - q; - - // initialize masks - ml = InternalInflateConstants.InflateMask[bl]; - md = InternalInflateConstants.InflateMask[bd]; - - // do until not enough input or output space for fast loop - do - { - // assume called with m >= 258 && n >= 10 - // get literal/length code - while (k < (20)) - { - // max bits for literal/length code - n--; - b |= (z.InputBuffer[p++] & 0xff) << k; - k += 8; - } - - t = b & ml; - tp = tl; - tp_index = tl_index; - tp_index_t_3 = (tp_index + t) * 3; - if ((e = tp[tp_index_t_3]) == 0) - { - b >>= (tp[tp_index_t_3 + 1]); - k -= (tp[tp_index_t_3 + 1]); - - s.window[q++] = (byte)tp[tp_index_t_3 + 2]; - m--; - continue; - } - do - { - b >>= (tp[tp_index_t_3 + 1]); - k -= (tp[tp_index_t_3 + 1]); - - if ((e & 16) != 0) + case LIT: // o: got literal, waiting for output space + if (m == 0) { - e &= 15; - c = tp[tp_index_t_3 + 2] + (b & InternalInflateConstants.InflateMask[e]); - - b >>= e; - k -= e; - - // decode distance base of block to copy - while (k < 15) + if (q == blocks.end && blocks.readAt != 0) { - // max bits for distance code - n--; - b |= (z.InputBuffer[p++] & 0xff) << k; - k += 8; + q = 0; + m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; } - - t = b & md; - tp = td; - tp_index = td_index; - tp_index_t_3 = (tp_index + t) * 3; - e = tp[tp_index_t_3]; - - do + if (m == 0) { - b >>= (tp[tp_index_t_3 + 1]); - k -= (tp[tp_index_t_3 + 1]); + blocks.writeAt = q; + r = blocks.Flush(r); + q = blocks.writeAt; + m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; - if ((e & 16) != 0) + if (q == blocks.end && blocks.readAt != 0) { - // get extra bits to add to distance base - e &= 15; - while (k < e) - { - // get extra bits (up to 13) - n--; - b |= (z.InputBuffer[p++] & 0xff) << k; - k += 8; - } - - d = - tp[tp_index_t_3 + 2] - + (b & InternalInflateConstants.InflateMask[e]); - - b >>= e; - k -= e; - - // do the copy - m -= c; - if (q >= d) - { - // offset before dest - // just copy - r = q - d; - if (q - r > 0 && 2 > (q - r)) - { - s.window[q++] = s.window[r++]; // minimum count is three, - s.window[q++] = s.window[r++]; // so unroll loop a little - c -= 2; - } - else - { - Array.Copy(s.window, r, s.window, q, 2); - q += 2; - r += 2; - c -= 2; - } - } - else - { - // else offset after destination - r = q - d; - do - { - r += s.end; // force pointer in window - } while (r < 0); // covers invalid distances - e = s.end - r; - if (c > e) - { - // if source crosses, - c -= e; // wrapped copy - if (q - r > 0 && e > (q - r)) - { - do - { - s.window[q++] = s.window[r++]; - } while (--e != 0); - } - else - { - Array.Copy(s.window, r, s.window, q, e); - q += e; - r += e; - e = 0; - } - r = 0; // copy rest from start of window - } - } - - // copy all or what's left - if (q - r > 0 && c > (q - r)) - { - do - { - s.window[q++] = s.window[r++]; - } while (--c != 0); - } - else - { - Array.Copy(s.window, r, s.window, q, c); - q += c; - r += c; - c = 0; - } - break; + q = 0; + m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; } - if ((e & 64) == 0) + if (m == 0) { - t += tp[tp_index_t_3 + 2]; - t += (b & InternalInflateConstants.InflateMask[e]); - tp_index_t_3 = (tp_index + t) * 3; - e = tp[tp_index_t_3]; - } - else - { - z.Message = "invalid distance code"; - - c = z.AvailableBytesIn - n; - c = (k >> 3) < c ? k >> 3 : c; - n += c; - p -= c; - k -= (c << 3); - - s.bitb = b; - s.bitk = k; + blocks.bitb = b; + blocks.bitk = k; z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p; - s.writeAt = q; - - return ZlibConstants.Z_DATA_ERROR; + blocks.writeAt = q; + return blocks.Flush(r); } - } while (true); - break; - } - - if ((e & 64) == 0) - { - t += tp[tp_index_t_3 + 2]; - t += (b & InternalInflateConstants.InflateMask[e]); - tp_index_t_3 = (tp_index + t) * 3; - if ((e = tp[tp_index_t_3]) == 0) - { - b >>= (tp[tp_index_t_3 + 1]); - k -= (tp[tp_index_t_3 + 1]); - s.window[q++] = (byte)tp[tp_index_t_3 + 2]; - m--; - break; } } - else if ((e & 32) != 0) - { - c = z.AvailableBytesIn - n; - c = (k >> 3) < c ? k >> 3 : c; - n += c; - p -= c; - k -= (c << 3); + r = ZlibConstants.Z_OK; - s.bitb = b; - s.bitk = k; + blocks.window[q++] = (byte)lit; + m--; + + mode = START; + break; + + case WASH: // o: got eob, possibly more output + if (k > 7) + { + // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + blocks.writeAt = q; + r = blocks.Flush(r); + q = blocks.writeAt; + m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; + + if (blocks.readAt != blocks.writeAt) + { + blocks.bitb = b; + blocks.bitk = k; z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p; - s.writeAt = q; - - return ZlibConstants.Z_STREAM_END; + blocks.writeAt = q; + return blocks.Flush(r); } - else - { - z.Message = "invalid literal/length code"; + mode = END; + goto case END; - c = z.AvailableBytesIn - n; - c = (k >> 3) < c ? k >> 3 : c; - n += c; - p -= c; - k -= (c << 3); + case END: + r = ZlibConstants.Z_STREAM_END; + blocks.bitb = b; + blocks.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); - s.bitb = b; - s.bitk = k; - z.AvailableBytesIn = n; - z.TotalBytesIn += p - z.NextIn; - z.NextIn = p; - s.writeAt = q; + case BADCODE: // x: got error - return ZlibConstants.Z_DATA_ERROR; - } - } while (true); - } while (m >= 258 && n >= 10); + r = ZlibConstants.Z_DATA_ERROR; - // not enough input or output--restore pointers and return - c = z.AvailableBytesIn - n; - c = (k >> 3) < c ? k >> 3 : c; - n += c; - p -= c; - k -= (c << 3); + blocks.bitb = b; + blocks.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); - s.bitb = b; - s.bitk = k; - z.AvailableBytesIn = n; - z.TotalBytesIn += p - z.NextIn; - z.NextIn = p; - s.writeAt = q; + default: + r = ZlibConstants.Z_STREAM_ERROR; - return ZlibConstants.Z_OK; + blocks.bitb = b; + blocks.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); + } } } - internal sealed class InflateManager + // Called with number of bytes left to write in window at least 258 + // (the maximum string length) and number of input bytes available + // at least ten. The ten bytes are six bytes for the longest length/ + // distance pair plus four bytes for overloading the bit buffer. + + internal int InflateFast( + int bl, + int bd, + int[] tl, + int tl_index, + int[] td, + int td_index, + InflateBlocks s, + ZlibCodec z + ) { - // preset dictionary flag in zlib header - private const int PRESET_DICT = 0x20; + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer - private const int Z_DEFLATED = 8; - private static readonly byte[] mark = { 0, 0, 0xff, 0xff }; + int tp_index_t_3; // (tp_index+t)*3 - internal ZlibCodec _codec; // pointer back to this zlib stream - internal InflateBlocks blocks; // current inflate_blocks state + // load input, output, bit values + p = z.NextIn; + n = z.AvailableBytesIn; + b = s.bitb; + k = s.bitk; + q = s.writeAt; + m = q < s.readAt ? s.readAt - q - 1 : s.end - q; - // mode dependent information + // initialize masks + ml = InternalInflateConstants.InflateMask[bl]; + md = InternalInflateConstants.InflateMask[bd]; - // if CHECK, check values to compare - internal uint computedCheck; // computed check value - internal uint expectedCheck; // stream check value - - // if BAD, inflateSync's marker bytes count - internal int marker; - internal int method; // if FLAGS, method byte - private InflateManagerMode mode; // current inflate mode - - // mode independent information - //internal int nowrap; // flag for no wrapper - - internal int wbits; // log2(window size) (8..15, defaults to 15) - - public InflateManager() { } - - public InflateManager(bool expectRfc1950HeaderBytes) + // do until not enough input or output space for fast loop + do { - HandleRfc1950HeaderBytes = expectRfc1950HeaderBytes; - } - - internal bool HandleRfc1950HeaderBytes { get; set; } = true; - - internal int Reset() - { - _codec.TotalBytesIn = _codec.TotalBytesOut = 0; - _codec.Message = null; - mode = HandleRfc1950HeaderBytes ? InflateManagerMode.METHOD : InflateManagerMode.BLOCKS; - blocks.Reset(); - return ZlibConstants.Z_OK; - } - - internal int End() - { - if (blocks != null) + // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) { - blocks.Free(); - } - blocks = null; - return ZlibConstants.Z_OK; - } - - internal int Initialize(ZlibCodec codec, int w) - { - _codec = codec; - _codec.Message = null; - blocks = null; - - // handle undocumented nowrap option (no zlib header or check) - //nowrap = 0; - //if (w < 0) - //{ - // w = - w; - // nowrap = 1; - //} - - // set window size - if (w < 8 || w > 15) - { - End(); - throw new ZlibException("Bad window size."); - - //return ZlibConstants.Z_STREAM_ERROR; - } - wbits = w; - - blocks = new InflateBlocks(codec, HandleRfc1950HeaderBytes ? this : null, 1 << w); - - // reset state - Reset(); - return ZlibConstants.Z_OK; - } - - internal int Inflate(FlushType flush) - { - int b; - - if (_codec.InputBuffer is null) - { - throw new ZlibException("InputBuffer is null. "); + // max bits for literal/length code + n--; + b |= (z.InputBuffer[p++] & 0xff) << k; + k += 8; } - // int f = (flush == FlushType.Finish) - // ? ZlibConstants.Z_BUF_ERROR - // : ZlibConstants.Z_OK; - - // workitem 8870 - int f = ZlibConstants.Z_OK; - int r = ZlibConstants.Z_BUF_ERROR; - - while (true) + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) { - switch (mode) + b >>= (tp[tp_index_t_3 + 1]); + k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + continue; + } + do + { + b >>= (tp[tp_index_t_3 + 1]); + k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) { - case InflateManagerMode.METHOD: - if (_codec.AvailableBytesIn == 0) + e &= 15; + c = tp[tp_index_t_3 + 2] + (b & InternalInflateConstants.InflateMask[e]); + + b >>= e; + k -= e; + + // decode distance base of block to copy + while (k < 15) + { + // max bits for distance code + n--; + b |= (z.InputBuffer[p++] & 0xff) << k; + k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do + { + b >>= (tp[tp_index_t_3 + 1]); + k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) { - return r; - } - r = f; - _codec.AvailableBytesIn--; - _codec.TotalBytesIn++; - if (((method = _codec.InputBuffer[_codec.NextIn++]) & 0xf) != Z_DEFLATED) - { - mode = InflateManagerMode.BAD; - _codec.Message = String.Format( - "unknown compression method (0x{0:X2})", - method - ); - marker = 5; // can't try inflateSync + // get extra bits to add to distance base + e &= 15; + while (k < e) + { + // get extra bits (up to 13) + n--; + b |= (z.InputBuffer[p++] & 0xff) << k; + k += 8; + } + + d = + tp[tp_index_t_3 + 2] + + (b & InternalInflateConstants.InflateMask[e]); + + b >>= e; + k -= e; + + // do the copy + m -= c; + if (q >= d) + { + // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) + { + s.window[q++] = s.window[r++]; // minimum count is three, + s.window[q++] = s.window[r++]; // so unroll loop a little + c -= 2; + } + else + { + Array.Copy(s.window, r, s.window, q, 2); + q += 2; + r += 2; + c -= 2; + } + } + else + { + // else offset after destination + r = q - d; + do + { + r += s.end; // force pointer in window + } while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) + { + // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) + { + do + { + s.window[q++] = s.window[r++]; + } while (--e != 0); + } + else + { + Array.Copy(s.window, r, s.window, q, e); + q += e; + r += e; + e = 0; + } + r = 0; // copy rest from start of window + } + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) + { + do + { + s.window[q++] = s.window[r++]; + } while (--c != 0); + } + else + { + Array.Copy(s.window, r, s.window, q, c); + q += c; + r += c; + c = 0; + } break; } - if ((method >> 4) + 8 > wbits) + if ((e & 64) == 0) { - mode = InflateManagerMode.BAD; - _codec.Message = String.Format( - "invalid window size ({0})", - (method >> 4) + 8 - ); - marker = 5; // can't try inflateSync - break; + t += tp[tp_index_t_3 + 2]; + t += (b & InternalInflateConstants.InflateMask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; } - mode = InflateManagerMode.FLAG; - break; - - case InflateManagerMode.FLAG: - if (_codec.AvailableBytesIn == 0) + else { - return r; + z.Message = "invalid distance code"; + + c = z.AvailableBytesIn - n; + c = (k >> 3) < c ? k >> 3 : c; + n += c; + p -= c; + k -= (c << 3); + + s.bitb = b; + s.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + s.writeAt = q; + + return ZlibConstants.Z_DATA_ERROR; } - r = f; - _codec.AvailableBytesIn--; - _codec.TotalBytesIn++; - b = (_codec.InputBuffer[_codec.NextIn++]) & 0xff; - - if ((((method << 8) + b) % 31) != 0) - { - mode = InflateManagerMode.BAD; - _codec.Message = "incorrect header check"; - marker = 5; // can't try inflateSync - break; - } - - mode = - ((b & PRESET_DICT) == 0) - ? InflateManagerMode.BLOCKS - : InflateManagerMode.DICT4; - break; - - case InflateManagerMode.DICT4: - if (_codec.AvailableBytesIn == 0) - { - return r; - } - r = f; - _codec.AvailableBytesIn--; - _codec.TotalBytesIn++; - expectedCheck = (uint)( - (_codec.InputBuffer[_codec.NextIn++] << 24) & 0xff000000 - ); - mode = InflateManagerMode.DICT3; - break; - - case InflateManagerMode.DICT3: - if (_codec.AvailableBytesIn == 0) - { - return r; - } - r = f; - _codec.AvailableBytesIn--; - _codec.TotalBytesIn++; - expectedCheck += (uint)( - (_codec.InputBuffer[_codec.NextIn++] << 16) & 0x00ff0000 - ); - mode = InflateManagerMode.DICT2; - break; - - case InflateManagerMode.DICT2: - - if (_codec.AvailableBytesIn == 0) - { - return r; - } - r = f; - _codec.AvailableBytesIn--; - _codec.TotalBytesIn++; - expectedCheck += (uint)( - (_codec.InputBuffer[_codec.NextIn++] << 8) & 0x0000ff00 - ); - mode = InflateManagerMode.DICT1; - break; - - case InflateManagerMode.DICT1: - if (_codec.AvailableBytesIn == 0) - { - return r; - } - r = f; - _codec.AvailableBytesIn--; - _codec.TotalBytesIn++; - expectedCheck += (uint)(_codec.InputBuffer[_codec.NextIn++] & 0x000000ff); - _codec._adler32 = expectedCheck; - mode = InflateManagerMode.DICT0; - return ZlibConstants.Z_NEED_DICT; - - case InflateManagerMode.DICT0: - mode = InflateManagerMode.BAD; - _codec.Message = "need dictionary"; - marker = 0; // can try inflateSync - return ZlibConstants.Z_STREAM_ERROR; - - case InflateManagerMode.BLOCKS: - r = blocks.Process(r); - if (r == ZlibConstants.Z_DATA_ERROR) - { - mode = InflateManagerMode.BAD; - marker = 0; // can try inflateSync - break; - } - - if (r == ZlibConstants.Z_OK) - { - r = f; - } - - if (r != ZlibConstants.Z_STREAM_END) - { - return r; - } - - r = f; - computedCheck = blocks.Reset(); - if (!HandleRfc1950HeaderBytes) - { - mode = InflateManagerMode.DONE; - return ZlibConstants.Z_STREAM_END; - } - mode = InflateManagerMode.CHECK4; - break; - - case InflateManagerMode.CHECK4: - if (_codec.AvailableBytesIn == 0) - { - return r; - } - r = f; - _codec.AvailableBytesIn--; - _codec.TotalBytesIn++; - expectedCheck = (uint)( - (_codec.InputBuffer[_codec.NextIn++] << 24) & 0xff000000 - ); - mode = InflateManagerMode.CHECK3; - break; - - case InflateManagerMode.CHECK3: - if (_codec.AvailableBytesIn == 0) - { - return r; - } - r = f; - _codec.AvailableBytesIn--; - _codec.TotalBytesIn++; - expectedCheck += (uint)( - (_codec.InputBuffer[_codec.NextIn++] << 16) & 0x00ff0000 - ); - mode = InflateManagerMode.CHECK2; - break; - - case InflateManagerMode.CHECK2: - if (_codec.AvailableBytesIn == 0) - { - return r; - } - r = f; - _codec.AvailableBytesIn--; - _codec.TotalBytesIn++; - expectedCheck += (uint)( - (_codec.InputBuffer[_codec.NextIn++] << 8) & 0x0000ff00 - ); - mode = InflateManagerMode.CHECK1; - break; - - case InflateManagerMode.CHECK1: - if (_codec.AvailableBytesIn == 0) - { - return r; - } - r = f; - _codec.AvailableBytesIn--; - _codec.TotalBytesIn++; - expectedCheck += (uint)(_codec.InputBuffer[_codec.NextIn++] & 0x000000ff); - if (computedCheck != expectedCheck) - { - mode = InflateManagerMode.BAD; - _codec.Message = "incorrect data check"; - marker = 5; // can't try inflateSync - break; - } - mode = InflateManagerMode.DONE; - return ZlibConstants.Z_STREAM_END; - - case InflateManagerMode.DONE: - return ZlibConstants.Z_STREAM_END; - - case InflateManagerMode.BAD: - throw new ZlibException(String.Format("Bad state ({0})", _codec.Message)); - - default: - throw new ZlibException("Stream error."); + } while (true); + break; } - } - } - internal int SetDictionary(byte[] dictionary) - { - int index = 0; - int length = dictionary.Length; - if (mode != InflateManagerMode.DICT0) - { - throw new ZlibException("Stream error."); - } - - if (Adler32.Calculate(1, dictionary) != _codec._adler32) - { - return ZlibConstants.Z_DATA_ERROR; - } - - _codec._adler32 = 1; - - if (length >= (1 << wbits)) - { - length = (1 << wbits) - 1; - index = dictionary.Length - length; - } - blocks.SetDictionary(dictionary, index, length); - mode = InflateManagerMode.BLOCKS; - return ZlibConstants.Z_OK; - } - - internal int Sync() - { - int n; // number of bytes to look at - int p; // pointer to bytes - int m; // number of marker bytes found in a row - long r, - w; // temporaries to save total_in and total_out - - // set up - if (mode != InflateManagerMode.BAD) - { - mode = InflateManagerMode.BAD; - marker = 0; - } - if ((n = _codec.AvailableBytesIn) == 0) - { - return ZlibConstants.Z_BUF_ERROR; - } - p = _codec.NextIn; - m = marker; - - // search - while (n != 0 && m < 4) - { - if (_codec.InputBuffer[p] == mark[m]) + if ((e & 64) == 0) { - m++; + t += tp[tp_index_t_3 + 2]; + t += (b & InternalInflateConstants.InflateMask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + b >>= (tp[tp_index_t_3 + 1]); + k -= (tp[tp_index_t_3 + 1]); + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + break; + } } - else if (_codec.InputBuffer[p] != 0) + else if ((e & 32) != 0) { - m = 0; + c = z.AvailableBytesIn - n; + c = (k >> 3) < c ? k >> 3 : c; + n += c; + p -= c; + k -= (c << 3); + + s.bitb = b; + s.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + s.writeAt = q; + + return ZlibConstants.Z_STREAM_END; } else { - m = 4 - m; + z.Message = "invalid literal/length code"; + + c = z.AvailableBytesIn - n; + c = (k >> 3) < c ? k >> 3 : c; + n += c; + p -= c; + k -= (c << 3); + + s.bitb = b; + s.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + s.writeAt = q; + + return ZlibConstants.Z_DATA_ERROR; } - p++; - n--; - } + } while (true); + } while (m >= 258 && n >= 10); - // restore - _codec.TotalBytesIn += p - _codec.NextIn; - _codec.NextIn = p; - _codec.AvailableBytesIn = n; - marker = m; + // not enough input or output--restore pointers and return + c = z.AvailableBytesIn - n; + c = (k >> 3) < c ? k >> 3 : c; + n += c; + p -= c; + k -= (c << 3); - // return no joy or set up to restart on a new block - if (m != 4) - { - return ZlibConstants.Z_DATA_ERROR; - } - r = _codec.TotalBytesIn; - w = _codec.TotalBytesOut; - Reset(); - _codec.TotalBytesIn = r; - _codec.TotalBytesOut = w; - mode = InflateManagerMode.BLOCKS; - return ZlibConstants.Z_OK; - } + s.bitb = b; + s.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + s.writeAt = q; - // Returns true if inflate is currently at the end of a block generated - // by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP - // implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH - // but removes the length bytes of the resulting empty stored block. When - // decompressing, PPP checks that at the end of input packet, inflate is - // waiting for these length bytes. - internal int SyncPoint(ZlibCodec z) - { - return blocks.SyncPoint(); - } - - #region Nested type: InflateManagerMode - - private enum InflateManagerMode - { - METHOD = 0, // waiting for method byte - FLAG = 1, // waiting for flag byte - DICT4 = 2, // four dictionary check bytes to go - DICT3 = 3, // three dictionary check bytes to go - DICT2 = 4, // two dictionary check bytes to go - DICT1 = 5, // one dictionary check byte to go - DICT0 = 6, // waiting for inflateSetDictionary - BLOCKS = 7, // decompressing blocks - CHECK4 = 8, // four check bytes to go - CHECK3 = 9, // three check bytes to go - CHECK2 = 10, // two check bytes to go - CHECK1 = 11, // one check byte to go - DONE = 12, // finished check, done - BAD = 13 // got an error--stay here - } - - #endregion + return ZlibConstants.Z_OK; } } + +internal sealed class InflateManager +{ + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_DEFLATED = 8; + private static readonly byte[] mark = { 0, 0, 0xff, 0xff }; + + internal ZlibCodec _codec; // pointer back to this zlib stream + internal InflateBlocks blocks; // current inflate_blocks state + + // mode dependent information + + // if CHECK, check values to compare + internal uint computedCheck; // computed check value + internal uint expectedCheck; // stream check value + + // if BAD, inflateSync's marker bytes count + internal int marker; + internal int method; // if FLAGS, method byte + private InflateManagerMode mode; // current inflate mode + + // mode independent information + //internal int nowrap; // flag for no wrapper + + internal int wbits; // log2(window size) (8..15, defaults to 15) + + public InflateManager() { } + + public InflateManager(bool expectRfc1950HeaderBytes) + { + HandleRfc1950HeaderBytes = expectRfc1950HeaderBytes; + } + + internal bool HandleRfc1950HeaderBytes { get; set; } = true; + + internal int Reset() + { + _codec.TotalBytesIn = _codec.TotalBytesOut = 0; + _codec.Message = null; + mode = HandleRfc1950HeaderBytes ? InflateManagerMode.METHOD : InflateManagerMode.BLOCKS; + blocks.Reset(); + return ZlibConstants.Z_OK; + } + + internal int End() + { + blocks?.Free(); + blocks = null; + return ZlibConstants.Z_OK; + } + + internal int Initialize(ZlibCodec codec, int w) + { + _codec = codec; + _codec.Message = null; + blocks = null; + + // handle undocumented nowrap option (no zlib header or check) + //nowrap = 0; + //if (w < 0) + //{ + // w = - w; + // nowrap = 1; + //} + + // set window size + if (w < 8 || w > 15) + { + End(); + throw new ZlibException("Bad window size."); + + //return ZlibConstants.Z_STREAM_ERROR; + } + wbits = w; + + blocks = new InflateBlocks(codec, HandleRfc1950HeaderBytes ? this : null, 1 << w); + + // reset state + Reset(); + return ZlibConstants.Z_OK; + } + + internal int Inflate(FlushType flush) + { + int b; + + if (_codec.InputBuffer is null) + { + throw new ZlibException("InputBuffer is null. "); + } + + // int f = (flush == FlushType.Finish) + // ? ZlibConstants.Z_BUF_ERROR + // : ZlibConstants.Z_OK; + + // workitem 8870 + var f = ZlibConstants.Z_OK; + var r = ZlibConstants.Z_BUF_ERROR; + + while (true) + { + switch (mode) + { + case InflateManagerMode.METHOD: + if (_codec.AvailableBytesIn == 0) + { + return r; + } + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + if (((method = _codec.InputBuffer[_codec.NextIn++]) & 0xf) != Z_DEFLATED) + { + mode = InflateManagerMode.BAD; + _codec.Message = string.Format( + "unknown compression method (0x{0:X2})", + method + ); + marker = 5; // can't try inflateSync + break; + } + if ((method >> 4) + 8 > wbits) + { + mode = InflateManagerMode.BAD; + _codec.Message = string.Format( + "invalid window size ({0})", + (method >> 4) + 8 + ); + marker = 5; // can't try inflateSync + break; + } + mode = InflateManagerMode.FLAG; + break; + + case InflateManagerMode.FLAG: + if (_codec.AvailableBytesIn == 0) + { + return r; + } + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + b = (_codec.InputBuffer[_codec.NextIn++]) & 0xff; + + if ((((method << 8) + b) % 31) != 0) + { + mode = InflateManagerMode.BAD; + _codec.Message = "incorrect header check"; + marker = 5; // can't try inflateSync + break; + } + + mode = + ((b & PRESET_DICT) == 0) + ? InflateManagerMode.BLOCKS + : InflateManagerMode.DICT4; + break; + + case InflateManagerMode.DICT4: + if (_codec.AvailableBytesIn == 0) + { + return r; + } + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + expectedCheck = (uint)( + (_codec.InputBuffer[_codec.NextIn++] << 24) & 0xff000000 + ); + mode = InflateManagerMode.DICT3; + break; + + case InflateManagerMode.DICT3: + if (_codec.AvailableBytesIn == 0) + { + return r; + } + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + expectedCheck += (uint)( + (_codec.InputBuffer[_codec.NextIn++] << 16) & 0x00ff0000 + ); + mode = InflateManagerMode.DICT2; + break; + + case InflateManagerMode.DICT2: + + if (_codec.AvailableBytesIn == 0) + { + return r; + } + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + expectedCheck += (uint)( + (_codec.InputBuffer[_codec.NextIn++] << 8) & 0x0000ff00 + ); + mode = InflateManagerMode.DICT1; + break; + + case InflateManagerMode.DICT1: + if (_codec.AvailableBytesIn == 0) + { + return r; + } + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + expectedCheck += (uint)(_codec.InputBuffer[_codec.NextIn++] & 0x000000ff); + _codec._adler32 = expectedCheck; + mode = InflateManagerMode.DICT0; + return ZlibConstants.Z_NEED_DICT; + + case InflateManagerMode.DICT0: + mode = InflateManagerMode.BAD; + _codec.Message = "need dictionary"; + marker = 0; // can try inflateSync + return ZlibConstants.Z_STREAM_ERROR; + + case InflateManagerMode.BLOCKS: + r = blocks.Process(r); + if (r == ZlibConstants.Z_DATA_ERROR) + { + mode = InflateManagerMode.BAD; + marker = 0; // can try inflateSync + break; + } + + if (r == ZlibConstants.Z_OK) + { + r = f; + } + + if (r != ZlibConstants.Z_STREAM_END) + { + return r; + } + + r = f; + computedCheck = blocks.Reset(); + if (!HandleRfc1950HeaderBytes) + { + mode = InflateManagerMode.DONE; + return ZlibConstants.Z_STREAM_END; + } + mode = InflateManagerMode.CHECK4; + break; + + case InflateManagerMode.CHECK4: + if (_codec.AvailableBytesIn == 0) + { + return r; + } + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + expectedCheck = (uint)( + (_codec.InputBuffer[_codec.NextIn++] << 24) & 0xff000000 + ); + mode = InflateManagerMode.CHECK3; + break; + + case InflateManagerMode.CHECK3: + if (_codec.AvailableBytesIn == 0) + { + return r; + } + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + expectedCheck += (uint)( + (_codec.InputBuffer[_codec.NextIn++] << 16) & 0x00ff0000 + ); + mode = InflateManagerMode.CHECK2; + break; + + case InflateManagerMode.CHECK2: + if (_codec.AvailableBytesIn == 0) + { + return r; + } + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + expectedCheck += (uint)( + (_codec.InputBuffer[_codec.NextIn++] << 8) & 0x0000ff00 + ); + mode = InflateManagerMode.CHECK1; + break; + + case InflateManagerMode.CHECK1: + if (_codec.AvailableBytesIn == 0) + { + return r; + } + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + expectedCheck += (uint)(_codec.InputBuffer[_codec.NextIn++] & 0x000000ff); + if (computedCheck != expectedCheck) + { + mode = InflateManagerMode.BAD; + _codec.Message = "incorrect data check"; + marker = 5; // can't try inflateSync + break; + } + mode = InflateManagerMode.DONE; + return ZlibConstants.Z_STREAM_END; + + case InflateManagerMode.DONE: + return ZlibConstants.Z_STREAM_END; + + case InflateManagerMode.BAD: + throw new ZlibException(string.Format("Bad state ({0})", _codec.Message)); + + default: + throw new ZlibException("Stream error."); + } + } + } + + internal int SetDictionary(byte[] dictionary) + { + var index = 0; + var length = dictionary.Length; + if (mode != InflateManagerMode.DICT0) + { + throw new ZlibException("Stream error."); + } + + if (Adler32.Calculate(1, dictionary) != _codec._adler32) + { + return ZlibConstants.Z_DATA_ERROR; + } + + _codec._adler32 = 1; + + if (length >= (1 << wbits)) + { + length = (1 << wbits) - 1; + index = dictionary.Length - length; + } + blocks.SetDictionary(dictionary, index, length); + mode = InflateManagerMode.BLOCKS; + return ZlibConstants.Z_OK; + } + + internal int Sync() + { + int n; // number of bytes to look at + int p; // pointer to bytes + int m; // number of marker bytes found in a row + long r, + w; // temporaries to save total_in and total_out + + // set up + if (mode != InflateManagerMode.BAD) + { + mode = InflateManagerMode.BAD; + marker = 0; + } + if ((n = _codec.AvailableBytesIn) == 0) + { + return ZlibConstants.Z_BUF_ERROR; + } + p = _codec.NextIn; + m = marker; + + // search + while (n != 0 && m < 4) + { + if (_codec.InputBuffer[p] == mark[m]) + { + m++; + } + else if (_codec.InputBuffer[p] != 0) + { + m = 0; + } + else + { + m = 4 - m; + } + p++; + n--; + } + + // restore + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + _codec.AvailableBytesIn = n; + marker = m; + + // return no joy or set up to restart on a new block + if (m != 4) + { + return ZlibConstants.Z_DATA_ERROR; + } + r = _codec.TotalBytesIn; + w = _codec.TotalBytesOut; + Reset(); + _codec.TotalBytesIn = r; + _codec.TotalBytesOut = w; + mode = InflateManagerMode.BLOCKS; + return ZlibConstants.Z_OK; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + // implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH + // but removes the length bytes of the resulting empty stored block. When + // decompressing, PPP checks that at the end of input packet, inflate is + // waiting for these length bytes. + internal int SyncPoint(ZlibCodec z) + { + return blocks.SyncPoint(); + } + + #region Nested type: InflateManagerMode + + private enum InflateManagerMode + { + METHOD = 0, // waiting for method byte + FLAG = 1, // waiting for flag byte + DICT4 = 2, // four dictionary check bytes to go + DICT3 = 3, // three dictionary check bytes to go + DICT2 = 4, // two dictionary check bytes to go + DICT1 = 5, // one dictionary check byte to go + DICT0 = 6, // waiting for inflateSetDictionary + BLOCKS = 7, // decompressing blocks + CHECK4 = 8, // four check bytes to go + CHECK3 = 9, // three check bytes to go + CHECK2 = 10, // two check bytes to go + CHECK1 = 11, // one check byte to go + DONE = 12, // finished check, done + BAD = 13 // got an error--stay here + } + + #endregion +} diff --git a/src/SharpCompress/Compressors/Deflate/Tree.cs b/src/SharpCompress/Compressors/Deflate/Tree.cs index 3804679d..7056e7a0 100644 --- a/src/SharpCompress/Compressors/Deflate/Tree.cs +++ b/src/SharpCompress/Compressors/Deflate/Tree.cs @@ -64,1171 +64,1170 @@ using System; -namespace SharpCompress.Compressors.Deflate +namespace SharpCompress.Compressors.Deflate; + +internal sealed partial class DeflateManager { - internal sealed partial class DeflateManager + #region Nested type: Tree + + private sealed class Tree { - #region Nested type: Tree + internal const int Buf_size = 8 * 2; + private static readonly int HEAP_SIZE = ((2 * InternalConstants.L_CODES) + 1); - private sealed class Tree + internal static readonly sbyte[] bl_order = { - internal const int Buf_size = 8 * 2; - private static readonly int HEAP_SIZE = (2 * InternalConstants.L_CODES + 1); + 16, + 17, + 18, + 0, + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15 + }; - internal static readonly sbyte[] bl_order = + // The lengths of the bit length codes are sent in order of decreasing + // probability, to avoid transmitting the lengths for unused bit + // length codes. + + // see definition of array dist_code below + //internal const int DIST_CODE_LEN = 512; + + private static readonly sbyte[] _dist_code = + { + 0, + 1, + 2, + 3, + 4, + 4, + 5, + 5, + 6, + 6, + 6, + 6, + 7, + 7, + 7, + 7, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 9, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 10, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 11, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 13, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 0, + 0, + 16, + 17, + 18, + 18, + 19, + 19, + 20, + 20, + 20, + 20, + 21, + 21, + 21, + 21, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 28, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29, + 29 + }; + + internal static readonly sbyte[] LengthCode = + { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 8, + 9, + 9, + 10, + 10, + 11, + 11, + 12, + 12, + 12, + 12, + 13, + 13, + 13, + 13, + 14, + 14, + 14, + 14, + 15, + 15, + 15, + 15, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 18, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 19, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 20, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 21, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 22, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 23, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 24, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 25, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 26, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 27, + 28 + }; + + internal static readonly int[] LengthBase = + { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 10, + 12, + 14, + 16, + 20, + 24, + 28, + 32, + 40, + 48, + 56, + 64, + 80, + 96, + 112, + 128, + 160, + 192, + 224, + 0 + }; + + internal static readonly int[] DistanceBase = + { + 0, + 1, + 2, + 3, + 4, + 6, + 8, + 12, + 16, + 24, + 32, + 48, + 64, + 96, + 128, + 192, + 256, + 384, + 512, + 768, + 1024, + 1536, + 2048, + 3072, + 4096, + 6144, + 8192, + 12288, + 16384, + 24576 + }; + + internal short[] dyn_tree; // the dynamic tree + internal int max_code; // largest code with non zero frequency + internal StaticTree staticTree; // the corresponding static tree + + /// + /// Map from a distance to a distance code. + /// + /// + /// No side effects. _dist_code[256] and _dist_code[257] are never used. + /// + internal static int DistanceCode(int dist) + { + return (dist < 256) + ? _dist_code[dist] + : _dist_code[256 + SharedUtils.URShift(dist, 7)]; + } + + // Compute the optimal bit lengths for a tree and update the total bit length + // for the current block. + // IN assertion: the fields freq and dad are set, heap[heap_max] and + // above are the tree nodes sorted by increasing frequency. + // OUT assertions: the field len is set to the optimal bit length, the + // array bl_count contains the frequencies for each bit length. + // The length opt_len is updated; static_len is also updated if stree is + // not null. + internal void gen_bitlen(DeflateManager s) + { + var tree = dyn_tree; + var stree = staticTree.treeCodes; + var extra = staticTree.extraBits; + var base_Renamed = staticTree.extraBase; + var max_length = staticTree.maxLength; + int h; // heap index + int n, + m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + var overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= InternalConstants.MAX_BITS; bits++) { - 16, - 17, - 18, - 0, - 8, - 7, - 9, - 6, - 10, - 5, - 11, - 4, - 12, - 3, - 13, - 2, - 14, - 1, - 15 - }; - - // The lengths of the bit length codes are sent in order of decreasing - // probability, to avoid transmitting the lengths for unused bit - // length codes. - - // see definition of array dist_code below - //internal const int DIST_CODE_LEN = 512; - - private static readonly sbyte[] _dist_code = - { - 0, - 1, - 2, - 3, - 4, - 4, - 5, - 5, - 6, - 6, - 6, - 6, - 7, - 7, - 7, - 7, - 8, - 8, - 8, - 8, - 8, - 8, - 8, - 8, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 10, - 11, - 11, - 11, - 11, - 11, - 11, - 11, - 11, - 11, - 11, - 11, - 11, - 11, - 11, - 11, - 11, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 13, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 14, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 15, - 0, - 0, - 16, - 17, - 18, - 18, - 19, - 19, - 20, - 20, - 20, - 20, - 21, - 21, - 21, - 21, - 22, - 22, - 22, - 22, - 22, - 22, - 22, - 22, - 23, - 23, - 23, - 23, - 23, - 23, - 23, - 23, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 28, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29, - 29 - }; - - internal static readonly sbyte[] LengthCode = - { - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 8, - 9, - 9, - 10, - 10, - 11, - 11, - 12, - 12, - 12, - 12, - 13, - 13, - 13, - 13, - 14, - 14, - 14, - 14, - 15, - 15, - 15, - 15, - 16, - 16, - 16, - 16, - 16, - 16, - 16, - 16, - 17, - 17, - 17, - 17, - 17, - 17, - 17, - 17, - 18, - 18, - 18, - 18, - 18, - 18, - 18, - 18, - 19, - 19, - 19, - 19, - 19, - 19, - 19, - 19, - 20, - 20, - 20, - 20, - 20, - 20, - 20, - 20, - 20, - 20, - 20, - 20, - 20, - 20, - 20, - 20, - 21, - 21, - 21, - 21, - 21, - 21, - 21, - 21, - 21, - 21, - 21, - 21, - 21, - 21, - 21, - 21, - 22, - 22, - 22, - 22, - 22, - 22, - 22, - 22, - 22, - 22, - 22, - 22, - 22, - 22, - 22, - 22, - 23, - 23, - 23, - 23, - 23, - 23, - 23, - 23, - 23, - 23, - 23, - 23, - 23, - 23, - 23, - 23, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 24, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 25, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 26, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 27, - 28 - }; - - internal static readonly int[] LengthBase = - { - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 10, - 12, - 14, - 16, - 20, - 24, - 28, - 32, - 40, - 48, - 56, - 64, - 80, - 96, - 112, - 128, - 160, - 192, - 224, - 0 - }; - - internal static readonly int[] DistanceBase = - { - 0, - 1, - 2, - 3, - 4, - 6, - 8, - 12, - 16, - 24, - 32, - 48, - 64, - 96, - 128, - 192, - 256, - 384, - 512, - 768, - 1024, - 1536, - 2048, - 3072, - 4096, - 6144, - 8192, - 12288, - 16384, - 24576 - }; - - internal short[] dyn_tree; // the dynamic tree - internal int max_code; // largest code with non zero frequency - internal StaticTree staticTree; // the corresponding static tree - - /// - /// Map from a distance to a distance code. - /// - /// - /// No side effects. _dist_code[256] and _dist_code[257] are never used. - /// - internal static int DistanceCode(int dist) - { - return (dist < 256) - ? _dist_code[dist] - : _dist_code[256 + SharedUtils.URShift(dist, 7)]; + s.bl_count[bits] = 0; } - // Compute the optimal bit lengths for a tree and update the total bit length - // for the current block. - // IN assertion: the fields freq and dad are set, heap[heap_max] and - // above are the tree nodes sorted by increasing frequency. - // OUT assertions: the field len is set to the optimal bit length, the - // array bl_count contains the frequencies for each bit length. - // The length opt_len is updated; static_len is also updated if stree is - // not null. - internal void gen_bitlen(DeflateManager s) + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[(s.heap[s.heap_max] * 2) + 1] = 0; // root of the heap + + for (h = s.heap_max + 1; h < HEAP_SIZE; h++) { - short[] tree = dyn_tree; - short[] stree = staticTree.treeCodes; - int[] extra = staticTree.extraBits; - int base_Renamed = staticTree.extraBase; - int max_length = staticTree.maxLength; - int h; // heap index - int n, - m; // iterate over the tree elements - int bits; // bit length - int xbits; // extra bits - short f; // frequency - int overflow = 0; // number of elements with bit length too large - - for (bits = 0; bits <= InternalConstants.MAX_BITS; bits++) + n = s.heap[h]; + bits = tree[(tree[(n * 2) + 1] * 2) + 1] + 1; + if (bits > max_length) { - s.bl_count[bits] = 0; + bits = max_length; + overflow++; + } + tree[(n * 2) + 1] = (short)bits; + + // We overwrite tree[n*2+1] which is no longer needed + + if (n > max_code) + { + continue; // not a leaf node } - // In a first pass, compute the optimal bit lengths (which may - // overflow in the case of the bit length tree). - tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap - - for (h = s.heap_max + 1; h < HEAP_SIZE; h++) + s.bl_count[bits]++; + xbits = 0; + if (n >= base_Renamed) { - n = s.heap[h]; - bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; - if (bits > max_length) - { - bits = max_length; - overflow++; - } - tree[n * 2 + 1] = (short)bits; - - // We overwrite tree[n*2+1] which is no longer needed - - if (n > max_code) - { - continue; // not a leaf node - } - - s.bl_count[bits]++; - xbits = 0; - if (n >= base_Renamed) - { - xbits = extra[n - base_Renamed]; - } - f = tree[n * 2]; - s.opt_len += f * (bits + xbits); - if (stree != null) - { - s.static_len += f * (stree[n * 2 + 1] + xbits); - } + xbits = extra[n - base_Renamed]; } - if (overflow == 0) + f = tree[n * 2]; + s.opt_len += f * (bits + xbits); + if (stree != null) { - return; - } - - // This happens for example on obj2 and pic of the Calgary corpus - // Find the first bit length which could increase: - do - { - bits = max_length - 1; - while (s.bl_count[bits] == 0) - { - bits--; - } - s.bl_count[bits]--; // move one leaf down the tree - s.bl_count[bits + 1] = (short)(s.bl_count[bits + 1] + 2); // move one overflow item as its brother - s.bl_count[max_length]--; - - // The brother of the overflow item also moves one step up, - // but this does not affect bl_count[max_length] - overflow -= 2; - } while (overflow > 0); - - for (bits = max_length; bits != 0; bits--) - { - n = s.bl_count[bits]; - while (n != 0) - { - m = s.heap[--h]; - if (m > max_code) - { - continue; - } - if (tree[m * 2 + 1] != bits) - { - s.opt_len = (int)( - s.opt_len + (bits - (long)tree[m * 2 + 1]) * tree[m * 2] - ); - tree[m * 2 + 1] = (short)bits; - } - n--; - } + s.static_len += f * (stree[(n * 2) + 1] + xbits); } } - - // Construct one Huffman tree and assigns the code bit strings and lengths. - // Update the total bit length for the current block. - // IN assertion: the field freq is set for all tree elements. - // OUT assertions: the fields len and code are set to the optimal bit length - // and corresponding code. The length opt_len is updated; static_len is - // also updated if stree is not null. The field max_code is set. - internal void build_tree(DeflateManager s) + if (overflow == 0) { - short[] tree = dyn_tree; - short[] stree = staticTree.treeCodes; - int elems = staticTree.elems; - int n, - m; // iterate over heap elements - int max_code = -1; // largest code with non zero frequency - int node; // new node being created - - // Construct the initial heap, with least frequent element in - // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. - // heap[0] is not used. - s.heap_len = 0; - s.heap_max = HEAP_SIZE; - - for (n = 0; n < elems; n++) - { - if (tree[n * 2] != 0) - { - s.heap[++s.heap_len] = max_code = n; - s.depth[n] = 0; - } - else - { - tree[n * 2 + 1] = 0; - } - } - - // The pkzip format requires that at least one distance code exists, - // and that at least one bit should be sent even if there is only one - // possible code. So to avoid special checks later on we force at least - // two codes of non zero frequency. - while (s.heap_len < 2) - { - node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); - tree[node * 2] = 1; - s.depth[node] = 0; - s.opt_len--; - if (stree != null) - { - s.static_len -= stree[node * 2 + 1]; - } - - // node is 0 or 1 so it does not have extra bits - } - this.max_code = max_code; - - // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, - // establish sub-heaps of increasing lengths: - - for (n = s.heap_len / 2; n >= 1; n--) - { - s.pqdownheap(tree, n); - } - - // Construct the Huffman tree by repeatedly combining the least two - // frequent nodes. - - node = elems; // next internal node of the tree - do - { - // n = node of least frequency - n = s.heap[1]; - s.heap[1] = s.heap[s.heap_len--]; - s.pqdownheap(tree, 1); - m = s.heap[1]; // m = node of next least frequency - - s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency - s.heap[--s.heap_max] = m; - - // Create a new node father of n and m - tree[node * 2] = unchecked((short)(tree[n * 2] + tree[m * 2])); - s.depth[node] = (sbyte)(Math.Max((byte)s.depth[n], (byte)s.depth[m]) + 1); - tree[n * 2 + 1] = tree[m * 2 + 1] = (short)node; - - // and insert the new node in the heap - s.heap[1] = node++; - s.pqdownheap(tree, 1); - } while (s.heap_len >= 2); - - s.heap[--s.heap_max] = s.heap[1]; - - // At this point, the fields freq and dad are set. We can now - // generate the bit lengths. - - gen_bitlen(s); - - // The field len is now set, we can generate the bit codes - gen_codes(tree, max_code, s.bl_count); + return; } - // Generate the codes for a given tree and bit counts (which need not be - // optimal). - // IN assertion: the array bl_count contains the bit length statistics for - // the given tree and the field len is set for all tree elements. - // OUT assertion: the field code is set for all tree elements of non - // zero code length. - internal static void gen_codes(short[] tree, int max_code, short[] bl_count) + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do { - var next_code = new short[InternalConstants.MAX_BITS + 1]; // next code value for each bit length - short code = 0; // running code value - int bits; // bit index - int n; // code index - - // The distribution counts are first used to generate the code values - // without bit reversal. - for (bits = 1; bits <= InternalConstants.MAX_BITS; bits++) + bits = max_length - 1; + while (s.bl_count[bits] == 0) { - unchecked - { - next_code[bits] = code = (short)((code + bl_count[bits - 1]) << 1); - } + bits--; } + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits + 1] = (short)(s.bl_count[bits + 1] + 2); // move one overflow item as its brother + s.bl_count[max_length]--; - // Check that the bit counts in bl_count are consistent. The last code - // must be all ones. - //Assert (code + bl_count[MAX_BITS]-1 == (1< 0); - for (n = 0; n <= max_code; n++) + for (bits = max_length; bits != 0; bits--) + { + n = s.bl_count[bits]; + while (n != 0) { - int len = tree[n * 2 + 1]; - if (len == 0) + m = s.heap[--h]; + if (m > max_code) { continue; } - - // Now reverse the bits - tree[n * 2] = unchecked((short)(bi_reverse(next_code[len]++, len))); + if (tree[(m * 2) + 1] != bits) + { + s.opt_len = (int)( + s.opt_len + ((bits - (long)tree[(m * 2) + 1]) * tree[m * 2]) + ); + tree[(m * 2) + 1] = (short)bits; + } + n--; } } - - // Reverse the first len bits of a code, using straightforward code (a faster - // method would use a table) - // IN assertion: 1 <= len <= 15 - internal static int bi_reverse(int code, int len) - { - int res = 0; - do - { - res |= code & 1; - code >>= 1; //SharedUtils.URShift(code, 1); - res <<= 1; - } while (--len > 0); - return res >> 1; - } } - #endregion + // Construct one Huffman tree and assigns the code bit strings and lengths. + // Update the total bit length for the current block. + // IN assertion: the field freq is set for all tree elements. + // OUT assertions: the fields len and code are set to the optimal bit length + // and corresponding code. The length opt_len is updated; static_len is + // also updated if stree is not null. The field max_code is set. + internal void build_tree(DeflateManager s) + { + var tree = dyn_tree; + var stree = staticTree.treeCodes; + var elems = staticTree.elems; + int n, + m; // iterate over heap elements + var max_code = -1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) + { + if (tree[n * 2] != 0) + { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + } + else + { + tree[(n * 2) + 1] = 0; + } + } + + // The pkzip format requires that at least one distance code exists, + // and that at least one bit should be sent even if there is only one + // possible code. So to avoid special checks later on we force at least + // two codes of non zero frequency. + while (s.heap_len < 2) + { + node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[node * 2] = 1; + s.depth[node] = 0; + s.opt_len--; + if (stree != null) + { + s.static_len -= stree[(node * 2) + 1]; + } + + // node is 0 or 1 so it does not have extra bits + } + this.max_code = max_code; + + // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + // establish sub-heaps of increasing lengths: + + for (n = s.heap_len / 2; n >= 1; n--) + { + s.pqdownheap(tree, n); + } + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node = elems; // next internal node of the tree + do + { + // n = node of least frequency + n = s.heap[1]; + s.heap[1] = s.heap[s.heap_len--]; + s.pqdownheap(tree, 1); + m = s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node * 2] = unchecked((short)(tree[n * 2] + tree[m * 2])); + s.depth[node] = (sbyte)(Math.Max((byte)s.depth[n], (byte)s.depth[m]) + 1); + tree[(n * 2) + 1] = tree[(m * 2) + 1] = (short)node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.pqdownheap(tree, 1); + } while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + gen_bitlen(s); + + // The field len is now set, we can generate the bit codes + gen_codes(tree, max_code, s.bl_count); + } + + // Generate the codes for a given tree and bit counts (which need not be + // optimal). + // IN assertion: the array bl_count contains the bit length statistics for + // the given tree and the field len is set for all tree elements. + // OUT assertion: the field code is set for all tree elements of non + // zero code length. + internal static void gen_codes(short[] tree, int max_code, short[] bl_count) + { + var next_code = new short[InternalConstants.MAX_BITS + 1]; // next code value for each bit length + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + for (bits = 1; bits <= InternalConstants.MAX_BITS; bits++) + { + unchecked + { + next_code[bits] = code = (short)((code + bl_count[bits - 1]) << 1); + } + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + //Assert (code + bl_count[MAX_BITS]-1 == (1<>= 1; //SharedUtils.URShift(code, 1); + res <<= 1; + } while (--len > 0); + return res >> 1; + } } + + #endregion } diff --git a/src/SharpCompress/Compressors/Deflate/Zlib.cs b/src/SharpCompress/Compressors/Deflate/Zlib.cs index b8cab22c..0c5865fb 100644 --- a/src/SharpCompress/Compressors/Deflate/Zlib.cs +++ b/src/SharpCompress/Compressors/Deflate/Zlib.cs @@ -65,946 +65,945 @@ using System; using System.IO; -namespace SharpCompress.Compressors.Deflate +namespace SharpCompress.Compressors.Deflate; + +/// +/// The compression level to be used when using a DeflateStream or ZlibStream with CompressionMode.Compress. +/// +public enum CompressionLevel { /// - /// The compression level to be used when using a DeflateStream or ZlibStream with CompressionMode.Compress. + /// None means that the data will be simply stored, with no change at all. + /// If you are producing ZIPs for use on Mac OSX, be aware that archives produced with CompressionLevel.None + /// cannot be opened with the default zip reader. Use a different CompressionLevel. /// - public enum CompressionLevel - { - /// - /// None means that the data will be simply stored, with no change at all. - /// If you are producing ZIPs for use on Mac OSX, be aware that archives produced with CompressionLevel.None - /// cannot be opened with the default zip reader. Use a different CompressionLevel. - /// - None = 0, - - /// - /// Same as None. - /// - Level0 = None, - - /// - /// The fastest but least effective compression. - /// - BestSpeed = 1, - - /// - /// A synonym for BestSpeed. - /// - Level1 = BestSpeed, - - /// - /// A little slower, but better, than level 1. - /// - Level2 = 2, - - /// - /// A little slower, but better, than level 2. - /// - Level3 = 3, - - /// - /// A little slower, but better, than level 3. - /// - Level4 = 4, - - /// - /// A little slower than level 4, but with better compression. - /// - Level5 = 5, - - /// - /// The default compression level, with a good balance of speed and compression efficiency. - /// - Default = 6, - - /// - /// A synonym for Default. - /// - Level6 = Default, - - /// - /// Pretty good compression! - /// - Level7 = 7, - - /// - /// Better compression than Level7! - /// - Level8 = 8, - - /// - /// The "best" compression, where best means greatest reduction in size of the input data stream. - /// This is also the slowest compression. - /// - BestCompression = 9, - - /// - /// A synonym for BestCompression. - /// - Level9 = BestCompression - } + None = 0, /// - /// Describes options for how the compression algorithm is executed. Different strategies - /// work better on different sorts of data. The strategy parameter can affect the compression - /// ratio and the speed of compression but not the correctness of the compresssion. + /// Same as None. /// - public enum CompressionStrategy - { - /// - /// The default strategy is probably the best for normal data. - /// - Default = 0, - - /// - /// The Filtered strategy is intended to be used most effectively with data produced by a - /// filter or predictor. By this definition, filtered data consists mostly of small - /// values with a somewhat random distribution. In this case, the compression algorithm - /// is tuned to compress them better. The effect of Filtered is to force more Huffman - /// coding and less string matching; it is a half-step between Default and HuffmanOnly. - /// - Filtered = 1, - - /// - /// Using HuffmanOnly will force the compressor to do Huffman encoding only, with no - /// string matching. - /// - HuffmanOnly = 2 - } + Level0 = None, /// - /// A general purpose exception class for exceptions in the Zlib library. + /// The fastest but least effective compression. /// - public class ZlibException : Exception - { - /// - /// The ZlibException class captures exception information generated - /// by the Zlib library. - /// - public ZlibException() { } + BestSpeed = 1, - /// - /// This ctor collects a message attached to the exception. - /// - /// - public ZlibException(String s) : base(s) { } + /// + /// A synonym for BestSpeed. + /// + Level1 = BestSpeed, + + /// + /// A little slower, but better, than level 1. + /// + Level2 = 2, + + /// + /// A little slower, but better, than level 2. + /// + Level3 = 3, + + /// + /// A little slower, but better, than level 3. + /// + Level4 = 4, + + /// + /// A little slower than level 4, but with better compression. + /// + Level5 = 5, + + /// + /// The default compression level, with a good balance of speed and compression efficiency. + /// + Default = 6, + + /// + /// A synonym for Default. + /// + Level6 = Default, + + /// + /// Pretty good compression! + /// + Level7 = 7, + + /// + /// Better compression than Level7! + /// + Level8 = 8, + + /// + /// The "best" compression, where best means greatest reduction in size of the input data stream. + /// This is also the slowest compression. + /// + BestCompression = 9, + + /// + /// A synonym for BestCompression. + /// + Level9 = BestCompression +} + +/// +/// Describes options for how the compression algorithm is executed. Different strategies +/// work better on different sorts of data. The strategy parameter can affect the compression +/// ratio and the speed of compression but not the correctness of the compresssion. +/// +public enum CompressionStrategy +{ + /// + /// The default strategy is probably the best for normal data. + /// + Default = 0, + + /// + /// The Filtered strategy is intended to be used most effectively with data produced by a + /// filter or predictor. By this definition, filtered data consists mostly of small + /// values with a somewhat random distribution. In this case, the compression algorithm + /// is tuned to compress them better. The effect of Filtered is to force more Huffman + /// coding and less string matching; it is a half-step between Default and HuffmanOnly. + /// + Filtered = 1, + + /// + /// Using HuffmanOnly will force the compressor to do Huffman encoding only, with no + /// string matching. + /// + HuffmanOnly = 2 +} + +/// +/// A general purpose exception class for exceptions in the Zlib library. +/// +public class ZlibException : Exception +{ + /// + /// The ZlibException class captures exception information generated + /// by the Zlib library. + /// + public ZlibException() { } + + /// + /// This ctor collects a message attached to the exception. + /// + /// + public ZlibException(string s) : base(s) { } +} + +internal class SharedUtils +{ + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + public static int URShift(int number, int bits) + { + return (int)((uint)number >> bits); } - internal class SharedUtils - { - /// - /// Performs an unsigned bitwise right shift with the specified number - /// - /// Number to operate on - /// Ammount of bits to shift - /// The resulting number from the shift operation - public static int URShift(int number, int bits) - { - return (int)((uint)number >> bits); - } - #if NOT - /// - /// Performs an unsigned bitwise right shift with the specified number - /// - /// Number to operate on - /// Ammount of bits to shift - /// The resulting number from the shift operation - public static long URShift(long number, int bits) - { - return (long)((UInt64)number >> bits); - } + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + public static long URShift(long number, int bits) + { + return (long)((UInt64)number >> bits); + } #endif - /// - /// Reads a number of characters from the current source TextReader and writes - /// the data to the target array at the specified index. - /// - /// - /// The source TextReader to read from - /// Contains the array of characteres read from the source TextReader. - /// The starting index of the target array. - /// The maximum number of characters to read from the source TextReader. - /// - /// - /// The number of characters read. The number will be less than or equal to - /// count depending on the data available in the source TextReader. Returns -1 - /// if the end of the stream is reached. - /// - public static Int32 ReadInput( - TextReader sourceTextReader, - byte[] target, - int start, - int count - ) - { - // Returns 0 bytes if not enough space in target - if (target.Length == 0) - { - return 0; - } - - char[] charArray = new char[target.Length]; - int bytesRead = sourceTextReader.Read(charArray, start, count); - - // Returns -1 if EOF - if (bytesRead == 0) - { - return -1; - } - - for (int index = start; index < start + bytesRead; index++) - { - target[index] = (byte)charArray[index]; - } - - return bytesRead; - } - } - - internal static class InternalConstants + /// + /// Reads a number of characters from the current source TextReader and writes + /// the data to the target array at the specified index. + /// + /// + /// The source TextReader to read from + /// Contains the array of characteres read from the source TextReader. + /// The starting index of the target array. + /// The maximum number of characters to read from the source TextReader. + /// + /// + /// The number of characters read. The number will be less than or equal to + /// count depending on the data available in the source TextReader. Returns -1 + /// if the end of the stream is reached. + /// + public static int ReadInput( + TextReader sourceTextReader, + byte[] target, + int start, + int count + ) { - internal static readonly int MAX_BITS = 15; - internal static readonly int BL_CODES = 19; - internal static readonly int D_CODES = 30; - internal static readonly int LITERALS = 256; - internal static readonly int LENGTH_CODES = 29; - internal static readonly int L_CODES = (LITERALS + 1 + LENGTH_CODES); - - // Bit length codes must not exceed MAX_BL_BITS bits - internal static readonly int MAX_BL_BITS = 7; - - // repeat previous bit length 3-6 times (2 bits of repeat count) - internal static readonly int REP_3_6 = 16; - - // repeat a zero length 3-10 times (3 bits of repeat count) - internal static readonly int REPZ_3_10 = 17; - - // repeat a zero length 11-138 times (7 bits of repeat count) - internal static readonly int REPZ_11_138 = 18; - } - - internal sealed class StaticTree - { - internal static readonly short[] lengthAndLiteralsTreeCodes = + // Returns 0 bytes if not enough space in target + if (target.Length == 0) { - 12, - 8, - 140, - 8, - 76, - 8, - 204, - 8, - 44, - 8, - 172, - 8, - 108, - 8, - 236, - 8, - 28, - 8, - 156, - 8, - 92, - 8, - 220, - 8, - 60, - 8, - 188, - 8, - 124, - 8, - 252, - 8, - 2, - 8, - 130, - 8, - 66, - 8, - 194, - 8, - 34, - 8, - 162, - 8, - 98, - 8, - 226, - 8, - 18, - 8, - 146, - 8, - 82, - 8, - 210, - 8, - 50, - 8, - 178, - 8, - 114, - 8, - 242, - 8, - 10, - 8, - 138, - 8, - 74, - 8, - 202, - 8, - 42, - 8, - 170, - 8, - 106, - 8, - 234, - 8, - 26, - 8, - 154, - 8, - 90, - 8, - 218, - 8, - 58, - 8, - 186, - 8, - 122, - 8, - 250, - 8, - 6, - 8, - 134, - 8, - 70, - 8, - 198, - 8, - 38, - 8, - 166, - 8, - 102, - 8, - 230, - 8, - 22, - 8, - 150, - 8, - 86, - 8, - 214, - 8, - 54, - 8, - 182, - 8, - 118, - 8, - 246, - 8, - 14, - 8, - 142, - 8, - 78, - 8, - 206, - 8, - 46, - 8, - 174, - 8, - 110, - 8, - 238, - 8, - 30, - 8, - 158, - 8, - 94, - 8, - 222, - 8, - 62, - 8, - 190, - 8, - 126, - 8, - 254, - 8, - 1, - 8, - 129, - 8, - 65, - 8, - 193, - 8, - 33, - 8, - 161, - 8, - 97, - 8, - 225, - 8, - 17, - 8, - 145, - 8, - 81, - 8, - 209, - 8, - 49, - 8, - 177, - 8, - 113, - 8, - 241, - 8, - 9, - 8, - 137, - 8, - 73, - 8, - 201, - 8, - 41, - 8, - 169, - 8, - 105, - 8, - 233, - 8, - 25, - 8, - 153, - 8, - 89, - 8, - 217, - 8, - 57, - 8, - 185, - 8, - 121, - 8, - 249, - 8, - 5, - 8, - 133, - 8, - 69, - 8, - 197, - 8, - 37, - 8, - 165, - 8, - 101, - 8, - 229, - 8, - 21, - 8, - 149, - 8, - 85, - 8, - 213, - 8, - 53, - 8, - 181, - 8, - 117, - 8, - 245, - 8, - 13, - 8, - 141, - 8, - 77, - 8, - 205, - 8, - 45, - 8, - 173, - 8, - 109, - 8, - 237, - 8, - 29, - 8, - 157, - 8, - 93, - 8, - 221, - 8, - 61, - 8, - 189, - 8, - 125, - 8, - 253, - 8, - 19, - 9, - 275, - 9, - 147, - 9, - 403, - 9, - 83, - 9, - 339, - 9, - 211, - 9, - 467, - 9, - 51, - 9, - 307, - 9, - 179, - 9, - 435, - 9, - 115, - 9, - 371, - 9, - 243, - 9, - 499, - 9, - 11, - 9, - 267, - 9, - 139, - 9, - 395, - 9, - 75, - 9, - 331, - 9, - 203, - 9, - 459, - 9, - 43, - 9, - 299, - 9, - 171, - 9, - 427, - 9, - 107, - 9, - 363, - 9, - 235, - 9, - 491, - 9, - 27, - 9, - 283, - 9, - 155, - 9, - 411, - 9, - 91, - 9, - 347, - 9, - 219, - 9, - 475, - 9, - 59, - 9, - 315, - 9, - 187, - 9, - 443, - 9, - 123, - 9, - 379, - 9, - 251, - 9, - 507, - 9, - 7, - 9, - 263, - 9, - 135, - 9, - 391, - 9, - 71, - 9, - 327, - 9, - 199, - 9, - 455, - 9, - 39, - 9, - 295, - 9, - 167, - 9, - 423, - 9, - 103, - 9, - 359, - 9, - 231, - 9, - 487, - 9, - 23, - 9, - 279, - 9, - 151, - 9, - 407, - 9, - 87, - 9, - 343, - 9, - 215, - 9, - 471, - 9, - 55, - 9, - 311, - 9, - 183, - 9, - 439, - 9, - 119, - 9, - 375, - 9, - 247, - 9, - 503, - 9, - 15, - 9, - 271, - 9, - 143, - 9, - 399, - 9, - 79, - 9, - 335, - 9, - 207, - 9, - 463, - 9, - 47, - 9, - 303, - 9, - 175, - 9, - 431, - 9, - 111, - 9, - 367, - 9, - 239, - 9, - 495, - 9, - 31, - 9, - 287, - 9, - 159, - 9, - 415, - 9, - 95, - 9, - 351, - 9, - 223, - 9, - 479, - 9, - 63, - 9, - 319, - 9, - 191, - 9, - 447, - 9, - 127, - 9, - 383, - 9, - 255, - 9, - 511, - 9, - 0, - 7, - 64, - 7, - 32, - 7, - 96, - 7, - 16, - 7, - 80, - 7, - 48, - 7, - 112, - 7, - 8, - 7, - 72, - 7, - 40, - 7, - 104, - 7, - 24, - 7, - 88, - 7, - 56, - 7, - 120, - 7, - 4, - 7, - 68, - 7, - 36, - 7, - 100, - 7, - 20, - 7, - 84, - 7, - 52, - 7, - 116, - 7, - 3, - 8, - 131, - 8, - 67, - 8, - 195, - 8, - 35, - 8, - 163, - 8, - 99, - 8, - 227, - 8 - }; - - internal static readonly short[] distTreeCodes = - { - 0, - 5, - 16, - 5, - 8, - 5, - 24, - 5, - 4, - 5, - 20, - 5, - 12, - 5, - 28, - 5, - 2, - 5, - 18, - 5, - 10, - 5, - 26, - 5, - 6, - 5, - 22, - 5, - 14, - 5, - 30, - 5, - 1, - 5, - 17, - 5, - 9, - 5, - 25, - 5, - 5, - 5, - 21, - 5, - 13, - 5, - 29, - 5, - 3, - 5, - 19, - 5, - 11, - 5, - 27, - 5, - 7, - 5, - 23, - 5 - }; - - // extra bits for each bit length code - internal static readonly int[] extra_blbits = - { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 2, - 3, - 7 - }; - - internal static readonly StaticTree Literals; - internal static readonly StaticTree Distances; - internal static readonly StaticTree BitLengths; - - internal short[]? treeCodes; // static tree or null - internal int[]? extraBits; // extra bits for each code or null - internal int extraBase; // base index for extra_bits - internal int elems; // max number of elements in the tree - internal int maxLength; // max bit length for the codes - - private StaticTree( - short[]? treeCodes, - int[]? extraBits, - int extraBase, - int elems, - int maxLength - ) - { - this.treeCodes = treeCodes; - this.extraBits = extraBits; - this.extraBase = extraBase; - this.elems = elems; - this.maxLength = maxLength; + return 0; } - static StaticTree() + var charArray = new char[target.Length]; + var bytesRead = sourceTextReader.Read(charArray, start, count); + + // Returns -1 if EOF + if (bytesRead == 0) { - Literals = new StaticTree( - lengthAndLiteralsTreeCodes, - DeflateManager.ExtraLengthBits, - InternalConstants.LITERALS + 1, - InternalConstants.L_CODES, - InternalConstants.MAX_BITS - ); - Distances = new StaticTree( - distTreeCodes, - DeflateManager.ExtraDistanceBits, - 0, - InternalConstants.D_CODES, - InternalConstants.MAX_BITS - ); - BitLengths = new StaticTree( - null, - extra_blbits, - 0, - InternalConstants.BL_CODES, - InternalConstants.MAX_BL_BITS - ); + return -1; } + + for (var index = start; index < start + bytesRead; index++) + { + target[index] = (byte)charArray[index]; + } + + return bytesRead; + } +} + +internal static class InternalConstants +{ + internal static readonly int MAX_BITS = 15; + internal static readonly int BL_CODES = 19; + internal static readonly int D_CODES = 30; + internal static readonly int LITERALS = 256; + internal static readonly int LENGTH_CODES = 29; + internal static readonly int L_CODES = (LITERALS + 1 + LENGTH_CODES); + + // Bit length codes must not exceed MAX_BL_BITS bits + internal static readonly int MAX_BL_BITS = 7; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + internal static readonly int REP_3_6 = 16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + internal static readonly int REPZ_3_10 = 17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + internal static readonly int REPZ_11_138 = 18; +} + +internal sealed class StaticTree +{ + internal static readonly short[] lengthAndLiteralsTreeCodes = + { + 12, + 8, + 140, + 8, + 76, + 8, + 204, + 8, + 44, + 8, + 172, + 8, + 108, + 8, + 236, + 8, + 28, + 8, + 156, + 8, + 92, + 8, + 220, + 8, + 60, + 8, + 188, + 8, + 124, + 8, + 252, + 8, + 2, + 8, + 130, + 8, + 66, + 8, + 194, + 8, + 34, + 8, + 162, + 8, + 98, + 8, + 226, + 8, + 18, + 8, + 146, + 8, + 82, + 8, + 210, + 8, + 50, + 8, + 178, + 8, + 114, + 8, + 242, + 8, + 10, + 8, + 138, + 8, + 74, + 8, + 202, + 8, + 42, + 8, + 170, + 8, + 106, + 8, + 234, + 8, + 26, + 8, + 154, + 8, + 90, + 8, + 218, + 8, + 58, + 8, + 186, + 8, + 122, + 8, + 250, + 8, + 6, + 8, + 134, + 8, + 70, + 8, + 198, + 8, + 38, + 8, + 166, + 8, + 102, + 8, + 230, + 8, + 22, + 8, + 150, + 8, + 86, + 8, + 214, + 8, + 54, + 8, + 182, + 8, + 118, + 8, + 246, + 8, + 14, + 8, + 142, + 8, + 78, + 8, + 206, + 8, + 46, + 8, + 174, + 8, + 110, + 8, + 238, + 8, + 30, + 8, + 158, + 8, + 94, + 8, + 222, + 8, + 62, + 8, + 190, + 8, + 126, + 8, + 254, + 8, + 1, + 8, + 129, + 8, + 65, + 8, + 193, + 8, + 33, + 8, + 161, + 8, + 97, + 8, + 225, + 8, + 17, + 8, + 145, + 8, + 81, + 8, + 209, + 8, + 49, + 8, + 177, + 8, + 113, + 8, + 241, + 8, + 9, + 8, + 137, + 8, + 73, + 8, + 201, + 8, + 41, + 8, + 169, + 8, + 105, + 8, + 233, + 8, + 25, + 8, + 153, + 8, + 89, + 8, + 217, + 8, + 57, + 8, + 185, + 8, + 121, + 8, + 249, + 8, + 5, + 8, + 133, + 8, + 69, + 8, + 197, + 8, + 37, + 8, + 165, + 8, + 101, + 8, + 229, + 8, + 21, + 8, + 149, + 8, + 85, + 8, + 213, + 8, + 53, + 8, + 181, + 8, + 117, + 8, + 245, + 8, + 13, + 8, + 141, + 8, + 77, + 8, + 205, + 8, + 45, + 8, + 173, + 8, + 109, + 8, + 237, + 8, + 29, + 8, + 157, + 8, + 93, + 8, + 221, + 8, + 61, + 8, + 189, + 8, + 125, + 8, + 253, + 8, + 19, + 9, + 275, + 9, + 147, + 9, + 403, + 9, + 83, + 9, + 339, + 9, + 211, + 9, + 467, + 9, + 51, + 9, + 307, + 9, + 179, + 9, + 435, + 9, + 115, + 9, + 371, + 9, + 243, + 9, + 499, + 9, + 11, + 9, + 267, + 9, + 139, + 9, + 395, + 9, + 75, + 9, + 331, + 9, + 203, + 9, + 459, + 9, + 43, + 9, + 299, + 9, + 171, + 9, + 427, + 9, + 107, + 9, + 363, + 9, + 235, + 9, + 491, + 9, + 27, + 9, + 283, + 9, + 155, + 9, + 411, + 9, + 91, + 9, + 347, + 9, + 219, + 9, + 475, + 9, + 59, + 9, + 315, + 9, + 187, + 9, + 443, + 9, + 123, + 9, + 379, + 9, + 251, + 9, + 507, + 9, + 7, + 9, + 263, + 9, + 135, + 9, + 391, + 9, + 71, + 9, + 327, + 9, + 199, + 9, + 455, + 9, + 39, + 9, + 295, + 9, + 167, + 9, + 423, + 9, + 103, + 9, + 359, + 9, + 231, + 9, + 487, + 9, + 23, + 9, + 279, + 9, + 151, + 9, + 407, + 9, + 87, + 9, + 343, + 9, + 215, + 9, + 471, + 9, + 55, + 9, + 311, + 9, + 183, + 9, + 439, + 9, + 119, + 9, + 375, + 9, + 247, + 9, + 503, + 9, + 15, + 9, + 271, + 9, + 143, + 9, + 399, + 9, + 79, + 9, + 335, + 9, + 207, + 9, + 463, + 9, + 47, + 9, + 303, + 9, + 175, + 9, + 431, + 9, + 111, + 9, + 367, + 9, + 239, + 9, + 495, + 9, + 31, + 9, + 287, + 9, + 159, + 9, + 415, + 9, + 95, + 9, + 351, + 9, + 223, + 9, + 479, + 9, + 63, + 9, + 319, + 9, + 191, + 9, + 447, + 9, + 127, + 9, + 383, + 9, + 255, + 9, + 511, + 9, + 0, + 7, + 64, + 7, + 32, + 7, + 96, + 7, + 16, + 7, + 80, + 7, + 48, + 7, + 112, + 7, + 8, + 7, + 72, + 7, + 40, + 7, + 104, + 7, + 24, + 7, + 88, + 7, + 56, + 7, + 120, + 7, + 4, + 7, + 68, + 7, + 36, + 7, + 100, + 7, + 20, + 7, + 84, + 7, + 52, + 7, + 116, + 7, + 3, + 8, + 131, + 8, + 67, + 8, + 195, + 8, + 35, + 8, + 163, + 8, + 99, + 8, + 227, + 8 + }; + + internal static readonly short[] distTreeCodes = + { + 0, + 5, + 16, + 5, + 8, + 5, + 24, + 5, + 4, + 5, + 20, + 5, + 12, + 5, + 28, + 5, + 2, + 5, + 18, + 5, + 10, + 5, + 26, + 5, + 6, + 5, + 22, + 5, + 14, + 5, + 30, + 5, + 1, + 5, + 17, + 5, + 9, + 5, + 25, + 5, + 5, + 5, + 21, + 5, + 13, + 5, + 29, + 5, + 3, + 5, + 19, + 5, + 11, + 5, + 27, + 5, + 7, + 5, + 23, + 5 + }; + + // extra bits for each bit length code + internal static readonly int[] extra_blbits = + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 3, + 7 + }; + + internal static readonly StaticTree Literals; + internal static readonly StaticTree Distances; + internal static readonly StaticTree BitLengths; + + internal short[]? treeCodes; // static tree or null + internal int[]? extraBits; // extra bits for each code or null + internal int extraBase; // base index for extra_bits + internal int elems; // max number of elements in the tree + internal int maxLength; // max bit length for the codes + + private StaticTree( + short[]? treeCodes, + int[]? extraBits, + int extraBase, + int elems, + int maxLength + ) + { + this.treeCodes = treeCodes; + this.extraBits = extraBits; + this.extraBase = extraBase; + this.elems = elems; + this.maxLength = maxLength; + } + + static StaticTree() + { + Literals = new StaticTree( + lengthAndLiteralsTreeCodes, + DeflateManager.ExtraLengthBits, + InternalConstants.LITERALS + 1, + InternalConstants.L_CODES, + InternalConstants.MAX_BITS + ); + Distances = new StaticTree( + distTreeCodes, + DeflateManager.ExtraDistanceBits, + 0, + InternalConstants.D_CODES, + InternalConstants.MAX_BITS + ); + BitLengths = new StaticTree( + null, + extra_blbits, + 0, + InternalConstants.BL_CODES, + InternalConstants.MAX_BL_BITS + ); } } diff --git a/src/SharpCompress/Compressors/Deflate/ZlibCodec.cs b/src/SharpCompress/Compressors/Deflate/ZlibCodec.cs index 4c8e0b78..c6b68dad 100644 --- a/src/SharpCompress/Compressors/Deflate/ZlibCodec.cs +++ b/src/SharpCompress/Compressors/Deflate/ZlibCodec.cs @@ -67,691 +67,690 @@ using System; -namespace SharpCompress.Compressors.Deflate +namespace SharpCompress.Compressors.Deflate; + +/// +/// Encoder and Decoder for ZLIB and DEFLATE (IETF RFC1950 and RFC1951). +/// +/// +/// +/// This class compresses and decompresses data according to the Deflate algorithm +/// and optionally, the ZLIB format, as documented in RFC 1950 - ZLIB and RFC 1951 - DEFLATE. +/// +internal sealed class ZlibCodec { /// - /// Encoder and Decoder for ZLIB and DEFLATE (IETF RFC1950 and RFC1951). + /// The buffer from which data is taken. + /// + public byte[] InputBuffer; + + /// + /// An index into the InputBuffer array, indicating where to start reading. + /// + public int NextIn; + + /// + /// The number of bytes available in the InputBuffer, starting at NextIn. + /// + /// + /// Generally you should set this to InputBuffer.Length before the first Inflate() or Deflate() call. + /// The class will update this number as calls to Inflate/Deflate are made. + /// + public int AvailableBytesIn; + + /// + /// Total number of bytes read so far, through all calls to Inflate()/Deflate(). + /// + public long TotalBytesIn; + + /// + /// Buffer to store output data. + /// + public byte[] OutputBuffer; + + /// + /// An index into the OutputBuffer array, indicating where to start writing. + /// + public int NextOut; + + /// + /// The number of bytes available in the OutputBuffer, starting at NextOut. + /// + /// + /// Generally you should set this to OutputBuffer.Length before the first Inflate() or Deflate() call. + /// The class will update this number as calls to Inflate/Deflate are made. + /// + public int AvailableBytesOut; + + /// + /// Total number of bytes written to the output so far, through all calls to Inflate()/Deflate(). + /// + public long TotalBytesOut; + + /// + /// used for diagnostics, when something goes wrong! + /// + public string Message; + + internal DeflateManager dstate; + internal InflateManager istate; + + internal uint _adler32; + + /// + /// The compression level to use in this codec. Useful only in compression mode. + /// + public CompressionLevel CompressLevel = CompressionLevel.Default; + + /// + /// The number of Window Bits to use. + /// + /// + /// This gauges the size of the sliding window, and hence the + /// compression effectiveness as well as memory consumption. It's best to just leave this + /// setting alone if you don't know what it is. The maximum value is 15 bits, which implies + /// a 32k window. + /// + public int WindowBits = ZlibConstants.WindowBitsDefault; + + /// + /// The compression strategy to use. + /// + /// + /// This is only effective in compression. The theory offered by ZLIB is that different + /// strategies could potentially produce significant differences in compression behavior + /// for different data sets. Unfortunately I don't have any good recommendations for how + /// to set it differently. When I tested changing the strategy I got minimally different + /// compression performance. It's best to leave this property alone if you don't have a + /// good feel for it. Or, you may want to produce a test harness that runs through the + /// different strategy options and evaluates them on different file types. If you do that, + /// let me know your results. + /// + public CompressionStrategy Strategy = CompressionStrategy.Default; + + /// + /// The Adler32 checksum on the data transferred through the codec so far. You probably don't need to look at this. + /// + public int Adler32 => (int)_adler32; + + /// + /// Create a ZlibCodec. + /// + /// + /// If you use this default constructor, you will later have to explicitly call + /// InitializeInflate() or InitializeDeflate() before using the ZlibCodec to compress + /// or decompress. + /// + public ZlibCodec() { } + + /// + /// Create a ZlibCodec that either compresses or decompresses. + /// + /// + /// Indicates whether the codec should compress (deflate) or decompress (inflate). + /// + public ZlibCodec(CompressionMode mode) + { + if (mode == CompressionMode.Compress) + { + var rc = InitializeDeflate(); + if (rc != ZlibConstants.Z_OK) + { + throw new ZlibException("Cannot initialize for deflate."); + } + } + else if (mode == CompressionMode.Decompress) + { + var rc = InitializeInflate(); + if (rc != ZlibConstants.Z_OK) + { + throw new ZlibException("Cannot initialize for inflate."); + } + } + else + { + throw new ZlibException("Invalid ZlibStreamFlavor."); + } + } + + /// + /// Initialize the inflation state. + /// + /// + /// It is not necessary to call this before using the ZlibCodec to inflate data; + /// It is implicitly called when you call the constructor. + /// + /// Z_OK if everything goes well. + public int InitializeInflate() + { + return InitializeInflate(WindowBits); + } + + /// + /// Initialize the inflation state with an explicit flag to + /// govern the handling of RFC1950 header bytes. /// /// /// - /// This class compresses and decompresses data according to the Deflate algorithm - /// and optionally, the ZLIB format, as documented in RFC 1950 - ZLIB and RFC 1951 - DEFLATE. + /// By default, the ZLIB header defined in RFC 1950 is expected. If + /// you want to read a zlib stream you should specify true for + /// expectRfc1950Header. If you have a deflate stream, you will want to specify + /// false. It is only necessary to invoke this initializer explicitly if you + /// want to specify false. /// - internal sealed class ZlibCodec + /// + /// whether to expect an RFC1950 header byte + /// pair when reading the stream of data to be inflated. + /// + /// Z_OK if everything goes well. + public int InitializeInflate(bool expectRfc1950Header) { - /// - /// The buffer from which data is taken. - /// - public byte[] InputBuffer; + return InitializeInflate(WindowBits, expectRfc1950Header); + } - /// - /// An index into the InputBuffer array, indicating where to start reading. - /// - public int NextIn; + /// + /// Initialize the ZlibCodec for inflation, with the specified number of window bits. + /// + /// The number of window bits to use. If you need to ask what that is, + /// then you shouldn't be calling this initializer. + /// Z_OK if all goes well. + public int InitializeInflate(int windowBits) + { + WindowBits = windowBits; + return InitializeInflate(windowBits, true); + } - /// - /// The number of bytes available in the InputBuffer, starting at NextIn. - /// - /// - /// Generally you should set this to InputBuffer.Length before the first Inflate() or Deflate() call. - /// The class will update this number as calls to Inflate/Deflate are made. - /// - public int AvailableBytesIn; - - /// - /// Total number of bytes read so far, through all calls to Inflate()/Deflate(). - /// - public long TotalBytesIn; - - /// - /// Buffer to store output data. - /// - public byte[] OutputBuffer; - - /// - /// An index into the OutputBuffer array, indicating where to start writing. - /// - public int NextOut; - - /// - /// The number of bytes available in the OutputBuffer, starting at NextOut. - /// - /// - /// Generally you should set this to OutputBuffer.Length before the first Inflate() or Deflate() call. - /// The class will update this number as calls to Inflate/Deflate are made. - /// - public int AvailableBytesOut; - - /// - /// Total number of bytes written to the output so far, through all calls to Inflate()/Deflate(). - /// - public long TotalBytesOut; - - /// - /// used for diagnostics, when something goes wrong! - /// - public string Message; - - internal DeflateManager dstate; - internal InflateManager istate; - - internal uint _adler32; - - /// - /// The compression level to use in this codec. Useful only in compression mode. - /// - public CompressionLevel CompressLevel = CompressionLevel.Default; - - /// - /// The number of Window Bits to use. - /// - /// - /// This gauges the size of the sliding window, and hence the - /// compression effectiveness as well as memory consumption. It's best to just leave this - /// setting alone if you don't know what it is. The maximum value is 15 bits, which implies - /// a 32k window. - /// - public int WindowBits = ZlibConstants.WindowBitsDefault; - - /// - /// The compression strategy to use. - /// - /// - /// This is only effective in compression. The theory offered by ZLIB is that different - /// strategies could potentially produce significant differences in compression behavior - /// for different data sets. Unfortunately I don't have any good recommendations for how - /// to set it differently. When I tested changing the strategy I got minimally different - /// compression performance. It's best to leave this property alone if you don't have a - /// good feel for it. Or, you may want to produce a test harness that runs through the - /// different strategy options and evaluates them on different file types. If you do that, - /// let me know your results. - /// - public CompressionStrategy Strategy = CompressionStrategy.Default; - - /// - /// The Adler32 checksum on the data transferred through the codec so far. You probably don't need to look at this. - /// - public int Adler32 => (int)_adler32; - - /// - /// Create a ZlibCodec. - /// - /// - /// If you use this default constructor, you will later have to explicitly call - /// InitializeInflate() or InitializeDeflate() before using the ZlibCodec to compress - /// or decompress. - /// - public ZlibCodec() { } - - /// - /// Create a ZlibCodec that either compresses or decompresses. - /// - /// - /// Indicates whether the codec should compress (deflate) or decompress (inflate). - /// - public ZlibCodec(CompressionMode mode) + /// + /// Initialize the inflation state with an explicit flag to govern the handling of + /// RFC1950 header bytes. + /// + /// + /// + /// If you want to read a zlib stream you should specify true for + /// expectRfc1950Header. In this case, the library will expect to find a ZLIB + /// header, as defined in RFC + /// 1950, in the compressed stream. If you will be reading a DEFLATE or + /// GZIP stream, which does not have such a header, you will want to specify + /// false. + /// + /// + /// whether to expect an RFC1950 header byte pair when reading + /// the stream of data to be inflated. + /// The number of window bits to use. If you need to ask what that is, + /// then you shouldn't be calling this initializer. + /// Z_OK if everything goes well. + public int InitializeInflate(int windowBits, bool expectRfc1950Header) + { + WindowBits = windowBits; + if (dstate != null) { - if (mode == CompressionMode.Compress) - { - int rc = InitializeDeflate(); - if (rc != ZlibConstants.Z_OK) - { - throw new ZlibException("Cannot initialize for deflate."); - } - } - else if (mode == CompressionMode.Decompress) - { - int rc = InitializeInflate(); - if (rc != ZlibConstants.Z_OK) - { - throw new ZlibException("Cannot initialize for inflate."); - } - } - else - { - throw new ZlibException("Invalid ZlibStreamFlavor."); - } + throw new ZlibException( + "You may not call InitializeInflate() after calling InitializeDeflate()." + ); + } + istate = new InflateManager(expectRfc1950Header); + return istate.Initialize(this, windowBits); + } + + /// + /// Inflate the data in the InputBuffer, placing the result in the OutputBuffer. + /// + /// + /// You must have set InputBuffer and OutputBuffer, NextIn and NextOut, and AvailableBytesIn and + /// AvailableBytesOut before calling this method. + /// + /// + /// + /// private void InflateBuffer() + /// { + /// int bufferSize = 1024; + /// byte[] buffer = new byte[bufferSize]; + /// ZlibCodec decompressor = new ZlibCodec(); + /// + /// Console.WriteLine("\n============================================"); + /// Console.WriteLine("Size of Buffer to Inflate: {0} bytes.", CompressedBytes.Length); + /// MemoryStream ms = new MemoryStream(DecompressedBytes); + /// + /// int rc = decompressor.InitializeInflate(); + /// + /// decompressor.InputBuffer = CompressedBytes; + /// decompressor.NextIn = 0; + /// decompressor.AvailableBytesIn = CompressedBytes.Length; + /// + /// decompressor.OutputBuffer = buffer; + /// + /// // pass 1: inflate + /// do + /// { + /// decompressor.NextOut = 0; + /// decompressor.AvailableBytesOut = buffer.Length; + /// rc = decompressor.Inflate(FlushType.None); + /// + /// if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END) + /// throw new Exception("inflating: " + decompressor.Message); + /// + /// ms.Write(decompressor.OutputBuffer, 0, buffer.Length - decompressor.AvailableBytesOut); + /// } + /// while (decompressor.AvailableBytesIn > 0 || decompressor.AvailableBytesOut == 0); + /// + /// // pass 2: finish and flush + /// do + /// { + /// decompressor.NextOut = 0; + /// decompressor.AvailableBytesOut = buffer.Length; + /// rc = decompressor.Inflate(FlushType.Finish); + /// + /// if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK) + /// throw new Exception("inflating: " + decompressor.Message); + /// + /// if (buffer.Length - decompressor.AvailableBytesOut > 0) + /// ms.Write(buffer, 0, buffer.Length - decompressor.AvailableBytesOut); + /// } + /// while (decompressor.AvailableBytesIn > 0 || decompressor.AvailableBytesOut == 0); + /// + /// decompressor.EndInflate(); + /// } + /// + /// + /// + /// The flush to use when inflating. + /// Z_OK if everything goes well. + public int Inflate(FlushType flush) + { + if (istate is null) + { + throw new ZlibException("No Inflate State!"); + } + return istate.Inflate(flush); + } + + /// + /// Ends an inflation session. + /// + /// + /// Call this after successively calling Inflate(). This will cause all buffers to be flushed. + /// After calling this you cannot call Inflate() without a intervening call to one of the + /// InitializeInflate() overloads. + /// + /// Z_OK if everything goes well. + public int EndInflate() + { + if (istate is null) + { + throw new ZlibException("No Inflate State!"); + } + var ret = istate.End(); + istate = null; + return ret; + } + + /// + /// I don't know what this does! + /// + /// Z_OK if everything goes well. + public int SyncInflate() + { + if (istate is null) + { + throw new ZlibException("No Inflate State!"); + } + return istate.Sync(); + } + + /// + /// Initialize the ZlibCodec for deflation operation. + /// + /// + /// The codec will use the MAX window bits and the default level of compression. + /// + /// + /// + /// int bufferSize = 40000; + /// byte[] CompressedBytes = new byte[bufferSize]; + /// byte[] DecompressedBytes = new byte[bufferSize]; + /// + /// ZlibCodec compressor = new ZlibCodec(); + /// + /// compressor.InitializeDeflate(CompressionLevel.Default); + /// + /// compressor.InputBuffer = System.Text.ASCIIEncoding.ASCII.GetBytes(TextToCompress); + /// compressor.NextIn = 0; + /// compressor.AvailableBytesIn = compressor.InputBuffer.Length; + /// + /// compressor.OutputBuffer = CompressedBytes; + /// compressor.NextOut = 0; + /// compressor.AvailableBytesOut = CompressedBytes.Length; + /// + /// while (compressor.TotalBytesIn != TextToCompress.Length && compressor.TotalBytesOut < bufferSize) + /// { + /// compressor.Deflate(FlushType.None); + /// } + /// + /// while (true) + /// { + /// int rc= compressor.Deflate(FlushType.Finish); + /// if (rc == ZlibConstants.Z_STREAM_END) break; + /// } + /// + /// compressor.EndDeflate(); + /// + /// + /// + /// Z_OK if all goes well. You generally don't need to check the return code. + public int InitializeDeflate() + { + return _InternalInitializeDeflate(true); + } + + /// + /// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel. + /// + /// + /// The codec will use the maximum window bits (15) and the specified + /// CompressionLevel. It will emit a ZLIB stream as it compresses. + /// + /// The compression level for the codec. + /// Z_OK if all goes well. + public int InitializeDeflate(CompressionLevel level) + { + CompressLevel = level; + return _InternalInitializeDeflate(true); + } + + /// + /// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel, + /// and the explicit flag governing whether to emit an RFC1950 header byte pair. + /// + /// + /// The codec will use the maximum window bits (15) and the specified CompressionLevel. + /// If you want to generate a zlib stream, you should specify true for + /// wantRfc1950Header. In this case, the library will emit a ZLIB + /// header, as defined in RFC + /// 1950, in the compressed stream. + /// + /// The compression level for the codec. + /// whether to emit an initial RFC1950 byte pair in the compressed stream. + /// Z_OK if all goes well. + public int InitializeDeflate(CompressionLevel level, bool wantRfc1950Header) + { + CompressLevel = level; + return _InternalInitializeDeflate(wantRfc1950Header); + } + + /// + /// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel, + /// and the specified number of window bits. + /// + /// + /// The codec will use the specified number of window bits and the specified CompressionLevel. + /// + /// The compression level for the codec. + /// the number of window bits to use. If you don't know what this means, don't use this method. + /// Z_OK if all goes well. + public int InitializeDeflate(CompressionLevel level, int bits) + { + CompressLevel = level; + WindowBits = bits; + return _InternalInitializeDeflate(true); + } + + /// + /// Initialize the ZlibCodec for deflation operation, using the specified + /// CompressionLevel, the specified number of window bits, and the explicit flag + /// governing whether to emit an RFC1950 header byte pair. + /// + /// + /// The compression level for the codec. + /// whether to emit an initial RFC1950 byte pair in the compressed stream. + /// the number of window bits to use. If you don't know what this means, don't use this method. + /// Z_OK if all goes well. + public int InitializeDeflate(CompressionLevel level, int bits, bool wantRfc1950Header) + { + CompressLevel = level; + WindowBits = bits; + return _InternalInitializeDeflate(wantRfc1950Header); + } + + private int _InternalInitializeDeflate(bool wantRfc1950Header) + { + if (istate != null) + { + throw new ZlibException( + "You may not call InitializeDeflate() after calling InitializeInflate()." + ); + } + dstate = new DeflateManager(); + dstate.WantRfc1950HeaderBytes = wantRfc1950Header; + + return dstate.Initialize(this, CompressLevel, WindowBits, Strategy); + } + + /// + /// Deflate one batch of data. + /// + /// + /// You must have set InputBuffer and OutputBuffer before calling this method. + /// + /// + /// + /// private void DeflateBuffer(CompressionLevel level) + /// { + /// int bufferSize = 1024; + /// byte[] buffer = new byte[bufferSize]; + /// ZlibCodec compressor = new ZlibCodec(); + /// + /// Console.WriteLine("\n============================================"); + /// Console.WriteLine("Size of Buffer to Deflate: {0} bytes.", UncompressedBytes.Length); + /// MemoryStream ms = new MemoryStream(); + /// + /// int rc = compressor.InitializeDeflate(level); + /// + /// compressor.InputBuffer = UncompressedBytes; + /// compressor.NextIn = 0; + /// compressor.AvailableBytesIn = UncompressedBytes.Length; + /// + /// compressor.OutputBuffer = buffer; + /// + /// // pass 1: deflate + /// do + /// { + /// compressor.NextOut = 0; + /// compressor.AvailableBytesOut = buffer.Length; + /// rc = compressor.Deflate(FlushType.None); + /// + /// if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END) + /// throw new Exception("deflating: " + compressor.Message); + /// + /// ms.Write(compressor.OutputBuffer, 0, buffer.Length - compressor.AvailableBytesOut); + /// } + /// while (compressor.AvailableBytesIn > 0 || compressor.AvailableBytesOut == 0); + /// + /// // pass 2: finish and flush + /// do + /// { + /// compressor.NextOut = 0; + /// compressor.AvailableBytesOut = buffer.Length; + /// rc = compressor.Deflate(FlushType.Finish); + /// + /// if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK) + /// throw new Exception("deflating: " + compressor.Message); + /// + /// if (buffer.Length - compressor.AvailableBytesOut > 0) + /// ms.Write(buffer, 0, buffer.Length - compressor.AvailableBytesOut); + /// } + /// while (compressor.AvailableBytesIn > 0 || compressor.AvailableBytesOut == 0); + /// + /// compressor.EndDeflate(); + /// + /// ms.Seek(0, SeekOrigin.Begin); + /// CompressedBytes = new byte[compressor.TotalBytesOut]; + /// ms.Read(CompressedBytes, 0, CompressedBytes.Length); + /// } + /// + /// + /// whether to flush all data as you deflate. Generally you will want to + /// use Z_NO_FLUSH here, in a series of calls to Deflate(), and then call EndDeflate() to + /// flush everything. + /// + /// Z_OK if all goes well. + public int Deflate(FlushType flush) + { + if (dstate is null) + { + throw new ZlibException("No Deflate State!"); + } + return dstate.Deflate(flush); + } + + /// + /// End a deflation session. + /// + /// + /// Call this after making a series of one or more calls to Deflate(). All buffers are flushed. + /// + /// Z_OK if all goes well. + public int EndDeflate() + { + if (dstate is null) + { + throw new ZlibException("No Deflate State!"); } - /// - /// Initialize the inflation state. - /// - /// - /// It is not necessary to call this before using the ZlibCodec to inflate data; - /// It is implicitly called when you call the constructor. - /// - /// Z_OK if everything goes well. - public int InitializeInflate() + // TODO: dinoch Tue, 03 Nov 2009 15:39 (test this) + //int ret = dstate.End(); + dstate = null; + return ZlibConstants.Z_OK; //ret; + } + + /// + /// Reset a codec for another deflation session. + /// + /// + /// Call this to reset the deflation state. For example if a thread is deflating + /// non-consecutive blocks, you can call Reset() after the Deflate(Sync) of the first + /// block and before the next Deflate(None) of the second block. + /// + /// Z_OK if all goes well. + public void ResetDeflate() + { + if (dstate is null) { - return InitializeInflate(WindowBits); + throw new ZlibException("No Deflate State!"); + } + dstate.Reset(); + } + + /// + /// Set the CompressionStrategy and CompressionLevel for a deflation session. + /// + /// the level of compression to use. + /// the strategy to use for compression. + /// Z_OK if all goes well. + public int SetDeflateParams(CompressionLevel level, CompressionStrategy strategy) + { + if (dstate is null) + { + throw new ZlibException("No Deflate State!"); + } + return dstate.SetParams(level, strategy); + } + + /// + /// Set the dictionary to be used for either Inflation or Deflation. + /// + /// The dictionary bytes to use. + /// Z_OK if all goes well. + public int SetDictionary(byte[] dictionary) + { + if (istate != null) + { + return istate.SetDictionary(dictionary); } - /// - /// Initialize the inflation state with an explicit flag to - /// govern the handling of RFC1950 header bytes. - /// - /// - /// - /// By default, the ZLIB header defined in RFC 1950 is expected. If - /// you want to read a zlib stream you should specify true for - /// expectRfc1950Header. If you have a deflate stream, you will want to specify - /// false. It is only necessary to invoke this initializer explicitly if you - /// want to specify false. - /// - /// - /// whether to expect an RFC1950 header byte - /// pair when reading the stream of data to be inflated. - /// - /// Z_OK if everything goes well. - public int InitializeInflate(bool expectRfc1950Header) + if (dstate != null) { - return InitializeInflate(WindowBits, expectRfc1950Header); + return dstate.SetDictionary(dictionary); } - /// - /// Initialize the ZlibCodec for inflation, with the specified number of window bits. - /// - /// The number of window bits to use. If you need to ask what that is, - /// then you shouldn't be calling this initializer. - /// Z_OK if all goes well. - public int InitializeInflate(int windowBits) + throw new ZlibException("No Inflate or Deflate state!"); + } + + // Flush as much pending output as possible. All deflate() output goes + // through this function so some applications may wish to modify it + // to avoid allocating a large strm->next_out buffer and copying into it. + // (See also read_buf()). + internal void flush_pending() + { + var len = dstate.pendingCount; + + if (len > AvailableBytesOut) { - WindowBits = windowBits; - return InitializeInflate(windowBits, true); + len = AvailableBytesOut; + } + if (len == 0) + { + return; } - /// - /// Initialize the inflation state with an explicit flag to govern the handling of - /// RFC1950 header bytes. - /// - /// - /// - /// If you want to read a zlib stream you should specify true for - /// expectRfc1950Header. In this case, the library will expect to find a ZLIB - /// header, as defined in RFC - /// 1950, in the compressed stream. If you will be reading a DEFLATE or - /// GZIP stream, which does not have such a header, you will want to specify - /// false. - /// - /// - /// whether to expect an RFC1950 header byte pair when reading - /// the stream of data to be inflated. - /// The number of window bits to use. If you need to ask what that is, - /// then you shouldn't be calling this initializer. - /// Z_OK if everything goes well. - public int InitializeInflate(int windowBits, bool expectRfc1950Header) + if ( + dstate.pending.Length <= dstate.nextPending + || OutputBuffer.Length <= NextOut + || dstate.pending.Length < (dstate.nextPending + len) + || OutputBuffer.Length < (NextOut + len) + ) { - WindowBits = windowBits; - if (dstate != null) - { - throw new ZlibException( - "You may not call InitializeInflate() after calling InitializeDeflate()." - ); - } - istate = new InflateManager(expectRfc1950Header); - return istate.Initialize(this, windowBits); + throw new ZlibException( + string.Format( + "Invalid State. (pending.Length={0}, pendingCount={1})", + dstate.pending.Length, + dstate.pendingCount + ) + ); } - /// - /// Inflate the data in the InputBuffer, placing the result in the OutputBuffer. - /// - /// - /// You must have set InputBuffer and OutputBuffer, NextIn and NextOut, and AvailableBytesIn and - /// AvailableBytesOut before calling this method. - /// - /// - /// - /// private void InflateBuffer() - /// { - /// int bufferSize = 1024; - /// byte[] buffer = new byte[bufferSize]; - /// ZlibCodec decompressor = new ZlibCodec(); - /// - /// Console.WriteLine("\n============================================"); - /// Console.WriteLine("Size of Buffer to Inflate: {0} bytes.", CompressedBytes.Length); - /// MemoryStream ms = new MemoryStream(DecompressedBytes); - /// - /// int rc = decompressor.InitializeInflate(); - /// - /// decompressor.InputBuffer = CompressedBytes; - /// decompressor.NextIn = 0; - /// decompressor.AvailableBytesIn = CompressedBytes.Length; - /// - /// decompressor.OutputBuffer = buffer; - /// - /// // pass 1: inflate - /// do - /// { - /// decompressor.NextOut = 0; - /// decompressor.AvailableBytesOut = buffer.Length; - /// rc = decompressor.Inflate(FlushType.None); - /// - /// if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END) - /// throw new Exception("inflating: " + decompressor.Message); - /// - /// ms.Write(decompressor.OutputBuffer, 0, buffer.Length - decompressor.AvailableBytesOut); - /// } - /// while (decompressor.AvailableBytesIn > 0 || decompressor.AvailableBytesOut == 0); - /// - /// // pass 2: finish and flush - /// do - /// { - /// decompressor.NextOut = 0; - /// decompressor.AvailableBytesOut = buffer.Length; - /// rc = decompressor.Inflate(FlushType.Finish); - /// - /// if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK) - /// throw new Exception("inflating: " + decompressor.Message); - /// - /// if (buffer.Length - decompressor.AvailableBytesOut > 0) - /// ms.Write(buffer, 0, buffer.Length - decompressor.AvailableBytesOut); - /// } - /// while (decompressor.AvailableBytesIn > 0 || decompressor.AvailableBytesOut == 0); - /// - /// decompressor.EndInflate(); - /// } - /// - /// - /// - /// The flush to use when inflating. - /// Z_OK if everything goes well. - public int Inflate(FlushType flush) + Array.Copy(dstate.pending, dstate.nextPending, OutputBuffer, NextOut, len); + + NextOut += len; + dstate.nextPending += len; + TotalBytesOut += len; + AvailableBytesOut -= len; + dstate.pendingCount -= len; + if (dstate.pendingCount == 0) { - if (istate is null) - { - throw new ZlibException("No Inflate State!"); - } - return istate.Inflate(flush); - } - - /// - /// Ends an inflation session. - /// - /// - /// Call this after successively calling Inflate(). This will cause all buffers to be flushed. - /// After calling this you cannot call Inflate() without a intervening call to one of the - /// InitializeInflate() overloads. - /// - /// Z_OK if everything goes well. - public int EndInflate() - { - if (istate is null) - { - throw new ZlibException("No Inflate State!"); - } - int ret = istate.End(); - istate = null; - return ret; - } - - /// - /// I don't know what this does! - /// - /// Z_OK if everything goes well. - public int SyncInflate() - { - if (istate is null) - { - throw new ZlibException("No Inflate State!"); - } - return istate.Sync(); - } - - /// - /// Initialize the ZlibCodec for deflation operation. - /// - /// - /// The codec will use the MAX window bits and the default level of compression. - /// - /// - /// - /// int bufferSize = 40000; - /// byte[] CompressedBytes = new byte[bufferSize]; - /// byte[] DecompressedBytes = new byte[bufferSize]; - /// - /// ZlibCodec compressor = new ZlibCodec(); - /// - /// compressor.InitializeDeflate(CompressionLevel.Default); - /// - /// compressor.InputBuffer = System.Text.ASCIIEncoding.ASCII.GetBytes(TextToCompress); - /// compressor.NextIn = 0; - /// compressor.AvailableBytesIn = compressor.InputBuffer.Length; - /// - /// compressor.OutputBuffer = CompressedBytes; - /// compressor.NextOut = 0; - /// compressor.AvailableBytesOut = CompressedBytes.Length; - /// - /// while (compressor.TotalBytesIn != TextToCompress.Length && compressor.TotalBytesOut < bufferSize) - /// { - /// compressor.Deflate(FlushType.None); - /// } - /// - /// while (true) - /// { - /// int rc= compressor.Deflate(FlushType.Finish); - /// if (rc == ZlibConstants.Z_STREAM_END) break; - /// } - /// - /// compressor.EndDeflate(); - /// - /// - /// - /// Z_OK if all goes well. You generally don't need to check the return code. - public int InitializeDeflate() - { - return _InternalInitializeDeflate(true); - } - - /// - /// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel. - /// - /// - /// The codec will use the maximum window bits (15) and the specified - /// CompressionLevel. It will emit a ZLIB stream as it compresses. - /// - /// The compression level for the codec. - /// Z_OK if all goes well. - public int InitializeDeflate(CompressionLevel level) - { - CompressLevel = level; - return _InternalInitializeDeflate(true); - } - - /// - /// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel, - /// and the explicit flag governing whether to emit an RFC1950 header byte pair. - /// - /// - /// The codec will use the maximum window bits (15) and the specified CompressionLevel. - /// If you want to generate a zlib stream, you should specify true for - /// wantRfc1950Header. In this case, the library will emit a ZLIB - /// header, as defined in RFC - /// 1950, in the compressed stream. - /// - /// The compression level for the codec. - /// whether to emit an initial RFC1950 byte pair in the compressed stream. - /// Z_OK if all goes well. - public int InitializeDeflate(CompressionLevel level, bool wantRfc1950Header) - { - CompressLevel = level; - return _InternalInitializeDeflate(wantRfc1950Header); - } - - /// - /// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel, - /// and the specified number of window bits. - /// - /// - /// The codec will use the specified number of window bits and the specified CompressionLevel. - /// - /// The compression level for the codec. - /// the number of window bits to use. If you don't know what this means, don't use this method. - /// Z_OK if all goes well. - public int InitializeDeflate(CompressionLevel level, int bits) - { - CompressLevel = level; - WindowBits = bits; - return _InternalInitializeDeflate(true); - } - - /// - /// Initialize the ZlibCodec for deflation operation, using the specified - /// CompressionLevel, the specified number of window bits, and the explicit flag - /// governing whether to emit an RFC1950 header byte pair. - /// - /// - /// The compression level for the codec. - /// whether to emit an initial RFC1950 byte pair in the compressed stream. - /// the number of window bits to use. If you don't know what this means, don't use this method. - /// Z_OK if all goes well. - public int InitializeDeflate(CompressionLevel level, int bits, bool wantRfc1950Header) - { - CompressLevel = level; - WindowBits = bits; - return _InternalInitializeDeflate(wantRfc1950Header); - } - - private int _InternalInitializeDeflate(bool wantRfc1950Header) - { - if (istate != null) - { - throw new ZlibException( - "You may not call InitializeDeflate() after calling InitializeInflate()." - ); - } - dstate = new DeflateManager(); - dstate.WantRfc1950HeaderBytes = wantRfc1950Header; - - return dstate.Initialize(this, CompressLevel, WindowBits, Strategy); - } - - /// - /// Deflate one batch of data. - /// - /// - /// You must have set InputBuffer and OutputBuffer before calling this method. - /// - /// - /// - /// private void DeflateBuffer(CompressionLevel level) - /// { - /// int bufferSize = 1024; - /// byte[] buffer = new byte[bufferSize]; - /// ZlibCodec compressor = new ZlibCodec(); - /// - /// Console.WriteLine("\n============================================"); - /// Console.WriteLine("Size of Buffer to Deflate: {0} bytes.", UncompressedBytes.Length); - /// MemoryStream ms = new MemoryStream(); - /// - /// int rc = compressor.InitializeDeflate(level); - /// - /// compressor.InputBuffer = UncompressedBytes; - /// compressor.NextIn = 0; - /// compressor.AvailableBytesIn = UncompressedBytes.Length; - /// - /// compressor.OutputBuffer = buffer; - /// - /// // pass 1: deflate - /// do - /// { - /// compressor.NextOut = 0; - /// compressor.AvailableBytesOut = buffer.Length; - /// rc = compressor.Deflate(FlushType.None); - /// - /// if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END) - /// throw new Exception("deflating: " + compressor.Message); - /// - /// ms.Write(compressor.OutputBuffer, 0, buffer.Length - compressor.AvailableBytesOut); - /// } - /// while (compressor.AvailableBytesIn > 0 || compressor.AvailableBytesOut == 0); - /// - /// // pass 2: finish and flush - /// do - /// { - /// compressor.NextOut = 0; - /// compressor.AvailableBytesOut = buffer.Length; - /// rc = compressor.Deflate(FlushType.Finish); - /// - /// if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK) - /// throw new Exception("deflating: " + compressor.Message); - /// - /// if (buffer.Length - compressor.AvailableBytesOut > 0) - /// ms.Write(buffer, 0, buffer.Length - compressor.AvailableBytesOut); - /// } - /// while (compressor.AvailableBytesIn > 0 || compressor.AvailableBytesOut == 0); - /// - /// compressor.EndDeflate(); - /// - /// ms.Seek(0, SeekOrigin.Begin); - /// CompressedBytes = new byte[compressor.TotalBytesOut]; - /// ms.Read(CompressedBytes, 0, CompressedBytes.Length); - /// } - /// - /// - /// whether to flush all data as you deflate. Generally you will want to - /// use Z_NO_FLUSH here, in a series of calls to Deflate(), and then call EndDeflate() to - /// flush everything. - /// - /// Z_OK if all goes well. - public int Deflate(FlushType flush) - { - if (dstate is null) - { - throw new ZlibException("No Deflate State!"); - } - return dstate.Deflate(flush); - } - - /// - /// End a deflation session. - /// - /// - /// Call this after making a series of one or more calls to Deflate(). All buffers are flushed. - /// - /// Z_OK if all goes well. - public int EndDeflate() - { - if (dstate is null) - { - throw new ZlibException("No Deflate State!"); - } - - // TODO: dinoch Tue, 03 Nov 2009 15:39 (test this) - //int ret = dstate.End(); - dstate = null; - return ZlibConstants.Z_OK; //ret; - } - - /// - /// Reset a codec for another deflation session. - /// - /// - /// Call this to reset the deflation state. For example if a thread is deflating - /// non-consecutive blocks, you can call Reset() after the Deflate(Sync) of the first - /// block and before the next Deflate(None) of the second block. - /// - /// Z_OK if all goes well. - public void ResetDeflate() - { - if (dstate is null) - { - throw new ZlibException("No Deflate State!"); - } - dstate.Reset(); - } - - /// - /// Set the CompressionStrategy and CompressionLevel for a deflation session. - /// - /// the level of compression to use. - /// the strategy to use for compression. - /// Z_OK if all goes well. - public int SetDeflateParams(CompressionLevel level, CompressionStrategy strategy) - { - if (dstate is null) - { - throw new ZlibException("No Deflate State!"); - } - return dstate.SetParams(level, strategy); - } - - /// - /// Set the dictionary to be used for either Inflation or Deflation. - /// - /// The dictionary bytes to use. - /// Z_OK if all goes well. - public int SetDictionary(byte[] dictionary) - { - if (istate != null) - { - return istate.SetDictionary(dictionary); - } - - if (dstate != null) - { - return dstate.SetDictionary(dictionary); - } - - throw new ZlibException("No Inflate or Deflate state!"); - } - - // Flush as much pending output as possible. All deflate() output goes - // through this function so some applications may wish to modify it - // to avoid allocating a large strm->next_out buffer and copying into it. - // (See also read_buf()). - internal void flush_pending() - { - int len = dstate.pendingCount; - - if (len > AvailableBytesOut) - { - len = AvailableBytesOut; - } - if (len == 0) - { - return; - } - - if ( - dstate.pending.Length <= dstate.nextPending - || OutputBuffer.Length <= NextOut - || dstate.pending.Length < (dstate.nextPending + len) - || OutputBuffer.Length < (NextOut + len) - ) - { - throw new ZlibException( - String.Format( - "Invalid State. (pending.Length={0}, pendingCount={1})", - dstate.pending.Length, - dstate.pendingCount - ) - ); - } - - Array.Copy(dstate.pending, dstate.nextPending, OutputBuffer, NextOut, len); - - NextOut += len; - dstate.nextPending += len; - TotalBytesOut += len; - AvailableBytesOut -= len; - dstate.pendingCount -= len; - if (dstate.pendingCount == 0) - { - dstate.nextPending = 0; - } - } - - // Read a new buffer from the current input stream, update the adler32 - // and total number of bytes read. All deflate() input goes through - // this function so some applications may wish to modify it to avoid - // allocating a large strm->next_in buffer and copying from it. - // (See also flush_pending()). - internal int read_buf(byte[] buf, int start, int size) - { - int len = AvailableBytesIn; - - if (len > size) - { - len = size; - } - if (len == 0) - { - return 0; - } - - AvailableBytesIn -= len; - - if (dstate.WantRfc1950HeaderBytes) - { - _adler32 = Algorithms.Adler32.Calculate(_adler32, InputBuffer.AsSpan(NextIn, len)); - } - Array.Copy(InputBuffer, NextIn, buf, start, len); - NextIn += len; - TotalBytesIn += len; - return len; + dstate.nextPending = 0; } } + + // Read a new buffer from the current input stream, update the adler32 + // and total number of bytes read. All deflate() input goes through + // this function so some applications may wish to modify it to avoid + // allocating a large strm->next_in buffer and copying from it. + // (See also flush_pending()). + internal int read_buf(byte[] buf, int start, int size) + { + var len = AvailableBytesIn; + + if (len > size) + { + len = size; + } + if (len == 0) + { + return 0; + } + + AvailableBytesIn -= len; + + if (dstate.WantRfc1950HeaderBytes) + { + _adler32 = Algorithms.Adler32.Calculate(_adler32, InputBuffer.AsSpan(NextIn, len)); + } + Array.Copy(InputBuffer, NextIn, buf, start, len); + NextIn += len; + TotalBytesIn += len; + return len; + } } diff --git a/src/SharpCompress/Compressors/Deflate/ZlibConstants.cs b/src/SharpCompress/Compressors/Deflate/ZlibConstants.cs index 48d6c170..1a372a31 100644 --- a/src/SharpCompress/Compressors/Deflate/ZlibConstants.cs +++ b/src/SharpCompress/Compressors/Deflate/ZlibConstants.cs @@ -61,65 +61,64 @@ // ----------------------------------------------------------------------- -namespace SharpCompress.Compressors.Deflate +namespace SharpCompress.Compressors.Deflate; + +/// +/// A bunch of constants used in the Zlib interface. +/// +internal static class ZlibConstants { /// - /// A bunch of constants used in the Zlib interface. + /// The maximum number of window bits for the Deflate algorithm. /// - internal static class ZlibConstants - { - /// - /// The maximum number of window bits for the Deflate algorithm. - /// - public const int WindowBitsMax = 15; // 32K LZ77 window + public const int WindowBitsMax = 15; // 32K LZ77 window - /// - /// The default number of window bits for the Deflate algorithm. - /// - public const int WindowBitsDefault = WindowBitsMax; + /// + /// The default number of window bits for the Deflate algorithm. + /// + public const int WindowBitsDefault = WindowBitsMax; - /// - /// indicates everything is A-OK - /// - public const int Z_OK = 0; + /// + /// indicates everything is A-OK + /// + public const int Z_OK = 0; - /// - /// Indicates that the last operation reached the end of the stream. - /// - public const int Z_STREAM_END = 1; + /// + /// Indicates that the last operation reached the end of the stream. + /// + public const int Z_STREAM_END = 1; - /// - /// The operation ended in need of a dictionary. - /// - public const int Z_NEED_DICT = 2; + /// + /// The operation ended in need of a dictionary. + /// + public const int Z_NEED_DICT = 2; - /// - /// There was an error with the stream - not enough data, not open and readable, etc. - /// - public const int Z_STREAM_ERROR = -2; + /// + /// There was an error with the stream - not enough data, not open and readable, etc. + /// + public const int Z_STREAM_ERROR = -2; - /// - /// There was an error with the data - not enough data, bad data, etc. - /// - public const int Z_DATA_ERROR = -3; + /// + /// There was an error with the data - not enough data, bad data, etc. + /// + public const int Z_DATA_ERROR = -3; - /// - /// There was an error with the working buffer. - /// - public const int Z_BUF_ERROR = -5; + /// + /// There was an error with the working buffer. + /// + public const int Z_BUF_ERROR = -5; - /// - /// The size of the working buffer used in the ZlibCodec class. Defaults to 8192 bytes. - /// + /// + /// The size of the working buffer used in the ZlibCodec class. Defaults to 8192 bytes. + /// #if NETCF - public const int WorkingBufferSizeDefault = 8192; + public const int WorkingBufferSizeDefault = 8192; #else - public const int WorkingBufferSizeDefault = 16384; + public const int WorkingBufferSizeDefault = 16384; #endif - /// - /// The minimum size of the working buffer used in the ZlibCodec class. Currently it is 128 bytes. - /// - public const int WorkingBufferSizeMin = 1024; - } + /// + /// The minimum size of the working buffer used in the ZlibCodec class. Currently it is 128 bytes. + /// + public const int WorkingBufferSizeMin = 1024; } diff --git a/src/SharpCompress/Compressors/Deflate/ZlibStream.cs b/src/SharpCompress/Compressors/Deflate/ZlibStream.cs index 8d8be7ae..22ccfdd8 100644 --- a/src/SharpCompress/Compressors/Deflate/ZlibStream.cs +++ b/src/SharpCompress/Compressors/Deflate/ZlibStream.cs @@ -29,320 +29,319 @@ using System; using System.IO; using System.Text; -namespace SharpCompress.Compressors.Deflate +namespace SharpCompress.Compressors.Deflate; + +public class ZlibStream : Stream { - public class ZlibStream : Stream + private readonly ZlibBaseStream _baseStream; + private bool _disposed; + + public ZlibStream(Stream stream, CompressionMode mode) + : this(stream, mode, CompressionLevel.Default, Encoding.UTF8) { } + + public ZlibStream(Stream stream, CompressionMode mode, CompressionLevel level) + : this(stream, mode, level, Encoding.UTF8) { } + + public ZlibStream( + Stream stream, + CompressionMode mode, + CompressionLevel level, + Encoding encoding + ) { - private readonly ZlibBaseStream _baseStream; - private bool _disposed; - - public ZlibStream(Stream stream, CompressionMode mode) - : this(stream, mode, CompressionLevel.Default, Encoding.UTF8) { } - - public ZlibStream(Stream stream, CompressionMode mode, CompressionLevel level) - : this(stream, mode, level, Encoding.UTF8) { } - - public ZlibStream( - Stream stream, - CompressionMode mode, - CompressionLevel level, - Encoding encoding - ) - { - _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.ZLIB, encoding); - } - - #region Zlib properties - - /// - /// This property sets the flush behavior on the stream. - /// Sorry, though, not sure exactly how to describe all the various settings. - /// - public virtual FlushType FlushMode - { - get => (_baseStream._flushMode); - set - { - if (_disposed) - { - throw new ObjectDisposedException("ZlibStream"); - } - _baseStream._flushMode = value; - } - } - - /// - /// The size of the working buffer for the compression codec. - /// - /// - /// - /// - /// The working buffer is used for all stream operations. The default size is - /// 1024 bytes. The minimum size is 128 bytes. You may get better performance - /// with a larger buffer. Then again, you might not. You would have to test - /// it. - /// - /// - /// - /// Set this before the first call to Read() or Write() on the - /// stream. If you try to set it afterwards, it will throw. - /// - /// - public int BufferSize - { - get => _baseStream._bufferSize; - set - { - if (_disposed) - { - throw new ObjectDisposedException("ZlibStream"); - } - if (_baseStream._workingBuffer != null) - { - throw new ZlibException("The working buffer is already set."); - } - if (value < ZlibConstants.WorkingBufferSizeMin) - { - throw new ZlibException( - String.Format( - "Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", - value, - ZlibConstants.WorkingBufferSizeMin - ) - ); - } - _baseStream._bufferSize = value; - } - } - - /// Returns the total number of bytes input so far. - public virtual long TotalIn => _baseStream._z.TotalBytesIn; - - /// Returns the total number of bytes output so far. - public virtual long TotalOut => _baseStream._z.TotalBytesOut; - - #endregion - - #region System.IO.Stream methods - - /// - /// Indicates whether the stream can be read. - /// - /// - /// The return value depends on whether the captive stream supports reading. - /// - public override bool CanRead - { - get - { - if (_disposed) - { - throw new ObjectDisposedException("ZlibStream"); - } - return _baseStream._stream.CanRead; - } - } - - /// - /// Indicates whether the stream supports Seek operations. - /// - /// - /// Always returns false. - /// - public override bool CanSeek => false; - - /// - /// Indicates whether the stream can be written. - /// - /// - /// The return value depends on whether the captive stream supports writing. - /// - public override bool CanWrite - { - get - { - if (_disposed) - { - throw new ObjectDisposedException("ZlibStream"); - } - return _baseStream._stream.CanWrite; - } - } - - /// - /// Reading this property always throws a . - /// - public override long Length => throw new NotSupportedException(); - - /// - /// The position of the stream pointer. - /// - /// - /// - /// Setting this property always throws a . Reading will return the total bytes - /// written out, if used in writing, or the total bytes read in, if used in - /// reading. The count may refer to compressed bytes or uncompressed bytes, - /// depending on how you've used the stream. - /// - public override long Position - { - get - { - if (_baseStream._streamMode == ZlibBaseStream.StreamMode.Writer) - { - return _baseStream._z.TotalBytesOut; - } - if (_baseStream._streamMode == ZlibBaseStream.StreamMode.Reader) - { - return _baseStream._z.TotalBytesIn; - } - return 0; - } - set => throw new NotSupportedException(); - } - - /// - /// Dispose the stream. - /// - /// - /// This may or may not result in a Close() call on the captive stream. - /// - protected override void Dispose(bool disposing) - { - try - { - if (!_disposed) - { - if (disposing) - { - _baseStream?.Dispose(); - } - _disposed = true; - } - } - finally - { - base.Dispose(disposing); - } - } - - /// - /// Flush the stream. - /// - public override void Flush() - { - if (_disposed) - { - throw new ObjectDisposedException("ZlibStream"); - } - _baseStream.Flush(); - } - - /// - /// Read data from the stream. - /// - /// - /// - /// - /// - /// If you wish to use the ZlibStream to compress data while reading, - /// you can create a ZlibStream with CompressionMode.Compress, - /// providing an uncompressed data stream. Then call Read() on that - /// ZlibStream, and the data read will be compressed. If you wish to - /// use the ZlibStream to decompress data while reading, you can create - /// a ZlibStream with CompressionMode.Decompress, providing a - /// readable compressed data stream. Then call Read() on that - /// ZlibStream, and the data will be decompressed as it is read. - /// - /// - /// - /// A ZlibStream can be used for Read() or Write(), but - /// not both. - /// - /// - /// - /// The buffer into which the read data should be placed. - /// the offset within that data array to put the first byte read. - /// the number of bytes to read. - public override int Read(byte[] buffer, int offset, int count) - { - if (_disposed) - { - throw new ObjectDisposedException("ZlibStream"); - } - return _baseStream.Read(buffer, offset, count); - } - - public override int ReadByte() - { - if (_disposed) - { - throw new ObjectDisposedException("ZlibStream"); - } - return _baseStream.ReadByte(); - } - - /// - /// Calling this method always throws a . - /// - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - /// - /// Calling this method always throws a . - /// - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - /// - /// Write data to the stream. - /// - /// - /// - /// - /// - /// If you wish to use the ZlibStream to compress data while writing, - /// you can create a ZlibStream with CompressionMode.Compress, - /// and a writable output stream. Then call Write() on that - /// ZlibStream, providing uncompressed data as input. The data sent to - /// the output stream will be the compressed form of the data written. If you - /// wish to use the ZlibStream to decompress data while writing, you - /// can create a ZlibStream with CompressionMode.Decompress, and a - /// writable output stream. Then call Write() on that stream, - /// providing previously compressed data. The data sent to the output stream - /// will be the decompressed form of the data written. - /// - /// - /// - /// A ZlibStream can be used for Read() or Write(), but not both. - /// - /// - /// The buffer holding data to write to the stream. - /// the offset within that data array to find the first byte to write. - /// the number of bytes to write. - public override void Write(byte[] buffer, int offset, int count) - { - if (_disposed) - { - throw new ObjectDisposedException("ZlibStream"); - } - _baseStream.Write(buffer, offset, count); - } - - public override void WriteByte(byte value) - { - if (_disposed) - { - throw new ObjectDisposedException("ZlibStream"); - } - _baseStream.WriteByte(value); - } - - #endregion System.IO.Stream methods + _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.ZLIB, encoding); } + + #region Zlib properties + + /// + /// This property sets the flush behavior on the stream. + /// Sorry, though, not sure exactly how to describe all the various settings. + /// + public virtual FlushType FlushMode + { + get => (_baseStream._flushMode); + set + { + if (_disposed) + { + throw new ObjectDisposedException("ZlibStream"); + } + _baseStream._flushMode = value; + } + } + + /// + /// The size of the working buffer for the compression codec. + /// + /// + /// + /// + /// The working buffer is used for all stream operations. The default size is + /// 1024 bytes. The minimum size is 128 bytes. You may get better performance + /// with a larger buffer. Then again, you might not. You would have to test + /// it. + /// + /// + /// + /// Set this before the first call to Read() or Write() on the + /// stream. If you try to set it afterwards, it will throw. + /// + /// + public int BufferSize + { + get => _baseStream._bufferSize; + set + { + if (_disposed) + { + throw new ObjectDisposedException("ZlibStream"); + } + if (_baseStream._workingBuffer != null) + { + throw new ZlibException("The working buffer is already set."); + } + if (value < ZlibConstants.WorkingBufferSizeMin) + { + throw new ZlibException( + string.Format( + "Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", + value, + ZlibConstants.WorkingBufferSizeMin + ) + ); + } + _baseStream._bufferSize = value; + } + } + + /// Returns the total number of bytes input so far. + public virtual long TotalIn => _baseStream._z.TotalBytesIn; + + /// Returns the total number of bytes output so far. + public virtual long TotalOut => _baseStream._z.TotalBytesOut; + + #endregion + + #region System.IO.Stream methods + + /// + /// Indicates whether the stream can be read. + /// + /// + /// The return value depends on whether the captive stream supports reading. + /// + public override bool CanRead + { + get + { + if (_disposed) + { + throw new ObjectDisposedException("ZlibStream"); + } + return _baseStream._stream.CanRead; + } + } + + /// + /// Indicates whether the stream supports Seek operations. + /// + /// + /// Always returns false. + /// + public override bool CanSeek => false; + + /// + /// Indicates whether the stream can be written. + /// + /// + /// The return value depends on whether the captive stream supports writing. + /// + public override bool CanWrite + { + get + { + if (_disposed) + { + throw new ObjectDisposedException("ZlibStream"); + } + return _baseStream._stream.CanWrite; + } + } + + /// + /// Reading this property always throws a . + /// + public override long Length => throw new NotSupportedException(); + + /// + /// The position of the stream pointer. + /// + /// + /// + /// Setting this property always throws a . Reading will return the total bytes + /// written out, if used in writing, or the total bytes read in, if used in + /// reading. The count may refer to compressed bytes or uncompressed bytes, + /// depending on how you've used the stream. + /// + public override long Position + { + get + { + if (_baseStream._streamMode == ZlibBaseStream.StreamMode.Writer) + { + return _baseStream._z.TotalBytesOut; + } + if (_baseStream._streamMode == ZlibBaseStream.StreamMode.Reader) + { + return _baseStream._z.TotalBytesIn; + } + return 0; + } + set => throw new NotSupportedException(); + } + + /// + /// Dispose the stream. + /// + /// + /// This may or may not result in a Close() call on the captive stream. + /// + protected override void Dispose(bool disposing) + { + try + { + if (!_disposed) + { + if (disposing) + { + _baseStream?.Dispose(); + } + _disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + + /// + /// Flush the stream. + /// + public override void Flush() + { + if (_disposed) + { + throw new ObjectDisposedException("ZlibStream"); + } + _baseStream.Flush(); + } + + /// + /// Read data from the stream. + /// + /// + /// + /// + /// + /// If you wish to use the ZlibStream to compress data while reading, + /// you can create a ZlibStream with CompressionMode.Compress, + /// providing an uncompressed data stream. Then call Read() on that + /// ZlibStream, and the data read will be compressed. If you wish to + /// use the ZlibStream to decompress data while reading, you can create + /// a ZlibStream with CompressionMode.Decompress, providing a + /// readable compressed data stream. Then call Read() on that + /// ZlibStream, and the data will be decompressed as it is read. + /// + /// + /// + /// A ZlibStream can be used for Read() or Write(), but + /// not both. + /// + /// + /// + /// The buffer into which the read data should be placed. + /// the offset within that data array to put the first byte read. + /// the number of bytes to read. + public override int Read(byte[] buffer, int offset, int count) + { + if (_disposed) + { + throw new ObjectDisposedException("ZlibStream"); + } + return _baseStream.Read(buffer, offset, count); + } + + public override int ReadByte() + { + if (_disposed) + { + throw new ObjectDisposedException("ZlibStream"); + } + return _baseStream.ReadByte(); + } + + /// + /// Calling this method always throws a . + /// + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + /// + /// Calling this method always throws a . + /// + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + /// + /// Write data to the stream. + /// + /// + /// + /// + /// + /// If you wish to use the ZlibStream to compress data while writing, + /// you can create a ZlibStream with CompressionMode.Compress, + /// and a writable output stream. Then call Write() on that + /// ZlibStream, providing uncompressed data as input. The data sent to + /// the output stream will be the compressed form of the data written. If you + /// wish to use the ZlibStream to decompress data while writing, you + /// can create a ZlibStream with CompressionMode.Decompress, and a + /// writable output stream. Then call Write() on that stream, + /// providing previously compressed data. The data sent to the output stream + /// will be the decompressed form of the data written. + /// + /// + /// + /// A ZlibStream can be used for Read() or Write(), but not both. + /// + /// + /// The buffer holding data to write to the stream. + /// the offset within that data array to find the first byte to write. + /// the number of bytes to write. + public override void Write(byte[] buffer, int offset, int count) + { + if (_disposed) + { + throw new ObjectDisposedException("ZlibStream"); + } + _baseStream.Write(buffer, offset, count); + } + + public override void WriteByte(byte value) + { + if (_disposed) + { + throw new ObjectDisposedException("ZlibStream"); + } + _baseStream.WriteByte(value); + } + + #endregion System.IO.Stream methods } diff --git a/src/SharpCompress/Compressors/Deflate64/BlockType.cs b/src/SharpCompress/Compressors/Deflate64/BlockType.cs index c8938079..5bd9eb83 100644 --- a/src/SharpCompress/Compressors/Deflate64/BlockType.cs +++ b/src/SharpCompress/Compressors/Deflate64/BlockType.cs @@ -2,12 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace SharpCompress.Compressors.Deflate64 +namespace SharpCompress.Compressors.Deflate64; + +internal enum BlockType { - internal enum BlockType - { - Uncompressed = 0, - Static = 1, - Dynamic = 2 - } + Uncompressed = 0, + Static = 1, + Dynamic = 2 } diff --git a/src/SharpCompress/Compressors/Deflate64/DeflateInput.cs b/src/SharpCompress/Compressors/Deflate64/DeflateInput.cs index 2305d241..f539e3bb 100644 --- a/src/SharpCompress/Compressors/Deflate64/DeflateInput.cs +++ b/src/SharpCompress/Compressors/Deflate64/DeflateInput.cs @@ -4,46 +4,45 @@ using System.Diagnostics; -namespace SharpCompress.Compressors.Deflate64 +namespace SharpCompress.Compressors.Deflate64; + +internal sealed class DeflateInput { - internal sealed class DeflateInput + public DeflateInput(byte[] buffer) { - public DeflateInput(byte[] buffer) + Buffer = buffer; + } + + public byte[] Buffer { get; } + public int Count { get; set; } + public int StartIndex { get; set; } + + internal void ConsumeBytes(int n) + { + Debug.Assert(n <= Count, "Should use more bytes than what we have in the buffer"); + StartIndex += n; + Count -= n; + Debug.Assert(StartIndex + Count <= Buffer.Length, "Input buffer is in invalid state!"); + } + + internal InputState DumpState() => new InputState(Count, StartIndex); + + internal void RestoreState(InputState state) + { + Count = state._count; + StartIndex = state._startIndex; + } + + internal /*readonly */ +readonly struct InputState + { + internal readonly int _count; + internal readonly int _startIndex; + + internal InputState(int count, int startIndex) { - Buffer = buffer; - } - - public byte[] Buffer { get; } - public int Count { get; set; } - public int StartIndex { get; set; } - - internal void ConsumeBytes(int n) - { - Debug.Assert(n <= Count, "Should use more bytes than what we have in the buffer"); - StartIndex += n; - Count -= n; - Debug.Assert(StartIndex + Count <= Buffer.Length, "Input buffer is in invalid state!"); - } - - internal InputState DumpState() => new InputState(Count, StartIndex); - - internal void RestoreState(InputState state) - { - Count = state._count; - StartIndex = state._startIndex; - } - - internal /*readonly */ - struct InputState - { - internal readonly int _count; - internal readonly int _startIndex; - - internal InputState(int count, int startIndex) - { - _count = count; - _startIndex = startIndex; - } + _count = count; + _startIndex = startIndex; } } } diff --git a/src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs b/src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs index 8b0992da..71e3f680 100644 --- a/src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs +++ b/src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs @@ -5,975 +5,974 @@ using System; using System.Diagnostics; -namespace SharpCompress.Compressors.Deflate64 +namespace SharpCompress.Compressors.Deflate64; + +internal static class FastEncoderStatics { - internal static class FastEncoderStatics + // static information for encoding, DO NOT MODIFY + + internal static ReadOnlySpan FAST_ENCODER_TREE_STRUCTURE_DATA => + new byte[] + { + 0xec, + 0xbd, + 0x07, + 0x60, + 0x1c, + 0x49, + 0x96, + 0x25, + 0x26, + 0x2f, + 0x6d, + 0xca, + 0x7b, + 0x7f, + 0x4a, + 0xf5, + 0x4a, + 0xd7, + 0xe0, + 0x74, + 0xa1, + 0x08, + 0x80, + 0x60, + 0x13, + 0x24, + 0xd8, + 0x90, + 0x40, + 0x10, + 0xec, + 0xc1, + 0x88, + 0xcd, + 0xe6, + 0x92, + 0xec, + 0x1d, + 0x69, + 0x47, + 0x23, + 0x29, + 0xab, + 0x2a, + 0x81, + 0xca, + 0x65, + 0x56, + 0x65, + 0x5d, + 0x66, + 0x16, + 0x40, + 0xcc, + 0xed, + 0x9d, + 0xbc, + 0xf7, + 0xde, + 0x7b, + 0xef, + 0xbd, + 0xf7, + 0xde, + 0x7b, + 0xef, + 0xbd, + 0xf7, + 0xba, + 0x3b, + 0x9d, + 0x4e, + 0x27, + 0xf7, + 0xdf, + 0xff, + 0x3f, + 0x5c, + 0x66, + 0x64, + 0x01, + 0x6c, + 0xf6, + 0xce, + 0x4a, + 0xda, + 0xc9, + 0x9e, + 0x21, + 0x80, + 0xaa, + 0xc8, + 0x1f, + 0x3f, + 0x7e, + 0x7c, + 0x1f, + 0x3f + }; + + internal static ReadOnlySpan B_FINAL_FAST_ENCODER_TREE_STRUCTURE_DATA => + new byte[] + { + 0xed, + 0xbd, + 0x07, + 0x60, + 0x1c, + 0x49, + 0x96, + 0x25, + 0x26, + 0x2f, + 0x6d, + 0xca, + 0x7b, + 0x7f, + 0x4a, + 0xf5, + 0x4a, + 0xd7, + 0xe0, + 0x74, + 0xa1, + 0x08, + 0x80, + 0x60, + 0x13, + 0x24, + 0xd8, + 0x90, + 0x40, + 0x10, + 0xec, + 0xc1, + 0x88, + 0xcd, + 0xe6, + 0x92, + 0xec, + 0x1d, + 0x69, + 0x47, + 0x23, + 0x29, + 0xab, + 0x2a, + 0x81, + 0xca, + 0x65, + 0x56, + 0x65, + 0x5d, + 0x66, + 0x16, + 0x40, + 0xcc, + 0xed, + 0x9d, + 0xbc, + 0xf7, + 0xde, + 0x7b, + 0xef, + 0xbd, + 0xf7, + 0xde, + 0x7b, + 0xef, + 0xbd, + 0xf7, + 0xba, + 0x3b, + 0x9d, + 0x4e, + 0x27, + 0xf7, + 0xdf, + 0xff, + 0x3f, + 0x5c, + 0x66, + 0x64, + 0x01, + 0x6c, + 0xf6, + 0xce, + 0x4a, + 0xda, + 0xc9, + 0x9e, + 0x21, + 0x80, + 0xaa, + 0xc8, + 0x1f, + 0x3f, + 0x7e, + 0x7c, + 0x1f, + 0x3f + }; + + // Output a currentMatch with length matchLen (>= MIN_MATCH) and displacement matchPos + // + // Optimisation: unlike the other encoders, here we have an array of codes for each currentMatch + // length (not just each currentMatch length slot), complete with all the extra bits filled in, in + // a single array element. + // + // There are many advantages to doing this: + // + // 1. A single array lookup on g_FastEncoderLiteralCodeInfo, instead of separate array lookups + // on g_LengthLookup (to get the length slot), g_FastEncoderLiteralTreeLength, + // g_FastEncoderLiteralTreeCode, g_ExtraLengthBits, and g_BitMask + // + // 2. The array is an array of ULONGs, so no access penalty, unlike for accessing those USHORT + // code arrays in the other encoders (although they could be made into ULONGs with some + // modifications to the source). + // + // Note, if we could guarantee that codeLen <= 16 always, then we could skip an if statement here. + // + // A completely different optimisation is used for the distance codes since, obviously, a table for + // all 8192 distances combining their extra bits is not feasible. The distance codeinfo table is + // made up of code[], len[] and # extraBits for this code. + // + // The advantages are similar to the above; a ULONG array instead of a USHORT and BYTE array, better + // cache locality, fewer memory operations. + // + + + // Encoding information for literal and Length. + // The least 5 significant bits are the length + // and the rest is the code bits. + + internal static readonly uint[] FAST_ENCODER_LITERAL_CODE_INFO = { - // static information for encoding, DO NOT MODIFY + 0x0000d7ee, + 0x0004d7ee, + 0x0002d7ee, + 0x0006d7ee, + 0x0001d7ee, + 0x0005d7ee, + 0x0003d7ee, + 0x0007d7ee, + 0x000037ee, + 0x0000c7ec, + 0x00000126, + 0x000437ee, + 0x000237ee, + 0x000637ee, + 0x000137ee, + 0x000537ee, + 0x000337ee, + 0x000737ee, + 0x0000b7ee, + 0x0004b7ee, + 0x0002b7ee, + 0x0006b7ee, + 0x0001b7ee, + 0x0005b7ee, + 0x0003b7ee, + 0x0007b7ee, + 0x000077ee, + 0x000477ee, + 0x000277ee, + 0x000677ee, + 0x000017ed, + 0x000177ee, + 0x00000526, + 0x000577ee, + 0x000023ea, + 0x0001c7ec, + 0x000377ee, + 0x000777ee, + 0x000217ed, + 0x000063ea, + 0x00000b68, + 0x00000ee9, + 0x00005beb, + 0x000013ea, + 0x00000467, + 0x00001b68, + 0x00000c67, + 0x00002ee9, + 0x00000768, + 0x00001768, + 0x00000f68, + 0x00001ee9, + 0x00001f68, + 0x00003ee9, + 0x000053ea, + 0x000001e9, + 0x000000e8, + 0x000021e9, + 0x000011e9, + 0x000010e8, + 0x000031e9, + 0x000033ea, + 0x000008e8, + 0x0000f7ee, + 0x0004f7ee, + 0x000018e8, + 0x000009e9, + 0x000004e8, + 0x000029e9, + 0x000014e8, + 0x000019e9, + 0x000073ea, + 0x0000dbeb, + 0x00000ce8, + 0x00003beb, + 0x0002f7ee, + 0x000039e9, + 0x00000bea, + 0x000005e9, + 0x00004bea, + 0x000025e9, + 0x000027ec, + 0x000015e9, + 0x000035e9, + 0x00000de9, + 0x00002bea, + 0x000127ec, + 0x0000bbeb, + 0x0006f7ee, + 0x0001f7ee, + 0x0000a7ec, + 0x00007beb, + 0x0005f7ee, + 0x0000fbeb, + 0x0003f7ee, + 0x0007f7ee, + 0x00000fee, + 0x00000326, + 0x00000267, + 0x00000a67, + 0x00000667, + 0x00000726, + 0x00001ce8, + 0x000002e8, + 0x00000e67, + 0x000000a6, + 0x0001a7ec, + 0x00002de9, + 0x000004a6, + 0x00000167, + 0x00000967, + 0x000002a6, + 0x00000567, + 0x000117ed, + 0x000006a6, + 0x000001a6, + 0x000005a6, + 0x00000d67, + 0x000012e8, + 0x00000ae8, + 0x00001de9, + 0x00001ae8, + 0x000007eb, + 0x000317ed, + 0x000067ec, + 0x000097ed, + 0x000297ed, + 0x00040fee, + 0x00020fee, + 0x00060fee, + 0x00010fee, + 0x00050fee, + 0x00030fee, + 0x00070fee, + 0x00008fee, + 0x00048fee, + 0x00028fee, + 0x00068fee, + 0x00018fee, + 0x00058fee, + 0x00038fee, + 0x00078fee, + 0x00004fee, + 0x00044fee, + 0x00024fee, + 0x00064fee, + 0x00014fee, + 0x00054fee, + 0x00034fee, + 0x00074fee, + 0x0000cfee, + 0x0004cfee, + 0x0002cfee, + 0x0006cfee, + 0x0001cfee, + 0x0005cfee, + 0x0003cfee, + 0x0007cfee, + 0x00002fee, + 0x00042fee, + 0x00022fee, + 0x00062fee, + 0x00012fee, + 0x00052fee, + 0x00032fee, + 0x00072fee, + 0x0000afee, + 0x0004afee, + 0x0002afee, + 0x0006afee, + 0x0001afee, + 0x0005afee, + 0x0003afee, + 0x0007afee, + 0x00006fee, + 0x00046fee, + 0x00026fee, + 0x00066fee, + 0x00016fee, + 0x00056fee, + 0x00036fee, + 0x00076fee, + 0x0000efee, + 0x0004efee, + 0x0002efee, + 0x0006efee, + 0x0001efee, + 0x0005efee, + 0x0003efee, + 0x0007efee, + 0x00001fee, + 0x00041fee, + 0x00021fee, + 0x00061fee, + 0x00011fee, + 0x00051fee, + 0x00031fee, + 0x00071fee, + 0x00009fee, + 0x00049fee, + 0x00029fee, + 0x00069fee, + 0x00019fee, + 0x00059fee, + 0x00039fee, + 0x00079fee, + 0x00005fee, + 0x00045fee, + 0x00025fee, + 0x00065fee, + 0x00015fee, + 0x00055fee, + 0x00035fee, + 0x00075fee, + 0x0000dfee, + 0x0004dfee, + 0x0002dfee, + 0x0006dfee, + 0x0001dfee, + 0x0005dfee, + 0x0003dfee, + 0x0007dfee, + 0x00003fee, + 0x00043fee, + 0x00023fee, + 0x00063fee, + 0x00013fee, + 0x00053fee, + 0x00033fee, + 0x00073fee, + 0x0000bfee, + 0x0004bfee, + 0x0002bfee, + 0x0006bfee, + 0x0001bfee, + 0x0005bfee, + 0x0003bfee, + 0x0007bfee, + 0x00007fee, + 0x00047fee, + 0x00027fee, + 0x00067fee, + 0x00017fee, + 0x000197ed, + 0x000397ed, + 0x000057ed, + 0x00057fee, + 0x000257ed, + 0x00037fee, + 0x000157ed, + 0x00077fee, + 0x000357ed, + 0x0000ffee, + 0x0004ffee, + 0x0002ffee, + 0x0006ffee, + 0x0001ffee, + 0x00000084, + 0x00000003, + 0x00000184, + 0x00000044, + 0x00000144, + 0x000000c5, + 0x000002c5, + 0x000001c5, + 0x000003c6, + 0x000007c6, + 0x00000026, + 0x00000426, + 0x000003a7, + 0x00000ba7, + 0x000007a7, + 0x00000fa7, + 0x00000227, + 0x00000627, + 0x00000a27, + 0x00000e27, + 0x00000068, + 0x00000868, + 0x00001068, + 0x00001868, + 0x00000369, + 0x00001369, + 0x00002369, + 0x00003369, + 0x000006ea, + 0x000026ea, + 0x000046ea, + 0x000066ea, + 0x000016eb, + 0x000036eb, + 0x000056eb, + 0x000076eb, + 0x000096eb, + 0x0000b6eb, + 0x0000d6eb, + 0x0000f6eb, + 0x00003dec, + 0x00007dec, + 0x0000bdec, + 0x0000fdec, + 0x00013dec, + 0x00017dec, + 0x0001bdec, + 0x0001fdec, + 0x00006bed, + 0x0000ebed, + 0x00016bed, + 0x0001ebed, + 0x00026bed, + 0x0002ebed, + 0x00036bed, + 0x0003ebed, + 0x000003ec, + 0x000043ec, + 0x000083ec, + 0x0000c3ec, + 0x000103ec, + 0x000143ec, + 0x000183ec, + 0x0001c3ec, + 0x00001bee, + 0x00009bee, + 0x00011bee, + 0x00019bee, + 0x00021bee, + 0x00029bee, + 0x00031bee, + 0x00039bee, + 0x00041bee, + 0x00049bee, + 0x00051bee, + 0x00059bee, + 0x00061bee, + 0x00069bee, + 0x00071bee, + 0x00079bee, + 0x000167f0, + 0x000367f0, + 0x000567f0, + 0x000767f0, + 0x000967f0, + 0x000b67f0, + 0x000d67f0, + 0x000f67f0, + 0x001167f0, + 0x001367f0, + 0x001567f0, + 0x001767f0, + 0x001967f0, + 0x001b67f0, + 0x001d67f0, + 0x001f67f0, + 0x000087ef, + 0x000187ef, + 0x000287ef, + 0x000387ef, + 0x000487ef, + 0x000587ef, + 0x000687ef, + 0x000787ef, + 0x000887ef, + 0x000987ef, + 0x000a87ef, + 0x000b87ef, + 0x000c87ef, + 0x000d87ef, + 0x000e87ef, + 0x000f87ef, + 0x0000e7f0, + 0x0002e7f0, + 0x0004e7f0, + 0x0006e7f0, + 0x0008e7f0, + 0x000ae7f0, + 0x000ce7f0, + 0x000ee7f0, + 0x0010e7f0, + 0x0012e7f0, + 0x0014e7f0, + 0x0016e7f0, + 0x0018e7f0, + 0x001ae7f0, + 0x001ce7f0, + 0x001ee7f0, + 0x0005fff3, + 0x000dfff3, + 0x0015fff3, + 0x001dfff3, + 0x0025fff3, + 0x002dfff3, + 0x0035fff3, + 0x003dfff3, + 0x0045fff3, + 0x004dfff3, + 0x0055fff3, + 0x005dfff3, + 0x0065fff3, + 0x006dfff3, + 0x0075fff3, + 0x007dfff3, + 0x0085fff3, + 0x008dfff3, + 0x0095fff3, + 0x009dfff3, + 0x00a5fff3, + 0x00adfff3, + 0x00b5fff3, + 0x00bdfff3, + 0x00c5fff3, + 0x00cdfff3, + 0x00d5fff3, + 0x00ddfff3, + 0x00e5fff3, + 0x00edfff3, + 0x00f5fff3, + 0x00fdfff3, + 0x0003fff3, + 0x000bfff3, + 0x0013fff3, + 0x001bfff3, + 0x0023fff3, + 0x002bfff3, + 0x0033fff3, + 0x003bfff3, + 0x0043fff3, + 0x004bfff3, + 0x0053fff3, + 0x005bfff3, + 0x0063fff3, + 0x006bfff3, + 0x0073fff3, + 0x007bfff3, + 0x0083fff3, + 0x008bfff3, + 0x0093fff3, + 0x009bfff3, + 0x00a3fff3, + 0x00abfff3, + 0x00b3fff3, + 0x00bbfff3, + 0x00c3fff3, + 0x00cbfff3, + 0x00d3fff3, + 0x00dbfff3, + 0x00e3fff3, + 0x00ebfff3, + 0x00f3fff3, + 0x00fbfff3, + 0x0007fff3, + 0x000ffff3, + 0x0017fff3, + 0x001ffff3, + 0x0027fff3, + 0x002ffff3, + 0x0037fff3, + 0x003ffff3, + 0x0047fff3, + 0x004ffff3, + 0x0057fff3, + 0x005ffff3, + 0x0067fff3, + 0x006ffff3, + 0x0077fff3, + 0x007ffff3, + 0x0087fff3, + 0x008ffff3, + 0x0097fff3, + 0x009ffff3, + 0x00a7fff3, + 0x00affff3, + 0x00b7fff3, + 0x00bffff3, + 0x00c7fff3, + 0x00cffff3, + 0x00d7fff3, + 0x00dffff3, + 0x00e7fff3, + 0x00effff3, + 0x00f7fff3, + 0x00fffff3, + 0x0001e7f1, + 0x0003e7f1, + 0x0005e7f1, + 0x0007e7f1, + 0x0009e7f1, + 0x000be7f1, + 0x000de7f1, + 0x000fe7f1, + 0x0011e7f1, + 0x0013e7f1, + 0x0015e7f1, + 0x0017e7f1, + 0x0019e7f1, + 0x001be7f1, + 0x001de7f1, + 0x001fe7f1, + 0x0021e7f1, + 0x0023e7f1, + 0x0025e7f1, + 0x0027e7f1, + 0x0029e7f1, + 0x002be7f1, + 0x002de7f1, + 0x002fe7f1, + 0x0031e7f1, + 0x0033e7f1, + 0x0035e7f1, + 0x0037e7f1, + 0x0039e7f1, + 0x003be7f1, + 0x003de7f1, + 0x000047eb + }; - internal static ReadOnlySpan FAST_ENCODER_TREE_STRUCTURE_DATA => - new byte[] + internal static readonly uint[] FAST_ENCODER_DISTANCE_CODE_INFO = + { + 0x00000f06, + 0x0001ff0a, + 0x0003ff0b, + 0x0007ff0b, + 0x0000ff19, + 0x00003f18, + 0x0000bf28, + 0x00007f28, + 0x00001f37, + 0x00005f37, + 0x00000d45, + 0x00002f46, + 0x00000054, + 0x00001d55, + 0x00000864, + 0x00000365, + 0x00000474, + 0x00001375, + 0x00000c84, + 0x00000284, + 0x00000a94, + 0x00000694, + 0x00000ea4, + 0x000001a4, + 0x000009b4, + 0x00000bb5, + 0x000005c4, + 0x00001bc5, + 0x000007d5, + 0x000017d5, + 0x00000000, + 0x00000100 + }; + + internal static readonly uint[] BIT_MASK = + { + 0, + 1, + 3, + 7, + 15, + 31, + 63, + 127, + 255, + 511, + 1023, + 2047, + 4095, + 8191, + 16383, + 32767 + }; + internal static readonly byte[] EXTRA_LENGTH_BITS = + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 0 + }; + internal static readonly byte[] EXTRA_DISTANCE_BITS = + { + 0, + 0, + 0, + 0, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 5, + 6, + 6, + 7, + 7, + 8, + 8, + 9, + 9, + 10, + 10, + 11, + 11, + 12, + 12, + 13, + 13, + 0, + 0 + }; + internal const int NUM_CHARS = 256; + internal const int NUM_LENGTH_BASE_CODES = 29; + internal const int NUM_DIST_BASE_CODES = 30; + + internal const uint FAST_ENCODER_POST_TREE_BIT_BUF = 0x0022; + internal const int FAST_ENCODER_POST_TREE_BIT_COUNT = 9; + + internal const uint NO_COMPRESSION_HEADER = 0x0; + internal const int NO_COMPRESSION_HEADER_BIT_COUNT = 3; + internal const uint B_FINAL_NO_COMPRESSION_HEADER = 0x1; + internal const int B_FINAL_NO_COMPRESSION_HEADER_BIT_COUNT = 3; + internal const int MAX_CODE_LEN = 16; + + private static readonly byte[] S_DIST_LOOKUP = CreateDistanceLookup(); + + private static byte[] CreateDistanceLookup() + { + var result = new byte[512]; + + // Generate the global slot tables which allow us to convert a distance + // (0..32K) to a distance slot (0..29) + // + // Distance table + // Extra Extra Extra + // Code Bits Dist Code Bits Dist Code Bits Distance + // ---- ---- ---- ---- ---- ------ ---- ---- -------- + // 0 0 1 10 4 33-48 20 9 1025-1536 + // 1 0 2 11 4 49-64 21 9 1537-2048 + // 2 0 3 12 5 65-96 22 10 2049-3072 + // 3 0 4 13 5 97-128 23 10 3073-4096 + // 4 1 5,6 14 6 129-192 24 11 4097-6144 + // 5 1 7,8 15 6 193-256 25 11 6145-8192 + // 6 2 9-12 16 7 257-384 26 12 8193-12288 + // 7 2 13-16 17 7 385-512 27 12 12289-16384 + // 8 3 17-24 18 8 513-768 28 13 16385-24576 + // 9 3 25-32 19 8 769-1024 29 13 24577-32768 + + // Initialize the mapping length (0..255) -> length code (0..28) + //int length = 0; + //for (code = 0; code < FastEncoderStatics.NumLengthBaseCodes-1; code++) { + // for (int n = 0; n < (1 << FastEncoderStatics.ExtraLengthBits[code]); n++) + // lengthLookup[length++] = (byte) code; + //} + //lengthLookup[length-1] = (byte) code; + + // Initialize the mapping dist (0..32K) -> dist code (0..29) + var dist = 0; + int code; + for (code = 0; code < 16; code++) + { + for (var n = 0; n < (1 << EXTRA_DISTANCE_BITS[code]); n++) { - 0xec, - 0xbd, - 0x07, - 0x60, - 0x1c, - 0x49, - 0x96, - 0x25, - 0x26, - 0x2f, - 0x6d, - 0xca, - 0x7b, - 0x7f, - 0x4a, - 0xf5, - 0x4a, - 0xd7, - 0xe0, - 0x74, - 0xa1, - 0x08, - 0x80, - 0x60, - 0x13, - 0x24, - 0xd8, - 0x90, - 0x40, - 0x10, - 0xec, - 0xc1, - 0x88, - 0xcd, - 0xe6, - 0x92, - 0xec, - 0x1d, - 0x69, - 0x47, - 0x23, - 0x29, - 0xab, - 0x2a, - 0x81, - 0xca, - 0x65, - 0x56, - 0x65, - 0x5d, - 0x66, - 0x16, - 0x40, - 0xcc, - 0xed, - 0x9d, - 0xbc, - 0xf7, - 0xde, - 0x7b, - 0xef, - 0xbd, - 0xf7, - 0xde, - 0x7b, - 0xef, - 0xbd, - 0xf7, - 0xba, - 0x3b, - 0x9d, - 0x4e, - 0x27, - 0xf7, - 0xdf, - 0xff, - 0x3f, - 0x5c, - 0x66, - 0x64, - 0x01, - 0x6c, - 0xf6, - 0xce, - 0x4a, - 0xda, - 0xc9, - 0x9e, - 0x21, - 0x80, - 0xaa, - 0xc8, - 0x1f, - 0x3f, - 0x7e, - 0x7c, - 0x1f, - 0x3f - }; - - internal static ReadOnlySpan B_FINAL_FAST_ENCODER_TREE_STRUCTURE_DATA => - new byte[] - { - 0xed, - 0xbd, - 0x07, - 0x60, - 0x1c, - 0x49, - 0x96, - 0x25, - 0x26, - 0x2f, - 0x6d, - 0xca, - 0x7b, - 0x7f, - 0x4a, - 0xf5, - 0x4a, - 0xd7, - 0xe0, - 0x74, - 0xa1, - 0x08, - 0x80, - 0x60, - 0x13, - 0x24, - 0xd8, - 0x90, - 0x40, - 0x10, - 0xec, - 0xc1, - 0x88, - 0xcd, - 0xe6, - 0x92, - 0xec, - 0x1d, - 0x69, - 0x47, - 0x23, - 0x29, - 0xab, - 0x2a, - 0x81, - 0xca, - 0x65, - 0x56, - 0x65, - 0x5d, - 0x66, - 0x16, - 0x40, - 0xcc, - 0xed, - 0x9d, - 0xbc, - 0xf7, - 0xde, - 0x7b, - 0xef, - 0xbd, - 0xf7, - 0xde, - 0x7b, - 0xef, - 0xbd, - 0xf7, - 0xba, - 0x3b, - 0x9d, - 0x4e, - 0x27, - 0xf7, - 0xdf, - 0xff, - 0x3f, - 0x5c, - 0x66, - 0x64, - 0x01, - 0x6c, - 0xf6, - 0xce, - 0x4a, - 0xda, - 0xc9, - 0x9e, - 0x21, - 0x80, - 0xaa, - 0xc8, - 0x1f, - 0x3f, - 0x7e, - 0x7c, - 0x1f, - 0x3f - }; - - // Output a currentMatch with length matchLen (>= MIN_MATCH) and displacement matchPos - // - // Optimisation: unlike the other encoders, here we have an array of codes for each currentMatch - // length (not just each currentMatch length slot), complete with all the extra bits filled in, in - // a single array element. - // - // There are many advantages to doing this: - // - // 1. A single array lookup on g_FastEncoderLiteralCodeInfo, instead of separate array lookups - // on g_LengthLookup (to get the length slot), g_FastEncoderLiteralTreeLength, - // g_FastEncoderLiteralTreeCode, g_ExtraLengthBits, and g_BitMask - // - // 2. The array is an array of ULONGs, so no access penalty, unlike for accessing those USHORT - // code arrays in the other encoders (although they could be made into ULONGs with some - // modifications to the source). - // - // Note, if we could guarantee that codeLen <= 16 always, then we could skip an if statement here. - // - // A completely different optimisation is used for the distance codes since, obviously, a table for - // all 8192 distances combining their extra bits is not feasible. The distance codeinfo table is - // made up of code[], len[] and # extraBits for this code. - // - // The advantages are similar to the above; a ULONG array instead of a USHORT and BYTE array, better - // cache locality, fewer memory operations. - // - - - // Encoding information for literal and Length. - // The least 5 significant bits are the length - // and the rest is the code bits. - - internal static readonly uint[] FAST_ENCODER_LITERAL_CODE_INFO = - { - 0x0000d7ee, - 0x0004d7ee, - 0x0002d7ee, - 0x0006d7ee, - 0x0001d7ee, - 0x0005d7ee, - 0x0003d7ee, - 0x0007d7ee, - 0x000037ee, - 0x0000c7ec, - 0x00000126, - 0x000437ee, - 0x000237ee, - 0x000637ee, - 0x000137ee, - 0x000537ee, - 0x000337ee, - 0x000737ee, - 0x0000b7ee, - 0x0004b7ee, - 0x0002b7ee, - 0x0006b7ee, - 0x0001b7ee, - 0x0005b7ee, - 0x0003b7ee, - 0x0007b7ee, - 0x000077ee, - 0x000477ee, - 0x000277ee, - 0x000677ee, - 0x000017ed, - 0x000177ee, - 0x00000526, - 0x000577ee, - 0x000023ea, - 0x0001c7ec, - 0x000377ee, - 0x000777ee, - 0x000217ed, - 0x000063ea, - 0x00000b68, - 0x00000ee9, - 0x00005beb, - 0x000013ea, - 0x00000467, - 0x00001b68, - 0x00000c67, - 0x00002ee9, - 0x00000768, - 0x00001768, - 0x00000f68, - 0x00001ee9, - 0x00001f68, - 0x00003ee9, - 0x000053ea, - 0x000001e9, - 0x000000e8, - 0x000021e9, - 0x000011e9, - 0x000010e8, - 0x000031e9, - 0x000033ea, - 0x000008e8, - 0x0000f7ee, - 0x0004f7ee, - 0x000018e8, - 0x000009e9, - 0x000004e8, - 0x000029e9, - 0x000014e8, - 0x000019e9, - 0x000073ea, - 0x0000dbeb, - 0x00000ce8, - 0x00003beb, - 0x0002f7ee, - 0x000039e9, - 0x00000bea, - 0x000005e9, - 0x00004bea, - 0x000025e9, - 0x000027ec, - 0x000015e9, - 0x000035e9, - 0x00000de9, - 0x00002bea, - 0x000127ec, - 0x0000bbeb, - 0x0006f7ee, - 0x0001f7ee, - 0x0000a7ec, - 0x00007beb, - 0x0005f7ee, - 0x0000fbeb, - 0x0003f7ee, - 0x0007f7ee, - 0x00000fee, - 0x00000326, - 0x00000267, - 0x00000a67, - 0x00000667, - 0x00000726, - 0x00001ce8, - 0x000002e8, - 0x00000e67, - 0x000000a6, - 0x0001a7ec, - 0x00002de9, - 0x000004a6, - 0x00000167, - 0x00000967, - 0x000002a6, - 0x00000567, - 0x000117ed, - 0x000006a6, - 0x000001a6, - 0x000005a6, - 0x00000d67, - 0x000012e8, - 0x00000ae8, - 0x00001de9, - 0x00001ae8, - 0x000007eb, - 0x000317ed, - 0x000067ec, - 0x000097ed, - 0x000297ed, - 0x00040fee, - 0x00020fee, - 0x00060fee, - 0x00010fee, - 0x00050fee, - 0x00030fee, - 0x00070fee, - 0x00008fee, - 0x00048fee, - 0x00028fee, - 0x00068fee, - 0x00018fee, - 0x00058fee, - 0x00038fee, - 0x00078fee, - 0x00004fee, - 0x00044fee, - 0x00024fee, - 0x00064fee, - 0x00014fee, - 0x00054fee, - 0x00034fee, - 0x00074fee, - 0x0000cfee, - 0x0004cfee, - 0x0002cfee, - 0x0006cfee, - 0x0001cfee, - 0x0005cfee, - 0x0003cfee, - 0x0007cfee, - 0x00002fee, - 0x00042fee, - 0x00022fee, - 0x00062fee, - 0x00012fee, - 0x00052fee, - 0x00032fee, - 0x00072fee, - 0x0000afee, - 0x0004afee, - 0x0002afee, - 0x0006afee, - 0x0001afee, - 0x0005afee, - 0x0003afee, - 0x0007afee, - 0x00006fee, - 0x00046fee, - 0x00026fee, - 0x00066fee, - 0x00016fee, - 0x00056fee, - 0x00036fee, - 0x00076fee, - 0x0000efee, - 0x0004efee, - 0x0002efee, - 0x0006efee, - 0x0001efee, - 0x0005efee, - 0x0003efee, - 0x0007efee, - 0x00001fee, - 0x00041fee, - 0x00021fee, - 0x00061fee, - 0x00011fee, - 0x00051fee, - 0x00031fee, - 0x00071fee, - 0x00009fee, - 0x00049fee, - 0x00029fee, - 0x00069fee, - 0x00019fee, - 0x00059fee, - 0x00039fee, - 0x00079fee, - 0x00005fee, - 0x00045fee, - 0x00025fee, - 0x00065fee, - 0x00015fee, - 0x00055fee, - 0x00035fee, - 0x00075fee, - 0x0000dfee, - 0x0004dfee, - 0x0002dfee, - 0x0006dfee, - 0x0001dfee, - 0x0005dfee, - 0x0003dfee, - 0x0007dfee, - 0x00003fee, - 0x00043fee, - 0x00023fee, - 0x00063fee, - 0x00013fee, - 0x00053fee, - 0x00033fee, - 0x00073fee, - 0x0000bfee, - 0x0004bfee, - 0x0002bfee, - 0x0006bfee, - 0x0001bfee, - 0x0005bfee, - 0x0003bfee, - 0x0007bfee, - 0x00007fee, - 0x00047fee, - 0x00027fee, - 0x00067fee, - 0x00017fee, - 0x000197ed, - 0x000397ed, - 0x000057ed, - 0x00057fee, - 0x000257ed, - 0x00037fee, - 0x000157ed, - 0x00077fee, - 0x000357ed, - 0x0000ffee, - 0x0004ffee, - 0x0002ffee, - 0x0006ffee, - 0x0001ffee, - 0x00000084, - 0x00000003, - 0x00000184, - 0x00000044, - 0x00000144, - 0x000000c5, - 0x000002c5, - 0x000001c5, - 0x000003c6, - 0x000007c6, - 0x00000026, - 0x00000426, - 0x000003a7, - 0x00000ba7, - 0x000007a7, - 0x00000fa7, - 0x00000227, - 0x00000627, - 0x00000a27, - 0x00000e27, - 0x00000068, - 0x00000868, - 0x00001068, - 0x00001868, - 0x00000369, - 0x00001369, - 0x00002369, - 0x00003369, - 0x000006ea, - 0x000026ea, - 0x000046ea, - 0x000066ea, - 0x000016eb, - 0x000036eb, - 0x000056eb, - 0x000076eb, - 0x000096eb, - 0x0000b6eb, - 0x0000d6eb, - 0x0000f6eb, - 0x00003dec, - 0x00007dec, - 0x0000bdec, - 0x0000fdec, - 0x00013dec, - 0x00017dec, - 0x0001bdec, - 0x0001fdec, - 0x00006bed, - 0x0000ebed, - 0x00016bed, - 0x0001ebed, - 0x00026bed, - 0x0002ebed, - 0x00036bed, - 0x0003ebed, - 0x000003ec, - 0x000043ec, - 0x000083ec, - 0x0000c3ec, - 0x000103ec, - 0x000143ec, - 0x000183ec, - 0x0001c3ec, - 0x00001bee, - 0x00009bee, - 0x00011bee, - 0x00019bee, - 0x00021bee, - 0x00029bee, - 0x00031bee, - 0x00039bee, - 0x00041bee, - 0x00049bee, - 0x00051bee, - 0x00059bee, - 0x00061bee, - 0x00069bee, - 0x00071bee, - 0x00079bee, - 0x000167f0, - 0x000367f0, - 0x000567f0, - 0x000767f0, - 0x000967f0, - 0x000b67f0, - 0x000d67f0, - 0x000f67f0, - 0x001167f0, - 0x001367f0, - 0x001567f0, - 0x001767f0, - 0x001967f0, - 0x001b67f0, - 0x001d67f0, - 0x001f67f0, - 0x000087ef, - 0x000187ef, - 0x000287ef, - 0x000387ef, - 0x000487ef, - 0x000587ef, - 0x000687ef, - 0x000787ef, - 0x000887ef, - 0x000987ef, - 0x000a87ef, - 0x000b87ef, - 0x000c87ef, - 0x000d87ef, - 0x000e87ef, - 0x000f87ef, - 0x0000e7f0, - 0x0002e7f0, - 0x0004e7f0, - 0x0006e7f0, - 0x0008e7f0, - 0x000ae7f0, - 0x000ce7f0, - 0x000ee7f0, - 0x0010e7f0, - 0x0012e7f0, - 0x0014e7f0, - 0x0016e7f0, - 0x0018e7f0, - 0x001ae7f0, - 0x001ce7f0, - 0x001ee7f0, - 0x0005fff3, - 0x000dfff3, - 0x0015fff3, - 0x001dfff3, - 0x0025fff3, - 0x002dfff3, - 0x0035fff3, - 0x003dfff3, - 0x0045fff3, - 0x004dfff3, - 0x0055fff3, - 0x005dfff3, - 0x0065fff3, - 0x006dfff3, - 0x0075fff3, - 0x007dfff3, - 0x0085fff3, - 0x008dfff3, - 0x0095fff3, - 0x009dfff3, - 0x00a5fff3, - 0x00adfff3, - 0x00b5fff3, - 0x00bdfff3, - 0x00c5fff3, - 0x00cdfff3, - 0x00d5fff3, - 0x00ddfff3, - 0x00e5fff3, - 0x00edfff3, - 0x00f5fff3, - 0x00fdfff3, - 0x0003fff3, - 0x000bfff3, - 0x0013fff3, - 0x001bfff3, - 0x0023fff3, - 0x002bfff3, - 0x0033fff3, - 0x003bfff3, - 0x0043fff3, - 0x004bfff3, - 0x0053fff3, - 0x005bfff3, - 0x0063fff3, - 0x006bfff3, - 0x0073fff3, - 0x007bfff3, - 0x0083fff3, - 0x008bfff3, - 0x0093fff3, - 0x009bfff3, - 0x00a3fff3, - 0x00abfff3, - 0x00b3fff3, - 0x00bbfff3, - 0x00c3fff3, - 0x00cbfff3, - 0x00d3fff3, - 0x00dbfff3, - 0x00e3fff3, - 0x00ebfff3, - 0x00f3fff3, - 0x00fbfff3, - 0x0007fff3, - 0x000ffff3, - 0x0017fff3, - 0x001ffff3, - 0x0027fff3, - 0x002ffff3, - 0x0037fff3, - 0x003ffff3, - 0x0047fff3, - 0x004ffff3, - 0x0057fff3, - 0x005ffff3, - 0x0067fff3, - 0x006ffff3, - 0x0077fff3, - 0x007ffff3, - 0x0087fff3, - 0x008ffff3, - 0x0097fff3, - 0x009ffff3, - 0x00a7fff3, - 0x00affff3, - 0x00b7fff3, - 0x00bffff3, - 0x00c7fff3, - 0x00cffff3, - 0x00d7fff3, - 0x00dffff3, - 0x00e7fff3, - 0x00effff3, - 0x00f7fff3, - 0x00fffff3, - 0x0001e7f1, - 0x0003e7f1, - 0x0005e7f1, - 0x0007e7f1, - 0x0009e7f1, - 0x000be7f1, - 0x000de7f1, - 0x000fe7f1, - 0x0011e7f1, - 0x0013e7f1, - 0x0015e7f1, - 0x0017e7f1, - 0x0019e7f1, - 0x001be7f1, - 0x001de7f1, - 0x001fe7f1, - 0x0021e7f1, - 0x0023e7f1, - 0x0025e7f1, - 0x0027e7f1, - 0x0029e7f1, - 0x002be7f1, - 0x002de7f1, - 0x002fe7f1, - 0x0031e7f1, - 0x0033e7f1, - 0x0035e7f1, - 0x0037e7f1, - 0x0039e7f1, - 0x003be7f1, - 0x003de7f1, - 0x000047eb - }; - - internal static readonly uint[] FAST_ENCODER_DISTANCE_CODE_INFO = - { - 0x00000f06, - 0x0001ff0a, - 0x0003ff0b, - 0x0007ff0b, - 0x0000ff19, - 0x00003f18, - 0x0000bf28, - 0x00007f28, - 0x00001f37, - 0x00005f37, - 0x00000d45, - 0x00002f46, - 0x00000054, - 0x00001d55, - 0x00000864, - 0x00000365, - 0x00000474, - 0x00001375, - 0x00000c84, - 0x00000284, - 0x00000a94, - 0x00000694, - 0x00000ea4, - 0x000001a4, - 0x000009b4, - 0x00000bb5, - 0x000005c4, - 0x00001bc5, - 0x000007d5, - 0x000017d5, - 0x00000000, - 0x00000100 - }; - - internal static readonly uint[] BIT_MASK = - { - 0, - 1, - 3, - 7, - 15, - 31, - 63, - 127, - 255, - 511, - 1023, - 2047, - 4095, - 8191, - 16383, - 32767 - }; - internal static readonly byte[] EXTRA_LENGTH_BITS = - { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 4, - 4, - 4, - 4, - 5, - 5, - 5, - 5, - 0 - }; - internal static readonly byte[] EXTRA_DISTANCE_BITS = - { - 0, - 0, - 0, - 0, - 1, - 1, - 2, - 2, - 3, - 3, - 4, - 4, - 5, - 5, - 6, - 6, - 7, - 7, - 8, - 8, - 9, - 9, - 10, - 10, - 11, - 11, - 12, - 12, - 13, - 13, - 0, - 0 - }; - internal const int NUM_CHARS = 256; - internal const int NUM_LENGTH_BASE_CODES = 29; - internal const int NUM_DIST_BASE_CODES = 30; - - internal const uint FAST_ENCODER_POST_TREE_BIT_BUF = 0x0022; - internal const int FAST_ENCODER_POST_TREE_BIT_COUNT = 9; - - internal const uint NO_COMPRESSION_HEADER = 0x0; - internal const int NO_COMPRESSION_HEADER_BIT_COUNT = 3; - internal const uint B_FINAL_NO_COMPRESSION_HEADER = 0x1; - internal const int B_FINAL_NO_COMPRESSION_HEADER_BIT_COUNT = 3; - internal const int MAX_CODE_LEN = 16; - - private static readonly byte[] S_DIST_LOOKUP = CreateDistanceLookup(); - - private static byte[] CreateDistanceLookup() - { - byte[] result = new byte[512]; - - // Generate the global slot tables which allow us to convert a distance - // (0..32K) to a distance slot (0..29) - // - // Distance table - // Extra Extra Extra - // Code Bits Dist Code Bits Dist Code Bits Distance - // ---- ---- ---- ---- ---- ------ ---- ---- -------- - // 0 0 1 10 4 33-48 20 9 1025-1536 - // 1 0 2 11 4 49-64 21 9 1537-2048 - // 2 0 3 12 5 65-96 22 10 2049-3072 - // 3 0 4 13 5 97-128 23 10 3073-4096 - // 4 1 5,6 14 6 129-192 24 11 4097-6144 - // 5 1 7,8 15 6 193-256 25 11 6145-8192 - // 6 2 9-12 16 7 257-384 26 12 8193-12288 - // 7 2 13-16 17 7 385-512 27 12 12289-16384 - // 8 3 17-24 18 8 513-768 28 13 16385-24576 - // 9 3 25-32 19 8 769-1024 29 13 24577-32768 - - // Initialize the mapping length (0..255) -> length code (0..28) - //int length = 0; - //for (code = 0; code < FastEncoderStatics.NumLengthBaseCodes-1; code++) { - // for (int n = 0; n < (1 << FastEncoderStatics.ExtraLengthBits[code]); n++) - // lengthLookup[length++] = (byte) code; - //} - //lengthLookup[length-1] = (byte) code; - - // Initialize the mapping dist (0..32K) -> dist code (0..29) - int dist = 0; - int code; - for (code = 0; code < 16; code++) - { - for (int n = 0; n < (1 << EXTRA_DISTANCE_BITS[code]); n++) - { - result[dist++] = (byte)code; - } + result[dist++] = (byte)code; } - - dist >>= 7; // from now on, all distances are divided by 128 - - for (; code < NUM_DIST_BASE_CODES; code++) - { - for (int n = 0; n < (1 << (EXTRA_DISTANCE_BITS[code] - 7)); n++) - { - result[256 + dist++] = (byte)code; - } - } - - return result; } - // Return the position slot (0...29) of a match offset (0...32767) - internal static int GetSlot(int pos) => - S_DIST_LOOKUP[((pos) < 256) ? (pos) : (256 + ((pos) >> 7))]; + dist >>= 7; // from now on, all distances are divided by 128 - // Reverse 'length' of the bits in code - public static uint BitReverse(uint code, int length) + for (; code < NUM_DIST_BASE_CODES; code++) { - uint newCode = 0; - - Debug.Assert(length > 0 && length <= 16, "Invalid len"); - do + for (var n = 0; n < (1 << (EXTRA_DISTANCE_BITS[code] - 7)); n++) { - newCode |= (code & 1); - newCode <<= 1; - code >>= 1; - } while (--length > 0); - - return newCode >> 1; + result[256 + dist++] = (byte)code; + } } + + return result; + } + + // Return the position slot (0...29) of a match offset (0...32767) + internal static int GetSlot(int pos) => + S_DIST_LOOKUP[((pos) < 256) ? (pos) : (256 + ((pos) >> 7))]; + + // Reverse 'length' of the bits in code + public static uint BitReverse(uint code, int length) + { + uint newCode = 0; + + Debug.Assert(length > 0 && length <= 16, "Invalid len"); + do + { + newCode |= (code & 1); + newCode <<= 1; + code >>= 1; + } while (--length > 0); + + return newCode >> 1; } } diff --git a/src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs b/src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs index eac0ff5f..051b613e 100644 --- a/src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs +++ b/src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs @@ -6,326 +6,325 @@ using System; using System.Diagnostics; using System.IO; -namespace SharpCompress.Compressors.Deflate64 +namespace SharpCompress.Compressors.Deflate64; + +// Strictly speaking this class is not a HuffmanTree, this class is +// a lookup table combined with a HuffmanTree. The idea is to speed up +// the lookup for short symbols (they should appear more frequently ideally.) +// However we don't want to create a huge table since it might take longer to +// build the table than decoding (Deflate usually generates new tables frequently.) +// +// Jean-loup Gailly and Mark Adler gave a very good explanation about this. +// The full text (algorithm.txt) can be found inside +// ftp://ftp.uu.net/pub/archiving/zip/zlib/zlib.zip. +// +// Following paper explains decoding in details: +// Hirschberg and Lelewer, "Efficient decoding of prefix codes," +// Comm. ACM, 33,4, April 1990, pp. 449-459. +// + +internal sealed class HuffmanTree { - // Strictly speaking this class is not a HuffmanTree, this class is - // a lookup table combined with a HuffmanTree. The idea is to speed up - // the lookup for short symbols (they should appear more frequently ideally.) - // However we don't want to create a huge table since it might take longer to - // build the table than decoding (Deflate usually generates new tables frequently.) - // - // Jean-loup Gailly and Mark Adler gave a very good explanation about this. - // The full text (algorithm.txt) can be found inside - // ftp://ftp.uu.net/pub/archiving/zip/zlib/zlib.zip. - // - // Following paper explains decoding in details: - // Hirschberg and Lelewer, "Efficient decoding of prefix codes," - // Comm. ACM, 33,4, April 1990, pp. 449-459. - // + internal const int MAX_LITERAL_TREE_ELEMENTS = 288; + internal const int MAX_DIST_TREE_ELEMENTS = 32; + internal const int END_OF_BLOCK_CODE = 256; + internal const int NUMBER_OF_CODE_LENGTH_TREE_ELEMENTS = 19; - internal sealed class HuffmanTree + private readonly int _tableBits; + private readonly short[] _table; + private readonly short[] _left; + private readonly short[] _right; + private readonly byte[] _codeLengthArray; +#if DEBUG + private uint[]? _codeArrayDebug; +#endif + + private readonly int _tableMask; + + // huffman tree for static block + public static HuffmanTree StaticLiteralLengthTree { get; } = + new HuffmanTree(GetStaticLiteralTreeLength()); + + public static HuffmanTree StaticDistanceTree { get; } = + new HuffmanTree(GetStaticDistanceTreeLength()); + + public HuffmanTree(byte[] codeLengths) { - internal const int MAX_LITERAL_TREE_ELEMENTS = 288; - internal const int MAX_DIST_TREE_ELEMENTS = 32; - internal const int END_OF_BLOCK_CODE = 256; - internal const int NUMBER_OF_CODE_LENGTH_TREE_ELEMENTS = 19; + Debug.Assert( + codeLengths.Length == MAX_LITERAL_TREE_ELEMENTS + || codeLengths.Length == MAX_DIST_TREE_ELEMENTS + || codeLengths.Length == NUMBER_OF_CODE_LENGTH_TREE_ELEMENTS, + "we only expect three kinds of Length here" + ); + _codeLengthArray = codeLengths; - private readonly int _tableBits; - private readonly short[] _table; - private readonly short[] _left; - private readonly short[] _right; - private readonly byte[] _codeLengthArray; + if (_codeLengthArray.Length == MAX_LITERAL_TREE_ELEMENTS) + { + // bits for Literal/Length tree table + _tableBits = 9; + } + else + { + // bits for distance tree table and code length tree table + _tableBits = 7; + } + _tableMask = (1 << _tableBits) - 1; + + _table = new short[1 << _tableBits]; + + // I need to find proof that left and right array will always be + // enough. I think they are. + _left = new short[2 * _codeLengthArray.Length]; + _right = new short[2 * _codeLengthArray.Length]; + + CreateTable(); + } + + // Generate the array contains huffman codes lengths for static huffman tree. + // The data is in RFC 1951. + private static byte[] GetStaticLiteralTreeLength() + { + var literalTreeLength = new byte[MAX_LITERAL_TREE_ELEMENTS]; + for (var i = 0; i <= 143; i++) + { + literalTreeLength[i] = 8; + } + + for (var i = 144; i <= 255; i++) + { + literalTreeLength[i] = 9; + } + + for (var i = 256; i <= 279; i++) + { + literalTreeLength[i] = 7; + } + + for (var i = 280; i <= 287; i++) + { + literalTreeLength[i] = 8; + } + + return literalTreeLength; + } + + private static byte[] GetStaticDistanceTreeLength() + { + var staticDistanceTreeLength = new byte[MAX_DIST_TREE_ELEMENTS]; + for (var i = 0; i < MAX_DIST_TREE_ELEMENTS; i++) + { + staticDistanceTreeLength[i] = 5; + } + return staticDistanceTreeLength; + } + + // Calculate the huffman code for each character based on the code length for each character. + // This algorithm is described in standard RFC 1951 + private uint[] CalculateHuffmanCode() + { + Span bitLengthCount = stackalloc uint[17]; + foreach (int codeLength in _codeLengthArray) + { + bitLengthCount[codeLength]++; + } + bitLengthCount[0] = 0; // clear count for length 0 + + Span nextCode = stackalloc uint[17]; + uint tempCode = 0; + for (var bits = 1; bits <= 16; bits++) + { + tempCode = (tempCode + bitLengthCount[bits - 1]) << 1; + nextCode[bits] = tempCode; + } + + var code = new uint[MAX_LITERAL_TREE_ELEMENTS]; + for (var i = 0; i < _codeLengthArray.Length; i++) + { + int len = _codeLengthArray[i]; + + if (len > 0) + { + code[i] = FastEncoderStatics.BitReverse(nextCode[len], len); + nextCode[len]++; + } + } + return code; + } + + private void CreateTable() + { + var codeArray = CalculateHuffmanCode(); #if DEBUG - private uint[]? _codeArrayDebug; + _codeArrayDebug = codeArray; #endif - private readonly int _tableMask; + var avail = (short)_codeLengthArray.Length; - // huffman tree for static block - public static HuffmanTree StaticLiteralLengthTree { get; } = - new HuffmanTree(GetStaticLiteralTreeLength()); - - public static HuffmanTree StaticDistanceTree { get; } = - new HuffmanTree(GetStaticDistanceTreeLength()); - - public HuffmanTree(byte[] codeLengths) + for (var ch = 0; ch < _codeLengthArray.Length; ch++) { - Debug.Assert( - codeLengths.Length == MAX_LITERAL_TREE_ELEMENTS - || codeLengths.Length == MAX_DIST_TREE_ELEMENTS - || codeLengths.Length == NUMBER_OF_CODE_LENGTH_TREE_ELEMENTS, - "we only expect three kinds of Length here" - ); - _codeLengthArray = codeLengths; - - if (_codeLengthArray.Length == MAX_LITERAL_TREE_ELEMENTS) + // length of this code + int len = _codeLengthArray[ch]; + if (len > 0) { - // bits for Literal/Length tree table - _tableBits = 9; - } - else - { - // bits for distance tree table and code length tree table - _tableBits = 7; - } - _tableMask = (1 << _tableBits) - 1; + // start value (bit reversed) + var start = (int)codeArray[ch]; - _table = new short[1 << _tableBits]; - - // I need to find proof that left and right array will always be - // enough. I think they are. - _left = new short[2 * _codeLengthArray.Length]; - _right = new short[2 * _codeLengthArray.Length]; - - CreateTable(); - } - - // Generate the array contains huffman codes lengths for static huffman tree. - // The data is in RFC 1951. - private static byte[] GetStaticLiteralTreeLength() - { - byte[] literalTreeLength = new byte[MAX_LITERAL_TREE_ELEMENTS]; - for (int i = 0; i <= 143; i++) - { - literalTreeLength[i] = 8; - } - - for (int i = 144; i <= 255; i++) - { - literalTreeLength[i] = 9; - } - - for (int i = 256; i <= 279; i++) - { - literalTreeLength[i] = 7; - } - - for (int i = 280; i <= 287; i++) - { - literalTreeLength[i] = 8; - } - - return literalTreeLength; - } - - private static byte[] GetStaticDistanceTreeLength() - { - byte[] staticDistanceTreeLength = new byte[MAX_DIST_TREE_ELEMENTS]; - for (int i = 0; i < MAX_DIST_TREE_ELEMENTS; i++) - { - staticDistanceTreeLength[i] = 5; - } - return staticDistanceTreeLength; - } - - // Calculate the huffman code for each character based on the code length for each character. - // This algorithm is described in standard RFC 1951 - private uint[] CalculateHuffmanCode() - { - Span bitLengthCount = stackalloc uint[17]; - foreach (int codeLength in _codeLengthArray) - { - bitLengthCount[codeLength]++; - } - bitLengthCount[0] = 0; // clear count for length 0 - - Span nextCode = stackalloc uint[17]; - uint tempCode = 0; - for (int bits = 1; bits <= 16; bits++) - { - tempCode = (tempCode + bitLengthCount[bits - 1]) << 1; - nextCode[bits] = tempCode; - } - - uint[] code = new uint[MAX_LITERAL_TREE_ELEMENTS]; - for (int i = 0; i < _codeLengthArray.Length; i++) - { - int len = _codeLengthArray[i]; - - if (len > 0) + if (len <= _tableBits) { - code[i] = FastEncoderStatics.BitReverse(nextCode[len], len); - nextCode[len]++; - } - } - return code; - } - - private void CreateTable() - { - uint[] codeArray = CalculateHuffmanCode(); -#if DEBUG - _codeArrayDebug = codeArray; -#endif - - short avail = (short)_codeLengthArray.Length; - - for (int ch = 0; ch < _codeLengthArray.Length; ch++) - { - // length of this code - int len = _codeLengthArray[ch]; - if (len > 0) - { - // start value (bit reversed) - int start = (int)codeArray[ch]; - - if (len <= _tableBits) + // If a particular symbol is shorter than nine bits, + // then that symbol's translation is duplicated + // in all those entries that start with that symbol's bits. + // For example, if the symbol is four bits, then it's duplicated + // 32 times in a nine-bit table. If a symbol is nine bits long, + // it appears in the table once. + // + // Make sure that in the loop below, code is always + // less than table_size. + // + // On last iteration we store at array index: + // initial_start_at + (locs-1)*increment + // = initial_start_at + locs*increment - increment + // = initial_start_at + (1 << tableBits) - increment + // = initial_start_at + table_size - increment + // + // Therefore we must ensure: + // initial_start_at + table_size - increment < table_size + // or: initial_start_at < increment + // + var increment = 1 << len; + if (start >= increment) { - // If a particular symbol is shorter than nine bits, - // then that symbol's translation is duplicated - // in all those entries that start with that symbol's bits. - // For example, if the symbol is four bits, then it's duplicated - // 32 times in a nine-bit table. If a symbol is nine bits long, - // it appears in the table once. - // - // Make sure that in the loop below, code is always - // less than table_size. - // - // On last iteration we store at array index: - // initial_start_at + (locs-1)*increment - // = initial_start_at + locs*increment - increment - // = initial_start_at + (1 << tableBits) - increment - // = initial_start_at + table_size - increment - // - // Therefore we must ensure: - // initial_start_at + table_size - increment < table_size - // or: initial_start_at < increment - // - int increment = 1 << len; - if (start >= increment) + throw new InvalidDataException("Deflate64: invalid Huffman data"); + } + + // Note the bits in the table are reverted. + var locs = 1 << (_tableBits - len); + for (var j = 0; j < locs; j++) + { + _table[start] = (short)ch; + start += increment; + } + } + else + { + // For any code which has length longer than num_elements, + // build a binary tree. + + var overflowBits = len - _tableBits; // the nodes we need to respent the data. + var codeBitMask = 1 << _tableBits; // mask to get current bit (the bits can't fit in the table) + + // the left, right table is used to repesent the + // the rest bits. When we got the first part (number bits.) and look at + // tbe table, we will need to follow the tree to find the real character. + // This is in place to avoid bloating the table if there are + // a few ones with long code. + var index = start & ((1 << _tableBits) - 1); + var array = _table; + + do + { + var value = array[index]; + + if (value == 0) { + // set up next pointer if this node is not used before. + array[index] = (short)-avail; // use next available slot. + value = (short)-avail; + avail++; + } + + if (value > 0) + { + // prevent an IndexOutOfRangeException from array[index] throw new InvalidDataException("Deflate64: invalid Huffman data"); } - // Note the bits in the table are reverted. - int locs = 1 << (_tableBits - len); - for (int j = 0; j < locs; j++) + Debug.Assert( + value < 0, + "CreateTable: Only negative numbers are used for tree pointers!" + ); + + if ((start & codeBitMask) == 0) { - _table[start] = (short)ch; - start += increment; + // if current bit is 0, go change the left array + array = _left; } - } - else - { - // For any code which has length longer than num_elements, - // build a binary tree. - - int overflowBits = len - _tableBits; // the nodes we need to respent the data. - int codeBitMask = 1 << _tableBits; // mask to get current bit (the bits can't fit in the table) - - // the left, right table is used to repesent the - // the rest bits. When we got the first part (number bits.) and look at - // tbe table, we will need to follow the tree to find the real character. - // This is in place to avoid bloating the table if there are - // a few ones with long code. - int index = start & ((1 << _tableBits) - 1); - short[] array = _table; - - do + else { - short value = array[index]; + // if current bit is 1, set value in the right array + array = _right; + } + index = -value; // go to next node - if (value == 0) - { - // set up next pointer if this node is not used before. - array[index] = (short)-avail; // use next available slot. - value = (short)-avail; - avail++; - } + codeBitMask <<= 1; + overflowBits--; + } while (overflowBits != 0); - if (value > 0) - { - // prevent an IndexOutOfRangeException from array[index] - throw new InvalidDataException("Deflate64: invalid Huffman data"); - } - - Debug.Assert( - value < 0, - "CreateTable: Only negative numbers are used for tree pointers!" - ); - - if ((start & codeBitMask) == 0) - { - // if current bit is 0, go change the left array - array = _left; - } - else - { - // if current bit is 1, set value in the right array - array = _right; - } - index = -value; // go to next node - - codeBitMask <<= 1; - overflowBits--; - } while (overflowBits != 0); - - array[index] = (short)ch; - } + array[index] = (short)ch; } } } + } - // - // This function will try to get enough bits from input and - // try to decode the bits. - // If there are no enought bits in the input, this function will return -1. - // - public int GetNextSymbol(InputBuffer input) - { - // Try to load 16 bits into input buffer if possible and get the bitBuffer value. - // If there aren't 16 bits available we will return all we have in the - // input buffer. - uint bitBuffer = input.TryLoad16Bits(); - if (input.AvailableBits == 0) - { // running out of input. - return -1; - } - - // decode an element - int symbol = _table[bitBuffer & _tableMask]; - if (symbol < 0) - { // this will be the start of the binary tree - // navigate the tree - uint mask = (uint)1 << _tableBits; - do - { - symbol = -symbol; - if ((bitBuffer & mask) == 0) - { - symbol = _left[symbol]; - } - else - { - symbol = _right[symbol]; - } - - mask <<= 1; - } while (symbol < 0); - } - - int codeLength = _codeLengthArray[symbol]; - - // huffman code lengths must be at least 1 bit long - if (codeLength <= 0) - { - throw new InvalidDataException("Deflate64: invalid Huffman data"); - } - - // - // If this code is longer than the # bits we had in the bit buffer (i.e. - // we read only part of the code), we can hit the entry in the table or the tree - // for another symbol. However the length of another symbol will not match the - // available bits count. - if (codeLength > input.AvailableBits) - { - // We already tried to load 16 bits and maximum length is 15, - // so this means we are running out of input. - return -1; - } - - input.SkipBits(codeLength); - return symbol; + // + // This function will try to get enough bits from input and + // try to decode the bits. + // If there are no enought bits in the input, this function will return -1. + // + public int GetNextSymbol(InputBuffer input) + { + // Try to load 16 bits into input buffer if possible and get the bitBuffer value. + // If there aren't 16 bits available we will return all we have in the + // input buffer. + var bitBuffer = input.TryLoad16Bits(); + if (input.AvailableBits == 0) + { // running out of input. + return -1; } + + // decode an element + int symbol = _table[bitBuffer & _tableMask]; + if (symbol < 0) + { // this will be the start of the binary tree + // navigate the tree + var mask = (uint)1 << _tableBits; + do + { + symbol = -symbol; + if ((bitBuffer & mask) == 0) + { + symbol = _left[symbol]; + } + else + { + symbol = _right[symbol]; + } + + mask <<= 1; + } while (symbol < 0); + } + + int codeLength = _codeLengthArray[symbol]; + + // huffman code lengths must be at least 1 bit long + if (codeLength <= 0) + { + throw new InvalidDataException("Deflate64: invalid Huffman data"); + } + + // + // If this code is longer than the # bits we had in the bit buffer (i.e. + // we read only part of the code), we can hit the entry in the table or the tree + // for another symbol. However the length of another symbol will not match the + // available bits count. + if (codeLength > input.AvailableBits) + { + // We already tried to load 16 bits and maximum length is 15, + // so this means we are running out of input. + return -1; + } + + input.SkipBits(codeLength); + return symbol; } } diff --git a/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs b/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs index 3864ff44..a4890899 100644 --- a/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs +++ b/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs @@ -32,818 +32,621 @@ using System; using System.Diagnostics; using System.IO; -namespace SharpCompress.Compressors.Deflate64 +namespace SharpCompress.Compressors.Deflate64; + +internal sealed class InflaterManaged { - internal sealed class InflaterManaged - { - // const tables used in decoding: + // const tables used in decoding: - // Extra bits for length code 257 - 285. - private static ReadOnlySpan S_EXTRA_LENGTH_BITS => - new byte[] - { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 4, - 4, - 4, - 4, - 5, - 5, - 5, - 5, - 16 - }; - - // The base length for length code 257 - 285. - // The formula to get the real length for a length code is lengthBase[code - 257] + (value stored in extraBits) - private static readonly int[] S_LENGTH_BASE = - { - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 13, - 15, - 17, - 19, - 23, - 27, - 31, - 35, - 43, - 51, - 59, - 67, - 83, - 99, - 115, - 131, - 163, - 195, - 227, - 3 - }; - - // The base distance for distance code 0 - 31 - // The real distance for a distance code is distanceBasePosition[code] + (value stored in extraBits) - private static readonly int[] S_DISTANCE_BASE_POSITION = + // Extra bits for length code 257 - 285. + private static ReadOnlySpan S_EXTRA_LENGTH_BITS => + new byte[] { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, 1, 2, + 2, + 2, + 2, + 3, + 3, + 3, 3, 4, + 4, + 4, + 4, 5, - 7, - 9, - 13, - 17, - 25, - 33, - 49, - 65, - 97, - 129, - 193, - 257, - 385, - 513, - 769, - 1025, - 1537, - 2049, - 3073, - 4097, - 6145, - 8193, - 12289, - 16385, - 24577, - 32769, - 49153 + 5, + 5, + 5, + 16 }; - // code lengths for code length alphabet is stored in following order - private static ReadOnlySpan S_CODE_ORDER => - new byte[] { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + // The base length for length code 257 - 285. + // The formula to get the real length for a length code is lengthBase[code - 257] + (value stored in extraBits) + private static readonly int[] S_LENGTH_BASE = + { + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 13, + 15, + 17, + 19, + 23, + 27, + 31, + 35, + 43, + 51, + 59, + 67, + 83, + 99, + 115, + 131, + 163, + 195, + 227, + 3 + }; - private static ReadOnlySpan S_STATIC_DISTANCE_TREE_TABLE => - new byte[] + // The base distance for distance code 0 - 31 + // The real distance for a distance code is distanceBasePosition[code] + (value stored in extraBits) + private static readonly int[] S_DISTANCE_BASE_POSITION = + { + 1, + 2, + 3, + 4, + 5, + 7, + 9, + 13, + 17, + 25, + 33, + 49, + 65, + 97, + 129, + 193, + 257, + 385, + 513, + 769, + 1025, + 1537, + 2049, + 3073, + 4097, + 6145, + 8193, + 12289, + 16385, + 24577, + 32769, + 49153 + }; + + // code lengths for code length alphabet is stored in following order + private static ReadOnlySpan S_CODE_ORDER => + new byte[] { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + private static ReadOnlySpan S_STATIC_DISTANCE_TREE_TABLE => + new byte[] + { + 0x00, + 0x10, + 0x08, + 0x18, + 0x04, + 0x14, + 0x0c, + 0x1c, + 0x02, + 0x12, + 0x0a, + 0x1a, + 0x06, + 0x16, + 0x0e, + 0x1e, + 0x01, + 0x11, + 0x09, + 0x19, + 0x05, + 0x15, + 0x0d, + 0x1d, + 0x03, + 0x13, + 0x0b, + 0x1b, + 0x07, + 0x17, + 0x0f, + 0x1f + }; + + private readonly OutputWindow _output; + private readonly InputBuffer _input; + private HuffmanTree _literalLengthTree; + private HuffmanTree _distanceTree; + + private InflaterState _state; + + //private bool _hasFormatReader; + private int _bfinal; + private BlockType _blockType; + + // uncompressed block + private readonly byte[] _blockLengthBuffer = new byte[4]; + private int _blockLength; + + // compressed block + private int _length; + private int _distanceCode; + private int _extraBits; + + private int _loopCounter; + private int _literalLengthCodeCount; + private int _distanceCodeCount; + private int _codeLengthCodeCount; + private int _codeArraySize; + private int _lengthCode; + + private readonly byte[] _codeList; // temporary array to store the code length for literal/Length and distance + private readonly byte[] _codeLengthTreeCodeLength; + private readonly bool _deflate64; + private HuffmanTree _codeLengthTree; + + //private IFileFormatReader _formatReader; // class to decode header and footer (e.g. gzip) + + internal InflaterManaged( /*IFileFormatReader reader, */ + bool deflate64 + ) + { + _output = new OutputWindow(); + _input = new InputBuffer(); + + _codeList = new byte[ + HuffmanTree.MAX_LITERAL_TREE_ELEMENTS + HuffmanTree.MAX_DIST_TREE_ELEMENTS + ]; + _codeLengthTreeCodeLength = new byte[HuffmanTree.NUMBER_OF_CODE_LENGTH_TREE_ELEMENTS]; + _deflate64 = deflate64; + //if (reader != null) + //{ + // _formatReader = reader; + // _hasFormatReader = true; + //} + Reset(); + } + + private void Reset() + { + _state = //_hasFormatReader ? + //InflaterState.ReadingHeader : // start by reading Header info + InflaterState.ReadingBFinal; // start by reading BFinal bit + } + + public void SetInput(byte[] inputBytes, int offset, int length) => + _input.SetInput(inputBytes, offset, length); // append the bytes + + public bool Finished() => + _state == InflaterState.Done || _state == InflaterState.VerifyingFooter; + + public int AvailableOutput => _output.AvailableBytes; + + public int Inflate(byte[] bytes, int offset, int length) + { + // copy bytes from output to outputbytes if we have available bytes + // if buffer is not filled up. keep decoding until no input are available + // if decodeBlock returns false. Throw an exception. + var count = 0; + do + { + var copied = _output.CopyTo(bytes, offset, length); + if (copied > 0) { - 0x00, - 0x10, - 0x08, - 0x18, - 0x04, - 0x14, - 0x0c, - 0x1c, - 0x02, - 0x12, - 0x0a, - 0x1a, - 0x06, - 0x16, - 0x0e, - 0x1e, - 0x01, - 0x11, - 0x09, - 0x19, - 0x05, - 0x15, - 0x0d, - 0x1d, - 0x03, - 0x13, - 0x0b, - 0x1b, - 0x07, - 0x17, - 0x0f, - 0x1f - }; + //if (_hasFormatReader) + //{ + // _formatReader.UpdateWithBytesRead(bytes, offset, copied); + //} - private readonly OutputWindow _output; - private readonly InputBuffer _input; - private HuffmanTree _literalLengthTree; - private HuffmanTree _distanceTree; - - private InflaterState _state; - - //private bool _hasFormatReader; - private int _bfinal; - private BlockType _blockType; - - // uncompressed block - private readonly byte[] _blockLengthBuffer = new byte[4]; - private int _blockLength; - - // compressed block - private int _length; - private int _distanceCode; - private int _extraBits; - - private int _loopCounter; - private int _literalLengthCodeCount; - private int _distanceCodeCount; - private int _codeLengthCodeCount; - private int _codeArraySize; - private int _lengthCode; - - private readonly byte[] _codeList; // temporary array to store the code length for literal/Length and distance - private readonly byte[] _codeLengthTreeCodeLength; - private readonly bool _deflate64; - private HuffmanTree _codeLengthTree; - - //private IFileFormatReader _formatReader; // class to decode header and footer (e.g. gzip) - - internal InflaterManaged( /*IFileFormatReader reader, */ - bool deflate64 - ) - { - _output = new OutputWindow(); - _input = new InputBuffer(); - - _codeList = new byte[ - HuffmanTree.MAX_LITERAL_TREE_ELEMENTS + HuffmanTree.MAX_DIST_TREE_ELEMENTS - ]; - _codeLengthTreeCodeLength = new byte[HuffmanTree.NUMBER_OF_CODE_LENGTH_TREE_ELEMENTS]; - _deflate64 = deflate64; - //if (reader != null) - //{ - // _formatReader = reader; - // _hasFormatReader = true; - //} - Reset(); - } - - private void Reset() - { - _state = //_hasFormatReader ? - //InflaterState.ReadingHeader : // start by reading Header info - InflaterState.ReadingBFinal; // start by reading BFinal bit - } - - public void SetInput(byte[] inputBytes, int offset, int length) => - _input.SetInput(inputBytes, offset, length); // append the bytes - - public bool Finished() => - _state == InflaterState.Done || _state == InflaterState.VerifyingFooter; - - public int AvailableOutput => _output.AvailableBytes; - - public int Inflate(byte[] bytes, int offset, int length) - { - // copy bytes from output to outputbytes if we have available bytes - // if buffer is not filled up. keep decoding until no input are available - // if decodeBlock returns false. Throw an exception. - int count = 0; - do - { - int copied = _output.CopyTo(bytes, offset, length); - if (copied > 0) - { - //if (_hasFormatReader) - //{ - // _formatReader.UpdateWithBytesRead(bytes, offset, copied); - //} - - offset += copied; - count += copied; - length -= copied; - } - - if (length == 0) - { // filled in the bytes array - break; - } - // Decode will return false when more input is needed - } while (!Finished() && Decode()); - - if (_state == InflaterState.VerifyingFooter) - { // finished reading CRC - // In this case finished is true and output window has all the data. - // But some data in output window might not be copied out. - if (_output.AvailableBytes == 0) - { - //_formatReader.Validate(); - } + offset += copied; + count += copied; + length -= copied; } - return count; + if (length == 0) + { // filled in the bytes array + break; + } + // Decode will return false when more input is needed + } while (!Finished() && Decode()); + + if (_state == InflaterState.VerifyingFooter) + { // finished reading CRC + // In this case finished is true and output window has all the data. + // But some data in output window might not be copied out. + if (_output.AvailableBytes == 0) + { + //_formatReader.Validate(); + } } - //Each block of compressed data begins with 3 header bits - // containing the following data: - // first bit BFINAL - // next 2 bits BTYPE - // Note that the header bits do not necessarily begin on a byte - // boundary, since a block does not necessarily occupy an integral - // number of bytes. - // BFINAL is set if and only if this is the last block of the data - // set. - // BTYPE specifies how the data are compressed, as follows: - // 00 - no compression - // 01 - compressed with fixed Huffman codes - // 10 - compressed with dynamic Huffman codes - // 11 - reserved (error) - // The only difference between the two compressed cases is how the - // Huffman codes for the literal/length and distance alphabets are - // defined. - // - // This function returns true for success (end of block or output window is full,) - // false if we are short of input - // - private bool Decode() - { - bool eob = false; - bool result = false; + return count; + } - if (Finished()) + //Each block of compressed data begins with 3 header bits + // containing the following data: + // first bit BFINAL + // next 2 bits BTYPE + // Note that the header bits do not necessarily begin on a byte + // boundary, since a block does not necessarily occupy an integral + // number of bytes. + // BFINAL is set if and only if this is the last block of the data + // set. + // BTYPE specifies how the data are compressed, as follows: + // 00 - no compression + // 01 - compressed with fixed Huffman codes + // 10 - compressed with dynamic Huffman codes + // 11 - reserved (error) + // The only difference between the two compressed cases is how the + // Huffman codes for the literal/length and distance alphabets are + // defined. + // + // This function returns true for success (end of block or output window is full,) + // false if we are short of input + // + private bool Decode() + { + var eob = false; + var result = false; + + if (Finished()) + { + return true; + } + + //if (_hasFormatReader) + //{ + // if (_state == InflaterState.ReadingHeader) + // { + // if (!_formatReader.ReadHeader(_input)) + // { + // return false; + // } + // _state = InflaterState.ReadingBFinal; + // } + // else if (_state == InflaterState.StartReadingFooter || _state == InflaterState.ReadingFooter) + // { + // if (!_formatReader.ReadFooter(_input)) + // return false; + + // _state = InflaterState.VerifyingFooter; + // return true; + // } + //} + + if (_state == InflaterState.ReadingBFinal) + { + // reading bfinal bit + // Need 1 bit + if (!_input.EnsureBitsAvailable(1)) { - return true; + return false; } - //if (_hasFormatReader) - //{ - // if (_state == InflaterState.ReadingHeader) - // { - // if (!_formatReader.ReadHeader(_input)) - // { - // return false; - // } - // _state = InflaterState.ReadingBFinal; - // } - // else if (_state == InflaterState.StartReadingFooter || _state == InflaterState.ReadingFooter) - // { - // if (!_formatReader.ReadFooter(_input)) - // return false; + _bfinal = _input.GetBits(1); + _state = InflaterState.ReadingBType; + } - // _state = InflaterState.VerifyingFooter; - // return true; - // } - //} - - if (_state == InflaterState.ReadingBFinal) + if (_state == InflaterState.ReadingBType) + { + // Need 2 bits + if (!_input.EnsureBitsAvailable(2)) { - // reading bfinal bit - // Need 1 bit - if (!_input.EnsureBitsAvailable(1)) - { - return false; - } - - _bfinal = _input.GetBits(1); _state = InflaterState.ReadingBType; + return false; } - if (_state == InflaterState.ReadingBType) - { - // Need 2 bits - if (!_input.EnsureBitsAvailable(2)) - { - _state = InflaterState.ReadingBType; - return false; - } - - _blockType = (BlockType)_input.GetBits(2); - if (_blockType == BlockType.Dynamic) - { - _state = InflaterState.ReadingNumLitCodes; - } - else if (_blockType == BlockType.Static) - { - _literalLengthTree = HuffmanTree.StaticLiteralLengthTree; - _distanceTree = HuffmanTree.StaticDistanceTree; - _state = InflaterState.DecodeTop; - } - else if (_blockType == BlockType.Uncompressed) - { - _state = InflaterState.UncompressedAligning; - } - else - { - throw new InvalidDataException("Deflate64: unknown block type"); - } - } - + _blockType = (BlockType)_input.GetBits(2); if (_blockType == BlockType.Dynamic) { - if (_state < InflaterState.DecodeTop) - { - // we are reading the header - result = DecodeDynamicBlockHeader(); - } - else - { - result = DecodeBlock(out eob); // this can returns true when output is full - } + _state = InflaterState.ReadingNumLitCodes; } else if (_blockType == BlockType.Static) { - result = DecodeBlock(out eob); + _literalLengthTree = HuffmanTree.StaticLiteralLengthTree; + _distanceTree = HuffmanTree.StaticDistanceTree; + _state = InflaterState.DecodeTop; } else if (_blockType == BlockType.Uncompressed) { - result = DecodeUncompressedBlock(out eob); + _state = InflaterState.UncompressedAligning; } else { throw new InvalidDataException("Deflate64: unknown block type"); } - - // - // If we reached the end of the block and the block we were decoding had - // bfinal=1 (final block) - // - if (eob && (_bfinal != 0)) - { - //if (_hasFormatReader) - // _state = InflaterState.StartReadingFooter; - //else - _state = InflaterState.Done; - } - return result; } - // Format of Non-compressed blocks (BTYPE=00): - // - // Any bits of input up to the next byte boundary are ignored. - // The rest of the block consists of the following information: - // - // 0 1 2 3 4... - // +---+---+---+---+================================+ - // | LEN | NLEN |... LEN bytes of literal data...| - // +---+---+---+---+================================+ - // - // LEN is the number of data bytes in the block. NLEN is the - // one's complement of LEN. - private bool DecodeUncompressedBlock(out bool endOfBlock) + if (_blockType == BlockType.Dynamic) { - endOfBlock = false; - while (true) + if (_state < InflaterState.DecodeTop) { - switch (_state) - { - case InflaterState.UncompressedAligning: // initial state when calling this function - // we must skip to a byte boundary - _input.SkipToByteBoundary(); - _state = InflaterState.UncompressedByte1; - goto case InflaterState.UncompressedByte1; - - case InflaterState.UncompressedByte1: // decoding block length - case InflaterState.UncompressedByte2: - case InflaterState.UncompressedByte3: - case InflaterState.UncompressedByte4: - int bits = _input.GetBits(8); - if (bits < 0) - { - return false; - } - - _blockLengthBuffer[_state - InflaterState.UncompressedByte1] = (byte)bits; - if (_state == InflaterState.UncompressedByte4) - { - _blockLength = - _blockLengthBuffer[0] + ((int)_blockLengthBuffer[1]) * 256; - int blockLengthComplement = - _blockLengthBuffer[2] + ((int)_blockLengthBuffer[3]) * 256; - - // make sure complement matches - if ((ushort)_blockLength != (ushort)(~blockLengthComplement)) - { - throw new InvalidDataException("Deflate64: invalid block length"); - } - } - - _state += 1; - break; - - case InflaterState.DecodingUncompressed: // copying block data - - // Directly copy bytes from input to output. - int bytesCopied = _output.CopyFrom(_input, _blockLength); - _blockLength -= bytesCopied; - - if (_blockLength == 0) - { - // Done with this block, need to re-init bit buffer for next block - _state = InflaterState.ReadingBFinal; - endOfBlock = true; - return true; - } - - // We can fail to copy all bytes for two reasons: - // Running out of Input - // running out of free space in output window - if (_output.FreeBytes == 0) - { - return true; - } - - return false; - - default: - Debug. /*Fail*/ - Assert(false, "check why we are here!"); - throw new InvalidDataException("Deflate64: unknown state"); - } + // we are reading the header + result = DecodeDynamicBlockHeader(); + } + else + { + result = DecodeBlock(out eob); // this can returns true when output is full } } - - private bool DecodeBlock(out bool endOfBlockCodeSeen) + else if (_blockType == BlockType.Static) { - endOfBlockCodeSeen = false; - - int freeBytes = _output.FreeBytes; // it is a little bit faster than frequently accessing the property - while (freeBytes > 65536) - { - // With Deflate64 we can have up to a 64kb length, so we ensure at least that much space is available - // in the OutputWindow to avoid overwriting previous unflushed output data. - - int symbol; - switch (_state) - { - case InflaterState.DecodeTop: - // decode an element from the literal tree - - // TODO: optimize this!!! - symbol = _literalLengthTree.GetNextSymbol(_input); - if (symbol < 0) - { - // running out of input - return false; - } - - if (symbol < 256) - { - // literal - _output.Write((byte)symbol); - --freeBytes; - } - else if (symbol == 256) - { - // end of block - endOfBlockCodeSeen = true; - // Reset state - _state = InflaterState.ReadingBFinal; - return true; - } - else - { - // length/distance pair - symbol -= 257; // length code started at 257 - if (symbol < 8) - { - symbol += 3; // match length = 3,4,5,6,7,8,9,10 - _extraBits = 0; - } - else if (!_deflate64 && symbol == 28) - { - // extra bits for code 285 is 0 - symbol = 258; // code 285 means length 258 - _extraBits = 0; - } - else - { - if (symbol < 0 || symbol >= S_EXTRA_LENGTH_BITS.Length) - { - throw new InvalidDataException("Deflate64: invalid data"); - } - _extraBits = S_EXTRA_LENGTH_BITS[symbol]; - Debug.Assert(_extraBits != 0, "We handle other cases separately!"); - } - _length = symbol; - goto case InflaterState.HaveInitialLength; - } - break; - - case InflaterState.HaveInitialLength: - if (_extraBits > 0) - { - _state = InflaterState.HaveInitialLength; - int bits = _input.GetBits(_extraBits); - if (bits < 0) - { - return false; - } - - if (_length < 0 || _length >= S_LENGTH_BASE.Length) - { - throw new InvalidDataException("Deflate64: invalid data"); - } - _length = S_LENGTH_BASE[_length] + bits; - } - _state = InflaterState.HaveFullLength; - goto case InflaterState.HaveFullLength; - - case InflaterState.HaveFullLength: - if (_blockType == BlockType.Dynamic) - { - _distanceCode = _distanceTree.GetNextSymbol(_input); - } - else - { - // get distance code directly for static block - _distanceCode = _input.GetBits(5); - if (_distanceCode >= 0) - { - _distanceCode = S_STATIC_DISTANCE_TREE_TABLE[_distanceCode]; - } - } - - if (_distanceCode < 0) - { - // running out input - return false; - } - - _state = InflaterState.HaveDistCode; - goto case InflaterState.HaveDistCode; - - case InflaterState.HaveDistCode: - // To avoid a table lookup we note that for distanceCode > 3, - // extra_bits = (distanceCode-2) >> 1 - int offset; - if (_distanceCode > 3) - { - _extraBits = (_distanceCode - 2) >> 1; - int bits = _input.GetBits(_extraBits); - if (bits < 0) - { - return false; - } - offset = S_DISTANCE_BASE_POSITION[_distanceCode] + bits; - } - else - { - offset = _distanceCode + 1; - } - - _output.WriteLengthDistance(_length, offset); - freeBytes -= _length; - _state = InflaterState.DecodeTop; - break; - - default: - Debug. /*Fail*/ - Assert(false, "check why we are here!"); - throw new InvalidDataException("Deflate64: unknown state"); - } - } - - return true; + result = DecodeBlock(out eob); + } + else if (_blockType == BlockType.Uncompressed) + { + result = DecodeUncompressedBlock(out eob); + } + else + { + throw new InvalidDataException("Deflate64: unknown block type"); } - // Format of the dynamic block header: - // 5 Bits: HLIT, # of Literal/Length codes - 257 (257 - 286) - // 5 Bits: HDIST, # of Distance codes - 1 (1 - 32) - // 4 Bits: HCLEN, # of Code Length codes - 4 (4 - 19) // - // (HCLEN + 4) x 3 bits: code lengths for the code length - // alphabet given just above, in the order: 16, 17, 18, - // 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + // If we reached the end of the block and the block we were decoding had + // bfinal=1 (final block) // - // These code lengths are interpreted as 3-bit integers - // (0-7); as above, a code length of 0 means the - // corresponding symbol (literal/length or distance code - // length) is not used. - // - // HLIT + 257 code lengths for the literal/length alphabet, - // encoded using the code length Huffman code - // - // HDIST + 1 code lengths for the distance alphabet, - // encoded using the code length Huffman code - // - // The code length repeat codes can cross from HLIT + 257 to the - // HDIST + 1 code lengths. In other words, all code lengths form - // a single sequence of HLIT + HDIST + 258 values. - private bool DecodeDynamicBlockHeader() + if (eob && (_bfinal != 0)) + { + //if (_hasFormatReader) + // _state = InflaterState.StartReadingFooter; + //else + _state = InflaterState.Done; + } + return result; + } + + // Format of Non-compressed blocks (BTYPE=00): + // + // Any bits of input up to the next byte boundary are ignored. + // The rest of the block consists of the following information: + // + // 0 1 2 3 4... + // +---+---+---+---+================================+ + // | LEN | NLEN |... LEN bytes of literal data...| + // +---+---+---+---+================================+ + // + // LEN is the number of data bytes in the block. NLEN is the + // one's complement of LEN. + private bool DecodeUncompressedBlock(out bool endOfBlock) + { + endOfBlock = false; + while (true) { switch (_state) { - case InflaterState.ReadingNumLitCodes: - _literalLengthCodeCount = _input.GetBits(5); - if (_literalLengthCodeCount < 0) + case InflaterState.UncompressedAligning: // initial state when calling this function + // we must skip to a byte boundary + _input.SkipToByteBoundary(); + _state = InflaterState.UncompressedByte1; + goto case InflaterState.UncompressedByte1; + + case InflaterState.UncompressedByte1: // decoding block length + case InflaterState.UncompressedByte2: + case InflaterState.UncompressedByte3: + case InflaterState.UncompressedByte4: + var bits = _input.GetBits(8); + if (bits < 0) { return false; } - _literalLengthCodeCount += 257; - _state = InflaterState.ReadingNumDistCodes; - goto case InflaterState.ReadingNumDistCodes; - case InflaterState.ReadingNumDistCodes: - _distanceCodeCount = _input.GetBits(5); - if (_distanceCodeCount < 0) + _blockLengthBuffer[_state - InflaterState.UncompressedByte1] = (byte)bits; + if (_state == InflaterState.UncompressedByte4) { + _blockLength = + _blockLengthBuffer[0] + (_blockLengthBuffer[1] * 256); + var blockLengthComplement = + _blockLengthBuffer[2] + (_blockLengthBuffer[3] * 256); + + // make sure complement matches + if ((ushort)_blockLength != (ushort)(~blockLengthComplement)) + { + throw new InvalidDataException("Deflate64: invalid block length"); + } + } + + _state += 1; + break; + + case InflaterState.DecodingUncompressed: // copying block data + + // Directly copy bytes from input to output. + var bytesCopied = _output.CopyFrom(_input, _blockLength); + _blockLength -= bytesCopied; + + if (_blockLength == 0) + { + // Done with this block, need to re-init bit buffer for next block + _state = InflaterState.ReadingBFinal; + endOfBlock = true; + return true; + } + + // We can fail to copy all bytes for two reasons: + // Running out of Input + // running out of free space in output window + if (_output.FreeBytes == 0) + { + return true; + } + + return false; + + default: + Debug. /*Fail*/ + Assert(false, "check why we are here!"); + throw new InvalidDataException("Deflate64: unknown state"); + } + } + } + + private bool DecodeBlock(out bool endOfBlockCodeSeen) + { + endOfBlockCodeSeen = false; + + var freeBytes = _output.FreeBytes; // it is a little bit faster than frequently accessing the property + while (freeBytes > 65536) + { + // With Deflate64 we can have up to a 64kb length, so we ensure at least that much space is available + // in the OutputWindow to avoid overwriting previous unflushed output data. + + int symbol; + switch (_state) + { + case InflaterState.DecodeTop: + // decode an element from the literal tree + + // TODO: optimize this!!! + symbol = _literalLengthTree.GetNextSymbol(_input); + if (symbol < 0) + { + // running out of input return false; } - _distanceCodeCount += 1; - _state = InflaterState.ReadingNumCodeLengthCodes; - goto case InflaterState.ReadingNumCodeLengthCodes; - case InflaterState.ReadingNumCodeLengthCodes: - _codeLengthCodeCount = _input.GetBits(4); - if (_codeLengthCodeCount < 0) + if (symbol < 256) { - return false; + // literal + _output.Write((byte)symbol); + --freeBytes; } - _codeLengthCodeCount += 4; - _loopCounter = 0; - _state = InflaterState.ReadingCodeLengthCodes; - goto case InflaterState.ReadingCodeLengthCodes; - - case InflaterState.ReadingCodeLengthCodes: - while (_loopCounter < _codeLengthCodeCount) + else if (symbol == 256) { - int bits = _input.GetBits(3); + // end of block + endOfBlockCodeSeen = true; + // Reset state + _state = InflaterState.ReadingBFinal; + return true; + } + else + { + // length/distance pair + symbol -= 257; // length code started at 257 + if (symbol < 8) + { + symbol += 3; // match length = 3,4,5,6,7,8,9,10 + _extraBits = 0; + } + else if (!_deflate64 && symbol == 28) + { + // extra bits for code 285 is 0 + symbol = 258; // code 285 means length 258 + _extraBits = 0; + } + else + { + if (symbol < 0 || symbol >= S_EXTRA_LENGTH_BITS.Length) + { + throw new InvalidDataException("Deflate64: invalid data"); + } + _extraBits = S_EXTRA_LENGTH_BITS[symbol]; + Debug.Assert(_extraBits != 0, "We handle other cases separately!"); + } + _length = symbol; + goto case InflaterState.HaveInitialLength; + } + break; + + case InflaterState.HaveInitialLength: + if (_extraBits > 0) + { + _state = InflaterState.HaveInitialLength; + var bits = _input.GetBits(_extraBits); if (bits < 0) { return false; } - _codeLengthTreeCodeLength[S_CODE_ORDER[_loopCounter]] = (byte)bits; - ++_loopCounter; - } - for (int i = _codeLengthCodeCount; i < S_CODE_ORDER.Length; i++) + if (_length < 0 || _length >= S_LENGTH_BASE.Length) + { + throw new InvalidDataException("Deflate64: invalid data"); + } + _length = S_LENGTH_BASE[_length] + bits; + } + _state = InflaterState.HaveFullLength; + goto case InflaterState.HaveFullLength; + + case InflaterState.HaveFullLength: + if (_blockType == BlockType.Dynamic) { - _codeLengthTreeCodeLength[S_CODE_ORDER[i]] = 0; + _distanceCode = _distanceTree.GetNextSymbol(_input); } - - // create huffman tree for code length - _codeLengthTree = new HuffmanTree(_codeLengthTreeCodeLength); - _codeArraySize = _literalLengthCodeCount + _distanceCodeCount; - _loopCounter = 0; // reset loop count - - _state = InflaterState.ReadingTreeCodesBefore; - goto case InflaterState.ReadingTreeCodesBefore; - - case InflaterState.ReadingTreeCodesBefore: - case InflaterState.ReadingTreeCodesAfter: - while (_loopCounter < _codeArraySize) + else { - if (_state == InflaterState.ReadingTreeCodesBefore) + // get distance code directly for static block + _distanceCode = _input.GetBits(5); + if (_distanceCode >= 0) { - if ((_lengthCode = _codeLengthTree.GetNextSymbol(_input)) < 0) - { - return false; - } + _distanceCode = S_STATIC_DISTANCE_TREE_TABLE[_distanceCode]; } - - // The alphabet for code lengths is as follows: - // 0 - 15: Represent code lengths of 0 - 15 - // 16: Copy the previous code length 3 - 6 times. - // The next 2 bits indicate repeat length - // (0 = 3, ... , 3 = 6) - // Example: Codes 8, 16 (+2 bits 11), - // 16 (+2 bits 10) will expand to - // 12 code lengths of 8 (1 + 6 + 5) - // 17: Repeat a code length of 0 for 3 - 10 times. - // (3 bits of length) - // 18: Repeat a code length of 0 for 11 - 138 times - // (7 bits of length) - if (_lengthCode <= 15) - { - _codeList[_loopCounter++] = (byte)_lengthCode; - } - else - { - int repeatCount; - if (_lengthCode == 16) - { - if (!_input.EnsureBitsAvailable(2)) - { - _state = InflaterState.ReadingTreeCodesAfter; - return false; - } - - if (_loopCounter == 0) - { - // can't have "prev code" on first code - throw new InvalidDataException(); - } - - byte previousCode = _codeList[_loopCounter - 1]; - repeatCount = _input.GetBits(2) + 3; - - if (_loopCounter + repeatCount > _codeArraySize) - { - throw new InvalidDataException(); - } - - for (int j = 0; j < repeatCount; j++) - { - _codeList[_loopCounter++] = previousCode; - } - } - else if (_lengthCode == 17) - { - if (!_input.EnsureBitsAvailable(3)) - { - _state = InflaterState.ReadingTreeCodesAfter; - return false; - } - - repeatCount = _input.GetBits(3) + 3; - - if (_loopCounter + repeatCount > _codeArraySize) - { - throw new InvalidDataException(); - } - - for (int j = 0; j < repeatCount; j++) - { - _codeList[_loopCounter++] = 0; - } - } - else - { - // code == 18 - if (!_input.EnsureBitsAvailable(7)) - { - _state = InflaterState.ReadingTreeCodesAfter; - return false; - } - - repeatCount = _input.GetBits(7) + 11; - - if (_loopCounter + repeatCount > _codeArraySize) - { - throw new InvalidDataException(); - } - - for (int j = 0; j < repeatCount; j++) - { - _codeList[_loopCounter++] = 0; - } - } - } - _state = InflaterState.ReadingTreeCodesBefore; // we want to read the next code. } + + if (_distanceCode < 0) + { + // running out input + return false; + } + + _state = InflaterState.HaveDistCode; + goto case InflaterState.HaveDistCode; + + case InflaterState.HaveDistCode: + // To avoid a table lookup we note that for distanceCode > 3, + // extra_bits = (distanceCode-2) >> 1 + int offset; + if (_distanceCode > 3) + { + _extraBits = (_distanceCode - 2) >> 1; + var bits = _input.GetBits(_extraBits); + if (bits < 0) + { + return false; + } + offset = S_DISTANCE_BASE_POSITION[_distanceCode] + bits; + } + else + { + offset = _distanceCode + 1; + } + + _output.WriteLengthDistance(_length, offset); + freeBytes -= _length; + _state = InflaterState.DecodeTop; break; default: @@ -851,32 +654,228 @@ namespace SharpCompress.Compressors.Deflate64 Assert(false, "check why we are here!"); throw new InvalidDataException("Deflate64: unknown state"); } - - byte[] literalTreeCodeLength = new byte[HuffmanTree.MAX_LITERAL_TREE_ELEMENTS]; - byte[] distanceTreeCodeLength = new byte[HuffmanTree.MAX_DIST_TREE_ELEMENTS]; - - // Create literal and distance tables - Array.Copy(_codeList, literalTreeCodeLength, _literalLengthCodeCount); - Array.Copy( - _codeList, - _literalLengthCodeCount, - distanceTreeCodeLength, - 0, - _distanceCodeCount - ); - - // Make sure there is an end-of-block code, otherwise how could we ever end? - if (literalTreeCodeLength[HuffmanTree.END_OF_BLOCK_CODE] == 0) - { - throw new InvalidDataException(); - } - - _literalLengthTree = new HuffmanTree(literalTreeCodeLength); - _distanceTree = new HuffmanTree(distanceTreeCodeLength); - _state = InflaterState.DecodeTop; - return true; } - public void Dispose() { } + return true; } + + // Format of the dynamic block header: + // 5 Bits: HLIT, # of Literal/Length codes - 257 (257 - 286) + // 5 Bits: HDIST, # of Distance codes - 1 (1 - 32) + // 4 Bits: HCLEN, # of Code Length codes - 4 (4 - 19) + // + // (HCLEN + 4) x 3 bits: code lengths for the code length + // alphabet given just above, in the order: 16, 17, 18, + // 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + // + // These code lengths are interpreted as 3-bit integers + // (0-7); as above, a code length of 0 means the + // corresponding symbol (literal/length or distance code + // length) is not used. + // + // HLIT + 257 code lengths for the literal/length alphabet, + // encoded using the code length Huffman code + // + // HDIST + 1 code lengths for the distance alphabet, + // encoded using the code length Huffman code + // + // The code length repeat codes can cross from HLIT + 257 to the + // HDIST + 1 code lengths. In other words, all code lengths form + // a single sequence of HLIT + HDIST + 258 values. + private bool DecodeDynamicBlockHeader() + { + switch (_state) + { + case InflaterState.ReadingNumLitCodes: + _literalLengthCodeCount = _input.GetBits(5); + if (_literalLengthCodeCount < 0) + { + return false; + } + _literalLengthCodeCount += 257; + _state = InflaterState.ReadingNumDistCodes; + goto case InflaterState.ReadingNumDistCodes; + + case InflaterState.ReadingNumDistCodes: + _distanceCodeCount = _input.GetBits(5); + if (_distanceCodeCount < 0) + { + return false; + } + _distanceCodeCount += 1; + _state = InflaterState.ReadingNumCodeLengthCodes; + goto case InflaterState.ReadingNumCodeLengthCodes; + + case InflaterState.ReadingNumCodeLengthCodes: + _codeLengthCodeCount = _input.GetBits(4); + if (_codeLengthCodeCount < 0) + { + return false; + } + _codeLengthCodeCount += 4; + _loopCounter = 0; + _state = InflaterState.ReadingCodeLengthCodes; + goto case InflaterState.ReadingCodeLengthCodes; + + case InflaterState.ReadingCodeLengthCodes: + while (_loopCounter < _codeLengthCodeCount) + { + var bits = _input.GetBits(3); + if (bits < 0) + { + return false; + } + _codeLengthTreeCodeLength[S_CODE_ORDER[_loopCounter]] = (byte)bits; + ++_loopCounter; + } + + for (var i = _codeLengthCodeCount; i < S_CODE_ORDER.Length; i++) + { + _codeLengthTreeCodeLength[S_CODE_ORDER[i]] = 0; + } + + // create huffman tree for code length + _codeLengthTree = new HuffmanTree(_codeLengthTreeCodeLength); + _codeArraySize = _literalLengthCodeCount + _distanceCodeCount; + _loopCounter = 0; // reset loop count + + _state = InflaterState.ReadingTreeCodesBefore; + goto case InflaterState.ReadingTreeCodesBefore; + + case InflaterState.ReadingTreeCodesBefore: + case InflaterState.ReadingTreeCodesAfter: + while (_loopCounter < _codeArraySize) + { + if (_state == InflaterState.ReadingTreeCodesBefore) + { + if ((_lengthCode = _codeLengthTree.GetNextSymbol(_input)) < 0) + { + return false; + } + } + + // The alphabet for code lengths is as follows: + // 0 - 15: Represent code lengths of 0 - 15 + // 16: Copy the previous code length 3 - 6 times. + // The next 2 bits indicate repeat length + // (0 = 3, ... , 3 = 6) + // Example: Codes 8, 16 (+2 bits 11), + // 16 (+2 bits 10) will expand to + // 12 code lengths of 8 (1 + 6 + 5) + // 17: Repeat a code length of 0 for 3 - 10 times. + // (3 bits of length) + // 18: Repeat a code length of 0 for 11 - 138 times + // (7 bits of length) + if (_lengthCode <= 15) + { + _codeList[_loopCounter++] = (byte)_lengthCode; + } + else + { + int repeatCount; + if (_lengthCode == 16) + { + if (!_input.EnsureBitsAvailable(2)) + { + _state = InflaterState.ReadingTreeCodesAfter; + return false; + } + + if (_loopCounter == 0) + { + // can't have "prev code" on first code + throw new InvalidDataException(); + } + + var previousCode = _codeList[_loopCounter - 1]; + repeatCount = _input.GetBits(2) + 3; + + if (_loopCounter + repeatCount > _codeArraySize) + { + throw new InvalidDataException(); + } + + for (var j = 0; j < repeatCount; j++) + { + _codeList[_loopCounter++] = previousCode; + } + } + else if (_lengthCode == 17) + { + if (!_input.EnsureBitsAvailable(3)) + { + _state = InflaterState.ReadingTreeCodesAfter; + return false; + } + + repeatCount = _input.GetBits(3) + 3; + + if (_loopCounter + repeatCount > _codeArraySize) + { + throw new InvalidDataException(); + } + + for (var j = 0; j < repeatCount; j++) + { + _codeList[_loopCounter++] = 0; + } + } + else + { + // code == 18 + if (!_input.EnsureBitsAvailable(7)) + { + _state = InflaterState.ReadingTreeCodesAfter; + return false; + } + + repeatCount = _input.GetBits(7) + 11; + + if (_loopCounter + repeatCount > _codeArraySize) + { + throw new InvalidDataException(); + } + + for (var j = 0; j < repeatCount; j++) + { + _codeList[_loopCounter++] = 0; + } + } + } + _state = InflaterState.ReadingTreeCodesBefore; // we want to read the next code. + } + break; + + default: + Debug. /*Fail*/ + Assert(false, "check why we are here!"); + throw new InvalidDataException("Deflate64: unknown state"); + } + + var literalTreeCodeLength = new byte[HuffmanTree.MAX_LITERAL_TREE_ELEMENTS]; + var distanceTreeCodeLength = new byte[HuffmanTree.MAX_DIST_TREE_ELEMENTS]; + + // Create literal and distance tables + Array.Copy(_codeList, literalTreeCodeLength, _literalLengthCodeCount); + Array.Copy( + _codeList, + _literalLengthCodeCount, + distanceTreeCodeLength, + 0, + _distanceCodeCount + ); + + // Make sure there is an end-of-block code, otherwise how could we ever end? + if (literalTreeCodeLength[HuffmanTree.END_OF_BLOCK_CODE] == 0) + { + throw new InvalidDataException(); + } + + _literalLengthTree = new HuffmanTree(literalTreeCodeLength); + _distanceTree = new HuffmanTree(distanceTreeCodeLength); + _state = InflaterState.DecodeTop; + return true; + } + + public void Dispose() { } } diff --git a/src/SharpCompress/Compressors/Deflate64/InflaterState.cs b/src/SharpCompress/Compressors/Deflate64/InflaterState.cs index 64450931..711c3245 100644 --- a/src/SharpCompress/Compressors/Deflate64/InflaterState.cs +++ b/src/SharpCompress/Compressors/Deflate64/InflaterState.cs @@ -2,41 +2,40 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace SharpCompress.Compressors.Deflate64 +namespace SharpCompress.Compressors.Deflate64; + +// Do not rearrange the enum values. +internal enum InflaterState { - // Do not rearrange the enum values. - internal enum InflaterState - { - ReadingHeader = 0, // Only applies to GZIP + ReadingHeader = 0, // Only applies to GZIP - ReadingBFinal = 2, // About to read bfinal bit - ReadingBType = 3, // About to read blockType bits + ReadingBFinal = 2, // About to read bfinal bit + ReadingBType = 3, // About to read blockType bits - ReadingNumLitCodes = 4, // About to read # literal codes - ReadingNumDistCodes = 5, // About to read # dist codes - ReadingNumCodeLengthCodes = 6, // About to read # code length codes - ReadingCodeLengthCodes = 7, // In the middle of reading the code length codes - ReadingTreeCodesBefore = 8, // In the middle of reading tree codes (loop top) - ReadingTreeCodesAfter = 9, // In the middle of reading tree codes (extension; code > 15) + ReadingNumLitCodes = 4, // About to read # literal codes + ReadingNumDistCodes = 5, // About to read # dist codes + ReadingNumCodeLengthCodes = 6, // About to read # code length codes + ReadingCodeLengthCodes = 7, // In the middle of reading the code length codes + ReadingTreeCodesBefore = 8, // In the middle of reading tree codes (loop top) + ReadingTreeCodesAfter = 9, // In the middle of reading tree codes (extension; code > 15) - DecodeTop = 10, // About to decode a literal (char/match) in a compressed block - HaveInitialLength = 11, // Decoding a match, have the literal code (base length) - HaveFullLength = 12, // Ditto, now have the full match length (incl. extra length bits) - HaveDistCode = 13, // Ditto, now have the distance code also, need extra dist bits + DecodeTop = 10, // About to decode a literal (char/match) in a compressed block + HaveInitialLength = 11, // Decoding a match, have the literal code (base length) + HaveFullLength = 12, // Ditto, now have the full match length (incl. extra length bits) + HaveDistCode = 13, // Ditto, now have the distance code also, need extra dist bits - /* uncompressed blocks */ - UncompressedAligning = 15, - UncompressedByte1 = 16, - UncompressedByte2 = 17, - UncompressedByte3 = 18, - UncompressedByte4 = 19, - DecodingUncompressed = 20, + /* uncompressed blocks */ + UncompressedAligning = 15, + UncompressedByte1 = 16, + UncompressedByte2 = 17, + UncompressedByte3 = 18, + UncompressedByte4 = 19, + DecodingUncompressed = 20, - // These three apply only to GZIP - StartReadingFooter = 21, // (Initialisation for reading footer) - ReadingFooter = 22, - VerifyingFooter = 23, + // These three apply only to GZIP + StartReadingFooter = 21, // (Initialisation for reading footer) + ReadingFooter = 22, + VerifyingFooter = 23, - Done = 24 // Finished - } + Done = 24 // Finished } diff --git a/src/SharpCompress/Compressors/Deflate64/InputBuffer.cs b/src/SharpCompress/Compressors/Deflate64/InputBuffer.cs index a7357565..28d61e42 100644 --- a/src/SharpCompress/Compressors/Deflate64/InputBuffer.cs +++ b/src/SharpCompress/Compressors/Deflate64/InputBuffer.cs @@ -7,40 +7,50 @@ using System; using System.Diagnostics; -namespace SharpCompress.Compressors.Deflate64 +namespace SharpCompress.Compressors.Deflate64; + +// This class can be used to read bits from an byte array quickly. +// Normally we get bits from 'bitBuffer' field and bitsInBuffer stores +// the number of bits available in 'BitBuffer'. +// When we used up the bits in bitBuffer, we will try to get byte from +// the byte array and copy the byte to appropiate position in bitBuffer. +// +// The byte array is not reused. We will go from 'start' to 'end'. +// When we reach the end, most read operations will return -1, +// which means we are running out of input. + +internal sealed class InputBuffer { - // This class can be used to read bits from an byte array quickly. - // Normally we get bits from 'bitBuffer' field and bitsInBuffer stores - // the number of bits available in 'BitBuffer'. - // When we used up the bits in bitBuffer, we will try to get byte from - // the byte array and copy the byte to appropiate position in bitBuffer. - // - // The byte array is not reused. We will go from 'start' to 'end'. - // When we reach the end, most read operations will return -1, - // which means we are running out of input. + private byte[] _buffer; // byte array to store input + private int _start; // start poisition of the buffer + private int _end; // end position of the buffer + private uint _bitBuffer = 0; // store the bits here, we can quickly shift in this buffer + private int _bitsInBuffer = 0; // number of bits available in bitBuffer - internal sealed class InputBuffer + /// Total bits available in the input buffer. + public int AvailableBits => _bitsInBuffer; + + /// Total bytes available in the input buffer. + public int AvailableBytes => (_end - _start) + (_bitsInBuffer / 8); + + /// Ensure that count bits are in the bit buffer. + /// Can be up to 16. + /// Returns false if input is not sufficient to make this true. + public bool EnsureBitsAvailable(int count) { - private byte[] _buffer; // byte array to store input - private int _start; // start poisition of the buffer - private int _end; // end position of the buffer - private uint _bitBuffer = 0; // store the bits here, we can quickly shift in this buffer - private int _bitsInBuffer = 0; // number of bits available in bitBuffer + Debug.Assert(0 < count && count <= 16, "count is invalid."); - /// Total bits available in the input buffer. - public int AvailableBits => _bitsInBuffer; - - /// Total bytes available in the input buffer. - public int AvailableBytes => (_end - _start) + (_bitsInBuffer / 8); - - /// Ensure that count bits are in the bit buffer. - /// Can be up to 16. - /// Returns false if input is not sufficient to make this true. - public bool EnsureBitsAvailable(int count) + // manual inlining to improve perf + if (_bitsInBuffer < count) { - Debug.Assert(0 < count && count <= 16, "count is invalid."); + if (NeedsInput()) + { + return false; + } + // insert a byte to bitbuffer + _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; + _bitsInBuffer += 8; - // manual inlining to improve perf if (_bitsInBuffer < count) { if (NeedsInput()) @@ -50,158 +60,147 @@ namespace SharpCompress.Compressors.Deflate64 // insert a byte to bitbuffer _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; _bitsInBuffer += 8; - - if (_bitsInBuffer < count) - { - if (NeedsInput()) - { - return false; - } - // insert a byte to bitbuffer - _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; - _bitsInBuffer += 8; - } } - - return true; } - /// - /// This function will try to load 16 or more bits into bitBuffer. - /// It returns whatever is contained in bitBuffer after loading. - /// The main difference between this and GetBits is that this will - /// never return -1. So the caller needs to check AvailableBits to - /// see how many bits are available. - /// - public uint TryLoad16Bits() + return true; + } + + /// + /// This function will try to load 16 or more bits into bitBuffer. + /// It returns whatever is contained in bitBuffer after loading. + /// The main difference between this and GetBits is that this will + /// never return -1. So the caller needs to check AvailableBits to + /// see how many bits are available. + /// + public uint TryLoad16Bits() + { + if (_bitsInBuffer < 8) { - if (_bitsInBuffer < 8) + if (_start < _end) { - if (_start < _end) - { - _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; - _bitsInBuffer += 8; - } - - if (_start < _end) - { - _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; - _bitsInBuffer += 8; - } - } - else if (_bitsInBuffer < 16) - { - if (_start < _end) - { - _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; - _bitsInBuffer += 8; - } + _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; + _bitsInBuffer += 8; } - return _bitBuffer; + if (_start < _end) + { + _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; + _bitsInBuffer += 8; + } } - - private uint GetBitMask(int count) => ((uint)1 << count) - 1; - - /// Gets count bits from the input buffer. Returns -1 if not enough bits available. - public int GetBits(int count) + else if (_bitsInBuffer < 16) { - Debug.Assert(0 < count && count <= 16, "count is invalid."); - - if (!EnsureBitsAvailable(count)) + if (_start < _end) { - return -1; + _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; + _bitsInBuffer += 8; } - - int result = (int)(_bitBuffer & GetBitMask(count)); - _bitBuffer >>= count; - _bitsInBuffer -= count; - return result; } - /// - /// Copies length bytes from input buffer to output buffer starting at output[offset]. - /// You have to make sure, that the buffer is byte aligned. If not enough bytes are - /// available, copies fewer bytes. - /// - /// Returns the number of bytes copied, 0 if no byte is available. - public int CopyTo(byte[] output, int offset, int length) + return _bitBuffer; + } + + private uint GetBitMask(int count) => ((uint)1 << count) - 1; + + /// Gets count bits from the input buffer. Returns -1 if not enough bits available. + public int GetBits(int count) + { + Debug.Assert(0 < count && count <= 16, "count is invalid."); + + if (!EnsureBitsAvailable(count)) { - Debug.Assert(output != null); - Debug.Assert(offset >= 0); - Debug.Assert(length >= 0); - Debug.Assert(offset <= output.Length - length); - Debug.Assert((_bitsInBuffer % 8) == 0); - - // Copy the bytes in bitBuffer first. - int bytesFromBitBuffer = 0; - while (_bitsInBuffer > 0 && length > 0) - { - output[offset++] = (byte)_bitBuffer; - _bitBuffer >>= 8; - _bitsInBuffer -= 8; - length--; - bytesFromBitBuffer++; - } - - if (length == 0) - { - return bytesFromBitBuffer; - } - - int avail = _end - _start; - if (length > avail) - { - length = avail; - } - - Array.Copy(_buffer, _start, output, offset, length); - _start += length; - return bytesFromBitBuffer + length; + return -1; } - /// - /// Return true is all input bytes are used. - /// This means the caller can call SetInput to add more input. - /// - public bool NeedsInput() => _start == _end; + var result = (int)(_bitBuffer & GetBitMask(count)); + _bitBuffer >>= count; + _bitsInBuffer -= count; + return result; + } - /// - /// Set the byte array to be processed. - /// All the bits remained in bitBuffer will be processed before the new bytes. - /// We don't clone the byte array here since it is expensive. - /// The caller should make sure after a buffer is passed in. - /// It will not be changed before calling this function again. - /// - public void SetInput(byte[] buffer, int offset, int length) + /// + /// Copies length bytes from input buffer to output buffer starting at output[offset]. + /// You have to make sure, that the buffer is byte aligned. If not enough bytes are + /// available, copies fewer bytes. + /// + /// Returns the number of bytes copied, 0 if no byte is available. + public int CopyTo(byte[] output, int offset, int length) + { + Debug.Assert(output != null); + Debug.Assert(offset >= 0); + Debug.Assert(length >= 0); + Debug.Assert(offset <= output.Length - length); + Debug.Assert((_bitsInBuffer % 8) == 0); + + // Copy the bytes in bitBuffer first. + var bytesFromBitBuffer = 0; + while (_bitsInBuffer > 0 && length > 0) { - Debug.Assert(buffer != null); - Debug.Assert(offset >= 0); - Debug.Assert(length >= 0); - Debug.Assert(offset <= buffer.Length - length); - Debug.Assert(_start == _end); - - _buffer = buffer; - _start = offset; - _end = offset + length; + output[offset++] = (byte)_bitBuffer; + _bitBuffer >>= 8; + _bitsInBuffer -= 8; + length--; + bytesFromBitBuffer++; } - /// Skip n bits in the buffer. - public void SkipBits(int n) + if (length == 0) { - Debug.Assert( - _bitsInBuffer >= n, - "No enough bits in the buffer, Did you call EnsureBitsAvailable?" - ); - _bitBuffer >>= n; - _bitsInBuffer -= n; + return bytesFromBitBuffer; } - /// Skips to the next byte boundary. - public void SkipToByteBoundary() + var avail = _end - _start; + if (length > avail) { - _bitBuffer >>= (_bitsInBuffer % 8); - _bitsInBuffer = _bitsInBuffer - (_bitsInBuffer % 8); + length = avail; } + + Array.Copy(_buffer, _start, output, offset, length); + _start += length; + return bytesFromBitBuffer + length; + } + + /// + /// Return true is all input bytes are used. + /// This means the caller can call SetInput to add more input. + /// + public bool NeedsInput() => _start == _end; + + /// + /// Set the byte array to be processed. + /// All the bits remained in bitBuffer will be processed before the new bytes. + /// We don't clone the byte array here since it is expensive. + /// The caller should make sure after a buffer is passed in. + /// It will not be changed before calling this function again. + /// + public void SetInput(byte[] buffer, int offset, int length) + { + Debug.Assert(buffer != null); + Debug.Assert(offset >= 0); + Debug.Assert(length >= 0); + Debug.Assert(offset <= buffer.Length - length); + Debug.Assert(_start == _end); + + _buffer = buffer; + _start = offset; + _end = offset + length; + } + + /// Skip n bits in the buffer. + public void SkipBits(int n) + { + Debug.Assert( + _bitsInBuffer >= n, + "No enough bits in the buffer, Did you call EnsureBitsAvailable?" + ); + _bitBuffer >>= n; + _bitsInBuffer -= n; + } + + /// Skips to the next byte boundary. + public void SkipToByteBoundary() + { + _bitBuffer >>= (_bitsInBuffer % 8); + _bitsInBuffer -= (_bitsInBuffer % 8); } } diff --git a/src/SharpCompress/Compressors/Deflate64/Match.cs b/src/SharpCompress/Compressors/Deflate64/Match.cs index 4d5ce54b..c75cbe7a 100644 --- a/src/SharpCompress/Compressors/Deflate64/Match.cs +++ b/src/SharpCompress/Compressors/Deflate64/Match.cs @@ -2,16 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace SharpCompress.Compressors.Deflate64 +namespace SharpCompress.Compressors.Deflate64; + +/// +/// This class represents a match in the history window. +/// +internal sealed class Match { - /// - /// This class represents a match in the history window. - /// - internal sealed class Match - { - internal MatchState State { get; set; } - internal int Position { get; set; } - internal int Length { get; set; } - internal byte Symbol { get; set; } - } + internal MatchState State { get; set; } + internal int Position { get; set; } + internal int Length { get; set; } + internal byte Symbol { get; set; } } diff --git a/src/SharpCompress/Compressors/Deflate64/MatchState.cs b/src/SharpCompress/Compressors/Deflate64/MatchState.cs index f88913bc..858e91e8 100644 --- a/src/SharpCompress/Compressors/Deflate64/MatchState.cs +++ b/src/SharpCompress/Compressors/Deflate64/MatchState.cs @@ -2,12 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace SharpCompress.Compressors.Deflate64 +namespace SharpCompress.Compressors.Deflate64; + +internal enum MatchState { - internal enum MatchState - { - HasSymbol = 1, - HasMatch = 2, - HasSymbolAndMatch = 3 - } + HasSymbol = 1, + HasMatch = 2, + HasSymbolAndMatch = 3 } diff --git a/src/SharpCompress/Compressors/Deflate64/OutputWindow.cs b/src/SharpCompress/Compressors/Deflate64/OutputWindow.cs index d48cd5fb..90b65cd3 100644 --- a/src/SharpCompress/Compressors/Deflate64/OutputWindow.cs +++ b/src/SharpCompress/Compressors/Deflate64/OutputWindow.cs @@ -5,149 +5,148 @@ using System; using System.Diagnostics; -namespace SharpCompress.Compressors.Deflate64 +namespace SharpCompress.Compressors.Deflate64; + +/// +/// This class maintains a window for decompressed output. +/// We need to keep this because the decompressed information can be +/// a literal or a length/distance pair. For length/distance pair, +/// we need to look back in the output window and copy bytes from there. +/// We use a byte array of WindowSize circularly. +/// +internal sealed class OutputWindow { - /// - /// This class maintains a window for decompressed output. - /// We need to keep this because the decompressed information can be - /// a literal or a length/distance pair. For length/distance pair, - /// we need to look back in the output window and copy bytes from there. - /// We use a byte array of WindowSize circularly. - /// - internal sealed class OutputWindow + // With Deflate64 we can have up to a 65536 length as well as up to a 65538 distance. This means we need a Window that is at + // least 131074 bytes long so we have space to retrieve up to a full 64kb in lookback and place it in our buffer without + // overwriting existing data. OutputWindow requires that the WindowSize be an exponent of 2, so we round up to 2^18. + private const int WINDOW_SIZE = 262144; + private const int WINDOW_MASK = 262143; + + private readonly byte[] _window = new byte[WINDOW_SIZE]; // The window is 2^18 bytes + private int _end; // this is the position to where we should write next byte + private int _bytesUsed; // The number of bytes in the output window which is not consumed. + + /// Add a byte to output window. + public void Write(byte b) { - // With Deflate64 we can have up to a 65536 length as well as up to a 65538 distance. This means we need a Window that is at - // least 131074 bytes long so we have space to retrieve up to a full 64kb in lookback and place it in our buffer without - // overwriting existing data. OutputWindow requires that the WindowSize be an exponent of 2, so we round up to 2^18. - private const int WINDOW_SIZE = 262144; - private const int WINDOW_MASK = 262143; + Debug.Assert(_bytesUsed < WINDOW_SIZE, "Can't add byte when window is full!"); + _window[_end++] = b; + _end &= WINDOW_MASK; + ++_bytesUsed; + } - private readonly byte[] _window = new byte[WINDOW_SIZE]; // The window is 2^18 bytes - private int _end; // this is the position to where we should write next byte - private int _bytesUsed; // The number of bytes in the output window which is not consumed. + public void WriteLengthDistance(int length, int distance) + { + Debug.Assert((_bytesUsed + length) <= WINDOW_SIZE, "No Enough space"); - /// Add a byte to output window. - public void Write(byte b) + // move backwards distance bytes in the output stream, + // and copy length bytes from this position to the output stream. + _bytesUsed += length; + var copyStart = (_end - distance) & WINDOW_MASK; // start position for coping. + + var border = WINDOW_SIZE - length; + if (copyStart <= border && _end < border) { - Debug.Assert(_bytesUsed < WINDOW_SIZE, "Can't add byte when window is full!"); - _window[_end++] = b; - _end &= WINDOW_MASK; - ++_bytesUsed; - } - - public void WriteLengthDistance(int length, int distance) - { - Debug.Assert((_bytesUsed + length) <= WINDOW_SIZE, "No Enough space"); - - // move backwards distance bytes in the output stream, - // and copy length bytes from this position to the output stream. - _bytesUsed += length; - int copyStart = (_end - distance) & WINDOW_MASK; // start position for coping. - - int border = WINDOW_SIZE - length; - if (copyStart <= border && _end < border) + if (length <= distance) { - if (length <= distance) - { - Array.Copy(_window, copyStart, _window, _end, length); - _end += length; - } - else - { - // The referenced string may overlap the current - // position; for example, if the last 2 bytes decoded have values - // X and Y, a string reference with - // adds X,Y,X,Y,X to the output stream. - while (length-- > 0) - { - _window[_end++] = _window[copyStart++]; - } - } + Array.Copy(_window, copyStart, _window, _end, length); + _end += length; } else { - // copy byte by byte + // The referenced string may overlap the current + // position; for example, if the last 2 bytes decoded have values + // X and Y, a string reference with + // adds X,Y,X,Y,X to the output stream. while (length-- > 0) { _window[_end++] = _window[copyStart++]; - _end &= WINDOW_MASK; - copyStart &= WINDOW_MASK; } } } - - /// - /// Copy up to length of bytes from input directly. - /// This is used for uncompressed block. - /// - public int CopyFrom(InputBuffer input, int length) + else { - length = Math.Min(Math.Min(length, WINDOW_SIZE - _bytesUsed), input.AvailableBytes); - int copied; - - // We might need wrap around to copy all bytes. - int tailLen = WINDOW_SIZE - _end; - if (length > tailLen) + // copy byte by byte + while (length-- > 0) { - // copy the first part - copied = input.CopyTo(_window, _end, tailLen); - if (copied == tailLen) - { - // only try to copy the second part if we have enough bytes in input - copied += input.CopyTo(_window, 0, length - tailLen); - } + _window[_end++] = _window[copyStart++]; + _end &= WINDOW_MASK; + copyStart &= WINDOW_MASK; } - else - { - // only one copy is needed if there is no wrap around. - copied = input.CopyTo(_window, _end, length); - } - - _end = (_end + copied) & WINDOW_MASK; - _bytesUsed += copied; - return copied; - } - - /// Free space in output window. - public int FreeBytes => WINDOW_SIZE - _bytesUsed; - - /// Bytes not consumed in output window. - public int AvailableBytes => _bytesUsed; - - /// Copy the decompressed bytes to output array. - public int CopyTo(byte[] output, int offset, int length) - { - int copyEnd; - - if (length > _bytesUsed) - { - // we can copy all the decompressed bytes out - copyEnd = _end; - length = _bytesUsed; - } - else - { - copyEnd = (_end - _bytesUsed + length) & WINDOW_MASK; // copy length of bytes - } - - int copied = length; - - int tailLen = length - copyEnd; - if (tailLen > 0) - { - // this means we need to copy two parts separately - // copy tailLen bytes from the end of output window - Array.Copy(_window, WINDOW_SIZE - tailLen, output, offset, tailLen); - offset += tailLen; - length = copyEnd; - } - Array.Copy(_window, copyEnd - length, output, offset, length); - _bytesUsed -= copied; - Debug.Assert( - _bytesUsed >= 0, - "check this function and find why we copied more bytes than we have" - ); - return copied; } } + + /// + /// Copy up to length of bytes from input directly. + /// This is used for uncompressed block. + /// + public int CopyFrom(InputBuffer input, int length) + { + length = Math.Min(Math.Min(length, WINDOW_SIZE - _bytesUsed), input.AvailableBytes); + int copied; + + // We might need wrap around to copy all bytes. + var tailLen = WINDOW_SIZE - _end; + if (length > tailLen) + { + // copy the first part + copied = input.CopyTo(_window, _end, tailLen); + if (copied == tailLen) + { + // only try to copy the second part if we have enough bytes in input + copied += input.CopyTo(_window, 0, length - tailLen); + } + } + else + { + // only one copy is needed if there is no wrap around. + copied = input.CopyTo(_window, _end, length); + } + + _end = (_end + copied) & WINDOW_MASK; + _bytesUsed += copied; + return copied; + } + + /// Free space in output window. + public int FreeBytes => WINDOW_SIZE - _bytesUsed; + + /// Bytes not consumed in output window. + public int AvailableBytes => _bytesUsed; + + /// Copy the decompressed bytes to output array. + public int CopyTo(byte[] output, int offset, int length) + { + int copyEnd; + + if (length > _bytesUsed) + { + // we can copy all the decompressed bytes out + copyEnd = _end; + length = _bytesUsed; + } + else + { + copyEnd = (_end - _bytesUsed + length) & WINDOW_MASK; // copy length of bytes + } + + var copied = length; + + var tailLen = length - copyEnd; + if (tailLen > 0) + { + // this means we need to copy two parts separately + // copy tailLen bytes from the end of output window + Array.Copy(_window, WINDOW_SIZE - tailLen, output, offset, tailLen); + offset += tailLen; + length = copyEnd; + } + Array.Copy(_window, copyEnd - length, output, offset, length); + _bytesUsed -= copied; + Debug.Assert( + _bytesUsed >= 0, + "check this function and find why we copied more bytes than we have" + ); + return copied; + } } diff --git a/src/SharpCompress/Compressors/Filters/BCJ2Filter.cs b/src/SharpCompress/Compressors/Filters/BCJ2Filter.cs index 636f5dc0..c3212118 100644 --- a/src/SharpCompress/Compressors/Filters/BCJ2Filter.cs +++ b/src/SharpCompress/Compressors/Filters/BCJ2Filter.cs @@ -1,230 +1,229 @@ -using System; +using System; using System.IO; -namespace SharpCompress.Compressors.Filters +namespace SharpCompress.Compressors.Filters; + +internal class BCJ2Filter : Stream { - internal class BCJ2Filter : Stream + private readonly Stream _baseStream; + private readonly byte[] _input = new byte[4096]; + private int _inputOffset; + private int _inputCount; + private bool _endReached; + + private long _position; + private readonly byte[] _output = new byte[4]; + private int _outputOffset; + private int _outputCount; + + private readonly byte[] _control; + private readonly byte[] _data1; + private readonly byte[] _data2; + + private int _controlPos; + private int _data1Pos; + private int _data2Pos; + + private readonly ushort[] _p = new ushort[256 + 2]; + private uint _range, + _code; + private byte _prevByte; + private bool _isDisposed; + + private const int K_NUM_TOP_BITS = 24; + private const int K_TOP_VALUE = 1 << K_NUM_TOP_BITS; + + private const int K_NUM_BIT_MODEL_TOTAL_BITS = 11; + private const int K_BIT_MODEL_TOTAL = 1 << K_NUM_BIT_MODEL_TOTAL_BITS; + private const int K_NUM_MOVE_BITS = 5; + + private static bool IsJ(byte b0, byte b1) { - private readonly Stream _baseStream; - private readonly byte[] _input = new byte[4096]; - private int _inputOffset; - private int _inputCount; - private bool _endReached; + return (b1 & 0xFE) == 0xE8 || IsJcc(b0, b1); + } - private long _position; - private readonly byte[] _output = new byte[4]; - private int _outputOffset; - private int _outputCount; + private static bool IsJcc(byte b0, byte b1) + { + return b0 == 0x0F && (b1 & 0xF0) == 0x80; + } - private readonly byte[] _control; - private readonly byte[] _data1; - private readonly byte[] _data2; + public BCJ2Filter(byte[] control, byte[] data1, byte[] data2, Stream baseStream) + { + _control = control; + _data1 = data1; + _data2 = data2; + _baseStream = baseStream; - private int _controlPos; - private int _data1Pos; - private int _data2Pos; - - private readonly ushort[] _p = new ushort[256 + 2]; - private uint _range, - _code; - private byte _prevByte; - private bool _isDisposed; - - private const int K_NUM_TOP_BITS = 24; - private const int K_TOP_VALUE = 1 << K_NUM_TOP_BITS; - - private const int K_NUM_BIT_MODEL_TOTAL_BITS = 11; - private const int K_BIT_MODEL_TOTAL = 1 << K_NUM_BIT_MODEL_TOTAL_BITS; - private const int K_NUM_MOVE_BITS = 5; - - private static bool IsJ(byte b0, byte b1) + int i; + for (i = 0; i < _p.Length; i++) { - return (b1 & 0xFE) == 0xE8 || IsJcc(b0, b1); + _p[i] = K_BIT_MODEL_TOTAL >> 1; } - private static bool IsJcc(byte b0, byte b1) + _code = 0; + _range = 0xFFFFFFFF; + for (i = 0; i < 5; i++) { - return b0 == 0x0F && (b1 & 0xF0) == 0x80; + _code = (_code << 8) | control[_controlPos++]; } + } - public BCJ2Filter(byte[] control, byte[] data1, byte[] data2, Stream baseStream) + protected override void Dispose(bool disposing) + { + if (_isDisposed) { - _control = control; - _data1 = data1; - _data2 = data2; - _baseStream = baseStream; + return; + } + _isDisposed = true; + base.Dispose(disposing); + _baseStream.Dispose(); + } - int i; - for (i = 0; i < _p.Length; i++) + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override void Flush() + { + throw new NotSupportedException(); + } + + public override long Length => _baseStream.Length + _data1.Length + _data2.Length; + + public override long Position + { + get => _position; + set => throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + var size = 0; + byte b = 0; + + while (!_endReached && size < count) + { + while (_outputOffset < _outputCount) { - _p[i] = K_BIT_MODEL_TOTAL >> 1; - } - - _code = 0; - _range = 0xFFFFFFFF; - for (i = 0; i < 5; i++) - { - _code = (_code << 8) | control[_controlPos++]; - } - } - - protected override void Dispose(bool disposing) - { - if (_isDisposed) - { - return; - } - _isDisposed = true; - base.Dispose(disposing); - _baseStream.Dispose(); - } - - public override bool CanRead => true; - - public override bool CanSeek => false; - - public override bool CanWrite => false; - - public override void Flush() - { - throw new NotSupportedException(); - } - - public override long Length => _baseStream.Length + _data1.Length + _data2.Length; - - public override long Position - { - get => _position; - set => throw new NotSupportedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - int size = 0; - byte b = 0; - - while (!_endReached && size < count) - { - while (_outputOffset < _outputCount) - { - b = _output[_outputOffset++]; - buffer[offset++] = b; - size++; - _position++; - - _prevByte = b; - if (size == count) - { - return size; - } - } - - if (_inputOffset == _inputCount) - { - _inputOffset = 0; - _inputCount = _baseStream.Read(_input, 0, _input.Length); - if (_inputCount == 0) - { - _endReached = true; - break; - } - } - - b = _input[_inputOffset++]; + b = _output[_outputOffset++]; buffer[offset++] = b; size++; _position++; - if (!IsJ(_prevByte, b)) + _prevByte = b; + if (size == count) { + return size; + } + } + + if (_inputOffset == _inputCount) + { + _inputOffset = 0; + _inputCount = _baseStream.Read(_input, 0, _input.Length); + if (_inputCount == 0) + { + _endReached = true; + break; + } + } + + b = _input[_inputOffset++]; + buffer[offset++] = b; + size++; + _position++; + + if (!IsJ(_prevByte, b)) + { + _prevByte = b; + } + else + { + int prob; + if (b == 0xE8) + { + prob = _prevByte; + } + else if (b == 0xE9) + { + prob = 256; + } + else + { + prob = 257; + } + + var bound = (_range >> K_NUM_BIT_MODEL_TOTAL_BITS) * _p[prob]; + if (_code < bound) + { + _range = bound; + _p[prob] += (ushort)((K_BIT_MODEL_TOTAL - _p[prob]) >> K_NUM_MOVE_BITS); + if (_range < K_TOP_VALUE) + { + _range <<= 8; + _code = (_code << 8) | _control[_controlPos++]; + } _prevByte = b; } else { - int prob; + _range -= bound; + _code -= bound; + _p[prob] -= (ushort)(_p[prob] >> K_NUM_MOVE_BITS); + if (_range < K_TOP_VALUE) + { + _range <<= 8; + _code = (_code << 8) | _control[_controlPos++]; + } + + uint dest; if (b == 0xE8) { - prob = _prevByte; - } - else if (b == 0xE9) - { - prob = 256; + dest = (uint)( + (_data1[_data1Pos++] << 24) + | (_data1[_data1Pos++] << 16) + | (_data1[_data1Pos++] << 8) + | _data1[_data1Pos++] + ); } else { - prob = 257; + dest = (uint)( + (_data2[_data2Pos++] << 24) + | (_data2[_data2Pos++] << 16) + | (_data2[_data2Pos++] << 8) + | _data2[_data2Pos++] + ); } + dest -= (uint)(_position + 4); - uint bound = (_range >> K_NUM_BIT_MODEL_TOTAL_BITS) * _p[prob]; - if (_code < bound) - { - _range = bound; - _p[prob] += (ushort)((K_BIT_MODEL_TOTAL - _p[prob]) >> K_NUM_MOVE_BITS); - if (_range < K_TOP_VALUE) - { - _range <<= 8; - _code = (_code << 8) | _control[_controlPos++]; - } - _prevByte = b; - } - else - { - _range -= bound; - _code -= bound; - _p[prob] -= (ushort)(_p[prob] >> K_NUM_MOVE_BITS); - if (_range < K_TOP_VALUE) - { - _range <<= 8; - _code = (_code << 8) | _control[_controlPos++]; - } - - uint dest; - if (b == 0xE8) - { - dest = (uint)( - (_data1[_data1Pos++] << 24) - | (_data1[_data1Pos++] << 16) - | (_data1[_data1Pos++] << 8) - | _data1[_data1Pos++] - ); - } - else - { - dest = (uint)( - (_data2[_data2Pos++] << 24) - | (_data2[_data2Pos++] << 16) - | (_data2[_data2Pos++] << 8) - | _data2[_data2Pos++] - ); - } - dest -= (uint)(_position + 4); - - _output[0] = (byte)dest; - _output[1] = (byte)(dest >> 8); - _output[2] = (byte)(dest >> 16); - _output[3] = (byte)(dest >> 24); - _outputOffset = 0; - _outputCount = 4; - } + _output[0] = (byte)dest; + _output[1] = (byte)(dest >> 8); + _output[2] = (byte)(dest >> 16); + _output[3] = (byte)(dest >> 24); + _outputOffset = 0; + _outputCount = 4; } } - - return size; } - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } + return size; + } - public override void SetLength(long value) - { - throw new NotSupportedException(); - } + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); } } diff --git a/src/SharpCompress/Compressors/Filters/BCJFilter.cs b/src/SharpCompress/Compressors/Filters/BCJFilter.cs index 57b025a8..416725db 100644 --- a/src/SharpCompress/Compressors/Filters/BCJFilter.cs +++ b/src/SharpCompress/Compressors/Filters/BCJFilter.cs @@ -1,125 +1,124 @@ -using System.IO; +using System.IO; -namespace SharpCompress.Compressors.Filters +namespace SharpCompress.Compressors.Filters; + +internal class BCJFilter : Filter { - internal class BCJFilter : Filter + private static readonly bool[] MASK_TO_ALLOWED_STATUS = { - private static readonly bool[] MASK_TO_ALLOWED_STATUS = + true, + true, + true, + false, + true, + false, + false, + false + }; + + private static readonly int[] MASK_TO_BIT_NUMBER = { 0, 1, 2, 2, 3, 3, 3, 3 }; + + private int _pos; + private int _prevMask; + + public BCJFilter(bool isEncoder, Stream baseStream) : base(isEncoder, baseStream, 5) + { + _pos = 5; + } + + private static bool Test86MsByte(byte b) + { + return b == 0x00 || b == 0xFF; + } + + protected override int Transform(byte[] buffer, int offset, int count) + { + var prevPos = offset - 1; + var end = offset + count - 5; + int i; + + for (i = offset; i <= end; ++i) { - true, - true, - true, - false, - true, - false, - false, - false - }; - - private static readonly int[] MASK_TO_BIT_NUMBER = { 0, 1, 2, 2, 3, 3, 3, 3 }; - - private int _pos; - private int _prevMask; - - public BCJFilter(bool isEncoder, Stream baseStream) : base(isEncoder, baseStream, 5) - { - _pos = 5; - } - - private static bool Test86MsByte(byte b) - { - return b == 0x00 || b == 0xFF; - } - - protected override int Transform(byte[] buffer, int offset, int count) - { - int prevPos = offset - 1; - int end = offset + count - 5; - int i; - - for (i = offset; i <= end; ++i) + if ((buffer[i] & 0xFE) != 0xE8) { - if ((buffer[i] & 0xFE) != 0xE8) - { - continue; - } - - prevPos = i - prevPos; - if ((prevPos & ~3) != 0) - { - // (unsigned)prevPos > 3 - _prevMask = 0; - } - else - { - _prevMask = (_prevMask << (prevPos - 1)) & 7; - if (_prevMask != 0) - { - if ( - !MASK_TO_ALLOWED_STATUS[_prevMask] - || Test86MsByte(buffer[i + 4 - MASK_TO_BIT_NUMBER[_prevMask]]) - ) - { - prevPos = i; - _prevMask = (_prevMask << 1) | 1; - continue; - } - } - } - - prevPos = i; - - if (Test86MsByte(buffer[i + 4])) - { - int src = - buffer[i + 1] - | (buffer[i + 2] << 8) - | (buffer[i + 3] << 16) - | (buffer[i + 4] << 24); - int dest; - while (true) - { - if (_isEncoder) - { - dest = src + (_pos + i - offset); - } - else - { - dest = src - (_pos + i - offset); - } - - if (_prevMask == 0) - { - break; - } - - int index = MASK_TO_BIT_NUMBER[_prevMask] * 8; - if (!Test86MsByte((byte)(dest >> (24 - index)))) - { - break; - } - - src = dest ^ ((1 << (32 - index)) - 1); - } - - buffer[i + 1] = (byte)dest; - buffer[i + 2] = (byte)(dest >> 8); - buffer[i + 3] = (byte)(dest >> 16); - buffer[i + 4] = (byte)(~(((dest >> 24) & 1) - 1)); - i += 4; - } - else - { - _prevMask = (_prevMask << 1) | 1; - } + continue; } prevPos = i - prevPos; - _prevMask = ((prevPos & ~3) != 0) ? 0 : _prevMask << (prevPos - 1); + if ((prevPos & ~3) != 0) + { + // (unsigned)prevPos > 3 + _prevMask = 0; + } + else + { + _prevMask = (_prevMask << (prevPos - 1)) & 7; + if (_prevMask != 0) + { + if ( + !MASK_TO_ALLOWED_STATUS[_prevMask] + || Test86MsByte(buffer[i + 4 - MASK_TO_BIT_NUMBER[_prevMask]]) + ) + { + prevPos = i; + _prevMask = (_prevMask << 1) | 1; + continue; + } + } + } - i -= offset; - _pos += i; - return i; + prevPos = i; + + if (Test86MsByte(buffer[i + 4])) + { + var src = + buffer[i + 1] + | (buffer[i + 2] << 8) + | (buffer[i + 3] << 16) + | (buffer[i + 4] << 24); + int dest; + while (true) + { + if (_isEncoder) + { + dest = src + (_pos + i - offset); + } + else + { + dest = src - (_pos + i - offset); + } + + if (_prevMask == 0) + { + break; + } + + var index = MASK_TO_BIT_NUMBER[_prevMask] * 8; + if (!Test86MsByte((byte)(dest >> (24 - index)))) + { + break; + } + + src = dest ^ ((1 << (32 - index)) - 1); + } + + buffer[i + 1] = (byte)dest; + buffer[i + 2] = (byte)(dest >> 8); + buffer[i + 3] = (byte)(dest >> 16); + buffer[i + 4] = (byte)(~(((dest >> 24) & 1) - 1)); + i += 4; + } + else + { + _prevMask = (_prevMask << 1) | 1; + } } + + prevPos = i - prevPos; + _prevMask = ((prevPos & ~3) != 0) ? 0 : _prevMask << (prevPos - 1); + + i -= offset; + _pos += i; + return i; } } diff --git a/src/SharpCompress/Compressors/Filters/BranchExecFilter.cs b/src/SharpCompress/Compressors/Filters/BranchExecFilter.cs index 6e8278cb..8dffc4d5 100644 --- a/src/SharpCompress/Compressors/Filters/BranchExecFilter.cs +++ b/src/SharpCompress/Compressors/Filters/BranchExecFilter.cs @@ -5,309 +5,326 @@ */ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace SharpCompress.Compressors.Filters +namespace SharpCompress.Compressors.Filters; + +[CLSCompliant(false)] +public sealed class BranchExecFilter { - [CLSCompliant(false)] - public sealed class BranchExecFilter + public enum Alignment : int { - public enum Alignment : int + ARCH_x86_ALIGNMENT = 1, + ARCH_PowerPC_ALIGNMENT = 4, + ARCH_IA64_ALIGNMENT = 16, + ARCH_ARM_ALIGNMENT = 4, + ARCH_ARMTHUMB_ALIGNMENT = 2, + ARCH_SPARC_ALIGNMENT = 4, + } + + public static void X86Converter(byte[] data, uint ip, ref uint state) + { + long i = 0; + long size = data.Length; + uint pos = 0; + var mask = state & 7; + if (size < 5) { - ARCH_x86_ALIGNMENT = 1, - ARCH_PowerPC_ALIGNMENT = 4, - ARCH_IA64_ALIGNMENT = 16, - ARCH_ARM_ALIGNMENT = 4, - ARCH_ARMTHUMB_ALIGNMENT = 2, - ARCH_SPARC_ALIGNMENT = 4, - } - - public static void X86Converter(byte[] data, UInt32 ip, ref UInt32 state) - { - long i = 0; - long size = data.Length; - UInt32 pos = 0; - UInt32 mask = state & 7; - if (size < 5) - return; - size -= 4; - ip += 5; - - for (; ; ) - { - i = pos; - - for (; i < size; i++) - { - if ((data[i] & 0xFE) == 0xE8) - { - break; - } - } - - UInt32 d = (UInt32)(i) - pos; - pos = (UInt32)i; - if (i >= size) - { - state = (d > 2 ? 0 : mask >> (int)d); - return; - } - if (d > 2) - { - mask = 0; - } - else - { - mask >>= (int)d; - if ( - mask != 0 - && ( - mask > 4 - || mask == 3 - || (((((data[(UInt32)(mask >> 1) + 1])) + 1) & 0xFE) == 0) - ) - ) - { - mask = (mask >> 1) | 4; - pos++; - continue; - } - } - - if ((((data[i + 4]) + 1) & 0xFE) == 0) - { - UInt32 inst = - ((UInt32)data[i + 4] << 24) - | ((UInt32)data[i + 3] << 16) - | ((UInt32)data[i + 2] << 8) - | ((UInt32)data[i + 1]); - UInt32 cur = ip + (UInt32)pos; - pos += 5; - - inst -= cur; - if (mask != 0) - { - UInt32 sh = (mask & 6) << 2; - if (((((((Byte)(inst >> (int)sh))) + 1) & 0xFE) == 0)) - { - inst ^= (((UInt32)0x100 << (int)sh) - 1); - inst -= cur; - } - mask = 0; - } - data[i + 1] = (Byte)inst; - data[i + 2] = (Byte)(inst >> 8); - data[i + 3] = (Byte)(inst >> 16); - data[i + 4] = (Byte)(0 - ((inst >> 24) & 1)); - } - else - { - mask = (mask >> 1) | 4; - pos++; - } - } - } - - public static void PowerPCConverter(byte[] data, UInt32 ip) - { - long i = 0; - long size = data.Length; - size &= ~(UInt32)3; - ip -= 4; - - for (; ; ) // infinite loop - { - for (; ; ) // infinite loop - { - if (i >= size) - return; - i += 4; - - if ((data[i - 4] & 0xFC) == 0x48 && (data[i - 1] & 3) == 1) - break; - } - { - UInt32 inst = BitConverter.ToUInt32(data, (int)i - 4); - - if (BitConverter.IsLittleEndian) - { - inst = Utility.SwapUINT32(inst); - } - - inst -= (UInt32)(ip + i); - inst &= 0x03FFFFFF; - inst |= 0x48000000; - - Utility.SetBigUInt32(ref data, inst, (i - 4)); - } - } - } - - public static void ARMConverter(byte[] data, UInt32 ip) - { - long i = 0; - long size = data.Length; - size &= ~(UInt32)3; - ip += 4; - - for (; ; ) // infinite loop - { - for (; ; ) // infinite loop - { - if (i >= size) - { - return; - } - - i += 4; - if (data[i - 1] == 0xEB) - break; - } - - UInt32 inst = BitConverter.ToUInt32(data, (int)i - 4); - inst <<= 2; - inst -= (UInt32)(ip + i); - inst >>= 2; - inst &= 0x00FFFFFF; - inst |= 0xEB000000; - - Utility.SetLittleUInt32(ref data, inst, i - 4); - } - } - - public static void ARMTConverter(byte[] data, UInt32 ip) - { - long i = 0; - long size = data.Length; - size &= ~(UInt32)1; - long lim = size - 4; - - for (; ; ) - { - UInt32 b1; - for (; ; ) - { - UInt32 b3; - if (i > lim) - return; - b1 = data[i + 1]; - b3 = data[i + 3]; - i += 2; - b1 ^= 8; - if ((b3 & b1) >= 0xF8) - break; - } - - UInt32 inst = - ((UInt32)b1 << 19) - + (((UInt32)data[i + 1] & 0x7) << 8) - + (((UInt32)data[i - 2] << 11)) - + (data[i]); - - i += 2; - - UInt32 cur = ((UInt32)(ip + i)) >> 1; - inst -= cur; - - data[i - 4] = (Byte)(inst >> 11); - data[i - 3] = (Byte)(0xF0 | ((inst >> 19) & 0x7)); - data[i - 2] = (Byte)inst; - data[i - 1] = (Byte)(0xF8 | (inst >> 8)); - } - } - - public static void IA64Converter(byte[] data, UInt32 ip) - { - UInt32 i = 0; - long size = data.Length; - if (size < 16) - throw new InvalidDataException("Unexpected data size"); - size -= 16; - - do - { - UInt32 m = ((UInt32)0x334B0000 >> (data[i] & 0x1E)) & 3; - if (m != 0) - { - m++; - do - { - UInt32 iterator = (UInt32)((i + (m * 5) - 8)); - if ( - ((data[iterator + 3] >> (int)m) & 15) == 5 - && ( - ((data[iterator - 1] | ((UInt32)data[iterator] << 8)) >> (int)m) - & 0x70 - ) == 0 - ) - { - UInt32 raw = BitConverter.ToUInt32(data, (int)iterator); - UInt32 inst = raw >> (int)m; - inst = (inst & 0xFFFFF) | ((inst & (1 << 23)) >> 3); - - inst <<= 4; - inst -= (ip + (UInt32)i); - inst >>= 4; - - inst &= 0x1FFFFF; - inst += 0x700000; - inst &= 0x8FFFFF; - raw &= ~((UInt32)0x8FFFFF << (int)m); - raw |= (inst << (int)m); - - Utility.SetLittleUInt32(ref data, raw, iterator); - } - } while (++m <= 4); - } - i += 16; - } while (i <= size); return; } - public static void SPARCConverter(byte[] data, UInt32 ip) - { - long i = 0; - long size = data.Length; - size &= ~(UInt32)3; - ip -= 4; + size -= 4; + ip += 5; + for (; ; ) + { + i = pos; + + for (; i < size; i++) + { + if ((data[i] & 0xFE) == 0xE8) + { + break; + } + } + + var d = (uint)(i) - pos; + pos = (uint)i; + if (i >= size) + { + state = (d > 2 ? 0 : mask >> (int)d); + return; + } + if (d > 2) + { + mask = 0; + } + else + { + mask >>= (int)d; + if ( + mask != 0 + && ( + mask > 4 + || mask == 3 + || (((((data[(mask >> 1) + 1])) + 1) & 0xFE) == 0) + ) + ) + { + mask = (mask >> 1) | 4; + pos++; + continue; + } + } + + if ((((data[i + 4]) + 1) & 0xFE) == 0) + { + var inst = + ((uint)data[i + 4] << 24) + | ((uint)data[i + 3] << 16) + | ((uint)data[i + 2] << 8) + | data[i + 1]; + var cur = ip + pos; + pos += 5; + + inst -= cur; + if (mask != 0) + { + var sh = (mask & 6) << 2; + if (((((((byte)(inst >> (int)sh))) + 1) & 0xFE) == 0)) + { + inst ^= (((uint)0x100 << (int)sh) - 1); + inst -= cur; + } + mask = 0; + } + data[i + 1] = (byte)inst; + data[i + 2] = (byte)(inst >> 8); + data[i + 3] = (byte)(inst >> 16); + data[i + 4] = (byte)(0 - ((inst >> 24) & 1)); + } + else + { + mask = (mask >> 1) | 4; + pos++; + } + } + } + + public static void PowerPCConverter(byte[] data, uint ip) + { + long i = 0; + long size = data.Length; + size &= ~(uint)3; + ip -= 4; + + for (; ; ) // infinite loop + { for (; ; ) // infinite loop { - for (; ; ) // infinite loop + if (i >= size) { - if (i >= size) - return; - - i += 4; - if ( - (data[i - 4] == 0x40 && (data[i - 3] & 0xC0) == 0) - || (data[i - 4] == 0x7F && (data[i - 3] >= 0xC0)) - ) - break; + return; } - UInt32 inst = BitConverter.ToUInt32(data, (int)i - 4); + i += 4; + + if ((data[i - 4] & 0xFC) == 0x48 && (data[i - 1] & 3) == 1) + { + break; + } + } + { + var inst = BitConverter.ToUInt32(data, (int)i - 4); if (BitConverter.IsLittleEndian) { inst = Utility.SwapUINT32(inst); } - inst <<= 2; - inst -= (UInt32)(ip + i); - - inst &= 0x01FFFFFF; - inst -= (UInt32)1 << 24; - inst ^= 0xFF000000; - inst >>= 2; - inst |= 0x40000000; + inst -= (uint)(ip + i); + inst &= 0x03FFFFFF; + inst |= 0x48000000; Utility.SetBigUInt32(ref data, inst, (i - 4)); } } } + + public static void ARMConverter(byte[] data, uint ip) + { + long i = 0; + long size = data.Length; + size &= ~(uint)3; + ip += 4; + + for (; ; ) // infinite loop + { + for (; ; ) // infinite loop + { + if (i >= size) + { + return; + } + + i += 4; + if (data[i - 1] == 0xEB) + { + break; + } + } + + var inst = BitConverter.ToUInt32(data, (int)i - 4); + inst <<= 2; + inst -= (uint)(ip + i); + inst >>= 2; + inst &= 0x00FFFFFF; + inst |= 0xEB000000; + + Utility.SetLittleUInt32(ref data, inst, i - 4); + } + } + + public static void ARMTConverter(byte[] data, uint ip) + { + long i = 0; + long size = data.Length; + size &= ~(uint)1; + var lim = size - 4; + + for (; ; ) + { + uint b1; + for (; ; ) + { + uint b3; + if (i > lim) + { + return; + } + + b1 = data[i + 1]; + b3 = data[i + 3]; + i += 2; + b1 ^= 8; + if ((b3 & b1) >= 0xF8) + { + break; + } + } + + var inst = + (b1 << 19) + + (((uint)data[i + 1] & 0x7) << 8) + + (((uint)data[i - 2] << 11)) + + (data[i]); + + i += 2; + + var cur = ((uint)(ip + i)) >> 1; + inst -= cur; + + data[i - 4] = (byte)(inst >> 11); + data[i - 3] = (byte)(0xF0 | ((inst >> 19) & 0x7)); + data[i - 2] = (byte)inst; + data[i - 1] = (byte)(0xF8 | (inst >> 8)); + } + } + + public static void IA64Converter(byte[] data, uint ip) + { + uint i = 0; + long size = data.Length; + if (size < 16) + { + throw new InvalidDataException("Unexpected data size"); + } + + size -= 16; + + do + { + var m = ((uint)0x334B0000 >> (data[i] & 0x1E)) & 3; + if (m != 0) + { + m++; + do + { + var iterator = (i + (m * 5) - 8); + if ( + ((data[iterator + 3] >> (int)m) & 15) == 5 + && ( + ((data[iterator - 1] | ((uint)data[iterator] << 8)) >> (int)m) + & 0x70 + ) == 0 + ) + { + var raw = BitConverter.ToUInt32(data, (int)iterator); + var inst = raw >> (int)m; + inst = (inst & 0xFFFFF) | ((inst & (1 << 23)) >> 3); + + inst <<= 4; + inst -= (ip + i); + inst >>= 4; + + inst &= 0x1FFFFF; + inst += 0x700000; + inst &= 0x8FFFFF; + raw &= ~((uint)0x8FFFFF << (int)m); + raw |= (inst << (int)m); + + Utility.SetLittleUInt32(ref data, raw, iterator); + } + } while (++m <= 4); + } + i += 16; + } while (i <= size); + return; + } + + public static void SPARCConverter(byte[] data, uint ip) + { + long i = 0; + long size = data.Length; + size &= ~(uint)3; + ip -= 4; + + for (; ; ) // infinite loop + { + for (; ; ) // infinite loop + { + if (i >= size) + { + return; + } + + i += 4; + if ( + (data[i - 4] == 0x40 && (data[i - 3] & 0xC0) == 0) + || (data[i - 4] == 0x7F && (data[i - 3] >= 0xC0)) + ) + { + break; + } + } + + var inst = BitConverter.ToUInt32(data, (int)i - 4); + + if (BitConverter.IsLittleEndian) + { + inst = Utility.SwapUINT32(inst); + } + + inst <<= 2; + inst -= (uint)(ip + i); + + inst &= 0x01FFFFFF; + inst -= (uint)1 << 24; + inst ^= 0xFF000000; + inst >>= 2; + inst |= 0x40000000; + + Utility.SetBigUInt32(ref data, inst, (i - 4)); + } + } } diff --git a/src/SharpCompress/Compressors/Filters/Filter.cs b/src/SharpCompress/Compressors/Filters/Filter.cs index 1ee8c076..d7f183f9 100644 --- a/src/SharpCompress/Compressors/Filters/Filter.cs +++ b/src/SharpCompress/Compressors/Filters/Filter.cs @@ -1,158 +1,157 @@ -using System; +using System; using System.IO; -namespace SharpCompress.Compressors.Filters +namespace SharpCompress.Compressors.Filters; + +internal abstract class Filter : Stream { - internal abstract class Filter : Stream + protected bool _isEncoder; + protected Stream _baseStream; + + private readonly byte[] _tail; + private readonly byte[] _window; + private int _transformed; + private int _read; + private bool _endReached; + private bool _isDisposed; + + protected Filter(bool isEncoder, Stream baseStream, int lookahead) { - protected bool _isEncoder; - protected Stream _baseStream; + _isEncoder = isEncoder; + _baseStream = baseStream; + _tail = new byte[lookahead - 1]; + _window = new byte[_tail.Length * 2]; + } - private readonly byte[] _tail; - private readonly byte[] _window; - private int _transformed; - private int _read; - private bool _endReached; - private bool _isDisposed; - - protected Filter(bool isEncoder, Stream baseStream, int lookahead) + protected override void Dispose(bool disposing) + { + if (_isDisposed) { - _isEncoder = isEncoder; - _baseStream = baseStream; - _tail = new byte[lookahead - 1]; - _window = new byte[_tail.Length * 2]; + return; } + _isDisposed = true; + base.Dispose(disposing); + _baseStream.Dispose(); + } - protected override void Dispose(bool disposing) + public override bool CanRead => !_isEncoder; + + public override bool CanSeek => false; + + public override bool CanWrite => _isEncoder; + + public override void Flush() + { + throw new NotSupportedException(); + } + + public override long Length => _baseStream.Length; + + public override long Position + { + get => _baseStream.Position; + set => throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + var size = 0; + + if (_transformed > 0) { - if (_isDisposed) + var copySize = _transformed; + if (copySize > count) { - return; + copySize = count; } - _isDisposed = true; - base.Dispose(disposing); - _baseStream.Dispose(); + Buffer.BlockCopy(_tail, 0, buffer, offset, copySize); + _transformed -= copySize; + _read -= copySize; + offset += copySize; + count -= copySize; + size += copySize; + Buffer.BlockCopy(_tail, copySize, _tail, 0, _read); } - - public override bool CanRead => !_isEncoder; - - public override bool CanSeek => false; - - public override bool CanWrite => _isEncoder; - - public override void Flush() + if (count == 0) { - throw new NotSupportedException(); - } - - public override long Length => _baseStream.Length; - - public override long Position - { - get => _baseStream.Position; - set => throw new NotSupportedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - int size = 0; - - if (_transformed > 0) - { - int copySize = _transformed; - if (copySize > count) - { - copySize = count; - } - Buffer.BlockCopy(_tail, 0, buffer, offset, copySize); - _transformed -= copySize; - _read -= copySize; - offset += copySize; - count -= copySize; - size += copySize; - Buffer.BlockCopy(_tail, copySize, _tail, 0, _read); - } - if (count == 0) - { - return size; - } - - int inSize = _read; - if (inSize > count) - { - inSize = count; - } - Buffer.BlockCopy(_tail, 0, buffer, offset, inSize); - _read -= inSize; - Buffer.BlockCopy(_tail, inSize, _tail, 0, _read); - while (!_endReached && inSize < count) - { - int baseRead = _baseStream.Read(buffer, offset + inSize, count - inSize); - inSize += baseRead; - if (baseRead == 0) - { - _endReached = true; - } - } - while (!_endReached && _read < _tail.Length) - { - int baseRead = _baseStream.Read(_tail, _read, _tail.Length - _read); - _read += baseRead; - if (baseRead == 0) - { - _endReached = true; - } - } - - if (inSize > _tail.Length) - { - _transformed = Transform(buffer, offset, inSize); - offset += _transformed; - count -= _transformed; - size += _transformed; - inSize -= _transformed; - _transformed = 0; - } - - if (count == 0) - { - return size; - } - - Buffer.BlockCopy(buffer, offset, _window, 0, inSize); - Buffer.BlockCopy(_tail, 0, _window, inSize, _read); - if (inSize + _read > _tail.Length) - { - _transformed = Transform(_window, 0, inSize + _read); - } - else - { - _transformed = inSize + _read; - } - Buffer.BlockCopy(_window, 0, buffer, offset, inSize); - Buffer.BlockCopy(_window, inSize, _tail, 0, _read); - size += inSize; - _transformed -= inSize; - return size; } - public override long Seek(long offset, SeekOrigin origin) + var inSize = _read; + if (inSize > count) { - throw new NotSupportedException(); + inSize = count; + } + Buffer.BlockCopy(_tail, 0, buffer, offset, inSize); + _read -= inSize; + Buffer.BlockCopy(_tail, inSize, _tail, 0, _read); + while (!_endReached && inSize < count) + { + var baseRead = _baseStream.Read(buffer, offset + inSize, count - inSize); + inSize += baseRead; + if (baseRead == 0) + { + _endReached = true; + } + } + while (!_endReached && _read < _tail.Length) + { + var baseRead = _baseStream.Read(_tail, _read, _tail.Length - _read); + _read += baseRead; + if (baseRead == 0) + { + _endReached = true; + } } - public override void SetLength(long value) + if (inSize > _tail.Length) { - throw new NotSupportedException(); + _transformed = Transform(buffer, offset, inSize); + offset += _transformed; + count -= _transformed; + size += _transformed; + inSize -= _transformed; + _transformed = 0; } - public override void Write(byte[] buffer, int offset, int count) + if (count == 0) { - Transform(buffer, offset, count); - _baseStream.Write(buffer, offset, count); + return size; } - protected abstract int Transform(byte[] buffer, int offset, int count); + Buffer.BlockCopy(buffer, offset, _window, 0, inSize); + Buffer.BlockCopy(_tail, 0, _window, inSize, _read); + if (inSize + _read > _tail.Length) + { + _transformed = Transform(_window, 0, inSize + _read); + } + else + { + _transformed = inSize + _read; + } + Buffer.BlockCopy(_window, 0, buffer, offset, inSize); + Buffer.BlockCopy(_window, inSize, _tail, 0, _read); + size += inSize; + _transformed -= inSize; + + return size; } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + Transform(buffer, offset, count); + _baseStream.Write(buffer, offset, count); + } + + protected abstract int Transform(byte[] buffer, int offset, int count); } diff --git a/src/SharpCompress/Compressors/LZMA/Bcj2DecoderStream.cs b/src/SharpCompress/Compressors/LZMA/Bcj2DecoderStream.cs index 649801a3..fe66d4cd 100644 --- a/src/SharpCompress/Compressors/LZMA/Bcj2DecoderStream.cs +++ b/src/SharpCompress/Compressors/LZMA/Bcj2DecoderStream.cs @@ -1,280 +1,263 @@ -using System; +using System; using System.Collections.Generic; using System.IO; -namespace SharpCompress.Compressors.LZMA +namespace SharpCompress.Compressors.LZMA; + +internal class Bcj2DecoderStream : DecoderStream2 { - internal class Bcj2DecoderStream : DecoderStream2 + private const int K_NUM_TOP_BITS = 24; + private const uint K_TOP_VALUE = (1 << K_NUM_TOP_BITS); + + private class RangeDecoder { - private const int K_NUM_TOP_BITS = 24; - private const uint K_TOP_VALUE = (1 << K_NUM_TOP_BITS); + internal readonly Stream _mStream; + internal uint _range; + internal uint _code; - private class RangeDecoder + public RangeDecoder(Stream stream) { - internal readonly Stream _mStream; - internal uint _range; - internal uint _code; - - public RangeDecoder(Stream stream) + _mStream = stream; + _range = 0xFFFFFFFF; + for (var i = 0; i < 5; i++) { - _mStream = stream; - _range = 0xFFFFFFFF; - for (int i = 0; i < 5; i++) - { - _code = (_code << 8) | ReadByte(); - } - } - - public byte ReadByte() - { - int bt = _mStream.ReadByte(); - if (bt < 0) - { - throw new EndOfStreamException(); - } - - return (byte)bt; - } - - public void Dispose() - { - _mStream.Dispose(); + _code = (_code << 8) | ReadByte(); } } - private class StatusDecoder + public byte ReadByte() { - private const int NUM_MOVE_BITS = 5; - - private const int K_NUM_BIT_MODEL_TOTAL_BITS = 11; - private const uint K_BIT_MODEL_TOTAL = 1u << K_NUM_BIT_MODEL_TOTAL_BITS; - - private uint _prob; - - public StatusDecoder() + var bt = _mStream.ReadByte(); + if (bt < 0) { - _prob = K_BIT_MODEL_TOTAL / 2; + throw new EndOfStreamException(); } - private void UpdateModel(uint symbol) - { - /* - Prob -= (Prob + ((symbol - 1) & ((1 << numMoveBits) - 1))) >> numMoveBits; - Prob += (1 - symbol) << (kNumBitModelTotalBits - numMoveBits); - */ - if (symbol == 0) - { - _prob += (K_BIT_MODEL_TOTAL - _prob) >> NUM_MOVE_BITS; - } - else - { - _prob -= (_prob) >> NUM_MOVE_BITS; - } - } + return (byte)bt; + } - public uint Decode(RangeDecoder decoder) + public void Dispose() + { + _mStream.Dispose(); + } + } + + private class StatusDecoder + { + private const int NUM_MOVE_BITS = 5; + + private const int K_NUM_BIT_MODEL_TOTAL_BITS = 11; + private const uint K_BIT_MODEL_TOTAL = 1u << K_NUM_BIT_MODEL_TOTAL_BITS; + + private uint _prob; + + public StatusDecoder() + { + _prob = K_BIT_MODEL_TOTAL / 2; + } + + public uint Decode(RangeDecoder decoder) + { + var newBound = (decoder._range >> K_NUM_BIT_MODEL_TOTAL_BITS) * _prob; + if (decoder._code < newBound) { - uint newBound = (decoder._range >> K_NUM_BIT_MODEL_TOTAL_BITS) * _prob; - if (decoder._code < newBound) - { - decoder._range = newBound; - _prob += (K_BIT_MODEL_TOTAL - _prob) >> NUM_MOVE_BITS; - if (decoder._range < K_TOP_VALUE) - { - decoder._code = (decoder._code << 8) | decoder.ReadByte(); - decoder._range <<= 8; - } - return 0; - } - decoder._range -= newBound; - decoder._code -= newBound; - _prob -= _prob >> NUM_MOVE_BITS; + decoder._range = newBound; + _prob += (K_BIT_MODEL_TOTAL - _prob) >> NUM_MOVE_BITS; if (decoder._range < K_TOP_VALUE) { decoder._code = (decoder._code << 8) | decoder.ReadByte(); decoder._range <<= 8; } - return 1; - } - } - - private readonly Stream _mMainStream; - private readonly Stream _mCallStream; - private readonly Stream _mJumpStream; - private readonly RangeDecoder _mRangeDecoder; - private readonly StatusDecoder[] _mStatusDecoder; - private long _mWritten; - private readonly IEnumerator _mIter; - private bool _mFinished; - private bool _isDisposed; - - public Bcj2DecoderStream(Stream[] streams, byte[] info, long limit) - { - if (info != null && info.Length > 0) - { - throw new NotSupportedException(); - } - - if (streams.Length != 4) - { - throw new NotSupportedException(); - } - - _mMainStream = streams[0]; - _mCallStream = streams[1]; - _mJumpStream = streams[2]; - _mRangeDecoder = new RangeDecoder(streams[3]); - - _mStatusDecoder = new StatusDecoder[256 + 2]; - for (int i = 0; i < _mStatusDecoder.Length; i++) - { - _mStatusDecoder[i] = new StatusDecoder(); - } - - _mIter = Run().GetEnumerator(); - } - - protected override void Dispose(bool disposing) - { - if (_isDisposed) - { - return; - } - _isDisposed = true; - base.Dispose(disposing); - _mMainStream.Dispose(); - _mCallStream.Dispose(); - _mJumpStream.Dispose(); - } - - private static bool IsJcc(byte b0, byte b1) - { - return b0 == 0x0F && (b1 & 0xF0) == 0x80; - } - - private static bool IsJ(byte b0, byte b1) - { - return (b1 & 0xFE) == 0xE8 || IsJcc(b0, b1); - } - - private static int GetIndex(byte b0, byte b1) - { - if (b1 == 0xE8) - { - return b0; - } - if (b1 == 0xE9) - { - return 256; - } - return 257; - } - - public override int Read(byte[] buffer, int offset, int count) - { - if (count == 0 || _mFinished) - { return 0; } - - for (int i = 0; i < count; i++) + decoder._range -= newBound; + decoder._code -= newBound; + _prob -= _prob >> NUM_MOVE_BITS; + if (decoder._range < K_TOP_VALUE) { - if (!_mIter.MoveNext()) - { - _mFinished = true; - return i; - } - - buffer[offset + i] = _mIter.Current; + decoder._code = (decoder._code << 8) | decoder.ReadByte(); + decoder._range <<= 8; } + return 1; + } + } - return count; + private readonly Stream _mMainStream; + private readonly Stream _mCallStream; + private readonly Stream _mJumpStream; + private readonly RangeDecoder _mRangeDecoder; + private readonly StatusDecoder[] _mStatusDecoder; + private long _mWritten; + private readonly IEnumerator _mIter; + private bool _mFinished; + private bool _isDisposed; + + public Bcj2DecoderStream(Stream[] streams, byte[] info, long limit) + { + if (info != null && info.Length > 0) + { + throw new NotSupportedException(); } - public override int ReadByte() + if (streams.Length != 4) { - if (_mFinished) - { - return -1; - } + throw new NotSupportedException(); + } + _mMainStream = streams[0]; + _mCallStream = streams[1]; + _mJumpStream = streams[2]; + _mRangeDecoder = new RangeDecoder(streams[3]); + + _mStatusDecoder = new StatusDecoder[256 + 2]; + for (var i = 0; i < _mStatusDecoder.Length; i++) + { + _mStatusDecoder[i] = new StatusDecoder(); + } + + _mIter = Run().GetEnumerator(); + } + + protected override void Dispose(bool disposing) + { + if (_isDisposed) + { + return; + } + _isDisposed = true; + base.Dispose(disposing); + _mMainStream.Dispose(); + _mCallStream.Dispose(); + _mJumpStream.Dispose(); + } + + private static bool IsJcc(byte b0, byte b1) + { + return b0 == 0x0F && (b1 & 0xF0) == 0x80; + } + + private static bool IsJ(byte b0, byte b1) + { + return (b1 & 0xFE) == 0xE8 || IsJcc(b0, b1); + } + + private static int GetIndex(byte b0, byte b1) + { + if (b1 == 0xE8) + { + return b0; + } + if (b1 == 0xE9) + { + return 256; + } + return 257; + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count == 0 || _mFinished) + { + return 0; + } + + for (var i = 0; i < count; i++) + { if (!_mIter.MoveNext()) { _mFinished = true; - return -1; + return i; } - return _mIter.Current; + buffer[offset + i] = _mIter.Current; } - public IEnumerable Run() + return count; + } + + public override int ReadByte() + { + if (_mFinished) { - const uint kBurstSize = (1u << 18); + return -1; + } - byte prevByte = 0; - uint processedBytes = 0; - for (; ; ) + if (!_mIter.MoveNext()) + { + _mFinished = true; + return -1; + } + + return _mIter.Current; + } + + public IEnumerable Run() + { + const uint kBurstSize = (1u << 18); + + byte prevByte = 0; + uint processedBytes = 0; + for (; ; ) + { + byte b = 0; + uint i; + for (i = 0; i < kBurstSize; i++) { - byte b = 0; - uint i; - for (i = 0; i < kBurstSize; i++) + var tmp = _mMainStream.ReadByte(); + if (tmp < 0) { - int tmp = _mMainStream.ReadByte(); - if (tmp < 0) + yield break; + } + + b = (byte)tmp; + _mWritten++; + yield return b; + if (IsJ(prevByte, b)) + { + break; + } + + prevByte = b; + } + + processedBytes += i; + if (i == kBurstSize) + { + continue; + } + + if (_mStatusDecoder[GetIndex(prevByte, b)].Decode(_mRangeDecoder) == 1) + { + var s = (b == 0xE8) ? _mCallStream : _mJumpStream; + + uint src = 0; + for (i = 0; i < 4; i++) + { + var b0 = s.ReadByte(); + if (b0 < 0) { - yield break; + throw new EndOfStreamException(); } - b = (byte)tmp; - _mWritten++; - yield return b; - if (IsJ(prevByte, b)) - { - break; - } - - prevByte = b; + src <<= 8; + src |= (uint)b0; } - processedBytes += i; - if (i == kBurstSize) - { - continue; - } - - if (_mStatusDecoder[GetIndex(prevByte, b)].Decode(_mRangeDecoder) == 1) - { - Stream s = (b == 0xE8) ? _mCallStream : _mJumpStream; - - uint src = 0; - for (i = 0; i < 4; i++) - { - int b0 = s.ReadByte(); - if (b0 < 0) - { - throw new EndOfStreamException(); - } - - src <<= 8; - src |= (uint)b0; - } - - uint dest = src - (uint)(_mWritten + 4); - _mWritten++; - yield return (byte)dest; - _mWritten++; - yield return (byte)(dest >> 8); - _mWritten++; - yield return (byte)(dest >> 16); - _mWritten++; - yield return (byte)(dest >> 24); - prevByte = (byte)(dest >> 24); - processedBytes += 4; - } - else - { - prevByte = b; - } + var dest = src - (uint)(_mWritten + 4); + _mWritten++; + yield return (byte)dest; + _mWritten++; + yield return (byte)(dest >> 8); + _mWritten++; + yield return (byte)(dest >> 16); + _mWritten++; + yield return (byte)(dest >> 24); + prevByte = (byte)(dest >> 24); + processedBytes += 4; + } + else + { + prevByte = b; } } } diff --git a/src/SharpCompress/Compressors/LZMA/BitVector.cs b/src/SharpCompress/Compressors/LZMA/BitVector.cs index 5995512a..5b40536d 100644 --- a/src/SharpCompress/Compressors/LZMA/BitVector.cs +++ b/src/SharpCompress/Compressors/LZMA/BitVector.cs @@ -1,100 +1,99 @@ -using System; +using System; using System.Collections.Generic; using System.Text; -namespace SharpCompress.Compressors.LZMA +namespace SharpCompress.Compressors.LZMA; + +internal class BitVector { - internal class BitVector + private readonly uint[] _mBits; + + public BitVector(int length) { - private readonly uint[] _mBits; + Length = length; + _mBits = new uint[(length + 31) >> 5]; + } - public BitVector(int length) + public BitVector(int length, bool initValue) + { + Length = length; + _mBits = new uint[(length + 31) >> 5]; + + if (initValue) { - Length = length; - _mBits = new uint[(length + 31) >> 5]; - } - - public BitVector(int length, bool initValue) - { - Length = length; - _mBits = new uint[(length + 31) >> 5]; - - if (initValue) + for (var i = 0; i < _mBits.Length; i++) { - for (int i = 0; i < _mBits.Length; i++) - { - _mBits[i] = ~0u; - } + _mBits[i] = ~0u; } } - - public BitVector(List bits) : this(bits.Count) - { - for (int i = 0; i < bits.Count; i++) - { - if (bits[i]) - { - SetBit(i); - } - } - } - - public bool[] ToArray() - { - bool[] bits = new bool[Length]; - for (int i = 0; i < bits.Length; i++) - { - bits[i] = this[i]; - } - return bits; - } - - public int Length { get; } - - public bool this[int index] - { - get - { - if (index < 0 || index >= Length) - { - throw new ArgumentOutOfRangeException(nameof(index)); - } - - return (_mBits[index >> 5] & (1u << (index & 31))) != 0; - } - } - - public void SetBit(int index) - { - if (index < 0 || index >= Length) - { - throw new ArgumentOutOfRangeException(nameof(index)); - } - - _mBits[index >> 5] |= 1u << (index & 31); - } - - internal bool GetAndSet(int index) - { - if (index < 0 || index >= Length) - { - throw new ArgumentOutOfRangeException(nameof(index)); - } - - uint bits = _mBits[index >> 5]; - uint mask = 1u << (index & 31); - _mBits[index >> 5] |= mask; - return (bits & mask) != 0; - } - - public override string ToString() - { - StringBuilder sb = new StringBuilder(Length); - for (int i = 0; i < Length; i++) - { - sb.Append(this[i] ? 'x' : '.'); - } - return sb.ToString(); - } + } + + public BitVector(List bits) : this(bits.Count) + { + for (var i = 0; i < bits.Count; i++) + { + if (bits[i]) + { + SetBit(i); + } + } + } + + public bool[] ToArray() + { + var bits = new bool[Length]; + for (var i = 0; i < bits.Length; i++) + { + bits[i] = this[i]; + } + return bits; + } + + public int Length { get; } + + public bool this[int index] + { + get + { + if (index < 0 || index >= Length) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + return (_mBits[index >> 5] & (1u << (index & 31))) != 0; + } + } + + public void SetBit(int index) + { + if (index < 0 || index >= Length) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + _mBits[index >> 5] |= 1u << (index & 31); + } + + internal bool GetAndSet(int index) + { + if (index < 0 || index >= Length) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + var bits = _mBits[index >> 5]; + var mask = 1u << (index & 31); + _mBits[index >> 5] |= mask; + return (bits & mask) != 0; + } + + public override string ToString() + { + var sb = new StringBuilder(Length); + for (var i = 0; i < Length; i++) + { + sb.Append(this[i] ? 'x' : '.'); + } + return sb.ToString(); } } diff --git a/src/SharpCompress/Compressors/LZMA/CRC.cs b/src/SharpCompress/Compressors/LZMA/CRC.cs index 478cb944..cccce883 100644 --- a/src/SharpCompress/Compressors/LZMA/CRC.cs +++ b/src/SharpCompress/Compressors/LZMA/CRC.cs @@ -1,89 +1,88 @@ -using System; +using System; using System.IO; -namespace SharpCompress.Compressors.LZMA +namespace SharpCompress.Compressors.LZMA; + +internal static class Crc { - internal static class Crc + internal const uint INIT_CRC = 0xFFFFFFFF; + internal static readonly uint[] TABLE = new uint[4 * 256]; + + static Crc() { - internal const uint INIT_CRC = 0xFFFFFFFF; - internal static readonly uint[] TABLE = new uint[4 * 256]; + const uint kCrcPoly = 0xEDB88320; - static Crc() + for (uint i = 0; i < 256; i++) { - const uint kCrcPoly = 0xEDB88320; - - for (uint i = 0; i < 256; i++) + var r = i; + for (var j = 0; j < 8; j++) { - uint r = i; - for (int j = 0; j < 8; j++) - { - r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); - } - - TABLE[i] = r; + r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); } - for (uint i = 256; i < TABLE.Length; i++) - { - uint r = TABLE[i - 256]; - TABLE[i] = TABLE[r & 0xFF] ^ (r >> 8); - } + TABLE[i] = r; } - public static uint From(Stream stream, long length) + for (uint i = 256; i < TABLE.Length; i++) { - uint crc = INIT_CRC; - byte[] buffer = new byte[Math.Min(length, 4 << 10)]; - while (length > 0) - { - int delta = stream.Read(buffer, 0, (int)Math.Min(length, buffer.Length)); - if (delta == 0) - { - throw new EndOfStreamException(); - } - crc = Update(crc, buffer, 0, delta); - length -= delta; - } - return Finish(crc); - } - - public static uint Finish(uint crc) - { - return ~crc; - } - - public static uint Update(uint crc, byte bt) - { - return TABLE[(crc & 0xFF) ^ bt] ^ (crc >> 8); - } - - public static uint Update(uint crc, uint value) - { - crc ^= value; - return TABLE[0x300 + (crc & 0xFF)] - ^ TABLE[0x200 + ((crc >> 8) & 0xFF)] - ^ TABLE[0x100 + ((crc >> 16) & 0xFF)] - ^ TABLE[0x000 + (crc >> 24)]; - } - - public static uint Update(uint crc, ulong value) - { - return Update(Update(crc, (uint)value), (uint)(value >> 32)); - } - - public static uint Update(uint crc, long value) - { - return Update(crc, (ulong)value); - } - - public static uint Update(uint crc, byte[] buffer, int offset, int length) - { - for (int i = 0; i < length; i++) - { - crc = Update(crc, buffer[offset + i]); - } - - return crc; + var r = TABLE[i - 256]; + TABLE[i] = TABLE[r & 0xFF] ^ (r >> 8); } } + + public static uint From(Stream stream, long length) + { + var crc = INIT_CRC; + var buffer = new byte[Math.Min(length, 4 << 10)]; + while (length > 0) + { + var delta = stream.Read(buffer, 0, (int)Math.Min(length, buffer.Length)); + if (delta == 0) + { + throw new EndOfStreamException(); + } + crc = Update(crc, buffer, 0, delta); + length -= delta; + } + return Finish(crc); + } + + public static uint Finish(uint crc) + { + return ~crc; + } + + public static uint Update(uint crc, byte bt) + { + return TABLE[(crc & 0xFF) ^ bt] ^ (crc >> 8); + } + + public static uint Update(uint crc, uint value) + { + crc ^= value; + return TABLE[0x300 + (crc & 0xFF)] + ^ TABLE[0x200 + ((crc >> 8) & 0xFF)] + ^ TABLE[0x100 + ((crc >> 16) & 0xFF)] + ^ TABLE[0x000 + (crc >> 24)]; + } + + public static uint Update(uint crc, ulong value) + { + return Update(Update(crc, (uint)value), (uint)(value >> 32)); + } + + public static uint Update(uint crc, long value) + { + return Update(crc, (ulong)value); + } + + public static uint Update(uint crc, byte[] buffer, int offset, int length) + { + for (var i = 0; i < length; i++) + { + crc = Update(crc, buffer[offset + i]); + } + + return crc; + } } diff --git a/src/SharpCompress/Compressors/LZMA/DecoderStream.cs b/src/SharpCompress/Compressors/LZMA/DecoderStream.cs index 084e2a5f..11c62528 100644 --- a/src/SharpCompress/Compressors/LZMA/DecoderStream.cs +++ b/src/SharpCompress/Compressors/LZMA/DecoderStream.cs @@ -1,231 +1,228 @@ -using System; +using System; using System.IO; using SharpCompress.Common.SevenZip; using SharpCompress.Compressors.LZMA.Utilites; using SharpCompress.IO; -namespace SharpCompress.Compressors.LZMA +namespace SharpCompress.Compressors.LZMA; + +internal abstract class DecoderStream2 : Stream { - internal abstract class DecoderStream2 : Stream + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override void Flush() { - public override bool CanRead => true; - - public override bool CanSeek => false; - - public override bool CanWrite => false; - - public override void Flush() - { - throw new NotSupportedException(); - } - - public override long Length => throw new NotSupportedException(); - - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } + throw new NotSupportedException(); } - internal static class DecoderStreamHelper + public override long Length => throw new NotSupportedException(); + + public override long Position { - private static int FindCoderIndexForOutStreamIndex(CFolder folderInfo, int outStreamIndex) - { - for (int coderIndex = 0; coderIndex < folderInfo._coders.Count; coderIndex++) - { - var coderInfo = folderInfo._coders[coderIndex]; - outStreamIndex -= coderInfo._numOutStreams; - if (outStreamIndex < 0) - { - return coderIndex; - } - } + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } - throw new InvalidOperationException("Could not link output stream to coder."); - } + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } - private static void FindPrimaryOutStreamIndex( - CFolder folderInfo, - out int primaryCoderIndex, - out int primaryOutStreamIndex - ) - { - bool foundPrimaryOutStream = false; - primaryCoderIndex = -1; - primaryOutStreamIndex = -1; + public override void SetLength(long value) + { + throw new NotSupportedException(); + } - for ( - int outStreamIndex = 0, coderIndex = 0; - coderIndex < folderInfo._coders.Count; - coderIndex++ - ) - { - for ( - int coderOutStreamIndex = 0; - coderOutStreamIndex < folderInfo._coders[coderIndex]._numOutStreams; - coderOutStreamIndex++, outStreamIndex++ - ) - { - if (folderInfo.FindBindPairForOutStream(outStreamIndex) < 0) - { - if (foundPrimaryOutStream) - { - throw new NotSupportedException("Multiple output streams."); - } - - foundPrimaryOutStream = true; - primaryCoderIndex = coderIndex; - primaryOutStreamIndex = outStreamIndex; - } - } - } - - if (!foundPrimaryOutStream) - { - throw new NotSupportedException("No output stream."); - } - } - - private static Stream CreateDecoderStream( - Stream[] packStreams, - long[] packSizes, - Stream[] outStreams, - CFolder folderInfo, - int coderIndex, - IPasswordProvider pass - ) - { - var coderInfo = folderInfo._coders[coderIndex]; - if (coderInfo._numOutStreams != 1) - { - throw new NotSupportedException("Multiple output streams are not supported."); - } - - int inStreamId = 0; - for (int i = 0; i < coderIndex; i++) - { - inStreamId += folderInfo._coders[i]._numInStreams; - } - - int outStreamId = 0; - for (int i = 0; i < coderIndex; i++) - { - outStreamId += folderInfo._coders[i]._numOutStreams; - } - - Stream[] inStreams = new Stream[coderInfo._numInStreams]; - - for (int i = 0; i < inStreams.Length; i++, inStreamId++) - { - int bindPairIndex = folderInfo.FindBindPairForInStream(inStreamId); - if (bindPairIndex >= 0) - { - int pairedOutIndex = folderInfo._bindPairs[bindPairIndex]._outIndex; - - if (outStreams[pairedOutIndex] != null) - { - throw new NotSupportedException( - "Overlapping stream bindings are not supported." - ); - } - - int otherCoderIndex = FindCoderIndexForOutStreamIndex( - folderInfo, - pairedOutIndex - ); - inStreams[i] = CreateDecoderStream( - packStreams, - packSizes, - outStreams, - folderInfo, - otherCoderIndex, - pass - ); - - //inStreamSizes[i] = folderInfo.UnpackSizes[pairedOutIndex]; - - if (outStreams[pairedOutIndex] != null) - { - throw new NotSupportedException( - "Overlapping stream bindings are not supported." - ); - } - - outStreams[pairedOutIndex] = inStreams[i]; - } - else - { - int index = folderInfo.FindPackStreamArrayIndex(inStreamId); - if (index < 0) - { - throw new NotSupportedException("Could not find input stream binding."); - } - - inStreams[i] = packStreams[index]; - - //inStreamSizes[i] = packSizes[index]; - } - } - - long unpackSize = folderInfo._unpackSizes[outStreamId]; - return DecoderRegistry.CreateDecoderStream( - coderInfo._methodId, - inStreams, - coderInfo._props, - pass, - unpackSize - ); - } - - internal static Stream CreateDecoderStream( - Stream inStream, - long startPos, - long[] packSizes, - CFolder folderInfo, - IPasswordProvider pass - ) - { - if (!folderInfo.CheckStructure()) - { - throw new NotSupportedException("Unsupported stream binding structure."); - } - - Stream[] inStreams = new Stream[folderInfo._packStreams.Count]; - for (int j = 0; j < folderInfo._packStreams.Count; j++) - { - inStreams[j] = new BufferedSubStream(inStream, startPos, packSizes[j]); - startPos += packSizes[j]; - } - - Stream[] outStreams = new Stream[folderInfo._unpackSizes.Count]; - - int primaryCoderIndex, - primaryOutStreamIndex; - FindPrimaryOutStreamIndex(folderInfo, out primaryCoderIndex, out primaryOutStreamIndex); - return CreateDecoderStream( - inStreams, - packSizes, - outStreams, - folderInfo, - primaryCoderIndex, - pass - ); - } + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } +} + +internal static class DecoderStreamHelper +{ + private static int FindCoderIndexForOutStreamIndex(CFolder folderInfo, int outStreamIndex) + { + for (var coderIndex = 0; coderIndex < folderInfo._coders.Count; coderIndex++) + { + var coderInfo = folderInfo._coders[coderIndex]; + outStreamIndex -= coderInfo._numOutStreams; + if (outStreamIndex < 0) + { + return coderIndex; + } + } + + throw new InvalidOperationException("Could not link output stream to coder."); + } + + private static void FindPrimaryOutStreamIndex( + CFolder folderInfo, + out int primaryCoderIndex, + out int primaryOutStreamIndex + ) + { + var foundPrimaryOutStream = false; + primaryCoderIndex = -1; + primaryOutStreamIndex = -1; + + for ( + int outStreamIndex = 0, coderIndex = 0; + coderIndex < folderInfo._coders.Count; + coderIndex++ + ) + { + for ( + var coderOutStreamIndex = 0; + coderOutStreamIndex < folderInfo._coders[coderIndex]._numOutStreams; + coderOutStreamIndex++, outStreamIndex++ + ) + { + if (folderInfo.FindBindPairForOutStream(outStreamIndex) < 0) + { + if (foundPrimaryOutStream) + { + throw new NotSupportedException("Multiple output streams."); + } + + foundPrimaryOutStream = true; + primaryCoderIndex = coderIndex; + primaryOutStreamIndex = outStreamIndex; + } + } + } + + if (!foundPrimaryOutStream) + { + throw new NotSupportedException("No output stream."); + } + } + + private static Stream CreateDecoderStream( + Stream[] packStreams, + long[] packSizes, + Stream[] outStreams, + CFolder folderInfo, + int coderIndex, + IPasswordProvider pass + ) + { + var coderInfo = folderInfo._coders[coderIndex]; + if (coderInfo._numOutStreams != 1) + { + throw new NotSupportedException("Multiple output streams are not supported."); + } + + var inStreamId = 0; + for (var i = 0; i < coderIndex; i++) + { + inStreamId += folderInfo._coders[i]._numInStreams; + } + + var outStreamId = 0; + for (var i = 0; i < coderIndex; i++) + { + outStreamId += folderInfo._coders[i]._numOutStreams; + } + + var inStreams = new Stream[coderInfo._numInStreams]; + + for (var i = 0; i < inStreams.Length; i++, inStreamId++) + { + var bindPairIndex = folderInfo.FindBindPairForInStream(inStreamId); + if (bindPairIndex >= 0) + { + var pairedOutIndex = folderInfo._bindPairs[bindPairIndex]._outIndex; + + if (outStreams[pairedOutIndex] != null) + { + throw new NotSupportedException( + "Overlapping stream bindings are not supported." + ); + } + + var otherCoderIndex = FindCoderIndexForOutStreamIndex( + folderInfo, + pairedOutIndex + ); + inStreams[i] = CreateDecoderStream( + packStreams, + packSizes, + outStreams, + folderInfo, + otherCoderIndex, + pass + ); + + //inStreamSizes[i] = folderInfo.UnpackSizes[pairedOutIndex]; + + if (outStreams[pairedOutIndex] != null) + { + throw new NotSupportedException( + "Overlapping stream bindings are not supported." + ); + } + + outStreams[pairedOutIndex] = inStreams[i]; + } + else + { + var index = folderInfo.FindPackStreamArrayIndex(inStreamId); + if (index < 0) + { + throw new NotSupportedException("Could not find input stream binding."); + } + + inStreams[i] = packStreams[index]; + + //inStreamSizes[i] = packSizes[index]; + } + } + + var unpackSize = folderInfo._unpackSizes[outStreamId]; + return DecoderRegistry.CreateDecoderStream( + coderInfo._methodId, + inStreams, + coderInfo._props, + pass, + unpackSize + ); + } + + internal static Stream CreateDecoderStream( + Stream inStream, + long startPos, + long[] packSizes, + CFolder folderInfo, + IPasswordProvider pass + ) + { + if (!folderInfo.CheckStructure()) + { + throw new NotSupportedException("Unsupported stream binding structure."); + } + + var inStreams = new Stream[folderInfo._packStreams.Count]; + for (var j = 0; j < folderInfo._packStreams.Count; j++) + { + inStreams[j] = new BufferedSubStream(inStream, startPos, packSizes[j]); + startPos += packSizes[j]; + } + + var outStreams = new Stream[folderInfo._unpackSizes.Count]; + + FindPrimaryOutStreamIndex(folderInfo, out var primaryCoderIndex, out var primaryOutStreamIndex); + return CreateDecoderStream( + inStreams, + packSizes, + outStreams, + folderInfo, + primaryCoderIndex, + pass + ); } } diff --git a/src/SharpCompress/Compressors/LZMA/ICoder.cs b/src/SharpCompress/Compressors/LZMA/ICoder.cs index a571ddc5..c0fd198b 100644 --- a/src/SharpCompress/Compressors/LZMA/ICoder.cs +++ b/src/SharpCompress/Compressors/LZMA/ICoder.cs @@ -1,171 +1,170 @@ using System; using System.IO; -namespace SharpCompress.Compressors.LZMA +namespace SharpCompress.Compressors.LZMA; + +/// +/// The exception that is thrown when an error in input stream occurs during decoding. +/// +internal class DataErrorException : Exception +{ + public DataErrorException() : base("Data Error") { } +} + +/// +/// The exception that is thrown when the value of an argument is outside the allowable range. +/// +internal class InvalidParamException : Exception +{ + public InvalidParamException() : base("Invalid Parameter") { } +} + +public interface ICodeProgress { /// - /// The exception that is thrown when an error in input stream occurs during decoding. + /// Callback progress. /// - internal class DataErrorException : Exception - { - public DataErrorException() : base("Data Error") { } - } - - /// - /// The exception that is thrown when the value of an argument is outside the allowable range. - /// - internal class InvalidParamException : Exception - { - public InvalidParamException() : base("Invalid Parameter") { } - } - - public interface ICodeProgress - { - /// - /// Callback progress. - /// - /// - /// input size. -1 if unknown. - /// - /// - /// output size. -1 if unknown. - /// - void SetProgress(Int64 inSize, Int64 outSize); - } - - internal interface ICoder - { - /// - /// Codes streams. - /// - /// - /// input Stream. - /// - /// - /// output Stream. - /// - /// - /// input Size. -1 if unknown. - /// - /// - /// output Size. -1 if unknown. - /// - /// - /// callback progress reference. - /// - void Code( - Stream inStream, - Stream outStream, - Int64 inSize, - Int64 outSize, - ICodeProgress progress - ); - } - - /* - public interface ICoder2 - { - void Code(ISequentialInStream []inStreams, - const UInt64 []inSizes, - ISequentialOutStream []outStreams, - UInt64 []outSizes, - ICodeProgress progress); - }; - */ - - /// - /// Provides the fields that represent properties idenitifiers for compressing. - /// - internal enum CoderPropId - { - /// - /// Specifies default property. - /// - DefaultProp = 0, - - /// - /// Specifies size of dictionary. - /// - DictionarySize, - - /// - /// Specifies size of memory for PPM*. - /// - UsedMemorySize, - - /// - /// Specifies order for PPM methods. - /// - Order, - - /// - /// Specifies Block Size. - /// - BlockSize, - - /// - /// Specifies number of postion state bits for LZMA (0 - x - 4). - /// - PosStateBits, - - /// - /// Specifies number of literal context bits for LZMA (0 - x - 8). - /// - LitContextBits, - - /// - /// Specifies number of literal position bits for LZMA (0 - x - 4). - /// - LitPosBits, - - /// - /// Specifies number of fast bytes for LZ*. - /// - NumFastBytes, - - /// - /// Specifies match finder. LZMA: "BT2", "BT4" or "BT4B". - /// - MatchFinder, - - /// - /// Specifies the number of match finder cyckes. - /// - MatchFinderCycles, - - /// - /// Specifies number of passes. - /// - NumPasses, - - /// - /// Specifies number of algorithm. - /// - Algorithm, - - /// - /// Specifies the number of threads. - /// - NumThreads, - - /// - /// Specifies mode with end marker. - /// - EndMarker - } - - internal interface ISetCoderProperties - { - void SetCoderProperties(CoderPropId[] propIDs, object[] properties); - } - - internal interface IWriteCoderProperties - { - void WriteCoderProperties(Stream outStream); - } - - internal interface ISetDecoderProperties - { - void SetDecoderProperties(byte[] properties); - } + /// + /// input size. -1 if unknown. + /// + /// + /// output size. -1 if unknown. + /// + void SetProgress(long inSize, long outSize); +} + +internal interface ICoder +{ + /// + /// Codes streams. + /// + /// + /// input Stream. + /// + /// + /// output Stream. + /// + /// + /// input Size. -1 if unknown. + /// + /// + /// output Size. -1 if unknown. + /// + /// + /// callback progress reference. + /// + void Code( + Stream inStream, + Stream outStream, + long inSize, + long outSize, + ICodeProgress progress + ); +} + +/* +public interface ICoder2 +{ + void Code(ISequentialInStream []inStreams, + const UInt64 []inSizes, + ISequentialOutStream []outStreams, + UInt64 []outSizes, + ICodeProgress progress); +}; +*/ + +/// +/// Provides the fields that represent properties idenitifiers for compressing. +/// +internal enum CoderPropId +{ + /// + /// Specifies default property. + /// + DefaultProp = 0, + + /// + /// Specifies size of dictionary. + /// + DictionarySize, + + /// + /// Specifies size of memory for PPM*. + /// + UsedMemorySize, + + /// + /// Specifies order for PPM methods. + /// + Order, + + /// + /// Specifies Block Size. + /// + BlockSize, + + /// + /// Specifies number of postion state bits for LZMA (0 - x - 4). + /// + PosStateBits, + + /// + /// Specifies number of literal context bits for LZMA (0 - x - 8). + /// + LitContextBits, + + /// + /// Specifies number of literal position bits for LZMA (0 - x - 4). + /// + LitPosBits, + + /// + /// Specifies number of fast bytes for LZ*. + /// + NumFastBytes, + + /// + /// Specifies match finder. LZMA: "BT2", "BT4" or "BT4B". + /// + MatchFinder, + + /// + /// Specifies the number of match finder cyckes. + /// + MatchFinderCycles, + + /// + /// Specifies number of passes. + /// + NumPasses, + + /// + /// Specifies number of algorithm. + /// + Algorithm, + + /// + /// Specifies the number of threads. + /// + NumThreads, + + /// + /// Specifies mode with end marker. + /// + EndMarker +} + +internal interface ISetCoderProperties +{ + void SetCoderProperties(CoderPropId[] propIDs, object[] properties); +} + +internal interface IWriteCoderProperties +{ + void WriteCoderProperties(Stream outStream); +} + +internal interface ISetDecoderProperties +{ + void SetDecoderProperties(byte[] properties); } diff --git a/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs b/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs index 6d630b59..958294a1 100644 --- a/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs +++ b/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs @@ -3,159 +3,317 @@ using System; using System.IO; -namespace SharpCompress.Compressors.LZMA.LZ +namespace SharpCompress.Compressors.LZMA.LZ; + +internal sealed class BinTree : InWindow { - internal sealed class BinTree : InWindow + private uint _cyclicBufferPos; + private uint _cyclicBufferSize; + private uint _matchMaxLen; + + private uint[] _son; + private uint[] _hash; + + private uint _cutValue = 0xFF; + private uint _hashMask; + private uint _hashSizeSum; + + private bool _hashArray = true; + + private const uint K_HASH2_SIZE = 1 << 10; + private const uint K_HASH3_SIZE = 1 << 16; + private const uint K_BT2_HASH_SIZE = 1 << 16; + private const uint K_START_MAX_LEN = 1; + private const uint K_HASH3_OFFSET = K_HASH2_SIZE; + private const uint K_EMPTY_HASH_VALUE = 0; + private const uint K_MAX_VAL_FOR_NORMALIZE = ((uint)1 << 31) - 1; + + private uint _kNumHashDirectBytes; + private uint _kMinMatchCheck = 4; + private uint _kFixHashSize = K_HASH2_SIZE + K_HASH3_SIZE; + + public void SetType(int numHashBytes) { - private UInt32 _cyclicBufferPos; - private UInt32 _cyclicBufferSize; - private UInt32 _matchMaxLen; - - private UInt32[] _son; - private UInt32[] _hash; - - private UInt32 _cutValue = 0xFF; - private UInt32 _hashMask; - private UInt32 _hashSizeSum; - - private bool _hashArray = true; - - private const UInt32 K_HASH2_SIZE = 1 << 10; - private const UInt32 K_HASH3_SIZE = 1 << 16; - private const UInt32 K_BT2_HASH_SIZE = 1 << 16; - private const UInt32 K_START_MAX_LEN = 1; - private const UInt32 K_HASH3_OFFSET = K_HASH2_SIZE; - private const UInt32 K_EMPTY_HASH_VALUE = 0; - private const UInt32 K_MAX_VAL_FOR_NORMALIZE = ((UInt32)1 << 31) - 1; - - private UInt32 _kNumHashDirectBytes; - private UInt32 _kMinMatchCheck = 4; - private UInt32 _kFixHashSize = K_HASH2_SIZE + K_HASH3_SIZE; - - public void SetType(int numHashBytes) + _hashArray = (numHashBytes > 2); + if (_hashArray) { - _hashArray = (numHashBytes > 2); - if (_hashArray) + _kNumHashDirectBytes = 0; + _kMinMatchCheck = 4; + _kFixHashSize = K_HASH2_SIZE + K_HASH3_SIZE; + } + else + { + _kNumHashDirectBytes = 2; + _kMinMatchCheck = 2 + 1; + _kFixHashSize = 0; + } + } + + public new void SetStream(Stream stream) + { + base.SetStream(stream); + } + + public new void ReleaseStream() + { + base.ReleaseStream(); + } + + public new void Init() + { + base.Init(); + for (uint i = 0; i < _hashSizeSum; i++) + { + _hash[i] = K_EMPTY_HASH_VALUE; + } + _cyclicBufferPos = 0; + ReduceOffsets(-1); + } + + public new void MovePos() + { + if (++_cyclicBufferPos >= _cyclicBufferSize) + { + _cyclicBufferPos = 0; + } + base.MovePos(); + if (_pos == K_MAX_VAL_FOR_NORMALIZE) + { + Normalize(); + } + } + + public new byte GetIndexByte(int index) + { + return base.GetIndexByte(index); + } + + public new uint GetMatchLen(int index, uint distance, uint limit) + { + return base.GetMatchLen(index, distance, limit); + } + + public new uint GetNumAvailableBytes() + { + return base.GetNumAvailableBytes(); + } + + public void Create( + uint historySize, + uint keepAddBufferBefore, + uint matchMaxLen, + uint keepAddBufferAfter + ) + { + if (historySize > K_MAX_VAL_FOR_NORMALIZE - 256) + { + throw new ArgumentOutOfRangeException(nameof(historySize)); + } + _cutValue = 16 + (matchMaxLen >> 1); + + var windowReservSize = + ((historySize + keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2) + 256; + + Create( + historySize + keepAddBufferBefore, + matchMaxLen + keepAddBufferAfter, + windowReservSize + ); + + _matchMaxLen = matchMaxLen; + + var cyclicBufferSize = historySize + 1; + if (_cyclicBufferSize != cyclicBufferSize) + { + _son = new uint[(_cyclicBufferSize = cyclicBufferSize) * 2]; + } + + var hs = K_BT2_HASH_SIZE; + + if (_hashArray) + { + hs = historySize - 1; + hs |= (hs >> 1); + hs |= (hs >> 2); + hs |= (hs >> 4); + hs |= (hs >> 8); + hs >>= 1; + hs |= 0xFFFF; + if (hs > (1 << 24)) { - _kNumHashDirectBytes = 0; - _kMinMatchCheck = 4; - _kFixHashSize = K_HASH2_SIZE + K_HASH3_SIZE; + hs >>= 1; + } + _hashMask = hs; + hs++; + hs += _kFixHashSize; + } + if (hs != _hashSizeSum) + { + _hash = new uint[_hashSizeSum = hs]; + } + } + + public uint GetMatches(uint[] distances) + { + uint lenLimit; + if (_pos + _matchMaxLen <= _streamPos) + { + lenLimit = _matchMaxLen; + } + else + { + lenLimit = _streamPos - _pos; + if (lenLimit < _kMinMatchCheck) + { + MovePos(); + return 0; + } + } + + uint offset = 0; + var matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0; + var cur = _bufferOffset + _pos; + var maxLen = K_START_MAX_LEN; // to avoid items for len < hashSize; + uint hashValue, + hash2Value = 0, + hash3Value = 0; + + if (_hashArray) + { + var temp = Crc.TABLE[_bufferBase[cur]] ^ _bufferBase[cur + 1]; + hash2Value = temp & (K_HASH2_SIZE - 1); + temp ^= ((uint)(_bufferBase[cur + 2]) << 8); + hash3Value = temp & (K_HASH3_SIZE - 1); + hashValue = (temp ^ (Crc.TABLE[_bufferBase[cur + 3]] << 5)) & _hashMask; + } + else + { + hashValue = _bufferBase[cur] ^ ((uint)(_bufferBase[cur + 1]) << 8); + } + + var curMatch = _hash[_kFixHashSize + hashValue]; + if (_hashArray) + { + var curMatch2 = _hash[hash2Value]; + var curMatch3 = _hash[K_HASH3_OFFSET + hash3Value]; + _hash[hash2Value] = _pos; + _hash[K_HASH3_OFFSET + hash3Value] = _pos; + if (curMatch2 > matchMinPos) + { + if (_bufferBase[_bufferOffset + curMatch2] == _bufferBase[cur]) + { + distances[offset++] = maxLen = 2; + distances[offset++] = _pos - curMatch2 - 1; + } + } + if (curMatch3 > matchMinPos) + { + if (_bufferBase[_bufferOffset + curMatch3] == _bufferBase[cur]) + { + if (curMatch3 == curMatch2) + { + offset -= 2; + } + distances[offset++] = maxLen = 3; + distances[offset++] = _pos - curMatch3 - 1; + curMatch2 = curMatch3; + } + } + if (offset != 0 && curMatch2 == curMatch) + { + offset -= 2; + maxLen = K_START_MAX_LEN; + } + } + + _hash[_kFixHashSize + hashValue] = _pos; + + var ptr0 = (_cyclicBufferPos << 1) + 1; + var ptr1 = (_cyclicBufferPos << 1); + + uint len0, + len1; + len0 = len1 = _kNumHashDirectBytes; + + if (_kNumHashDirectBytes != 0) + { + if (curMatch > matchMinPos) + { + if ( + _bufferBase[_bufferOffset + curMatch + _kNumHashDirectBytes] + != _bufferBase[cur + _kNumHashDirectBytes] + ) + { + distances[offset++] = maxLen = _kNumHashDirectBytes; + distances[offset++] = _pos - curMatch - 1; + } + } + } + + var count = _cutValue; + + while (true) + { + if (curMatch <= matchMinPos || count-- == 0) + { + _son[ptr0] = _son[ptr1] = K_EMPTY_HASH_VALUE; + break; + } + var delta = _pos - curMatch; + var cyclicPos = + ( + (delta <= _cyclicBufferPos) + ? (_cyclicBufferPos - delta) + : (_cyclicBufferPos - delta + _cyclicBufferSize) + ) << 1; + + var pby1 = _bufferOffset + curMatch; + var len = Math.Min(len0, len1); + if (_bufferBase[pby1 + len] == _bufferBase[cur + len]) + { + while (++len != lenLimit) + { + if (_bufferBase[pby1 + len] != _bufferBase[cur + len]) + { + break; + } + } + if (maxLen < len) + { + distances[offset++] = maxLen = len; + distances[offset++] = delta - 1; + if (len == lenLimit) + { + _son[ptr1] = _son[cyclicPos]; + _son[ptr0] = _son[cyclicPos + 1]; + break; + } + } + } + if (_bufferBase[pby1 + len] < _bufferBase[cur + len]) + { + _son[ptr1] = curMatch; + ptr1 = cyclicPos + 1; + curMatch = _son[ptr1]; + len1 = len; } else { - _kNumHashDirectBytes = 2; - _kMinMatchCheck = 2 + 1; - _kFixHashSize = 0; + _son[ptr0] = curMatch; + ptr0 = cyclicPos; + curMatch = _son[ptr0]; + len0 = len; } } + MovePos(); + return offset; + } - public new void SetStream(Stream stream) + public void Skip(uint num) + { + do { - base.SetStream(stream); - } - - public new void ReleaseStream() - { - base.ReleaseStream(); - } - - public new void Init() - { - base.Init(); - for (UInt32 i = 0; i < _hashSizeSum; i++) - { - _hash[i] = K_EMPTY_HASH_VALUE; - } - _cyclicBufferPos = 0; - ReduceOffsets(-1); - } - - public new void MovePos() - { - if (++_cyclicBufferPos >= _cyclicBufferSize) - { - _cyclicBufferPos = 0; - } - base.MovePos(); - if (_pos == K_MAX_VAL_FOR_NORMALIZE) - { - Normalize(); - } - } - - public new Byte GetIndexByte(Int32 index) - { - return base.GetIndexByte(index); - } - - public new UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit) - { - return base.GetMatchLen(index, distance, limit); - } - - public new UInt32 GetNumAvailableBytes() - { - return base.GetNumAvailableBytes(); - } - - public void Create( - UInt32 historySize, - UInt32 keepAddBufferBefore, - UInt32 matchMaxLen, - UInt32 keepAddBufferAfter - ) - { - if (historySize > K_MAX_VAL_FOR_NORMALIZE - 256) - { - throw new ArgumentOutOfRangeException(nameof(historySize)); - } - _cutValue = 16 + (matchMaxLen >> 1); - - UInt32 windowReservSize = - (historySize + keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + 256; - - base.Create( - historySize + keepAddBufferBefore, - matchMaxLen + keepAddBufferAfter, - windowReservSize - ); - - _matchMaxLen = matchMaxLen; - - UInt32 cyclicBufferSize = historySize + 1; - if (_cyclicBufferSize != cyclicBufferSize) - { - _son = new UInt32[(_cyclicBufferSize = cyclicBufferSize) * 2]; - } - - UInt32 hs = K_BT2_HASH_SIZE; - - if (_hashArray) - { - hs = historySize - 1; - hs |= (hs >> 1); - hs |= (hs >> 2); - hs |= (hs >> 4); - hs |= (hs >> 8); - hs >>= 1; - hs |= 0xFFFF; - if (hs > (1 << 24)) - { - hs >>= 1; - } - _hashMask = hs; - hs++; - hs += _kFixHashSize; - } - if (hs != _hashSizeSum) - { - _hash = new UInt32[_hashSizeSum = hs]; - } - } - - public UInt32 GetMatches(UInt32[] distances) - { - UInt32 lenLimit; + uint lenLimit; if (_pos + _matchMaxLen <= _streamPos) { lenLimit = _matchMaxLen; @@ -166,92 +324,41 @@ namespace SharpCompress.Compressors.LZMA.LZ if (lenLimit < _kMinMatchCheck) { MovePos(); - return 0; + continue; } } - UInt32 offset = 0; - UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0; - UInt32 cur = _bufferOffset + _pos; - UInt32 maxLen = K_START_MAX_LEN; // to avoid items for len < hashSize; - UInt32 hashValue, - hash2Value = 0, - hash3Value = 0; + var matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0; + var cur = _bufferOffset + _pos; + + uint hashValue; if (_hashArray) { - UInt32 temp = Crc.TABLE[_bufferBase[cur]] ^ _bufferBase[cur + 1]; - hash2Value = temp & (K_HASH2_SIZE - 1); - temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8); - hash3Value = temp & (K_HASH3_SIZE - 1); + var temp = Crc.TABLE[_bufferBase[cur]] ^ _bufferBase[cur + 1]; + var hash2Value = temp & (K_HASH2_SIZE - 1); + _hash[hash2Value] = _pos; + temp ^= ((uint)(_bufferBase[cur + 2]) << 8); + var hash3Value = temp & (K_HASH3_SIZE - 1); + _hash[K_HASH3_OFFSET + hash3Value] = _pos; hashValue = (temp ^ (Crc.TABLE[_bufferBase[cur + 3]] << 5)) & _hashMask; } else { - hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8); - } - - UInt32 curMatch = _hash[_kFixHashSize + hashValue]; - if (_hashArray) - { - UInt32 curMatch2 = _hash[hash2Value]; - UInt32 curMatch3 = _hash[K_HASH3_OFFSET + hash3Value]; - _hash[hash2Value] = _pos; - _hash[K_HASH3_OFFSET + hash3Value] = _pos; - if (curMatch2 > matchMinPos) - { - if (_bufferBase[_bufferOffset + curMatch2] == _bufferBase[cur]) - { - distances[offset++] = maxLen = 2; - distances[offset++] = _pos - curMatch2 - 1; - } - } - if (curMatch3 > matchMinPos) - { - if (_bufferBase[_bufferOffset + curMatch3] == _bufferBase[cur]) - { - if (curMatch3 == curMatch2) - { - offset -= 2; - } - distances[offset++] = maxLen = 3; - distances[offset++] = _pos - curMatch3 - 1; - curMatch2 = curMatch3; - } - } - if (offset != 0 && curMatch2 == curMatch) - { - offset -= 2; - maxLen = K_START_MAX_LEN; - } + hashValue = _bufferBase[cur] ^ ((uint)(_bufferBase[cur + 1]) << 8); } + var curMatch = _hash[_kFixHashSize + hashValue]; _hash[_kFixHashSize + hashValue] = _pos; - UInt32 ptr0 = (_cyclicBufferPos << 1) + 1; - UInt32 ptr1 = (_cyclicBufferPos << 1); + var ptr0 = (_cyclicBufferPos << 1) + 1; + var ptr1 = (_cyclicBufferPos << 1); - UInt32 len0, + uint len0, len1; len0 = len1 = _kNumHashDirectBytes; - if (_kNumHashDirectBytes != 0) - { - if (curMatch > matchMinPos) - { - if ( - _bufferBase[_bufferOffset + curMatch + _kNumHashDirectBytes] - != _bufferBase[cur + _kNumHashDirectBytes] - ) - { - distances[offset++] = maxLen = _kNumHashDirectBytes; - distances[offset++] = _pos - curMatch - 1; - } - } - } - - UInt32 count = _cutValue; - + var count = _cutValue; while (true) { if (curMatch <= matchMinPos || count-- == 0) @@ -259,16 +366,17 @@ namespace SharpCompress.Compressors.LZMA.LZ _son[ptr0] = _son[ptr1] = K_EMPTY_HASH_VALUE; break; } - UInt32 delta = _pos - curMatch; - UInt32 cyclicPos = + + var delta = _pos - curMatch; + var cyclicPos = ( (delta <= _cyclicBufferPos) ? (_cyclicBufferPos - delta) : (_cyclicBufferPos - delta + _cyclicBufferSize) ) << 1; - UInt32 pby1 = _bufferOffset + curMatch; - UInt32 len = Math.Min(len0, len1); + var pby1 = _bufferOffset + curMatch; + var len = Math.Min(len0, len1); if (_bufferBase[pby1 + len] == _bufferBase[cur + len]) { while (++len != lenLimit) @@ -278,16 +386,11 @@ namespace SharpCompress.Compressors.LZMA.LZ break; } } - if (maxLen < len) + if (len == lenLimit) { - distances[offset++] = maxLen = len; - distances[offset++] = delta - 1; - if (len == lenLimit) - { - _son[ptr1] = _son[cyclicPos]; - _son[ptr0] = _son[cyclicPos + 1]; - break; - } + _son[ptr1] = _son[cyclicPos]; + _son[ptr0] = _son[cyclicPos + 1]; + break; } } if (_bufferBase[pby1 + len] < _bufferBase[cur + len]) @@ -306,140 +409,36 @@ namespace SharpCompress.Compressors.LZMA.LZ } } MovePos(); - return offset; - } + } while (--num != 0); + } - public void Skip(UInt32 num) + private void NormalizeLinks(uint[] items, uint numItems, uint subValue) + { + for (uint i = 0; i < numItems; i++) { - do + var value = items[i]; + if (value <= subValue) { - UInt32 lenLimit; - if (_pos + _matchMaxLen <= _streamPos) - { - lenLimit = _matchMaxLen; - } - else - { - lenLimit = _streamPos - _pos; - if (lenLimit < _kMinMatchCheck) - { - MovePos(); - continue; - } - } - - UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0; - UInt32 cur = _bufferOffset + _pos; - - UInt32 hashValue; - - if (_hashArray) - { - UInt32 temp = Crc.TABLE[_bufferBase[cur]] ^ _bufferBase[cur + 1]; - UInt32 hash2Value = temp & (K_HASH2_SIZE - 1); - _hash[hash2Value] = _pos; - temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8); - UInt32 hash3Value = temp & (K_HASH3_SIZE - 1); - _hash[K_HASH3_OFFSET + hash3Value] = _pos; - hashValue = (temp ^ (Crc.TABLE[_bufferBase[cur + 3]] << 5)) & _hashMask; - } - else - { - hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8); - } - - UInt32 curMatch = _hash[_kFixHashSize + hashValue]; - _hash[_kFixHashSize + hashValue] = _pos; - - UInt32 ptr0 = (_cyclicBufferPos << 1) + 1; - UInt32 ptr1 = (_cyclicBufferPos << 1); - - UInt32 len0, - len1; - len0 = len1 = _kNumHashDirectBytes; - - UInt32 count = _cutValue; - while (true) - { - if (curMatch <= matchMinPos || count-- == 0) - { - _son[ptr0] = _son[ptr1] = K_EMPTY_HASH_VALUE; - break; - } - - UInt32 delta = _pos - curMatch; - UInt32 cyclicPos = - ( - (delta <= _cyclicBufferPos) - ? (_cyclicBufferPos - delta) - : (_cyclicBufferPos - delta + _cyclicBufferSize) - ) << 1; - - UInt32 pby1 = _bufferOffset + curMatch; - UInt32 len = Math.Min(len0, len1); - if (_bufferBase[pby1 + len] == _bufferBase[cur + len]) - { - while (++len != lenLimit) - { - if (_bufferBase[pby1 + len] != _bufferBase[cur + len]) - { - break; - } - } - if (len == lenLimit) - { - _son[ptr1] = _son[cyclicPos]; - _son[ptr0] = _son[cyclicPos + 1]; - break; - } - } - if (_bufferBase[pby1 + len] < _bufferBase[cur + len]) - { - _son[ptr1] = curMatch; - ptr1 = cyclicPos + 1; - curMatch = _son[ptr1]; - len1 = len; - } - else - { - _son[ptr0] = curMatch; - ptr0 = cyclicPos; - curMatch = _son[ptr0]; - len0 = len; - } - } - MovePos(); - } while (--num != 0); - } - - private void NormalizeLinks(UInt32[] items, UInt32 numItems, UInt32 subValue) - { - for (UInt32 i = 0; i < numItems; i++) - { - UInt32 value = items[i]; - if (value <= subValue) - { - value = K_EMPTY_HASH_VALUE; - } - else - { - value -= subValue; - } - items[i] = value; + value = K_EMPTY_HASH_VALUE; } - } - - private void Normalize() - { - UInt32 subValue = _pos - _cyclicBufferSize; - NormalizeLinks(_son, _cyclicBufferSize * 2, subValue); - NormalizeLinks(_hash, _hashSizeSum, subValue); - ReduceOffsets((Int32)subValue); - } - - public void SetCutValue(UInt32 cutValue) - { - _cutValue = cutValue; + else + { + value -= subValue; + } + items[i] = value; } } + + private void Normalize() + { + var subValue = _pos - _cyclicBufferSize; + NormalizeLinks(_son, _cyclicBufferSize * 2, subValue); + NormalizeLinks(_hash, _hashSizeSum, subValue); + ReduceOffsets((int)subValue); + } + + public void SetCutValue(uint cutValue) + { + _cutValue = cutValue; + } } diff --git a/src/SharpCompress/Compressors/LZMA/LZ/LzInWindow.cs b/src/SharpCompress/Compressors/LZMA/LZ/LzInWindow.cs index 943009d2..26353624 100644 --- a/src/SharpCompress/Compressors/LZMA/LZ/LzInWindow.cs +++ b/src/SharpCompress/Compressors/LZMA/LZ/LzInWindow.cs @@ -1,186 +1,184 @@ #nullable disable -using System; using System.IO; -namespace SharpCompress.Compressors.LZMA.LZ +namespace SharpCompress.Compressors.LZMA.LZ; + +internal class InWindow { - internal class InWindow + public byte[] _bufferBase; // pointer to buffer with data + private Stream _stream; + private uint _posLimit; // offset (from _buffer) of first byte when new block reading must be done + private bool _streamEndWasReached; // if (true) then _streamPos shows real end of stream + + private uint _pointerToLastSafePosition; + + public uint _bufferOffset; + + public uint _blockSize; // Size of Allocated memory block + public uint _pos; // offset (from _buffer) of curent byte + private uint _keepSizeBefore; // how many BYTEs must be kept in buffer before _pos + private uint _keepSizeAfter; // how many BYTEs must be kept buffer after _pos + public uint _streamPos; // offset (from _buffer) of first not read byte from Stream + + public void MoveBlock() { - public Byte[] _bufferBase; // pointer to buffer with data - private Stream _stream; - private UInt32 _posLimit; // offset (from _buffer) of first byte when new block reading must be done - private bool _streamEndWasReached; // if (true) then _streamPos shows real end of stream + var offset = _bufferOffset + _pos - _keepSizeBefore; - private UInt32 _pointerToLastSafePosition; - - public UInt32 _bufferOffset; - - public UInt32 _blockSize; // Size of Allocated memory block - public UInt32 _pos; // offset (from _buffer) of curent byte - private UInt32 _keepSizeBefore; // how many BYTEs must be kept in buffer before _pos - private UInt32 _keepSizeAfter; // how many BYTEs must be kept buffer after _pos - public UInt32 _streamPos; // offset (from _buffer) of first not read byte from Stream - - public void MoveBlock() + // we need one additional byte, since MovePos moves on 1 byte. + if (offset > 0) { - UInt32 offset = _bufferOffset + _pos - _keepSizeBefore; - - // we need one additional byte, since MovePos moves on 1 byte. - if (offset > 0) - { - offset--; - } - - UInt32 numBytes = _bufferOffset + _streamPos - offset; - - // check negative offset ???? - for (UInt32 i = 0; i < numBytes; i++) - { - _bufferBase[i] = _bufferBase[offset + i]; - } - _bufferOffset -= offset; + offset--; } - public virtual void ReadBlock() + var numBytes = _bufferOffset + _streamPos - offset; + + // check negative offset ???? + for (uint i = 0; i < numBytes; i++) { - if (_streamEndWasReached) + _bufferBase[i] = _bufferBase[offset + i]; + } + _bufferOffset -= offset; + } + + public virtual void ReadBlock() + { + if (_streamEndWasReached) + { + return; + } + while (true) + { + var size = (int)((0 - _bufferOffset) + _blockSize - _streamPos); + if (size == 0) { return; } - while (true) + var numReadBytes = + _stream != null + ? _stream.Read(_bufferBase, (int)(_bufferOffset + _streamPos), size) + : 0; + if (numReadBytes == 0) { - int size = (int)((0 - _bufferOffset) + _blockSize - _streamPos); - if (size == 0) - { - return; - } - int numReadBytes = - _stream != null - ? _stream.Read(_bufferBase, (int)(_bufferOffset + _streamPos), size) - : 0; - if (numReadBytes == 0) - { - _posLimit = _streamPos; - UInt32 pointerToPostion = _bufferOffset + _posLimit; - if (pointerToPostion > _pointerToLastSafePosition) - { - _posLimit = _pointerToLastSafePosition - _bufferOffset; - } - - _streamEndWasReached = true; - return; - } - _streamPos += (UInt32)numReadBytes; - if (_streamPos >= _pos + _keepSizeAfter) - { - _posLimit = _streamPos - _keepSizeAfter; - } - } - } - - private void Free() - { - _bufferBase = null; - } - - public void Create(UInt32 keepSizeBefore, UInt32 keepSizeAfter, UInt32 keepSizeReserv) - { - _keepSizeBefore = keepSizeBefore; - _keepSizeAfter = keepSizeAfter; - UInt32 blockSize = keepSizeBefore + keepSizeAfter + keepSizeReserv; - if (_bufferBase is null || _blockSize != blockSize) - { - Free(); - _blockSize = blockSize; - _bufferBase = new Byte[_blockSize]; - } - _pointerToLastSafePosition = _blockSize - keepSizeAfter; - _streamEndWasReached = false; - } - - public void SetStream(Stream stream) - { - _stream = stream; - if (_streamEndWasReached) - { - _streamEndWasReached = false; - if (IsDataStarved) - { - ReadBlock(); - } - } - } - - public void ReleaseStream() - { - _stream = null; - } - - public void Init() - { - _bufferOffset = 0; - _pos = 0; - _streamPos = 0; - _streamEndWasReached = false; - ReadBlock(); - } - - public void MovePos() - { - _pos++; - if (_pos > _posLimit) - { - UInt32 pointerToPostion = _bufferOffset + _pos; + _posLimit = _streamPos; + var pointerToPostion = _bufferOffset + _posLimit; if (pointerToPostion > _pointerToLastSafePosition) { - MoveBlock(); + _posLimit = _pointerToLastSafePosition - _bufferOffset; } + + _streamEndWasReached = true; + return; + } + _streamPos += (uint)numReadBytes; + if (_streamPos >= _pos + _keepSizeAfter) + { + _posLimit = _streamPos - _keepSizeAfter; + } + } + } + + private void Free() + { + _bufferBase = null; + } + + public void Create(uint keepSizeBefore, uint keepSizeAfter, uint keepSizeReserv) + { + _keepSizeBefore = keepSizeBefore; + _keepSizeAfter = keepSizeAfter; + var blockSize = keepSizeBefore + keepSizeAfter + keepSizeReserv; + if (_bufferBase is null || _blockSize != blockSize) + { + Free(); + _blockSize = blockSize; + _bufferBase = new byte[_blockSize]; + } + _pointerToLastSafePosition = _blockSize - keepSizeAfter; + _streamEndWasReached = false; + } + + public void SetStream(Stream stream) + { + _stream = stream; + if (_streamEndWasReached) + { + _streamEndWasReached = false; + if (IsDataStarved) + { ReadBlock(); } } - - public Byte GetIndexByte(Int32 index) - { - return _bufferBase[_bufferOffset + _pos + index]; - } - - // index + limit have not to exceed _keepSizeAfter; - public UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit) - { - if (_streamEndWasReached) - { - if ((_pos + index) + limit > _streamPos) - { - limit = _streamPos - (UInt32)(_pos + index); - } - } - distance++; - - // Byte *pby = _buffer + (size_t)_pos + index; - UInt32 pby = _bufferOffset + _pos + (UInt32)index; - - UInt32 i; - for (i = 0; i < limit && _bufferBase[pby + i] == _bufferBase[pby + i - distance]; i++) - { - ; - } - return i; - } - - public UInt32 GetNumAvailableBytes() - { - return _streamPos - _pos; - } - - public void ReduceOffsets(Int32 subValue) - { - _bufferOffset += (UInt32)subValue; - _posLimit -= (UInt32)subValue; - _pos -= (UInt32)subValue; - _streamPos -= (UInt32)subValue; - } - - public bool IsDataStarved => _streamPos - _pos < _keepSizeAfter; } + + public void ReleaseStream() + { + _stream = null; + } + + public void Init() + { + _bufferOffset = 0; + _pos = 0; + _streamPos = 0; + _streamEndWasReached = false; + ReadBlock(); + } + + public void MovePos() + { + _pos++; + if (_pos > _posLimit) + { + var pointerToPostion = _bufferOffset + _pos; + if (pointerToPostion > _pointerToLastSafePosition) + { + MoveBlock(); + } + ReadBlock(); + } + } + + public byte GetIndexByte(int index) + { + return _bufferBase[_bufferOffset + _pos + index]; + } + + // index + limit have not to exceed _keepSizeAfter; + public uint GetMatchLen(int index, uint distance, uint limit) + { + if (_streamEndWasReached) + { + if ((_pos + index) + limit > _streamPos) + { + limit = _streamPos - (uint)(_pos + index); + } + } + distance++; + + // Byte *pby = _buffer + (size_t)_pos + index; + var pby = _bufferOffset + _pos + (uint)index; + + uint i; + for (i = 0; i < limit && _bufferBase[pby + i] == _bufferBase[pby + i - distance]; i++) + { + ; + } + return i; + } + + public uint GetNumAvailableBytes() + { + return _streamPos - _pos; + } + + public void ReduceOffsets(int subValue) + { + _bufferOffset += (uint)subValue; + _posLimit -= (uint)subValue; + _pos -= (uint)subValue; + _streamPos -= (uint)subValue; + } + + public bool IsDataStarved => _streamPos - _pos < _keepSizeAfter; } diff --git a/src/SharpCompress/Compressors/LZMA/LZ/LzOutWindow.cs b/src/SharpCompress/Compressors/LZMA/LZ/LzOutWindow.cs index 6542ff91..42eb38b3 100644 --- a/src/SharpCompress/Compressors/LZMA/LZ/LzOutWindow.cs +++ b/src/SharpCompress/Compressors/LZMA/LZ/LzOutWindow.cs @@ -3,205 +3,204 @@ using System; using System.IO; -namespace SharpCompress.Compressors.LZMA.LZ +namespace SharpCompress.Compressors.LZMA.LZ; + +internal class OutWindow { - internal class OutWindow + private byte[] _buffer; + private int _windowSize; + private int _pos; + private int _streamPos; + private int _pendingLen; + private int _pendingDist; + private Stream _stream; + + public long _total; + public long _limit; + + public void Create(int windowSize) { - private byte[] _buffer; - private int _windowSize; - private int _pos; - private int _streamPos; - private int _pendingLen; - private int _pendingDist; - private Stream _stream; - - public long _total; - public long _limit; - - public void Create(int windowSize) + if (_windowSize != windowSize) + { + _buffer = new byte[windowSize]; + } + else + { + _buffer[windowSize - 1] = 0; + } + _windowSize = windowSize; + _pos = 0; + _streamPos = 0; + _pendingLen = 0; + _total = 0; + _limit = 0; + } + + public void Reset() + { + Create(_windowSize); + } + + public void Init(Stream stream) + { + ReleaseStream(); + _stream = stream; + } + + public void Train(Stream stream) + { + var len = stream.Length; + var size = (len < _windowSize) ? (int)len : _windowSize; + stream.Position = len - size; + _total = 0; + _limit = size; + _pos = _windowSize - size; + CopyStream(stream, size); + if (_pos == _windowSize) { - if (_windowSize != windowSize) - { - _buffer = new byte[windowSize]; - } - else - { - _buffer[windowSize - 1] = 0; - } - _windowSize = windowSize; _pos = 0; - _streamPos = 0; - _pendingLen = 0; - _total = 0; - _limit = 0; } + _streamPos = _pos; + } - public void Reset() + public void ReleaseStream() + { + Flush(); + _stream = null; + } + + public void Flush() + { + if (_stream is null) { - Create(_windowSize); + return; } - - public void Init(Stream stream) + var size = _pos - _streamPos; + if (size == 0) { - ReleaseStream(); - _stream = stream; + return; } - - public void Train(Stream stream) + _stream.Write(_buffer, _streamPos, size); + if (_pos >= _windowSize) { - long len = stream.Length; - int size = (len < _windowSize) ? (int)len : _windowSize; - stream.Position = len - size; - _total = 0; - _limit = size; - _pos = _windowSize - size; - CopyStream(stream, size); - if (_pos == _windowSize) + _pos = 0; + } + _streamPos = _pos; + } + + public void CopyBlock(int distance, int len) + { + var size = len; + var pos = _pos - distance - 1; + if (pos < 0) + { + pos += _windowSize; + } + for (; size > 0 && _pos < _windowSize && _total < _limit; size--) + { + if (pos >= _windowSize) { - _pos = 0; + pos = 0; } - _streamPos = _pos; - } - - public void ReleaseStream() - { - Flush(); - _stream = null; - } - - public void Flush() - { - if (_stream is null) - { - return; - } - int size = _pos - _streamPos; - if (size == 0) - { - return; - } - _stream.Write(_buffer, _streamPos, size); - if (_pos >= _windowSize) - { - _pos = 0; - } - _streamPos = _pos; - } - - public void CopyBlock(int distance, int len) - { - int size = len; - int pos = _pos - distance - 1; - if (pos < 0) - { - pos += _windowSize; - } - for (; size > 0 && _pos < _windowSize && _total < _limit; size--) - { - if (pos >= _windowSize) - { - pos = 0; - } - _buffer[_pos++] = _buffer[pos++]; - _total++; - if (_pos >= _windowSize) - { - Flush(); - } - } - _pendingLen = size; - _pendingDist = distance; - } - - public void PutByte(byte b) - { - _buffer[_pos++] = b; + _buffer[_pos++] = _buffer[pos++]; _total++; if (_pos >= _windowSize) { Flush(); } } - - public byte GetByte(int distance) - { - int pos = _pos - distance - 1; - if (pos < 0) - { - pos += _windowSize; - } - return _buffer[pos]; - } - - public int CopyStream(Stream stream, int len) - { - int size = len; - while (size > 0 && _pos < _windowSize && _total < _limit) - { - int curSize = _windowSize - _pos; - if (curSize > _limit - _total) - { - curSize = (int)(_limit - _total); - } - if (curSize > size) - { - curSize = size; - } - int numReadBytes = stream.Read(_buffer, _pos, curSize); - if (numReadBytes == 0) - { - throw new DataErrorException(); - } - size -= numReadBytes; - _pos += numReadBytes; - _total += numReadBytes; - if (_pos >= _windowSize) - { - Flush(); - } - } - return len - size; - } - - public void SetLimit(long size) - { - _limit = _total + size; - } - - public bool HasSpace => _pos < _windowSize && _total < _limit; - - public bool HasPending => _pendingLen > 0; - - public int Read(byte[] buffer, int offset, int count) - { - if (_streamPos >= _pos) - { - return 0; - } - - int size = _pos - _streamPos; - if (size > count) - { - size = count; - } - Buffer.BlockCopy(_buffer, _streamPos, buffer, offset, size); - _streamPos += size; - if (_streamPos >= _windowSize) - { - _pos = 0; - _streamPos = 0; - } - return size; - } - - public void CopyPending() - { - if (_pendingLen > 0) - { - CopyBlock(_pendingDist, _pendingLen); - } - } - - public int AvailableBytes => _pos - _streamPos; + _pendingLen = size; + _pendingDist = distance; } + + public void PutByte(byte b) + { + _buffer[_pos++] = b; + _total++; + if (_pos >= _windowSize) + { + Flush(); + } + } + + public byte GetByte(int distance) + { + var pos = _pos - distance - 1; + if (pos < 0) + { + pos += _windowSize; + } + return _buffer[pos]; + } + + public int CopyStream(Stream stream, int len) + { + var size = len; + while (size > 0 && _pos < _windowSize && _total < _limit) + { + var curSize = _windowSize - _pos; + if (curSize > _limit - _total) + { + curSize = (int)(_limit - _total); + } + if (curSize > size) + { + curSize = size; + } + var numReadBytes = stream.Read(_buffer, _pos, curSize); + if (numReadBytes == 0) + { + throw new DataErrorException(); + } + size -= numReadBytes; + _pos += numReadBytes; + _total += numReadBytes; + if (_pos >= _windowSize) + { + Flush(); + } + } + return len - size; + } + + public void SetLimit(long size) + { + _limit = _total + size; + } + + public bool HasSpace => _pos < _windowSize && _total < _limit; + + public bool HasPending => _pendingLen > 0; + + public int Read(byte[] buffer, int offset, int count) + { + if (_streamPos >= _pos) + { + return 0; + } + + var size = _pos - _streamPos; + if (size > count) + { + size = count; + } + Buffer.BlockCopy(_buffer, _streamPos, buffer, offset, size); + _streamPos += size; + if (_streamPos >= _windowSize) + { + _pos = 0; + _streamPos = 0; + } + return size; + } + + public void CopyPending() + { + if (_pendingLen > 0) + { + CopyBlock(_pendingDist, _pendingLen); + } + } + + public int AvailableBytes => _pos - _streamPos; } diff --git a/src/SharpCompress/Compressors/LZMA/LZipStream.cs b/src/SharpCompress/Compressors/LZMA/LZipStream.cs index 912e4eff..5d799619 100644 --- a/src/SharpCompress/Compressors/LZMA/LZipStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LZipStream.cs @@ -4,245 +4,244 @@ using System.IO; using SharpCompress.Crypto; using SharpCompress.IO; -namespace SharpCompress.Compressors.LZMA +namespace SharpCompress.Compressors.LZMA; + +// TODO: +// - Write as well as read +// - Multi-volume support +// - Use of the data size / member size values at the end of the stream + +/// +/// Stream supporting the LZIP format, as documented at http://www.nongnu.org/lzip/manual/lzip_manual.html +/// +public sealed class LZipStream : Stream { - // TODO: - // - Write as well as read - // - Multi-volume support - // - Use of the data size / member size values at the end of the stream + private readonly Stream _stream; + private readonly CountingWritableSubStream? _countingWritableSubStream; + private bool _disposed; + private bool _finished; - /// - /// Stream supporting the LZIP format, as documented at http://www.nongnu.org/lzip/manual/lzip_manual.html - /// - public sealed class LZipStream : Stream + private long _writeCount; + + public LZipStream(Stream stream, CompressionMode mode) { - private readonly Stream _stream; - private readonly CountingWritableSubStream? _countingWritableSubStream; - private bool _disposed; - private bool _finished; + Mode = mode; - private long _writeCount; - - public LZipStream(Stream stream, CompressionMode mode) + if (mode == CompressionMode.Decompress) { - Mode = mode; - - if (mode == CompressionMode.Decompress) + var dSize = ValidateAndReadSize(stream); + if (dSize == 0) { - int dSize = ValidateAndReadSize(stream); - if (dSize == 0) - { - throw new IOException("Not an LZip stream"); - } - byte[] properties = GetProperties(dSize); - _stream = new LzmaStream(properties, stream); - } - else - { - //default - int dSize = 104 * 1024; - WriteHeaderSize(stream); - - _countingWritableSubStream = new CountingWritableSubStream(stream); - _stream = new Crc32Stream( - new LzmaStream( - new LzmaEncoderProperties(true, dSize), - false, - _countingWritableSubStream - ) - ); + throw new IOException("Not an LZip stream"); } + var properties = GetProperties(dSize); + _stream = new LzmaStream(properties, stream); } - - public void Finish() + else { - if (!_finished) - { - if (Mode == CompressionMode.Compress) - { - var crc32Stream = (Crc32Stream)_stream; - crc32Stream.WrappedStream.Dispose(); - crc32Stream.Dispose(); - var compressedCount = _countingWritableSubStream!.Count; + //default + var dSize = 104 * 1024; + WriteHeaderSize(stream); - Span intBuf = stackalloc byte[8]; - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, crc32Stream.Crc); - _countingWritableSubStream.Write(intBuf.Slice(0, 4)); - - BinaryPrimitives.WriteInt64LittleEndian(intBuf, _writeCount); - _countingWritableSubStream.Write(intBuf); - - //total with headers - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, compressedCount + 6 + 20); - _countingWritableSubStream.Write(intBuf); - } - _finished = true; - } + _countingWritableSubStream = new CountingWritableSubStream(stream); + _stream = new Crc32Stream( + new LzmaStream( + new LzmaEncoderProperties(true, dSize), + false, + _countingWritableSubStream + ) + ); } + } - #region Stream methods - - protected override void Dispose(bool disposing) + public void Finish() + { + if (!_finished) { - if (_disposed) + if (Mode == CompressionMode.Compress) { - return; - } - _disposed = true; - if (disposing) - { - Finish(); - _stream.Dispose(); + var crc32Stream = (Crc32Stream)_stream; + crc32Stream.WrappedStream.Dispose(); + crc32Stream.Dispose(); + var compressedCount = _countingWritableSubStream!.Count; + + Span intBuf = stackalloc byte[8]; + BinaryPrimitives.WriteUInt32LittleEndian(intBuf, crc32Stream.Crc); + _countingWritableSubStream.Write(intBuf.Slice(0, 4)); + + BinaryPrimitives.WriteInt64LittleEndian(intBuf, _writeCount); + _countingWritableSubStream.Write(intBuf); + + //total with headers + BinaryPrimitives.WriteUInt64LittleEndian(intBuf, compressedCount + 6 + 20); + _countingWritableSubStream.Write(intBuf); } + _finished = true; } + } - public CompressionMode Mode { get; } + #region Stream methods - public override bool CanRead => Mode == CompressionMode.Decompress; - - public override bool CanSeek => false; - - public override bool CanWrite => Mode == CompressionMode.Compress; - - public override void Flush() + protected override void Dispose(bool disposing) + { + if (_disposed) { - _stream.Flush(); + return; } - - // TODO: Both Length and Position are sometimes feasible, but would require - // reading the output length when we initialize. - public override long Length => throw new NotImplementedException(); - - public override long Position + _disposed = true; + if (disposing) { - get => throw new NotImplementedException(); - set => throw new NotImplementedException(); + Finish(); + _stream.Dispose(); } + } - public override int Read(byte[] buffer, int offset, int count) => - _stream.Read(buffer, offset, count); + public CompressionMode Mode { get; } - public override int ReadByte() => _stream.ReadByte(); + public override bool CanRead => Mode == CompressionMode.Decompress; - public override long Seek(long offset, SeekOrigin origin) => - throw new NotSupportedException(); + public override bool CanSeek => false; - public override void SetLength(long value) => throw new NotImplementedException(); + public override bool CanWrite => Mode == CompressionMode.Compress; + + public override void Flush() + { + _stream.Flush(); + } + + // TODO: Both Length and Position are sometimes feasible, but would require + // reading the output length when we initialize. + public override long Length => throw new NotImplementedException(); + + public override long Position + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } + + public override int Read(byte[] buffer, int offset, int count) => + _stream.Read(buffer, offset, count); + + public override int ReadByte() => _stream.ReadByte(); + + public override long Seek(long offset, SeekOrigin origin) => + throw new NotSupportedException(); + + public override void SetLength(long value) => throw new NotImplementedException(); #if !NETFRAMEWORK && !NETSTANDARD2_0 - public override int Read(Span buffer) - { - return _stream.Read(buffer); - } + public override int Read(Span buffer) + { + return _stream.Read(buffer); + } - public override void Write(ReadOnlySpan buffer) - { - _stream.Write(buffer); + public override void Write(ReadOnlySpan buffer) + { + _stream.Write(buffer); - _writeCount += buffer.Length; - } + _writeCount += buffer.Length; + } #endif - public override void Write(byte[] buffer, int offset, int count) - { - _stream.Write(buffer, offset, count); - _writeCount += count; - } - - public override void WriteByte(byte value) - { - _stream.WriteByte(value); - ++_writeCount; - } - - #endregion - - /// - /// Determines if the given stream is positioned at the start of a v1 LZip - /// file, as indicated by the ASCII characters "LZIP" and a version byte - /// of 1, followed by at least one byte. - /// - /// The stream to read from. Must not be null. - /// true if the given stream is an LZip file, false otherwise. - public static bool IsLZipFile(Stream stream) => ValidateAndReadSize(stream) != 0; - - /// - /// Reads the 6-byte header of the stream, and returns 0 if either the header - /// couldn't be read or it isn't a validate LZIP header, or the dictionary - /// size if it *is* a valid LZIP file. - /// - public static int ValidateAndReadSize(Stream stream) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - // Read the header - Span header = stackalloc byte[6]; - int n = stream.Read(header); - - // TODO: Handle reading only part of the header? - - if (n != 6) - { - return 0; - } - - if ( - header[0] != 'L' - || header[1] != 'Z' - || header[2] != 'I' - || header[3] != 'P' - || header[4] != 1 /* version 1 */ - ) - { - return 0; - } - int basePower = header[5] & 0x1F; - int subtractionNumerator = (header[5] & 0xE0) >> 5; - return (1 << basePower) - subtractionNumerator * (1 << (basePower - 4)); - } - - private static readonly byte[] headerBytes = new byte[6] - { - (byte)'L', - (byte)'Z', - (byte)'I', - (byte)'P', - 1, - 113 - }; - - public static void WriteHeaderSize(Stream stream) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - // hard coding the dictionary size encoding - stream.Write(headerBytes, 0, 6); - } - - /// - /// Creates a byte array to communicate the parameters and dictionary size to LzmaStream. - /// - private static byte[] GetProperties(int dictionarySize) => - new byte[] - { - // Parameters as per http://www.nongnu.org/lzip/manual/lzip_manual.html#Stream-format - // but encoded as a single byte in the format LzmaStream expects. - // literal_context_bits = 3 - // literal_pos_state_bits = 0 - // pos_state_bits = 2 - 93, - // Dictionary size as 4-byte little-endian value - (byte)(dictionarySize & 0xff), - (byte)((dictionarySize >> 8) & 0xff), - (byte)((dictionarySize >> 16) & 0xff), - (byte)((dictionarySize >> 24) & 0xff) - }; + public override void Write(byte[] buffer, int offset, int count) + { + _stream.Write(buffer, offset, count); + _writeCount += count; } + + public override void WriteByte(byte value) + { + _stream.WriteByte(value); + ++_writeCount; + } + + #endregion + + /// + /// Determines if the given stream is positioned at the start of a v1 LZip + /// file, as indicated by the ASCII characters "LZIP" and a version byte + /// of 1, followed by at least one byte. + /// + /// The stream to read from. Must not be null. + /// true if the given stream is an LZip file, false otherwise. + public static bool IsLZipFile(Stream stream) => ValidateAndReadSize(stream) != 0; + + /// + /// Reads the 6-byte header of the stream, and returns 0 if either the header + /// couldn't be read or it isn't a validate LZIP header, or the dictionary + /// size if it *is* a valid LZIP file. + /// + public static int ValidateAndReadSize(Stream stream) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + // Read the header + Span header = stackalloc byte[6]; + var n = stream.Read(header); + + // TODO: Handle reading only part of the header? + + if (n != 6) + { + return 0; + } + + if ( + header[0] != 'L' + || header[1] != 'Z' + || header[2] != 'I' + || header[3] != 'P' + || header[4] != 1 /* version 1 */ + ) + { + return 0; + } + var basePower = header[5] & 0x1F; + var subtractionNumerator = (header[5] & 0xE0) >> 5; + return (1 << basePower) - (subtractionNumerator * (1 << (basePower - 4))); + } + + private static readonly byte[] headerBytes = new byte[6] + { + (byte)'L', + (byte)'Z', + (byte)'I', + (byte)'P', + 1, + 113 + }; + + public static void WriteHeaderSize(Stream stream) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + // hard coding the dictionary size encoding + stream.Write(headerBytes, 0, 6); + } + + /// + /// Creates a byte array to communicate the parameters and dictionary size to LzmaStream. + /// + private static byte[] GetProperties(int dictionarySize) => + new byte[] + { + // Parameters as per http://www.nongnu.org/lzip/manual/lzip_manual.html#Stream-format + // but encoded as a single byte in the format LzmaStream expects. + // literal_context_bits = 3 + // literal_pos_state_bits = 0 + // pos_state_bits = 2 + 93, + // Dictionary size as 4-byte little-endian value + (byte)(dictionarySize & 0xff), + (byte)((dictionarySize >> 8) & 0xff), + (byte)((dictionarySize >> 16) & 0xff), + (byte)((dictionarySize >> 24) & 0xff) + }; } diff --git a/src/SharpCompress/Compressors/LZMA/Log.cs b/src/SharpCompress/Compressors/LZMA/Log.cs index a31d2b69..d19a46d2 100644 --- a/src/SharpCompress/Compressors/LZMA/Log.cs +++ b/src/SharpCompress/Compressors/LZMA/Log.cs @@ -1,86 +1,85 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; -namespace SharpCompress.Compressors.LZMA +namespace SharpCompress.Compressors.LZMA; + +internal static class Log { - internal static class Log + private static readonly Stack INDENT = new Stack(); + private static bool NEEDS_INDENT = true; + + static Log() { - private static readonly Stack INDENT = new Stack(); - private static bool NEEDS_INDENT = true; + INDENT.Push(""); + } - static Log() + public static void PushIndent(string indent = " ") + { + INDENT.Push(INDENT.Peek() + indent); + } + + public static void PopIndent() + { + if (INDENT.Count == 1) { - INDENT.Push(""); + throw new InvalidOperationException(); } - public static void PushIndent(string indent = " ") - { - INDENT.Push(INDENT.Peek() + indent); - } + INDENT.Pop(); + } - public static void PopIndent() + private static void EnsureIndent() + { + if (NEEDS_INDENT) { - if (INDENT.Count == 1) - { - throw new InvalidOperationException(); - } - - INDENT.Pop(); - } - - private static void EnsureIndent() - { - if (NEEDS_INDENT) - { - NEEDS_INDENT = false; - Debug.Write(INDENT.Peek()); - } - } - - public static void Write(object value) - { - EnsureIndent(); - Debug.Write(value); - } - - public static void Write(string text) - { - EnsureIndent(); - Debug.Write(text); - } - - public static void Write(string format, params object[] args) - { - EnsureIndent(); - Debug.Write(string.Format(format, args)); - } - - public static void WriteLine() - { - Debug.WriteLine(""); - NEEDS_INDENT = true; - } - - public static void WriteLine(object value) - { - EnsureIndent(); - Debug.WriteLine(value); - NEEDS_INDENT = true; - } - - public static void WriteLine(string text) - { - EnsureIndent(); - Debug.WriteLine(text); - NEEDS_INDENT = true; - } - - public static void WriteLine(string format, params object[] args) - { - EnsureIndent(); - Debug.WriteLine(string.Format(format, args)); - NEEDS_INDENT = true; + NEEDS_INDENT = false; + Debug.Write(INDENT.Peek()); } } + + public static void Write(object value) + { + EnsureIndent(); + Debug.Write(value); + } + + public static void Write(string text) + { + EnsureIndent(); + Debug.Write(text); + } + + public static void Write(string format, params object[] args) + { + EnsureIndent(); + Debug.Write(string.Format(format, args)); + } + + public static void WriteLine() + { + Debug.WriteLine(""); + NEEDS_INDENT = true; + } + + public static void WriteLine(object value) + { + EnsureIndent(); + Debug.WriteLine(value); + NEEDS_INDENT = true; + } + + public static void WriteLine(string text) + { + EnsureIndent(); + Debug.WriteLine(text); + NEEDS_INDENT = true; + } + + public static void WriteLine(string format, params object[] args) + { + EnsureIndent(); + Debug.WriteLine(string.Format(format, args)); + NEEDS_INDENT = true; + } } diff --git a/src/SharpCompress/Compressors/LZMA/LzmaBase.cs b/src/SharpCompress/Compressors/LZMA/LzmaBase.cs index 74ca3cfb..e1d98639 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaBase.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaBase.cs @@ -1,109 +1,108 @@ -namespace SharpCompress.Compressors.LZMA +namespace SharpCompress.Compressors.LZMA; + +internal abstract class Base { - internal abstract class Base + public const uint K_NUM_REP_DISTANCES = 4; + public const uint K_NUM_STATES = 12; + + // static byte []kLiteralNextStates = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; + // static byte []kMatchNextStates = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; + // static byte []kRepNextStates = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; + // static byte []kShortRepNextStates = {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; + + public struct State { - public const uint K_NUM_REP_DISTANCES = 4; - public const uint K_NUM_STATES = 12; + public uint _index; - // static byte []kLiteralNextStates = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; - // static byte []kMatchNextStates = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; - // static byte []kRepNextStates = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; - // static byte []kShortRepNextStates = {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; - - public struct State + public void Init() { - public uint _index; + _index = 0; + } - public void Init() + public void UpdateChar() + { + if (_index < 4) { _index = 0; } - - public void UpdateChar() + else if (_index < 10) { - if (_index < 4) - { - _index = 0; - } - else if (_index < 10) - { - _index -= 3; - } - else - { - _index -= 6; - } + _index -= 3; } - - public void UpdateMatch() + else { - _index = (uint)(_index < 7 ? 7 : 10); - } - - public void UpdateRep() - { - _index = (uint)(_index < 7 ? 8 : 11); - } - - public void UpdateShortRep() - { - _index = (uint)(_index < 7 ? 9 : 11); - } - - public bool IsCharState() - { - return _index < 7; + _index -= 6; } } - public const int K_NUM_POS_SLOT_BITS = 6; - public const int K_DIC_LOG_SIZE_MIN = 0; - - // public const int kDicLogSizeMax = 30; - // public const uint kDistTableSizeMax = kDicLogSizeMax * 2; - - public const int K_NUM_LEN_TO_POS_STATES_BITS = 2; // it's for speed optimization - public const uint K_NUM_LEN_TO_POS_STATES = 1 << K_NUM_LEN_TO_POS_STATES_BITS; - - public const uint K_MATCH_MIN_LEN = 2; - - public static uint GetLenToPosState(uint len) + public void UpdateMatch() { - len -= K_MATCH_MIN_LEN; - if (len < K_NUM_LEN_TO_POS_STATES) - { - return len; - } - return K_NUM_LEN_TO_POS_STATES - 1; + _index = (uint)(_index < 7 ? 7 : 10); } - public const int K_NUM_ALIGN_BITS = 4; - public const uint K_ALIGN_TABLE_SIZE = 1 << K_NUM_ALIGN_BITS; - public const uint K_ALIGN_MASK = (K_ALIGN_TABLE_SIZE - 1); + public void UpdateRep() + { + _index = (uint)(_index < 7 ? 8 : 11); + } - public const uint K_START_POS_MODEL_INDEX = 4; - public const uint K_END_POS_MODEL_INDEX = 14; - public const uint K_NUM_POS_MODELS = K_END_POS_MODEL_INDEX - K_START_POS_MODEL_INDEX; + public void UpdateShortRep() + { + _index = (uint)(_index < 7 ? 9 : 11); + } - public const uint K_NUM_FULL_DISTANCES = 1 << ((int)K_END_POS_MODEL_INDEX / 2); - - public const uint K_NUM_LIT_POS_STATES_BITS_ENCODING_MAX = 4; - public const uint K_NUM_LIT_CONTEXT_BITS_MAX = 8; - - public const int K_NUM_POS_STATES_BITS_MAX = 4; - public const uint K_NUM_POS_STATES_MAX = (1 << K_NUM_POS_STATES_BITS_MAX); - public const int K_NUM_POS_STATES_BITS_ENCODING_MAX = 4; - public const uint K_NUM_POS_STATES_ENCODING_MAX = (1 << K_NUM_POS_STATES_BITS_ENCODING_MAX); - - public const int K_NUM_LOW_LEN_BITS = 3; - public const int K_NUM_MID_LEN_BITS = 3; - public const int K_NUM_HIGH_LEN_BITS = 8; - public const uint K_NUM_LOW_LEN_SYMBOLS = 1 << K_NUM_LOW_LEN_BITS; - public const uint K_NUM_MID_LEN_SYMBOLS = 1 << K_NUM_MID_LEN_BITS; - - public const uint K_NUM_LEN_SYMBOLS = - K_NUM_LOW_LEN_SYMBOLS + K_NUM_MID_LEN_SYMBOLS + (1 << K_NUM_HIGH_LEN_BITS); - - public const uint K_MATCH_MAX_LEN = K_MATCH_MIN_LEN + K_NUM_LEN_SYMBOLS - 1; + public bool IsCharState() + { + return _index < 7; + } } + + public const int K_NUM_POS_SLOT_BITS = 6; + public const int K_DIC_LOG_SIZE_MIN = 0; + + // public const int kDicLogSizeMax = 30; + // public const uint kDistTableSizeMax = kDicLogSizeMax * 2; + + public const int K_NUM_LEN_TO_POS_STATES_BITS = 2; // it's for speed optimization + public const uint K_NUM_LEN_TO_POS_STATES = 1 << K_NUM_LEN_TO_POS_STATES_BITS; + + public const uint K_MATCH_MIN_LEN = 2; + + public static uint GetLenToPosState(uint len) + { + len -= K_MATCH_MIN_LEN; + if (len < K_NUM_LEN_TO_POS_STATES) + { + return len; + } + return K_NUM_LEN_TO_POS_STATES - 1; + } + + public const int K_NUM_ALIGN_BITS = 4; + public const uint K_ALIGN_TABLE_SIZE = 1 << K_NUM_ALIGN_BITS; + public const uint K_ALIGN_MASK = (K_ALIGN_TABLE_SIZE - 1); + + public const uint K_START_POS_MODEL_INDEX = 4; + public const uint K_END_POS_MODEL_INDEX = 14; + public const uint K_NUM_POS_MODELS = K_END_POS_MODEL_INDEX - K_START_POS_MODEL_INDEX; + + public const uint K_NUM_FULL_DISTANCES = 1 << ((int)K_END_POS_MODEL_INDEX / 2); + + public const uint K_NUM_LIT_POS_STATES_BITS_ENCODING_MAX = 4; + public const uint K_NUM_LIT_CONTEXT_BITS_MAX = 8; + + public const int K_NUM_POS_STATES_BITS_MAX = 4; + public const uint K_NUM_POS_STATES_MAX = (1 << K_NUM_POS_STATES_BITS_MAX); + public const int K_NUM_POS_STATES_BITS_ENCODING_MAX = 4; + public const uint K_NUM_POS_STATES_ENCODING_MAX = (1 << K_NUM_POS_STATES_BITS_ENCODING_MAX); + + public const int K_NUM_LOW_LEN_BITS = 3; + public const int K_NUM_MID_LEN_BITS = 3; + public const int K_NUM_HIGH_LEN_BITS = 8; + public const uint K_NUM_LOW_LEN_SYMBOLS = 1 << K_NUM_LOW_LEN_BITS; + public const uint K_NUM_MID_LEN_SYMBOLS = 1 << K_NUM_MID_LEN_BITS; + + public const uint K_NUM_LEN_SYMBOLS = + K_NUM_LOW_LEN_SYMBOLS + K_NUM_MID_LEN_SYMBOLS + (1 << K_NUM_HIGH_LEN_BITS); + + public const uint K_MATCH_MAX_LEN = K_MATCH_MIN_LEN + K_NUM_LEN_SYMBOLS - 1; } diff --git a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs index a3841197..8889dd68 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs @@ -5,518 +5,517 @@ using System.IO; using SharpCompress.Compressors.LZMA.LZ; using SharpCompress.Compressors.LZMA.RangeCoder; -namespace SharpCompress.Compressors.LZMA +namespace SharpCompress.Compressors.LZMA; + +public class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream { - public class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream + private class LenDecoder { - private class LenDecoder - { - private BitDecoder _choice = new BitDecoder(); - private BitDecoder _choice2 = new BitDecoder(); - private readonly BitTreeDecoder[] _lowCoder = new BitTreeDecoder[ - Base.K_NUM_POS_STATES_MAX - ]; - private readonly BitTreeDecoder[] _midCoder = new BitTreeDecoder[ - Base.K_NUM_POS_STATES_MAX - ]; - private BitTreeDecoder _highCoder = new BitTreeDecoder(Base.K_NUM_HIGH_LEN_BITS); - private uint _numPosStates; - - public void Create(uint numPosStates) - { - for (uint posState = _numPosStates; posState < numPosStates; posState++) - { - _lowCoder[posState] = new BitTreeDecoder(Base.K_NUM_LOW_LEN_BITS); - _midCoder[posState] = new BitTreeDecoder(Base.K_NUM_MID_LEN_BITS); - } - _numPosStates = numPosStates; - } - - public void Init() - { - _choice.Init(); - for (uint posState = 0; posState < _numPosStates; posState++) - { - _lowCoder[posState].Init(); - _midCoder[posState].Init(); - } - _choice2.Init(); - _highCoder.Init(); - } - - public uint Decode(RangeCoder.Decoder rangeDecoder, uint posState) - { - if (_choice.Decode(rangeDecoder) == 0) - { - return _lowCoder[posState].Decode(rangeDecoder); - } - uint symbol = Base.K_NUM_LOW_LEN_SYMBOLS; - if (_choice2.Decode(rangeDecoder) == 0) - { - symbol += _midCoder[posState].Decode(rangeDecoder); - } - else - { - symbol += Base.K_NUM_MID_LEN_SYMBOLS; - symbol += _highCoder.Decode(rangeDecoder); - } - return symbol; - } - } - - private class LiteralDecoder - { - private struct Decoder2 - { - private BitDecoder[] _decoders; - - public void Create() - { - _decoders = new BitDecoder[0x300]; - } - - public void Init() - { - for (int i = 0; i < 0x300; i++) - { - _decoders[i].Init(); - } - } - - public byte DecodeNormal(RangeCoder.Decoder rangeDecoder) - { - uint symbol = 1; - do - { - symbol = (symbol << 1) | _decoders[symbol].Decode(rangeDecoder); - } while (symbol < 0x100); - return (byte)symbol; - } - - public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, byte matchByte) - { - uint symbol = 1; - do - { - uint matchBit = (uint)(matchByte >> 7) & 1; - matchByte <<= 1; - uint bit = _decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder); - symbol = (symbol << 1) | bit; - if (matchBit != bit) - { - while (symbol < 0x100) - { - symbol = (symbol << 1) | _decoders[symbol].Decode(rangeDecoder); - } - break; - } - } while (symbol < 0x100); - return (byte)symbol; - } - } - - private Decoder2[] _coders; - private int _numPrevBits; - private int _numPosBits; - private uint _posMask; - - public void Create(int numPosBits, int numPrevBits) - { - if (_coders != null && _numPrevBits == numPrevBits && _numPosBits == numPosBits) - { - return; - } - _numPosBits = numPosBits; - _posMask = ((uint)1 << numPosBits) - 1; - _numPrevBits = numPrevBits; - uint numStates = (uint)1 << (_numPrevBits + _numPosBits); - _coders = new Decoder2[numStates]; - for (uint i = 0; i < numStates; i++) - { - _coders[i].Create(); - } - } - - public void Init() - { - uint numStates = (uint)1 << (_numPrevBits + _numPosBits); - for (uint i = 0; i < numStates; i++) - { - _coders[i].Init(); - } - } - - private uint GetState(uint pos, byte prevByte) - { - return ((pos & _posMask) << _numPrevBits) + (uint)(prevByte >> (8 - _numPrevBits)); - } - - public byte DecodeNormal(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte) - { - return _coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder); - } - - public byte DecodeWithMatchByte( - RangeCoder.Decoder rangeDecoder, - uint pos, - byte prevByte, - byte matchByte - ) - { - return _coders[GetState(pos, prevByte)].DecodeWithMatchByte( - rangeDecoder, - matchByte - ); - } - } - - private OutWindow _outWindow; - - private readonly BitDecoder[] _isMatchDecoders = new BitDecoder[ - Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX + private BitDecoder _choice = new BitDecoder(); + private BitDecoder _choice2 = new BitDecoder(); + private readonly BitTreeDecoder[] _lowCoder = new BitTreeDecoder[ + Base.K_NUM_POS_STATES_MAX ]; - private readonly BitDecoder[] _isRepDecoders = new BitDecoder[Base.K_NUM_STATES]; - private readonly BitDecoder[] _isRepG0Decoders = new BitDecoder[Base.K_NUM_STATES]; - private readonly BitDecoder[] _isRepG1Decoders = new BitDecoder[Base.K_NUM_STATES]; - private readonly BitDecoder[] _isRepG2Decoders = new BitDecoder[Base.K_NUM_STATES]; - private readonly BitDecoder[] _isRep0LongDecoders = new BitDecoder[ - Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX + private readonly BitTreeDecoder[] _midCoder = new BitTreeDecoder[ + Base.K_NUM_POS_STATES_MAX ]; + private BitTreeDecoder _highCoder = new BitTreeDecoder(Base.K_NUM_HIGH_LEN_BITS); + private uint _numPosStates; - private readonly BitTreeDecoder[] _posSlotDecoder = new BitTreeDecoder[ - Base.K_NUM_LEN_TO_POS_STATES - ]; - private readonly BitDecoder[] _posDecoders = new BitDecoder[ - Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX - ]; - - private BitTreeDecoder _posAlignDecoder = new BitTreeDecoder(Base.K_NUM_ALIGN_BITS); - - private readonly LenDecoder _lenDecoder = new LenDecoder(); - private readonly LenDecoder _repLenDecoder = new LenDecoder(); - - private readonly LiteralDecoder _literalDecoder = new LiteralDecoder(); - - private int _dictionarySize; - - private uint _posStateMask; - - private Base.State _state = new Base.State(); - private uint _rep0, - _rep1, - _rep2, - _rep3; - - public Decoder() + public void Create(uint numPosStates) { - _dictionarySize = -1; - for (int i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++) + for (var posState = _numPosStates; posState < numPosStates; posState++) { - _posSlotDecoder[i] = new BitTreeDecoder(Base.K_NUM_POS_SLOT_BITS); + _lowCoder[posState] = new BitTreeDecoder(Base.K_NUM_LOW_LEN_BITS); + _midCoder[posState] = new BitTreeDecoder(Base.K_NUM_MID_LEN_BITS); } + _numPosStates = numPosStates; } - private void CreateDictionary() + public void Init() { - if (_dictionarySize < 0) + _choice.Init(); + for (uint posState = 0; posState < _numPosStates; posState++) { - throw new InvalidParamException(); + _lowCoder[posState].Init(); + _midCoder[posState].Init(); } - _outWindow = new OutWindow(); - int blockSize = Math.Max(_dictionarySize, (1 << 12)); - _outWindow.Create(blockSize); + _choice2.Init(); + _highCoder.Init(); } - private void SetLiteralProperties(int lp, int lc) + public uint Decode(RangeCoder.Decoder rangeDecoder, uint posState) { - if (lp > 8) + if (_choice.Decode(rangeDecoder) == 0) { - throw new InvalidParamException(); + return _lowCoder[posState].Decode(rangeDecoder); } - if (lc > 8) + var symbol = Base.K_NUM_LOW_LEN_SYMBOLS; + if (_choice2.Decode(rangeDecoder) == 0) { - throw new InvalidParamException(); - } - _literalDecoder.Create(lp, lc); - } - - private void SetPosBitsProperties(int pb) - { - if (pb > Base.K_NUM_POS_STATES_BITS_MAX) - { - throw new InvalidParamException(); - } - uint numPosStates = (uint)1 << pb; - _lenDecoder.Create(numPosStates); - _repLenDecoder.Create(numPosStates); - _posStateMask = numPosStates - 1; - } - - private void Init() - { - uint i; - for (i = 0; i < Base.K_NUM_STATES; i++) - { - for (uint j = 0; j <= _posStateMask; j++) - { - uint index = (i << Base.K_NUM_POS_STATES_BITS_MAX) + j; - _isMatchDecoders[index].Init(); - _isRep0LongDecoders[index].Init(); - } - _isRepDecoders[i].Init(); - _isRepG0Decoders[i].Init(); - _isRepG1Decoders[i].Init(); - _isRepG2Decoders[i].Init(); - } - - _literalDecoder.Init(); - for (i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++) - { - _posSlotDecoder[i].Init(); - } - - // _PosSpecDecoder.Init(); - for (i = 0; i < Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX; i++) - { - _posDecoders[i].Init(); - } - - _lenDecoder.Init(); - _repLenDecoder.Init(); - _posAlignDecoder.Init(); - - _state.Init(); - _rep0 = 0; - _rep1 = 0; - _rep2 = 0; - _rep3 = 0; - } - - public void Code( - Stream inStream, - Stream outStream, - Int64 inSize, - Int64 outSize, - ICodeProgress progress - ) - { - if (_outWindow is null) - { - CreateDictionary(); - } - _outWindow.Init(outStream); - if (outSize > 0) - { - _outWindow.SetLimit(outSize); + symbol += _midCoder[posState].Decode(rangeDecoder); } else { - _outWindow.SetLimit(Int64.MaxValue - _outWindow._total); + symbol += Base.K_NUM_MID_LEN_SYMBOLS; + symbol += _highCoder.Decode(rangeDecoder); + } + return symbol; + } + } + + private class LiteralDecoder + { + private struct Decoder2 + { + private BitDecoder[] _decoders; + + public void Create() + { + _decoders = new BitDecoder[0x300]; } - RangeCoder.Decoder rangeDecoder = new RangeCoder.Decoder(); - rangeDecoder.Init(inStream); + public void Init() + { + for (var i = 0; i < 0x300; i++) + { + _decoders[i].Init(); + } + } - Code(_dictionarySize, _outWindow, rangeDecoder); + public byte DecodeNormal(RangeCoder.Decoder rangeDecoder) + { + uint symbol = 1; + do + { + symbol = (symbol << 1) | _decoders[symbol].Decode(rangeDecoder); + } while (symbol < 0x100); + return (byte)symbol; + } - _outWindow.ReleaseStream(); - rangeDecoder.ReleaseStream(); - - _outWindow = null; + public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, byte matchByte) + { + uint symbol = 1; + do + { + var matchBit = (uint)(matchByte >> 7) & 1; + matchByte <<= 1; + var bit = _decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder); + symbol = (symbol << 1) | bit; + if (matchBit != bit) + { + while (symbol < 0x100) + { + symbol = (symbol << 1) | _decoders[symbol].Decode(rangeDecoder); + } + break; + } + } while (symbol < 0x100); + return (byte)symbol; + } } - internal bool Code(int dictionarySize, OutWindow outWindow, RangeCoder.Decoder rangeDecoder) + private Decoder2[] _coders; + private int _numPrevBits; + private int _numPosBits; + private uint _posMask; + + public void Create(int numPosBits, int numPrevBits) { - int dictionarySizeCheck = Math.Max(dictionarySize, 1); - - outWindow.CopyPending(); - - while (outWindow.HasSpace) + if (_coders != null && _numPrevBits == numPrevBits && _numPosBits == numPosBits) { - uint posState = (uint)outWindow._total & _posStateMask; - if ( - _isMatchDecoders[ - (_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState - ].Decode(rangeDecoder) == 0 - ) + return; + } + _numPosBits = numPosBits; + _posMask = ((uint)1 << numPosBits) - 1; + _numPrevBits = numPrevBits; + var numStates = (uint)1 << (_numPrevBits + _numPosBits); + _coders = new Decoder2[numStates]; + for (uint i = 0; i < numStates; i++) + { + _coders[i].Create(); + } + } + + public void Init() + { + var numStates = (uint)1 << (_numPrevBits + _numPosBits); + for (uint i = 0; i < numStates; i++) + { + _coders[i].Init(); + } + } + + private uint GetState(uint pos, byte prevByte) + { + return ((pos & _posMask) << _numPrevBits) + (uint)(prevByte >> (8 - _numPrevBits)); + } + + public byte DecodeNormal(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte) + { + return _coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder); + } + + public byte DecodeWithMatchByte( + RangeCoder.Decoder rangeDecoder, + uint pos, + byte prevByte, + byte matchByte + ) + { + return _coders[GetState(pos, prevByte)].DecodeWithMatchByte( + rangeDecoder, + matchByte + ); + } + } + + private OutWindow _outWindow; + + private readonly BitDecoder[] _isMatchDecoders = new BitDecoder[ + Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX + ]; + private readonly BitDecoder[] _isRepDecoders = new BitDecoder[Base.K_NUM_STATES]; + private readonly BitDecoder[] _isRepG0Decoders = new BitDecoder[Base.K_NUM_STATES]; + private readonly BitDecoder[] _isRepG1Decoders = new BitDecoder[Base.K_NUM_STATES]; + private readonly BitDecoder[] _isRepG2Decoders = new BitDecoder[Base.K_NUM_STATES]; + private readonly BitDecoder[] _isRep0LongDecoders = new BitDecoder[ + Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX + ]; + + private readonly BitTreeDecoder[] _posSlotDecoder = new BitTreeDecoder[ + Base.K_NUM_LEN_TO_POS_STATES + ]; + private readonly BitDecoder[] _posDecoders = new BitDecoder[ + Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX + ]; + + private BitTreeDecoder _posAlignDecoder = new BitTreeDecoder(Base.K_NUM_ALIGN_BITS); + + private readonly LenDecoder _lenDecoder = new LenDecoder(); + private readonly LenDecoder _repLenDecoder = new LenDecoder(); + + private readonly LiteralDecoder _literalDecoder = new LiteralDecoder(); + + private int _dictionarySize; + + private uint _posStateMask; + + private Base.State _state = new Base.State(); + private uint _rep0, + _rep1, + _rep2, + _rep3; + + public Decoder() + { + _dictionarySize = -1; + for (var i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++) + { + _posSlotDecoder[i] = new BitTreeDecoder(Base.K_NUM_POS_SLOT_BITS); + } + } + + private void CreateDictionary() + { + if (_dictionarySize < 0) + { + throw new InvalidParamException(); + } + _outWindow = new OutWindow(); + var blockSize = Math.Max(_dictionarySize, (1 << 12)); + _outWindow.Create(blockSize); + } + + private void SetLiteralProperties(int lp, int lc) + { + if (lp > 8) + { + throw new InvalidParamException(); + } + if (lc > 8) + { + throw new InvalidParamException(); + } + _literalDecoder.Create(lp, lc); + } + + private void SetPosBitsProperties(int pb) + { + if (pb > Base.K_NUM_POS_STATES_BITS_MAX) + { + throw new InvalidParamException(); + } + var numPosStates = (uint)1 << pb; + _lenDecoder.Create(numPosStates); + _repLenDecoder.Create(numPosStates); + _posStateMask = numPosStates - 1; + } + + private void Init() + { + uint i; + for (i = 0; i < Base.K_NUM_STATES; i++) + { + for (uint j = 0; j <= _posStateMask; j++) + { + var index = (i << Base.K_NUM_POS_STATES_BITS_MAX) + j; + _isMatchDecoders[index].Init(); + _isRep0LongDecoders[index].Init(); + } + _isRepDecoders[i].Init(); + _isRepG0Decoders[i].Init(); + _isRepG1Decoders[i].Init(); + _isRepG2Decoders[i].Init(); + } + + _literalDecoder.Init(); + for (i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++) + { + _posSlotDecoder[i].Init(); + } + + // _PosSpecDecoder.Init(); + for (i = 0; i < Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX; i++) + { + _posDecoders[i].Init(); + } + + _lenDecoder.Init(); + _repLenDecoder.Init(); + _posAlignDecoder.Init(); + + _state.Init(); + _rep0 = 0; + _rep1 = 0; + _rep2 = 0; + _rep3 = 0; + } + + public void Code( + Stream inStream, + Stream outStream, + long inSize, + long outSize, + ICodeProgress progress + ) + { + if (_outWindow is null) + { + CreateDictionary(); + } + _outWindow.Init(outStream); + if (outSize > 0) + { + _outWindow.SetLimit(outSize); + } + else + { + _outWindow.SetLimit(long.MaxValue - _outWindow._total); + } + + var rangeDecoder = new RangeCoder.Decoder(); + rangeDecoder.Init(inStream); + + Code(_dictionarySize, _outWindow, rangeDecoder); + + _outWindow.ReleaseStream(); + rangeDecoder.ReleaseStream(); + + _outWindow = null; + } + + internal bool Code(int dictionarySize, OutWindow outWindow, RangeCoder.Decoder rangeDecoder) + { + var dictionarySizeCheck = Math.Max(dictionarySize, 1); + + outWindow.CopyPending(); + + while (outWindow.HasSpace) + { + var posState = (uint)outWindow._total & _posStateMask; + if ( + _isMatchDecoders[ + (_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState + ].Decode(rangeDecoder) == 0 + ) + { + byte b; + var prevByte = outWindow.GetByte(0); + if (!_state.IsCharState()) { - byte b; - byte prevByte = outWindow.GetByte(0); - if (!_state.IsCharState()) - { - b = _literalDecoder.DecodeWithMatchByte( - rangeDecoder, - (uint)outWindow._total, - prevByte, - outWindow.GetByte((int)_rep0) - ); - } - else - { - b = _literalDecoder.DecodeNormal( - rangeDecoder, - (uint)outWindow._total, - prevByte - ); - } - outWindow.PutByte(b); - _state.UpdateChar(); + b = _literalDecoder.DecodeWithMatchByte( + rangeDecoder, + (uint)outWindow._total, + prevByte, + outWindow.GetByte((int)_rep0) + ); } else { - uint len; - if (_isRepDecoders[_state._index].Decode(rangeDecoder) == 1) + b = _literalDecoder.DecodeNormal( + rangeDecoder, + (uint)outWindow._total, + prevByte + ); + } + outWindow.PutByte(b); + _state.UpdateChar(); + } + else + { + uint len; + if (_isRepDecoders[_state._index].Decode(rangeDecoder) == 1) + { + if (_isRepG0Decoders[_state._index].Decode(rangeDecoder) == 0) { - if (_isRepG0Decoders[_state._index].Decode(rangeDecoder) == 0) + if ( + _isRep0LongDecoders[ + (_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState + ].Decode(rangeDecoder) == 0 + ) { - if ( - _isRep0LongDecoders[ - (_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState - ].Decode(rangeDecoder) == 0 - ) - { - _state.UpdateShortRep(); - outWindow.PutByte(outWindow.GetByte((int)_rep0)); - continue; - } + _state.UpdateShortRep(); + outWindow.PutByte(outWindow.GetByte((int)_rep0)); + continue; } - else - { - UInt32 distance; - if (_isRepG1Decoders[_state._index].Decode(rangeDecoder) == 0) - { - distance = _rep1; - } - else - { - if (_isRepG2Decoders[_state._index].Decode(rangeDecoder) == 0) - { - distance = _rep2; - } - else - { - distance = _rep3; - _rep3 = _rep2; - } - _rep2 = _rep1; - } - _rep1 = _rep0; - _rep0 = distance; - } - len = _repLenDecoder.Decode(rangeDecoder, posState) + Base.K_MATCH_MIN_LEN; - _state.UpdateRep(); } else { - _rep3 = _rep2; - _rep2 = _rep1; - _rep1 = _rep0; - len = Base.K_MATCH_MIN_LEN + _lenDecoder.Decode(rangeDecoder, posState); - _state.UpdateMatch(); - uint posSlot = _posSlotDecoder[Base.GetLenToPosState(len)].Decode( - rangeDecoder - ); - if (posSlot >= Base.K_START_POS_MODEL_INDEX) + uint distance; + if (_isRepG1Decoders[_state._index].Decode(rangeDecoder) == 0) { - int numDirectBits = (int)((posSlot >> 1) - 1); - _rep0 = ((2 | (posSlot & 1)) << numDirectBits); - if (posSlot < Base.K_END_POS_MODEL_INDEX) - { - _rep0 += BitTreeDecoder.ReverseDecode( - _posDecoders, - _rep0 - posSlot - 1, - rangeDecoder, - numDirectBits - ); - } - else - { - _rep0 += ( - rangeDecoder.DecodeDirectBits( - numDirectBits - Base.K_NUM_ALIGN_BITS - ) << Base.K_NUM_ALIGN_BITS - ); - _rep0 += _posAlignDecoder.ReverseDecode(rangeDecoder); - } + distance = _rep1; } else { - _rep0 = posSlot; + if (_isRepG2Decoders[_state._index].Decode(rangeDecoder) == 0) + { + distance = _rep2; + } + else + { + distance = _rep3; + _rep3 = _rep2; + } + _rep2 = _rep1; } + _rep1 = _rep0; + _rep0 = distance; } - if (_rep0 >= outWindow._total || _rep0 >= dictionarySizeCheck) - { - if (_rep0 == 0xFFFFFFFF) - { - return true; - } - throw new DataErrorException(); - } - outWindow.CopyBlock((int)_rep0, (int)len); + len = _repLenDecoder.Decode(rangeDecoder, posState) + Base.K_MATCH_MIN_LEN; + _state.UpdateRep(); } - } - return false; - } - - public void SetDecoderProperties(byte[] properties) - { - if (properties.Length < 1) - { - throw new InvalidParamException(); - } - int lc = properties[0] % 9; - int remainder = properties[0] / 9; - int lp = remainder % 5; - int pb = remainder / 5; - if (pb > Base.K_NUM_POS_STATES_BITS_MAX) - { - throw new InvalidParamException(); - } - SetLiteralProperties(lp, lc); - SetPosBitsProperties(pb); - Init(); - if (properties.Length >= 5) - { - _dictionarySize = 0; - for (int i = 0; i < 4; i++) + else { - _dictionarySize += properties[1 + i] << (i * 8); + _rep3 = _rep2; + _rep2 = _rep1; + _rep1 = _rep0; + len = Base.K_MATCH_MIN_LEN + _lenDecoder.Decode(rangeDecoder, posState); + _state.UpdateMatch(); + var posSlot = _posSlotDecoder[Base.GetLenToPosState(len)].Decode( + rangeDecoder + ); + if (posSlot >= Base.K_START_POS_MODEL_INDEX) + { + var numDirectBits = (int)((posSlot >> 1) - 1); + _rep0 = ((2 | (posSlot & 1)) << numDirectBits); + if (posSlot < Base.K_END_POS_MODEL_INDEX) + { + _rep0 += BitTreeDecoder.ReverseDecode( + _posDecoders, + _rep0 - posSlot - 1, + rangeDecoder, + numDirectBits + ); + } + else + { + _rep0 += ( + rangeDecoder.DecodeDirectBits( + numDirectBits - Base.K_NUM_ALIGN_BITS + ) << Base.K_NUM_ALIGN_BITS + ); + _rep0 += _posAlignDecoder.ReverseDecode(rangeDecoder); + } + } + else + { + _rep0 = posSlot; + } } + if (_rep0 >= outWindow._total || _rep0 >= dictionarySizeCheck) + { + if (_rep0 == 0xFFFFFFFF) + { + return true; + } + throw new DataErrorException(); + } + outWindow.CopyBlock((int)_rep0, (int)len); } } - - public void Train(Stream stream) - { - if (_outWindow is null) - { - CreateDictionary(); - } - _outWindow.Train(stream); - } - - /* - public override bool CanRead { get { return true; }} - public override bool CanWrite { get { return true; }} - public override bool CanSeek { get { return true; }} - public override long Length { get { return 0; }} - public override long Position - { - get { return 0; } - set { } - } - public override void Flush() { } - public override int Read(byte[] buffer, int offset, int count) - { - return 0; - } - public override void Write(byte[] buffer, int offset, int count) - { - } - public override long Seek(long offset, System.IO.SeekOrigin origin) - { - return 0; - } - public override void SetLength(long value) {} - */ + return false; } + + public void SetDecoderProperties(byte[] properties) + { + if (properties.Length < 1) + { + throw new InvalidParamException(); + } + var lc = properties[0] % 9; + var remainder = properties[0] / 9; + var lp = remainder % 5; + var pb = remainder / 5; + if (pb > Base.K_NUM_POS_STATES_BITS_MAX) + { + throw new InvalidParamException(); + } + SetLiteralProperties(lp, lc); + SetPosBitsProperties(pb); + Init(); + if (properties.Length >= 5) + { + _dictionarySize = 0; + for (var i = 0; i < 4; i++) + { + _dictionarySize += properties[1 + i] << (i * 8); + } + } + } + + public void Train(Stream stream) + { + if (_outWindow is null) + { + CreateDictionary(); + } + _outWindow.Train(stream); + } + + /* + public override bool CanRead { get { return true; }} + public override bool CanWrite { get { return true; }} + public override bool CanSeek { get { return true; }} + public override long Length { get { return 0; }} + public override long Position + { + get { return 0; } + set { } + } + public override void Flush() { } + public override int Read(byte[] buffer, int offset, int count) + { + return 0; + } + public override void Write(byte[] buffer, int offset, int count) + { + } + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + return 0; + } + public override void SetLength(long value) {} + */ } diff --git a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs index c1ab3b18..be1e8a08 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs @@ -5,1146 +5,1258 @@ using System.IO; using SharpCompress.Compressors.LZMA.LZ; using SharpCompress.Compressors.LZMA.RangeCoder; -namespace SharpCompress.Compressors.LZMA +namespace SharpCompress.Compressors.LZMA; + +internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties { - internal class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties + private enum EMatchFinderType { - private enum EMatchFinderType + Bt2, + Bt4 + } + + private const uint K_IFINITY_PRICE = 0xFFFFFFF; + + private static readonly byte[] G_FAST_POS = new byte[1 << 11]; + + static Encoder() + { + const byte kFastSlots = 22; + var c = 2; + G_FAST_POS[0] = 0; + G_FAST_POS[1] = 1; + for (byte slotFast = 2; slotFast < kFastSlots; slotFast++) { - Bt2, - Bt4 - } - - private const UInt32 K_IFINITY_PRICE = 0xFFFFFFF; - - private static readonly Byte[] G_FAST_POS = new Byte[1 << 11]; - - static Encoder() - { - const Byte kFastSlots = 22; - int c = 2; - G_FAST_POS[0] = 0; - G_FAST_POS[1] = 1; - for (Byte slotFast = 2; slotFast < kFastSlots; slotFast++) + var k = ((uint)1 << ((slotFast >> 1) - 1)); + for (uint j = 0; j < k; j++, c++) { - UInt32 k = ((UInt32)1 << ((slotFast >> 1) - 1)); - for (UInt32 j = 0; j < k; j++, c++) - { - G_FAST_POS[c] = slotFast; - } + G_FAST_POS[c] = slotFast; } } + } - private static UInt32 GetPosSlot(UInt32 pos) + private static uint GetPosSlot(uint pos) + { + if (pos < (1 << 11)) { - if (pos < (1 << 11)) - { - return G_FAST_POS[pos]; - } - if (pos < (1 << 21)) - { - return (UInt32)(G_FAST_POS[pos >> 10] + 20); - } - return (UInt32)(G_FAST_POS[pos >> 20] + 40); + return G_FAST_POS[pos]; } - - private static UInt32 GetPosSlot2(UInt32 pos) + if (pos < (1 << 21)) { - if (pos < (1 << 17)) - { - return (UInt32)(G_FAST_POS[pos >> 6] + 12); - } - if (pos < (1 << 27)) - { - return (UInt32)(G_FAST_POS[pos >> 16] + 32); - } - return (UInt32)(G_FAST_POS[pos >> 26] + 52); + return (uint)(G_FAST_POS[pos >> 10] + 20); } + return (uint)(G_FAST_POS[pos >> 20] + 40); + } - private Base.State _state = new Base.State(); - private Byte _previousByte; - private readonly UInt32[] _repDistances = new UInt32[Base.K_NUM_REP_DISTANCES]; - - private void BaseInit() + private static uint GetPosSlot2(uint pos) + { + if (pos < (1 << 17)) { - _state.Init(); - _previousByte = 0; - for (UInt32 i = 0; i < Base.K_NUM_REP_DISTANCES; i++) - { - _repDistances[i] = 0; - } + return (uint)(G_FAST_POS[pos >> 6] + 12); } - - private const int K_DEFAULT_DICTIONARY_LOG_SIZE = 22; - private const UInt32 K_NUM_FAST_BYTES_DEFAULT = 0x20; - - private class LiteralEncoder + if (pos < (1 << 27)) { - public struct Encoder2 + return (uint)(G_FAST_POS[pos >> 16] + 32); + } + return (uint)(G_FAST_POS[pos >> 26] + 52); + } + + private Base.State _state = new Base.State(); + private byte _previousByte; + private readonly uint[] _repDistances = new uint[Base.K_NUM_REP_DISTANCES]; + + private void BaseInit() + { + _state.Init(); + _previousByte = 0; + for (uint i = 0; i < Base.K_NUM_REP_DISTANCES; i++) + { + _repDistances[i] = 0; + } + } + + private const int K_DEFAULT_DICTIONARY_LOG_SIZE = 22; + private const uint K_NUM_FAST_BYTES_DEFAULT = 0x20; + + private class LiteralEncoder + { + public struct Encoder2 + { + private BitEncoder[] _encoders; + + public void Create() { - private BitEncoder[] _encoders; - - public void Create() - { - _encoders = new BitEncoder[0x300]; - } - - public void Init() - { - for (int i = 0; i < 0x300; i++) - { - _encoders[i].Init(); - } - } - - public void Encode(RangeCoder.Encoder rangeEncoder, byte symbol) - { - uint context = 1; - for (int i = 7; i >= 0; i--) - { - uint bit = (uint)((symbol >> i) & 1); - _encoders[context].Encode(rangeEncoder, bit); - context = (context << 1) | bit; - } - } - - public void EncodeMatched( - RangeCoder.Encoder rangeEncoder, - byte matchByte, - byte symbol - ) - { - uint context = 1; - bool same = true; - for (int i = 7; i >= 0; i--) - { - uint bit = (uint)((symbol >> i) & 1); - uint state = context; - if (same) - { - uint matchBit = (uint)((matchByte >> i) & 1); - state += ((1 + matchBit) << 8); - same = (matchBit == bit); - } - _encoders[state].Encode(rangeEncoder, bit); - context = (context << 1) | bit; - } - } - - public uint GetPrice(bool matchMode, byte matchByte, byte symbol) - { - uint price = 0; - uint context = 1; - int i = 7; - if (matchMode) - { - for (; i >= 0; i--) - { - uint matchBit = (uint)(matchByte >> i) & 1; - uint bit = (uint)(symbol >> i) & 1; - price += _encoders[((1 + matchBit) << 8) + context].GetPrice(bit); - context = (context << 1) | bit; - if (matchBit != bit) - { - i--; - break; - } - } - } - for (; i >= 0; i--) - { - uint bit = (uint)(symbol >> i) & 1; - price += _encoders[context].GetPrice(bit); - context = (context << 1) | bit; - } - return price; - } - } - - private Encoder2[] _coders; - private int _numPrevBits; - private int _numPosBits; - private uint _posMask; - - public void Create(int numPosBits, int numPrevBits) - { - if (_coders != null && _numPrevBits == numPrevBits && _numPosBits == numPosBits) - { - return; - } - _numPosBits = numPosBits; - _posMask = ((uint)1 << numPosBits) - 1; - _numPrevBits = numPrevBits; - uint numStates = (uint)1 << (_numPrevBits + _numPosBits); - _coders = new Encoder2[numStates]; - for (uint i = 0; i < numStates; i++) - { - _coders[i].Create(); - } + _encoders = new BitEncoder[0x300]; } public void Init() { - uint numStates = (uint)1 << (_numPrevBits + _numPosBits); - for (uint i = 0; i < numStates; i++) + for (var i = 0; i < 0x300; i++) { - _coders[i].Init(); + _encoders[i].Init(); } } - public Encoder2 GetSubCoder(UInt32 pos, Byte prevByte) + public void Encode(RangeCoder.Encoder rangeEncoder, byte symbol) { - return _coders[ - ((pos & _posMask) << _numPrevBits) + (uint)(prevByte >> (8 - _numPrevBits)) - ]; - } - } - - private class LenEncoder - { - private BitEncoder _choice = new BitEncoder(); - private BitEncoder _choice2 = new BitEncoder(); - private readonly BitTreeEncoder[] _lowCoder = new BitTreeEncoder[ - Base.K_NUM_POS_STATES_ENCODING_MAX - ]; - private readonly BitTreeEncoder[] _midCoder = new BitTreeEncoder[ - Base.K_NUM_POS_STATES_ENCODING_MAX - ]; - private BitTreeEncoder _highCoder = new BitTreeEncoder(Base.K_NUM_HIGH_LEN_BITS); - - public LenEncoder() - { - for (UInt32 posState = 0; posState < Base.K_NUM_POS_STATES_ENCODING_MAX; posState++) + uint context = 1; + for (var i = 7; i >= 0; i--) { - _lowCoder[posState] = new BitTreeEncoder(Base.K_NUM_LOW_LEN_BITS); - _midCoder[posState] = new BitTreeEncoder(Base.K_NUM_MID_LEN_BITS); + var bit = (uint)((symbol >> i) & 1); + _encoders[context].Encode(rangeEncoder, bit); + context = (context << 1) | bit; } } - public void Init(UInt32 numPosStates) + public void EncodeMatched( + RangeCoder.Encoder rangeEncoder, + byte matchByte, + byte symbol + ) { - _choice.Init(); - _choice2.Init(); - for (UInt32 posState = 0; posState < numPosStates; posState++) + uint context = 1; + var same = true; + for (var i = 7; i >= 0; i--) { - _lowCoder[posState].Init(); - _midCoder[posState].Init(); - } - _highCoder.Init(); - } - - public void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState) - { - if (symbol < Base.K_NUM_LOW_LEN_SYMBOLS) - { - _choice.Encode(rangeEncoder, 0); - _lowCoder[posState].Encode(rangeEncoder, symbol); - } - else - { - symbol -= Base.K_NUM_LOW_LEN_SYMBOLS; - _choice.Encode(rangeEncoder, 1); - if (symbol < Base.K_NUM_MID_LEN_SYMBOLS) + var bit = (uint)((symbol >> i) & 1); + var state = context; + if (same) { - _choice2.Encode(rangeEncoder, 0); - _midCoder[posState].Encode(rangeEncoder, symbol); + var matchBit = (uint)((matchByte >> i) & 1); + state += ((1 + matchBit) << 8); + same = (matchBit == bit); } - else + _encoders[state].Encode(rangeEncoder, bit); + context = (context << 1) | bit; + } + } + + public uint GetPrice(bool matchMode, byte matchByte, byte symbol) + { + uint price = 0; + uint context = 1; + var i = 7; + if (matchMode) + { + for (; i >= 0; i--) { - _choice2.Encode(rangeEncoder, 1); - _highCoder.Encode(rangeEncoder, symbol - Base.K_NUM_MID_LEN_SYMBOLS); - } - } - } - - public void SetPrices(UInt32 posState, UInt32 numSymbols, UInt32[] prices, UInt32 st) - { - UInt32 a0 = _choice.GetPrice0(); - UInt32 a1 = _choice.GetPrice1(); - UInt32 b0 = a1 + _choice2.GetPrice0(); - UInt32 b1 = a1 + _choice2.GetPrice1(); - UInt32 i = 0; - for (i = 0; i < Base.K_NUM_LOW_LEN_SYMBOLS; i++) - { - if (i >= numSymbols) - { - return; - } - prices[st + i] = a0 + _lowCoder[posState].GetPrice(i); - } - for (; i < Base.K_NUM_LOW_LEN_SYMBOLS + Base.K_NUM_MID_LEN_SYMBOLS; i++) - { - if (i >= numSymbols) - { - return; - } - prices[st + i] = - b0 + _midCoder[posState].GetPrice(i - Base.K_NUM_LOW_LEN_SYMBOLS); - } - for (; i < numSymbols; i++) - { - prices[st + i] = - b1 - + _highCoder.GetPrice( - i - Base.K_NUM_LOW_LEN_SYMBOLS - Base.K_NUM_MID_LEN_SYMBOLS - ); - } - } - } - - private const UInt32 K_NUM_LEN_SPEC_SYMBOLS = - Base.K_NUM_LOW_LEN_SYMBOLS + Base.K_NUM_MID_LEN_SYMBOLS; - - private class LenPriceTableEncoder : LenEncoder - { - private readonly UInt32[] _prices = new UInt32[ - Base.K_NUM_LEN_SYMBOLS << Base.K_NUM_POS_STATES_BITS_ENCODING_MAX - ]; - private UInt32 _tableSize; - private readonly UInt32[] _counters = new UInt32[Base.K_NUM_POS_STATES_ENCODING_MAX]; - - public void SetTableSize(UInt32 tableSize) - { - _tableSize = tableSize; - } - - public UInt32 GetPrice(UInt32 symbol, UInt32 posState) - { - return _prices[posState * Base.K_NUM_LEN_SYMBOLS + symbol]; - } - - private void UpdateTable(UInt32 posState) - { - SetPrices(posState, _tableSize, _prices, posState * Base.K_NUM_LEN_SYMBOLS); - _counters[posState] = _tableSize; - } - - public void UpdateTables(UInt32 numPosStates) - { - for (UInt32 posState = 0; posState < numPosStates; posState++) - { - UpdateTable(posState); - } - } - - public new void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState) - { - base.Encode(rangeEncoder, symbol, posState); - if (--_counters[posState] == 0) - { - UpdateTable(posState); - } - } - } - - private const UInt32 K_NUM_OPTS = 1 << 12; - - private class Optimal - { - public Base.State _state; - - public bool _prev1IsChar; - public bool _prev2; - - public UInt32 _posPrev2; - public UInt32 _backPrev2; - - public UInt32 _price; - public UInt32 _posPrev; - public UInt32 _backPrev; - - public UInt32 _backs0; - public UInt32 _backs1; - public UInt32 _backs2; - public UInt32 _backs3; - - public void MakeAsChar() - { - _backPrev = 0xFFFFFFFF; - _prev1IsChar = false; - } - - public void MakeAsShortRep() - { - _backPrev = 0; - ; - _prev1IsChar = false; - } - - public bool IsShortRep() - { - return (_backPrev == 0); - } - } - - private readonly Optimal[] _optimum = new Optimal[K_NUM_OPTS]; - private BinTree _matchFinder; - private readonly RangeCoder.Encoder _rangeEncoder = new RangeCoder.Encoder(); - - private readonly BitEncoder[] _isMatch = new BitEncoder[ - Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX - ]; - - private readonly BitEncoder[] _isRep = new BitEncoder[Base.K_NUM_STATES]; - private readonly BitEncoder[] _isRepG0 = new BitEncoder[Base.K_NUM_STATES]; - private readonly BitEncoder[] _isRepG1 = new BitEncoder[Base.K_NUM_STATES]; - private readonly BitEncoder[] _isRepG2 = new BitEncoder[Base.K_NUM_STATES]; - - private readonly BitEncoder[] _isRep0Long = new BitEncoder[ - Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX - ]; - - private readonly BitTreeEncoder[] _posSlotEncoder = new BitTreeEncoder[ - Base.K_NUM_LEN_TO_POS_STATES - ]; - - private readonly BitEncoder[] _posEncoders = new BitEncoder[ - Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX - ]; - - private BitTreeEncoder _posAlignEncoder = new BitTreeEncoder(Base.K_NUM_ALIGN_BITS); - - private readonly LenPriceTableEncoder _lenEncoder = new LenPriceTableEncoder(); - private readonly LenPriceTableEncoder _repMatchLenEncoder = new LenPriceTableEncoder(); - - private readonly LiteralEncoder _literalEncoder = new LiteralEncoder(); - - private readonly UInt32[] _matchDistances = new UInt32[Base.K_MATCH_MAX_LEN * 2 + 2]; - - private UInt32 _numFastBytes = K_NUM_FAST_BYTES_DEFAULT; - private UInt32 _longestMatchLength; - private UInt32 _numDistancePairs; - - private UInt32 _additionalOffset; - - private UInt32 _optimumEndIndex; - private UInt32 _optimumCurrentIndex; - - private bool _longestMatchWasFound; - - private readonly UInt32[] _posSlotPrices = new UInt32[ - 1 << (Base.K_NUM_POS_SLOT_BITS + Base.K_NUM_LEN_TO_POS_STATES_BITS) - ]; - private readonly UInt32[] _distancesPrices = new UInt32[ - Base.K_NUM_FULL_DISTANCES << Base.K_NUM_LEN_TO_POS_STATES_BITS - ]; - private readonly UInt32[] _alignPrices = new UInt32[Base.K_ALIGN_TABLE_SIZE]; - private UInt32 _alignPriceCount; - - private UInt32 _distTableSize = (K_DEFAULT_DICTIONARY_LOG_SIZE * 2); - - private int _posStateBits = 2; - private UInt32 _posStateMask = (4 - 1); - private int _numLiteralPosStateBits; - private int _numLiteralContextBits = 3; - - private UInt32 _dictionarySize = (1 << K_DEFAULT_DICTIONARY_LOG_SIZE); - private UInt32 _dictionarySizePrev = 0xFFFFFFFF; - private UInt32 _numFastBytesPrev = 0xFFFFFFFF; - - private Int64 _nowPos64; - private bool _finished; - private Stream _inStream; - - private EMatchFinderType _matchFinderType = EMatchFinderType.Bt4; - private bool _writeEndMark; - - private bool _needReleaseMfStream; - private bool _processingMode; - - private void Create() - { - if (_matchFinder is null) - { - var bt = new BinTree(); - int numHashBytes = 4; - if (_matchFinderType == EMatchFinderType.Bt2) - { - numHashBytes = 2; - } - bt.SetType(numHashBytes); - _matchFinder = bt; - } - _literalEncoder.Create(_numLiteralPosStateBits, _numLiteralContextBits); - - if (_dictionarySize == _dictionarySizePrev && _numFastBytesPrev == _numFastBytes) - { - return; - } - _matchFinder.Create( - _dictionarySize, - K_NUM_OPTS, - _numFastBytes, - Base.K_MATCH_MAX_LEN + 1 + K_NUM_OPTS - ); - _dictionarySizePrev = _dictionarySize; - _numFastBytesPrev = _numFastBytes; - } - - public Encoder() - { - for (int i = 0; i < K_NUM_OPTS; i++) - { - _optimum[i] = new Optimal(); - } - for (int i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++) - { - _posSlotEncoder[i] = new BitTreeEncoder(Base.K_NUM_POS_SLOT_BITS); - } - } - - private void SetWriteEndMarkerMode(bool writeEndMarker) - { - _writeEndMark = writeEndMarker; - } - - private void Init() - { - BaseInit(); - _rangeEncoder.Init(); - - uint i; - for (i = 0; i < Base.K_NUM_STATES; i++) - { - for (uint j = 0; j <= _posStateMask; j++) - { - uint complexState = (i << Base.K_NUM_POS_STATES_BITS_MAX) + j; - _isMatch[complexState].Init(); - _isRep0Long[complexState].Init(); - } - _isRep[i].Init(); - _isRepG0[i].Init(); - _isRepG1[i].Init(); - _isRepG2[i].Init(); - } - _literalEncoder.Init(); - for (i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++) - { - _posSlotEncoder[i].Init(); - } - for (i = 0; i < Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX; i++) - { - _posEncoders[i].Init(); - } - - _lenEncoder.Init((UInt32)1 << _posStateBits); - _repMatchLenEncoder.Init((UInt32)1 << _posStateBits); - - _posAlignEncoder.Init(); - - _longestMatchWasFound = false; - _optimumEndIndex = 0; - _optimumCurrentIndex = 0; - _additionalOffset = 0; - } - - private void ReadMatchDistances(out UInt32 lenRes, out UInt32 numDistancePairs) - { - lenRes = 0; - numDistancePairs = _matchFinder.GetMatches(_matchDistances); - if (numDistancePairs > 0) - { - lenRes = _matchDistances[numDistancePairs - 2]; - if (lenRes == _numFastBytes) - { - lenRes += _matchFinder.GetMatchLen( - (int)lenRes - 1, - _matchDistances[numDistancePairs - 1], - Base.K_MATCH_MAX_LEN - lenRes - ); - } - } - _additionalOffset++; - } - - private void MovePos(UInt32 num) - { - if (num > 0) - { - _matchFinder.Skip(num); - _additionalOffset += num; - } - } - - private UInt32 GetRepLen1Price(Base.State state, UInt32 posState) - { - return _isRepG0[state._index].GetPrice0() - + _isRep0Long[ - (state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState - ].GetPrice0(); - } - - private UInt32 GetPureRepPrice(UInt32 repIndex, Base.State state, UInt32 posState) - { - UInt32 price; - if (repIndex == 0) - { - price = _isRepG0[state._index].GetPrice0(); - price += _isRep0Long[ - (state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState - ].GetPrice1(); - } - else - { - price = _isRepG0[state._index].GetPrice1(); - if (repIndex == 1) - { - price += _isRepG1[state._index].GetPrice0(); - } - else - { - price += _isRepG1[state._index].GetPrice1(); - price += _isRepG2[state._index].GetPrice(repIndex - 2); - } - } - return price; - } - - private UInt32 GetRepPrice(UInt32 repIndex, UInt32 len, Base.State state, UInt32 posState) - { - UInt32 price = _repMatchLenEncoder.GetPrice(len - Base.K_MATCH_MIN_LEN, posState); - return price + GetPureRepPrice(repIndex, state, posState); - } - - private UInt32 GetPosLenPrice(UInt32 pos, UInt32 len, UInt32 posState) - { - UInt32 price; - UInt32 lenToPosState = Base.GetLenToPosState(len); - if (pos < Base.K_NUM_FULL_DISTANCES) - { - price = _distancesPrices[(lenToPosState * Base.K_NUM_FULL_DISTANCES) + pos]; - } - else - { - price = - _posSlotPrices[(lenToPosState << Base.K_NUM_POS_SLOT_BITS) + GetPosSlot2(pos)] - + _alignPrices[pos & Base.K_ALIGN_MASK]; - } - return price + _lenEncoder.GetPrice(len - Base.K_MATCH_MIN_LEN, posState); - } - - private UInt32 Backward(out UInt32 backRes, UInt32 cur) - { - _optimumEndIndex = cur; - UInt32 posMem = _optimum[cur]._posPrev; - UInt32 backMem = _optimum[cur]._backPrev; - do - { - if (_optimum[cur]._prev1IsChar) - { - _optimum[posMem].MakeAsChar(); - _optimum[posMem]._posPrev = posMem - 1; - if (_optimum[cur]._prev2) - { - _optimum[posMem - 1]._prev1IsChar = false; - _optimum[posMem - 1]._posPrev = _optimum[cur]._posPrev2; - _optimum[posMem - 1]._backPrev = _optimum[cur]._backPrev2; - } - } - UInt32 posPrev = posMem; - UInt32 backCur = backMem; - - backMem = _optimum[posPrev]._backPrev; - posMem = _optimum[posPrev]._posPrev; - - _optimum[posPrev]._backPrev = backCur; - _optimum[posPrev]._posPrev = cur; - cur = posPrev; - } while (cur > 0); - backRes = _optimum[0]._backPrev; - _optimumCurrentIndex = _optimum[0]._posPrev; - return _optimumCurrentIndex; - } - - private readonly UInt32[] _reps = new UInt32[Base.K_NUM_REP_DISTANCES]; - private readonly UInt32[] _repLens = new UInt32[Base.K_NUM_REP_DISTANCES]; - - private UInt32 GetOptimum(UInt32 position, out UInt32 backRes) - { - if (_optimumEndIndex != _optimumCurrentIndex) - { - UInt32 lenRes = _optimum[_optimumCurrentIndex]._posPrev - _optimumCurrentIndex; - backRes = _optimum[_optimumCurrentIndex]._backPrev; - _optimumCurrentIndex = _optimum[_optimumCurrentIndex]._posPrev; - return lenRes; - } - _optimumCurrentIndex = _optimumEndIndex = 0; - - UInt32 lenMain, - numDistancePairs; - if (!_longestMatchWasFound) - { - ReadMatchDistances(out lenMain, out numDistancePairs); - } - else - { - lenMain = _longestMatchLength; - numDistancePairs = _numDistancePairs; - _longestMatchWasFound = false; - } - - UInt32 numAvailableBytes = _matchFinder.GetNumAvailableBytes() + 1; - if (numAvailableBytes < 2) - { - backRes = 0xFFFFFFFF; - return 1; - } - if (numAvailableBytes > Base.K_MATCH_MAX_LEN) - { - numAvailableBytes = Base.K_MATCH_MAX_LEN; - } - - UInt32 repMaxIndex = 0; - UInt32 i; - for (i = 0; i < Base.K_NUM_REP_DISTANCES; i++) - { - _reps[i] = _repDistances[i]; - _repLens[i] = _matchFinder.GetMatchLen(0 - 1, _reps[i], Base.K_MATCH_MAX_LEN); - if (_repLens[i] > _repLens[repMaxIndex]) - { - repMaxIndex = i; - } - } - if (_repLens[repMaxIndex] >= _numFastBytes) - { - backRes = repMaxIndex; - UInt32 lenRes = _repLens[repMaxIndex]; - MovePos(lenRes - 1); - return lenRes; - } - - if (lenMain >= _numFastBytes) - { - backRes = _matchDistances[numDistancePairs - 1] + Base.K_NUM_REP_DISTANCES; - MovePos(lenMain - 1); - return lenMain; - } - - Byte currentByte = _matchFinder.GetIndexByte(0 - 1); - Byte matchByte = _matchFinder.GetIndexByte((Int32)(0 - _repDistances[0] - 1 - 1)); - - if (lenMain < 2 && currentByte != matchByte && _repLens[repMaxIndex] < 2) - { - backRes = 0xFFFFFFFF; - return 1; - } - - _optimum[0]._state = _state; - - UInt32 posState = (position & _posStateMask); - - _optimum[1]._price = - _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].GetPrice0() - + _literalEncoder - .GetSubCoder(position, _previousByte) - .GetPrice(!_state.IsCharState(), matchByte, currentByte); - _optimum[1].MakeAsChar(); - - UInt32 matchPrice = _isMatch[ - (_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState - ].GetPrice1(); - UInt32 repMatchPrice = matchPrice + _isRep[_state._index].GetPrice1(); - - if (matchByte == currentByte) - { - UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(_state, posState); - if (shortRepPrice < _optimum[1]._price) - { - _optimum[1]._price = shortRepPrice; - _optimum[1].MakeAsShortRep(); - } - } - - UInt32 lenEnd = ((lenMain >= _repLens[repMaxIndex]) ? lenMain : _repLens[repMaxIndex]); - - if (lenEnd < 2) - { - backRes = _optimum[1]._backPrev; - return 1; - } - - _optimum[1]._posPrev = 0; - - _optimum[0]._backs0 = _reps[0]; - _optimum[0]._backs1 = _reps[1]; - _optimum[0]._backs2 = _reps[2]; - _optimum[0]._backs3 = _reps[3]; - - UInt32 len = lenEnd; - do - { - _optimum[len--]._price = K_IFINITY_PRICE; - } while (len >= 2); - - for (i = 0; i < Base.K_NUM_REP_DISTANCES; i++) - { - UInt32 repLen = _repLens[i]; - if (repLen < 2) - { - continue; - } - UInt32 price = repMatchPrice + GetPureRepPrice(i, _state, posState); - do - { - UInt32 curAndLenPrice = - price + _repMatchLenEncoder.GetPrice(repLen - 2, posState); - Optimal optimum = _optimum[repLen]; - if (curAndLenPrice < optimum._price) - { - optimum._price = curAndLenPrice; - optimum._posPrev = 0; - optimum._backPrev = i; - optimum._prev1IsChar = false; - } - } while (--repLen >= 2); - } - - UInt32 normalMatchPrice = matchPrice + _isRep[_state._index].GetPrice0(); - - len = ((_repLens[0] >= 2) ? _repLens[0] + 1 : 2); - if (len <= lenMain) - { - UInt32 offs = 0; - while (len > _matchDistances[offs]) - { - offs += 2; - } - for (; ; len++) - { - UInt32 distance = _matchDistances[offs + 1]; - UInt32 curAndLenPrice = - normalMatchPrice + GetPosLenPrice(distance, len, posState); - Optimal optimum = _optimum[len]; - if (curAndLenPrice < optimum._price) - { - optimum._price = curAndLenPrice; - optimum._posPrev = 0; - optimum._backPrev = distance + Base.K_NUM_REP_DISTANCES; - optimum._prev1IsChar = false; - } - if (len == _matchDistances[offs]) - { - offs += 2; - if (offs == numDistancePairs) + var matchBit = (uint)(matchByte >> i) & 1; + var bit = (uint)(symbol >> i) & 1; + price += _encoders[((1 + matchBit) << 8) + context].GetPrice(bit); + context = (context << 1) | bit; + if (matchBit != bit) { + i--; break; } } } + for (; i >= 0; i--) + { + var bit = (uint)(symbol >> i) & 1; + price += _encoders[context].GetPrice(bit); + context = (context << 1) | bit; + } + return price; } + } - UInt32 cur = 0; + private Encoder2[] _coders; + private int _numPrevBits; + private int _numPosBits; + private uint _posMask; - while (true) + public void Create(int numPosBits, int numPrevBits) + { + if (_coders != null && _numPrevBits == numPrevBits && _numPosBits == numPosBits) { - cur++; - if (cur == lenEnd) + return; + } + _numPosBits = numPosBits; + _posMask = ((uint)1 << numPosBits) - 1; + _numPrevBits = numPrevBits; + var numStates = (uint)1 << (_numPrevBits + _numPosBits); + _coders = new Encoder2[numStates]; + for (uint i = 0; i < numStates; i++) + { + _coders[i].Create(); + } + } + + public void Init() + { + var numStates = (uint)1 << (_numPrevBits + _numPosBits); + for (uint i = 0; i < numStates; i++) + { + _coders[i].Init(); + } + } + + public Encoder2 GetSubCoder(uint pos, byte prevByte) + { + return _coders[ + ((pos & _posMask) << _numPrevBits) + (uint)(prevByte >> (8 - _numPrevBits)) + ]; + } + } + + private class LenEncoder + { + private BitEncoder _choice = new BitEncoder(); + private BitEncoder _choice2 = new BitEncoder(); + private readonly BitTreeEncoder[] _lowCoder = new BitTreeEncoder[ + Base.K_NUM_POS_STATES_ENCODING_MAX + ]; + private readonly BitTreeEncoder[] _midCoder = new BitTreeEncoder[ + Base.K_NUM_POS_STATES_ENCODING_MAX + ]; + private BitTreeEncoder _highCoder = new BitTreeEncoder(Base.K_NUM_HIGH_LEN_BITS); + + public LenEncoder() + { + for (uint posState = 0; posState < Base.K_NUM_POS_STATES_ENCODING_MAX; posState++) + { + _lowCoder[posState] = new BitTreeEncoder(Base.K_NUM_LOW_LEN_BITS); + _midCoder[posState] = new BitTreeEncoder(Base.K_NUM_MID_LEN_BITS); + } + } + + public void Init(uint numPosStates) + { + _choice.Init(); + _choice2.Init(); + for (uint posState = 0; posState < numPosStates; posState++) + { + _lowCoder[posState].Init(); + _midCoder[posState].Init(); + } + _highCoder.Init(); + } + + public void Encode(RangeCoder.Encoder rangeEncoder, uint symbol, uint posState) + { + if (symbol < Base.K_NUM_LOW_LEN_SYMBOLS) + { + _choice.Encode(rangeEncoder, 0); + _lowCoder[posState].Encode(rangeEncoder, symbol); + } + else + { + symbol -= Base.K_NUM_LOW_LEN_SYMBOLS; + _choice.Encode(rangeEncoder, 1); + if (symbol < Base.K_NUM_MID_LEN_SYMBOLS) { - return Backward(out backRes, cur); + _choice2.Encode(rangeEncoder, 0); + _midCoder[posState].Encode(rangeEncoder, symbol); } - UInt32 newLen; - ReadMatchDistances(out newLen, out numDistancePairs); - if (newLen >= _numFastBytes) + else { - _numDistancePairs = numDistancePairs; - _longestMatchLength = newLen; - _longestMatchWasFound = true; - return Backward(out backRes, cur); + _choice2.Encode(rangeEncoder, 1); + _highCoder.Encode(rangeEncoder, symbol - Base.K_NUM_MID_LEN_SYMBOLS); } - position++; - UInt32 posPrev = _optimum[cur]._posPrev; - Base.State state; - if (_optimum[cur]._prev1IsChar) + } + } + + public void SetPrices(uint posState, uint numSymbols, uint[] prices, uint st) + { + var a0 = _choice.GetPrice0(); + var a1 = _choice.GetPrice1(); + var b0 = a1 + _choice2.GetPrice0(); + var b1 = a1 + _choice2.GetPrice1(); + uint i = 0; + for (i = 0; i < Base.K_NUM_LOW_LEN_SYMBOLS; i++) + { + if (i >= numSymbols) { - posPrev--; - if (_optimum[cur]._prev2) + return; + } + prices[st + i] = a0 + _lowCoder[posState].GetPrice(i); + } + for (; i < Base.K_NUM_LOW_LEN_SYMBOLS + Base.K_NUM_MID_LEN_SYMBOLS; i++) + { + if (i >= numSymbols) + { + return; + } + prices[st + i] = + b0 + _midCoder[posState].GetPrice(i - Base.K_NUM_LOW_LEN_SYMBOLS); + } + for (; i < numSymbols; i++) + { + prices[st + i] = + b1 + + _highCoder.GetPrice( + i - Base.K_NUM_LOW_LEN_SYMBOLS - Base.K_NUM_MID_LEN_SYMBOLS + ); + } + } + } + + private const uint K_NUM_LEN_SPEC_SYMBOLS = + Base.K_NUM_LOW_LEN_SYMBOLS + Base.K_NUM_MID_LEN_SYMBOLS; + + private class LenPriceTableEncoder : LenEncoder + { + private readonly uint[] _prices = new uint[ + Base.K_NUM_LEN_SYMBOLS << Base.K_NUM_POS_STATES_BITS_ENCODING_MAX + ]; + private uint _tableSize; + private readonly uint[] _counters = new uint[Base.K_NUM_POS_STATES_ENCODING_MAX]; + + public void SetTableSize(uint tableSize) + { + _tableSize = tableSize; + } + + public uint GetPrice(uint symbol, uint posState) + { + return _prices[(posState * Base.K_NUM_LEN_SYMBOLS) + symbol]; + } + + private void UpdateTable(uint posState) + { + SetPrices(posState, _tableSize, _prices, posState * Base.K_NUM_LEN_SYMBOLS); + _counters[posState] = _tableSize; + } + + public void UpdateTables(uint numPosStates) + { + for (uint posState = 0; posState < numPosStates; posState++) + { + UpdateTable(posState); + } + } + + public new void Encode(RangeCoder.Encoder rangeEncoder, uint symbol, uint posState) + { + base.Encode(rangeEncoder, symbol, posState); + if (--_counters[posState] == 0) + { + UpdateTable(posState); + } + } + } + + private const uint K_NUM_OPTS = 1 << 12; + + private class Optimal + { + public Base.State _state; + + public bool _prev1IsChar; + public bool _prev2; + + public uint _posPrev2; + public uint _backPrev2; + + public uint _price; + public uint _posPrev; + public uint _backPrev; + + public uint _backs0; + public uint _backs1; + public uint _backs2; + public uint _backs3; + + public void MakeAsChar() + { + _backPrev = 0xFFFFFFFF; + _prev1IsChar = false; + } + + public void MakeAsShortRep() + { + _backPrev = 0; + ; + _prev1IsChar = false; + } + + public bool IsShortRep() + { + return (_backPrev == 0); + } + } + + private readonly Optimal[] _optimum = new Optimal[K_NUM_OPTS]; + private BinTree _matchFinder; + private readonly RangeCoder.Encoder _rangeEncoder = new RangeCoder.Encoder(); + + private readonly BitEncoder[] _isMatch = new BitEncoder[ + Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX + ]; + + private readonly BitEncoder[] _isRep = new BitEncoder[Base.K_NUM_STATES]; + private readonly BitEncoder[] _isRepG0 = new BitEncoder[Base.K_NUM_STATES]; + private readonly BitEncoder[] _isRepG1 = new BitEncoder[Base.K_NUM_STATES]; + private readonly BitEncoder[] _isRepG2 = new BitEncoder[Base.K_NUM_STATES]; + + private readonly BitEncoder[] _isRep0Long = new BitEncoder[ + Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX + ]; + + private readonly BitTreeEncoder[] _posSlotEncoder = new BitTreeEncoder[ + Base.K_NUM_LEN_TO_POS_STATES + ]; + + private readonly BitEncoder[] _posEncoders = new BitEncoder[ + Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX + ]; + + private BitTreeEncoder _posAlignEncoder = new BitTreeEncoder(Base.K_NUM_ALIGN_BITS); + + private readonly LenPriceTableEncoder _lenEncoder = new LenPriceTableEncoder(); + private readonly LenPriceTableEncoder _repMatchLenEncoder = new LenPriceTableEncoder(); + + private readonly LiteralEncoder _literalEncoder = new LiteralEncoder(); + + private readonly uint[] _matchDistances = new uint[(Base.K_MATCH_MAX_LEN * 2) + 2]; + + private uint _numFastBytes = K_NUM_FAST_BYTES_DEFAULT; + private uint _longestMatchLength; + private uint _numDistancePairs; + + private uint _additionalOffset; + + private uint _optimumEndIndex; + private uint _optimumCurrentIndex; + + private bool _longestMatchWasFound; + + private readonly uint[] _posSlotPrices = new uint[ + 1 << (Base.K_NUM_POS_SLOT_BITS + Base.K_NUM_LEN_TO_POS_STATES_BITS) + ]; + private readonly uint[] _distancesPrices = new uint[ + Base.K_NUM_FULL_DISTANCES << Base.K_NUM_LEN_TO_POS_STATES_BITS + ]; + private readonly uint[] _alignPrices = new uint[Base.K_ALIGN_TABLE_SIZE]; + private uint _alignPriceCount; + + private uint _distTableSize = (K_DEFAULT_DICTIONARY_LOG_SIZE * 2); + + private int _posStateBits = 2; + private uint _posStateMask = (4 - 1); + private int _numLiteralPosStateBits; + private int _numLiteralContextBits = 3; + + private uint _dictionarySize = (1 << K_DEFAULT_DICTIONARY_LOG_SIZE); + private uint _dictionarySizePrev = 0xFFFFFFFF; + private uint _numFastBytesPrev = 0xFFFFFFFF; + + private long _nowPos64; + private bool _finished; + private Stream _inStream; + + private EMatchFinderType _matchFinderType = EMatchFinderType.Bt4; + private bool _writeEndMark; + + private bool _needReleaseMfStream; + private bool _processingMode; + + private void Create() + { + if (_matchFinder is null) + { + var bt = new BinTree(); + var numHashBytes = 4; + if (_matchFinderType == EMatchFinderType.Bt2) + { + numHashBytes = 2; + } + bt.SetType(numHashBytes); + _matchFinder = bt; + } + _literalEncoder.Create(_numLiteralPosStateBits, _numLiteralContextBits); + + if (_dictionarySize == _dictionarySizePrev && _numFastBytesPrev == _numFastBytes) + { + return; + } + _matchFinder.Create( + _dictionarySize, + K_NUM_OPTS, + _numFastBytes, + Base.K_MATCH_MAX_LEN + 1 + K_NUM_OPTS + ); + _dictionarySizePrev = _dictionarySize; + _numFastBytesPrev = _numFastBytes; + } + + public Encoder() + { + for (var i = 0; i < K_NUM_OPTS; i++) + { + _optimum[i] = new Optimal(); + } + for (var i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++) + { + _posSlotEncoder[i] = new BitTreeEncoder(Base.K_NUM_POS_SLOT_BITS); + } + } + + private void SetWriteEndMarkerMode(bool writeEndMarker) + { + _writeEndMark = writeEndMarker; + } + + private void Init() + { + BaseInit(); + _rangeEncoder.Init(); + + uint i; + for (i = 0; i < Base.K_NUM_STATES; i++) + { + for (uint j = 0; j <= _posStateMask; j++) + { + var complexState = (i << Base.K_NUM_POS_STATES_BITS_MAX) + j; + _isMatch[complexState].Init(); + _isRep0Long[complexState].Init(); + } + _isRep[i].Init(); + _isRepG0[i].Init(); + _isRepG1[i].Init(); + _isRepG2[i].Init(); + } + _literalEncoder.Init(); + for (i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++) + { + _posSlotEncoder[i].Init(); + } + for (i = 0; i < Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX; i++) + { + _posEncoders[i].Init(); + } + + _lenEncoder.Init((uint)1 << _posStateBits); + _repMatchLenEncoder.Init((uint)1 << _posStateBits); + + _posAlignEncoder.Init(); + + _longestMatchWasFound = false; + _optimumEndIndex = 0; + _optimumCurrentIndex = 0; + _additionalOffset = 0; + } + + private void ReadMatchDistances(out uint lenRes, out uint numDistancePairs) + { + lenRes = 0; + numDistancePairs = _matchFinder.GetMatches(_matchDistances); + if (numDistancePairs > 0) + { + lenRes = _matchDistances[numDistancePairs - 2]; + if (lenRes == _numFastBytes) + { + lenRes += _matchFinder.GetMatchLen( + (int)lenRes - 1, + _matchDistances[numDistancePairs - 1], + Base.K_MATCH_MAX_LEN - lenRes + ); + } + } + _additionalOffset++; + } + + private void MovePos(uint num) + { + if (num > 0) + { + _matchFinder.Skip(num); + _additionalOffset += num; + } + } + + private uint GetRepLen1Price(Base.State state, uint posState) + { + return _isRepG0[state._index].GetPrice0() + + _isRep0Long[ + (state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState + ].GetPrice0(); + } + + private uint GetPureRepPrice(uint repIndex, Base.State state, uint posState) + { + uint price; + if (repIndex == 0) + { + price = _isRepG0[state._index].GetPrice0(); + price += _isRep0Long[ + (state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState + ].GetPrice1(); + } + else + { + price = _isRepG0[state._index].GetPrice1(); + if (repIndex == 1) + { + price += _isRepG1[state._index].GetPrice0(); + } + else + { + price += _isRepG1[state._index].GetPrice1(); + price += _isRepG2[state._index].GetPrice(repIndex - 2); + } + } + return price; + } + + private uint GetRepPrice(uint repIndex, uint len, Base.State state, uint posState) + { + var price = _repMatchLenEncoder.GetPrice(len - Base.K_MATCH_MIN_LEN, posState); + return price + GetPureRepPrice(repIndex, state, posState); + } + + private uint GetPosLenPrice(uint pos, uint len, uint posState) + { + uint price; + var lenToPosState = Base.GetLenToPosState(len); + if (pos < Base.K_NUM_FULL_DISTANCES) + { + price = _distancesPrices[(lenToPosState * Base.K_NUM_FULL_DISTANCES) + pos]; + } + else + { + price = + _posSlotPrices[(lenToPosState << Base.K_NUM_POS_SLOT_BITS) + GetPosSlot2(pos)] + + _alignPrices[pos & Base.K_ALIGN_MASK]; + } + return price + _lenEncoder.GetPrice(len - Base.K_MATCH_MIN_LEN, posState); + } + + private uint Backward(out uint backRes, uint cur) + { + _optimumEndIndex = cur; + var posMem = _optimum[cur]._posPrev; + var backMem = _optimum[cur]._backPrev; + do + { + if (_optimum[cur]._prev1IsChar) + { + _optimum[posMem].MakeAsChar(); + _optimum[posMem]._posPrev = posMem - 1; + if (_optimum[cur]._prev2) + { + _optimum[posMem - 1]._prev1IsChar = false; + _optimum[posMem - 1]._posPrev = _optimum[cur]._posPrev2; + _optimum[posMem - 1]._backPrev = _optimum[cur]._backPrev2; + } + } + var posPrev = posMem; + var backCur = backMem; + + backMem = _optimum[posPrev]._backPrev; + posMem = _optimum[posPrev]._posPrev; + + _optimum[posPrev]._backPrev = backCur; + _optimum[posPrev]._posPrev = cur; + cur = posPrev; + } while (cur > 0); + backRes = _optimum[0]._backPrev; + _optimumCurrentIndex = _optimum[0]._posPrev; + return _optimumCurrentIndex; + } + + private readonly uint[] _reps = new uint[Base.K_NUM_REP_DISTANCES]; + private readonly uint[] _repLens = new uint[Base.K_NUM_REP_DISTANCES]; + + private uint GetOptimum(uint position, out uint backRes) + { + if (_optimumEndIndex != _optimumCurrentIndex) + { + var lenRes = _optimum[_optimumCurrentIndex]._posPrev - _optimumCurrentIndex; + backRes = _optimum[_optimumCurrentIndex]._backPrev; + _optimumCurrentIndex = _optimum[_optimumCurrentIndex]._posPrev; + return lenRes; + } + _optimumCurrentIndex = _optimumEndIndex = 0; + + uint lenMain, + numDistancePairs; + if (!_longestMatchWasFound) + { + ReadMatchDistances(out lenMain, out numDistancePairs); + } + else + { + lenMain = _longestMatchLength; + numDistancePairs = _numDistancePairs; + _longestMatchWasFound = false; + } + + var numAvailableBytes = _matchFinder.GetNumAvailableBytes() + 1; + if (numAvailableBytes < 2) + { + backRes = 0xFFFFFFFF; + return 1; + } + if (numAvailableBytes > Base.K_MATCH_MAX_LEN) + { + numAvailableBytes = Base.K_MATCH_MAX_LEN; + } + + uint repMaxIndex = 0; + uint i; + for (i = 0; i < Base.K_NUM_REP_DISTANCES; i++) + { + _reps[i] = _repDistances[i]; + _repLens[i] = _matchFinder.GetMatchLen(0 - 1, _reps[i], Base.K_MATCH_MAX_LEN); + if (_repLens[i] > _repLens[repMaxIndex]) + { + repMaxIndex = i; + } + } + if (_repLens[repMaxIndex] >= _numFastBytes) + { + backRes = repMaxIndex; + var lenRes = _repLens[repMaxIndex]; + MovePos(lenRes - 1); + return lenRes; + } + + if (lenMain >= _numFastBytes) + { + backRes = _matchDistances[numDistancePairs - 1] + Base.K_NUM_REP_DISTANCES; + MovePos(lenMain - 1); + return lenMain; + } + + var currentByte = _matchFinder.GetIndexByte(0 - 1); + var matchByte = _matchFinder.GetIndexByte((int)(0 - _repDistances[0] - 1 - 1)); + + if (lenMain < 2 && currentByte != matchByte && _repLens[repMaxIndex] < 2) + { + backRes = 0xFFFFFFFF; + return 1; + } + + _optimum[0]._state = _state; + + var posState = (position & _posStateMask); + + _optimum[1]._price = + _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].GetPrice0() + + _literalEncoder + .GetSubCoder(position, _previousByte) + .GetPrice(!_state.IsCharState(), matchByte, currentByte); + _optimum[1].MakeAsChar(); + + var matchPrice = _isMatch[ + (_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState + ].GetPrice1(); + var repMatchPrice = matchPrice + _isRep[_state._index].GetPrice1(); + + if (matchByte == currentByte) + { + var shortRepPrice = repMatchPrice + GetRepLen1Price(_state, posState); + if (shortRepPrice < _optimum[1]._price) + { + _optimum[1]._price = shortRepPrice; + _optimum[1].MakeAsShortRep(); + } + } + + var lenEnd = ((lenMain >= _repLens[repMaxIndex]) ? lenMain : _repLens[repMaxIndex]); + + if (lenEnd < 2) + { + backRes = _optimum[1]._backPrev; + return 1; + } + + _optimum[1]._posPrev = 0; + + _optimum[0]._backs0 = _reps[0]; + _optimum[0]._backs1 = _reps[1]; + _optimum[0]._backs2 = _reps[2]; + _optimum[0]._backs3 = _reps[3]; + + var len = lenEnd; + do + { + _optimum[len--]._price = K_IFINITY_PRICE; + } while (len >= 2); + + for (i = 0; i < Base.K_NUM_REP_DISTANCES; i++) + { + var repLen = _repLens[i]; + if (repLen < 2) + { + continue; + } + var price = repMatchPrice + GetPureRepPrice(i, _state, posState); + do + { + var curAndLenPrice = + price + _repMatchLenEncoder.GetPrice(repLen - 2, posState); + var optimum = _optimum[repLen]; + if (curAndLenPrice < optimum._price) + { + optimum._price = curAndLenPrice; + optimum._posPrev = 0; + optimum._backPrev = i; + optimum._prev1IsChar = false; + } + } while (--repLen >= 2); + } + + var normalMatchPrice = matchPrice + _isRep[_state._index].GetPrice0(); + + len = ((_repLens[0] >= 2) ? _repLens[0] + 1 : 2); + if (len <= lenMain) + { + uint offs = 0; + while (len > _matchDistances[offs]) + { + offs += 2; + } + for (; ; len++) + { + var distance = _matchDistances[offs + 1]; + var curAndLenPrice = + normalMatchPrice + GetPosLenPrice(distance, len, posState); + var optimum = _optimum[len]; + if (curAndLenPrice < optimum._price) + { + optimum._price = curAndLenPrice; + optimum._posPrev = 0; + optimum._backPrev = distance + Base.K_NUM_REP_DISTANCES; + optimum._prev1IsChar = false; + } + if (len == _matchDistances[offs]) + { + offs += 2; + if (offs == numDistancePairs) { - state = _optimum[_optimum[cur]._posPrev2]._state; - if (_optimum[cur]._backPrev2 < Base.K_NUM_REP_DISTANCES) - { - state.UpdateRep(); - } - else - { - state.UpdateMatch(); - } + break; + } + } + } + } + + uint cur = 0; + + while (true) + { + cur++; + if (cur == lenEnd) + { + return Backward(out backRes, cur); + } + ReadMatchDistances(out var newLen, out numDistancePairs); + if (newLen >= _numFastBytes) + { + _numDistancePairs = numDistancePairs; + _longestMatchLength = newLen; + _longestMatchWasFound = true; + return Backward(out backRes, cur); + } + position++; + var posPrev = _optimum[cur]._posPrev; + Base.State state; + if (_optimum[cur]._prev1IsChar) + { + posPrev--; + if (_optimum[cur]._prev2) + { + state = _optimum[_optimum[cur]._posPrev2]._state; + if (_optimum[cur]._backPrev2 < Base.K_NUM_REP_DISTANCES) + { + state.UpdateRep(); } else { - state = _optimum[posPrev]._state; + state.UpdateMatch(); } - state.UpdateChar(); } else { state = _optimum[posPrev]._state; } - if (posPrev == cur - 1) + state.UpdateChar(); + } + else + { + state = _optimum[posPrev]._state; + } + if (posPrev == cur - 1) + { + if (_optimum[cur].IsShortRep()) { - if (_optimum[cur].IsShortRep()) - { - state.UpdateShortRep(); - } - else - { - state.UpdateChar(); - } + state.UpdateShortRep(); } else { - UInt32 pos; - if (_optimum[cur]._prev1IsChar && _optimum[cur]._prev2) + state.UpdateChar(); + } + } + else + { + uint pos; + if (_optimum[cur]._prev1IsChar && _optimum[cur]._prev2) + { + posPrev = _optimum[cur]._posPrev2; + pos = _optimum[cur]._backPrev2; + state.UpdateRep(); + } + else + { + pos = _optimum[cur]._backPrev; + if (pos < Base.K_NUM_REP_DISTANCES) { - posPrev = _optimum[cur]._posPrev2; - pos = _optimum[cur]._backPrev2; state.UpdateRep(); } else { - pos = _optimum[cur]._backPrev; - if (pos < Base.K_NUM_REP_DISTANCES) - { - state.UpdateRep(); - } - else - { - state.UpdateMatch(); - } + state.UpdateMatch(); } - Optimal opt = _optimum[posPrev]; - if (pos < Base.K_NUM_REP_DISTANCES) + } + var opt = _optimum[posPrev]; + if (pos < Base.K_NUM_REP_DISTANCES) + { + if (pos == 0) { - if (pos == 0) - { - _reps[0] = opt._backs0; - _reps[1] = opt._backs1; - _reps[2] = opt._backs2; - _reps[3] = opt._backs3; - } - else if (pos == 1) - { - _reps[0] = opt._backs1; - _reps[1] = opt._backs0; - _reps[2] = opt._backs2; - _reps[3] = opt._backs3; - } - else if (pos == 2) - { - _reps[0] = opt._backs2; - _reps[1] = opt._backs0; - _reps[2] = opt._backs1; - _reps[3] = opt._backs3; - } - else - { - _reps[0] = opt._backs3; - _reps[1] = opt._backs0; - _reps[2] = opt._backs1; - _reps[3] = opt._backs2; - } + _reps[0] = opt._backs0; + _reps[1] = opt._backs1; + _reps[2] = opt._backs2; + _reps[3] = opt._backs3; + } + else if (pos == 1) + { + _reps[0] = opt._backs1; + _reps[1] = opt._backs0; + _reps[2] = opt._backs2; + _reps[3] = opt._backs3; + } + else if (pos == 2) + { + _reps[0] = opt._backs2; + _reps[1] = opt._backs0; + _reps[2] = opt._backs1; + _reps[3] = opt._backs3; } else { - _reps[0] = (pos - Base.K_NUM_REP_DISTANCES); + _reps[0] = opt._backs3; _reps[1] = opt._backs0; _reps[2] = opt._backs1; _reps[3] = opt._backs2; } } - _optimum[cur]._state = state; - _optimum[cur]._backs0 = _reps[0]; - _optimum[cur]._backs1 = _reps[1]; - _optimum[cur]._backs2 = _reps[2]; - _optimum[cur]._backs3 = _reps[3]; - UInt32 curPrice = _optimum[cur]._price; - - currentByte = _matchFinder.GetIndexByte(0 - 1); - matchByte = _matchFinder.GetIndexByte((Int32)(0 - _reps[0] - 1 - 1)); - - posState = (position & _posStateMask); - - UInt32 curAnd1Price = - curPrice - + _isMatch[ - (state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState - ].GetPrice0() - + _literalEncoder - .GetSubCoder(position, _matchFinder.GetIndexByte(0 - 2)) - .GetPrice(!state.IsCharState(), matchByte, currentByte); - - Optimal nextOptimum = _optimum[cur + 1]; - - bool nextIsChar = false; - if (curAnd1Price < nextOptimum._price) + else { - nextOptimum._price = curAnd1Price; + _reps[0] = (pos - Base.K_NUM_REP_DISTANCES); + _reps[1] = opt._backs0; + _reps[2] = opt._backs1; + _reps[3] = opt._backs2; + } + } + _optimum[cur]._state = state; + _optimum[cur]._backs0 = _reps[0]; + _optimum[cur]._backs1 = _reps[1]; + _optimum[cur]._backs2 = _reps[2]; + _optimum[cur]._backs3 = _reps[3]; + var curPrice = _optimum[cur]._price; + + currentByte = _matchFinder.GetIndexByte(0 - 1); + matchByte = _matchFinder.GetIndexByte((int)(0 - _reps[0] - 1 - 1)); + + posState = (position & _posStateMask); + + var curAnd1Price = + curPrice + + _isMatch[ + (state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState + ].GetPrice0() + + _literalEncoder + .GetSubCoder(position, _matchFinder.GetIndexByte(0 - 2)) + .GetPrice(!state.IsCharState(), matchByte, currentByte); + + var nextOptimum = _optimum[cur + 1]; + + var nextIsChar = false; + if (curAnd1Price < nextOptimum._price) + { + nextOptimum._price = curAnd1Price; + nextOptimum._posPrev = cur; + nextOptimum.MakeAsChar(); + nextIsChar = true; + } + + matchPrice = + curPrice + + _isMatch[ + (state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState + ].GetPrice1(); + repMatchPrice = matchPrice + _isRep[state._index].GetPrice1(); + + if ( + matchByte == currentByte + && !(nextOptimum._posPrev < cur && nextOptimum._backPrev == 0) + ) + { + var shortRepPrice = repMatchPrice + GetRepLen1Price(state, posState); + if (shortRepPrice <= nextOptimum._price) + { + nextOptimum._price = shortRepPrice; nextOptimum._posPrev = cur; - nextOptimum.MakeAsChar(); + nextOptimum.MakeAsShortRep(); nextIsChar = true; } + } - matchPrice = - curPrice - + _isMatch[ - (state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState - ].GetPrice1(); - repMatchPrice = matchPrice + _isRep[state._index].GetPrice1(); + var numAvailableBytesFull = _matchFinder.GetNumAvailableBytes() + 1; + numAvailableBytesFull = Math.Min(K_NUM_OPTS - 1 - cur, numAvailableBytesFull); + numAvailableBytes = numAvailableBytesFull; - if ( - matchByte == currentByte - && !(nextOptimum._posPrev < cur && nextOptimum._backPrev == 0) - ) + if (numAvailableBytes < 2) + { + continue; + } + if (numAvailableBytes > _numFastBytes) + { + numAvailableBytes = _numFastBytes; + } + if (!nextIsChar && matchByte != currentByte) + { + // try Literal + rep0 + var t = Math.Min(numAvailableBytesFull - 1, _numFastBytes); + var lenTest2 = _matchFinder.GetMatchLen(0, _reps[0], t); + if (lenTest2 >= 2) { - UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(state, posState); - if (shortRepPrice <= nextOptimum._price) + var state2 = state; + state2.UpdateChar(); + var posStateNext = (position + 1) & _posStateMask; + var nextRepMatchPrice = + curAnd1Price + + _isMatch[ + (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext + ].GetPrice1() + + _isRep[state2._index].GetPrice1(); { - nextOptimum._price = shortRepPrice; - nextOptimum._posPrev = cur; - nextOptimum.MakeAsShortRep(); - nextIsChar = true; - } - } - - UInt32 numAvailableBytesFull = _matchFinder.GetNumAvailableBytes() + 1; - numAvailableBytesFull = Math.Min(K_NUM_OPTS - 1 - cur, numAvailableBytesFull); - numAvailableBytes = numAvailableBytesFull; - - if (numAvailableBytes < 2) - { - continue; - } - if (numAvailableBytes > _numFastBytes) - { - numAvailableBytes = _numFastBytes; - } - if (!nextIsChar && matchByte != currentByte) - { - // try Literal + rep0 - UInt32 t = Math.Min(numAvailableBytesFull - 1, _numFastBytes); - UInt32 lenTest2 = _matchFinder.GetMatchLen(0, _reps[0], t); - if (lenTest2 >= 2) - { - Base.State state2 = state; - state2.UpdateChar(); - UInt32 posStateNext = (position + 1) & _posStateMask; - UInt32 nextRepMatchPrice = - curAnd1Price - + _isMatch[ - (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext - ].GetPrice1() - + _isRep[state2._index].GetPrice1(); - { - UInt32 offset = cur + 1 + lenTest2; - while (lenEnd < offset) - { - _optimum[++lenEnd]._price = K_IFINITY_PRICE; - } - UInt32 curAndLenPrice = - nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); - Optimal optimum = _optimum[offset]; - if (curAndLenPrice < optimum._price) - { - optimum._price = curAndLenPrice; - optimum._posPrev = cur + 1; - optimum._backPrev = 0; - optimum._prev1IsChar = true; - optimum._prev2 = false; - } - } - } - } - - UInt32 startLen = 2; // speed optimization - - for (UInt32 repIndex = 0; repIndex < Base.K_NUM_REP_DISTANCES; repIndex++) - { - UInt32 lenTest = _matchFinder.GetMatchLen( - 0 - 1, - _reps[repIndex], - numAvailableBytes - ); - if (lenTest < 2) - { - continue; - } - UInt32 lenTestTemp = lenTest; - do - { - while (lenEnd < cur + lenTest) + var offset = cur + 1 + lenTest2; + while (lenEnd < offset) { _optimum[++lenEnd]._price = K_IFINITY_PRICE; } - UInt32 curAndLenPrice = - repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState); - Optimal optimum = _optimum[cur + lenTest]; + var curAndLenPrice = + nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); + var optimum = _optimum[offset]; if (curAndLenPrice < optimum._price) { optimum._price = curAndLenPrice; - optimum._posPrev = cur; - optimum._backPrev = repIndex; - optimum._prev1IsChar = false; + optimum._posPrev = cur + 1; + optimum._backPrev = 0; + optimum._prev1IsChar = true; + optimum._prev2 = false; } - } while (--lenTest >= 2); - lenTest = lenTestTemp; + } + } + } - if (repIndex == 0) + uint startLen = 2; // speed optimization + + for (uint repIndex = 0; repIndex < Base.K_NUM_REP_DISTANCES; repIndex++) + { + var lenTest = _matchFinder.GetMatchLen( + 0 - 1, + _reps[repIndex], + numAvailableBytes + ); + if (lenTest < 2) + { + continue; + } + var lenTestTemp = lenTest; + do + { + while (lenEnd < cur + lenTest) { - startLen = lenTest + 1; + _optimum[++lenEnd]._price = K_IFINITY_PRICE; + } + var curAndLenPrice = + repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState); + var optimum = _optimum[cur + lenTest]; + if (curAndLenPrice < optimum._price) + { + optimum._price = curAndLenPrice; + optimum._posPrev = cur; + optimum._backPrev = repIndex; + optimum._prev1IsChar = false; + } + } while (--lenTest >= 2); + lenTest = lenTestTemp; + + if (repIndex == 0) + { + startLen = lenTest + 1; + } + + // if (_maxMode) + if (lenTest < numAvailableBytesFull) + { + var t = Math.Min(numAvailableBytesFull - 1 - lenTest, _numFastBytes); + var lenTest2 = _matchFinder.GetMatchLen( + (int)lenTest, + _reps[repIndex], + t + ); + if (lenTest2 >= 2) + { + var state2 = state; + state2.UpdateRep(); + var posStateNext = (position + lenTest) & _posStateMask; + var curAndLenCharPrice = + repMatchPrice + + GetRepPrice(repIndex, lenTest, state, posState) + + _isMatch[ + (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext + ].GetPrice0() + + _literalEncoder + .GetSubCoder( + position + lenTest, + _matchFinder.GetIndexByte((int)lenTest - 1 - 1) + ) + .GetPrice( + true, + _matchFinder.GetIndexByte( + (int)lenTest - 1 - (int)(_reps[repIndex] + 1) + ), + _matchFinder.GetIndexByte((int)lenTest - 1) + ); + state2.UpdateChar(); + posStateNext = (position + lenTest + 1) & _posStateMask; + var nextMatchPrice = + curAndLenCharPrice + + _isMatch[ + (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext + ].GetPrice1(); + var nextRepMatchPrice = + nextMatchPrice + _isRep[state2._index].GetPrice1(); + + // for(; lenTest2 >= 2; lenTest2--) + { + var offset = lenTest + 1 + lenTest2; + while (lenEnd < cur + offset) + { + _optimum[++lenEnd]._price = K_IFINITY_PRICE; + } + var curAndLenPrice = + nextRepMatchPrice + + GetRepPrice(0, lenTest2, state2, posStateNext); + var optimum = _optimum[cur + offset]; + if (curAndLenPrice < optimum._price) + { + optimum._price = curAndLenPrice; + optimum._posPrev = cur + lenTest + 1; + optimum._backPrev = 0; + optimum._prev1IsChar = true; + optimum._prev2 = true; + optimum._posPrev2 = cur; + optimum._backPrev2 = repIndex; + } + } + } + } + } + + if (newLen > numAvailableBytes) + { + newLen = numAvailableBytes; + for ( + numDistancePairs = 0; + newLen > _matchDistances[numDistancePairs]; + numDistancePairs += 2 + ) + { + ; + } + _matchDistances[numDistancePairs] = newLen; + numDistancePairs += 2; + } + if (newLen >= startLen) + { + normalMatchPrice = matchPrice + _isRep[state._index].GetPrice0(); + while (lenEnd < cur + newLen) + { + _optimum[++lenEnd]._price = K_IFINITY_PRICE; + } + + uint offs = 0; + while (startLen > _matchDistances[offs]) + { + offs += 2; + } + + for (var lenTest = startLen; ; lenTest++) + { + var curBack = _matchDistances[offs + 1]; + var curAndLenPrice = + normalMatchPrice + GetPosLenPrice(curBack, lenTest, posState); + var optimum = _optimum[cur + lenTest]; + if (curAndLenPrice < optimum._price) + { + optimum._price = curAndLenPrice; + optimum._posPrev = cur; + optimum._backPrev = curBack + Base.K_NUM_REP_DISTANCES; + optimum._prev1IsChar = false; } - // if (_maxMode) - if (lenTest < numAvailableBytesFull) + if (lenTest == _matchDistances[offs]) { - UInt32 t = Math.Min(numAvailableBytesFull - 1 - lenTest, _numFastBytes); - UInt32 lenTest2 = _matchFinder.GetMatchLen( - (Int32)lenTest, - _reps[repIndex], - t - ); - if (lenTest2 >= 2) + if (lenTest < numAvailableBytesFull) { - Base.State state2 = state; - state2.UpdateRep(); - UInt32 posStateNext = (position + lenTest) & _posStateMask; - UInt32 curAndLenCharPrice = - repMatchPrice - + GetRepPrice(repIndex, lenTest, state, posState) - + _isMatch[ - (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext - ].GetPrice0() - + _literalEncoder - .GetSubCoder( - position + lenTest, - _matchFinder.GetIndexByte((Int32)lenTest - 1 - 1) - ) - .GetPrice( - true, - _matchFinder.GetIndexByte( - (Int32)lenTest - 1 - (Int32)(_reps[repIndex] + 1) - ), - _matchFinder.GetIndexByte((Int32)lenTest - 1) - ); - state2.UpdateChar(); - posStateNext = (position + lenTest + 1) & _posStateMask; - UInt32 nextMatchPrice = - curAndLenCharPrice - + _isMatch[ - (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext - ].GetPrice1(); - UInt32 nextRepMatchPrice = - nextMatchPrice + _isRep[state2._index].GetPrice1(); - - // for(; lenTest2 >= 2; lenTest2--) + var t = Math.Min( + numAvailableBytesFull - 1 - lenTest, + _numFastBytes + ); + var lenTest2 = _matchFinder.GetMatchLen( + (int)lenTest, + curBack, + t + ); + if (lenTest2 >= 2) { - UInt32 offset = lenTest + 1 + lenTest2; + var state2 = state; + state2.UpdateMatch(); + var posStateNext = (position + lenTest) & _posStateMask; + var curAndLenCharPrice = + curAndLenPrice + + _isMatch[ + (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + + posStateNext + ].GetPrice0() + + _literalEncoder + .GetSubCoder( + position + lenTest, + _matchFinder.GetIndexByte((int)lenTest - 1 - 1) + ) + .GetPrice( + true, + _matchFinder.GetIndexByte( + (int)lenTest - (int)(curBack + 1) - 1 + ), + _matchFinder.GetIndexByte((int)lenTest - 1) + ); + state2.UpdateChar(); + posStateNext = (position + lenTest + 1) & _posStateMask; + var nextMatchPrice = + curAndLenCharPrice + + _isMatch[ + (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + + posStateNext + ].GetPrice1(); + var nextRepMatchPrice = + nextMatchPrice + _isRep[state2._index].GetPrice1(); + + var offset = lenTest + 1 + lenTest2; while (lenEnd < cur + offset) { _optimum[++lenEnd]._price = K_IFINITY_PRICE; } - UInt32 curAndLenPrice = + curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); - Optimal optimum = _optimum[cur + offset]; + optimum = _optimum[cur + offset]; if (curAndLenPrice < optimum._price) { optimum._price = curAndLenPrice; @@ -1153,238 +1265,98 @@ namespace SharpCompress.Compressors.LZMA optimum._prev1IsChar = true; optimum._prev2 = true; optimum._posPrev2 = cur; - optimum._backPrev2 = repIndex; + optimum._backPrev2 = curBack + Base.K_NUM_REP_DISTANCES; } } } - } - } - - if (newLen > numAvailableBytes) - { - newLen = numAvailableBytes; - for ( - numDistancePairs = 0; - newLen > _matchDistances[numDistancePairs]; - numDistancePairs += 2 - ) - { - ; - } - _matchDistances[numDistancePairs] = newLen; - numDistancePairs += 2; - } - if (newLen >= startLen) - { - normalMatchPrice = matchPrice + _isRep[state._index].GetPrice0(); - while (lenEnd < cur + newLen) - { - _optimum[++lenEnd]._price = K_IFINITY_PRICE; - } - - UInt32 offs = 0; - while (startLen > _matchDistances[offs]) - { offs += 2; - } - - for (UInt32 lenTest = startLen; ; lenTest++) - { - UInt32 curBack = _matchDistances[offs + 1]; - UInt32 curAndLenPrice = - normalMatchPrice + GetPosLenPrice(curBack, lenTest, posState); - Optimal optimum = _optimum[cur + lenTest]; - if (curAndLenPrice < optimum._price) + if (offs == numDistancePairs) { - optimum._price = curAndLenPrice; - optimum._posPrev = cur; - optimum._backPrev = curBack + Base.K_NUM_REP_DISTANCES; - optimum._prev1IsChar = false; - } - - if (lenTest == _matchDistances[offs]) - { - if (lenTest < numAvailableBytesFull) - { - UInt32 t = Math.Min( - numAvailableBytesFull - 1 - lenTest, - _numFastBytes - ); - UInt32 lenTest2 = _matchFinder.GetMatchLen( - (Int32)lenTest, - curBack, - t - ); - if (lenTest2 >= 2) - { - Base.State state2 = state; - state2.UpdateMatch(); - UInt32 posStateNext = (position + lenTest) & _posStateMask; - UInt32 curAndLenCharPrice = - curAndLenPrice - + _isMatch[ - (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) - + posStateNext - ].GetPrice0() - + _literalEncoder - .GetSubCoder( - position + lenTest, - _matchFinder.GetIndexByte((Int32)lenTest - 1 - 1) - ) - .GetPrice( - true, - _matchFinder.GetIndexByte( - (Int32)lenTest - (Int32)(curBack + 1) - 1 - ), - _matchFinder.GetIndexByte((Int32)lenTest - 1) - ); - state2.UpdateChar(); - posStateNext = (position + lenTest + 1) & _posStateMask; - UInt32 nextMatchPrice = - curAndLenCharPrice - + _isMatch[ - (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) - + posStateNext - ].GetPrice1(); - UInt32 nextRepMatchPrice = - nextMatchPrice + _isRep[state2._index].GetPrice1(); - - UInt32 offset = lenTest + 1 + lenTest2; - while (lenEnd < cur + offset) - { - _optimum[++lenEnd]._price = K_IFINITY_PRICE; - } - curAndLenPrice = - nextRepMatchPrice - + GetRepPrice(0, lenTest2, state2, posStateNext); - optimum = _optimum[cur + offset]; - if (curAndLenPrice < optimum._price) - { - optimum._price = curAndLenPrice; - optimum._posPrev = cur + lenTest + 1; - optimum._backPrev = 0; - optimum._prev1IsChar = true; - optimum._prev2 = true; - optimum._posPrev2 = cur; - optimum._backPrev2 = curBack + Base.K_NUM_REP_DISTANCES; - } - } - } - offs += 2; - if (offs == numDistancePairs) - { - break; - } + break; } } } } } + } - private bool ChangePair(UInt32 smallDist, UInt32 bigDist) + private bool ChangePair(uint smallDist, uint bigDist) + { + const int kDif = 7; + return (smallDist < ((uint)(1) << (32 - kDif)) && bigDist >= (smallDist << kDif)); + } + + private void WriteEndMarker(uint posState) + { + if (!_writeEndMark) { - const int kDif = 7; - return (smallDist < ((UInt32)(1) << (32 - kDif)) && bigDist >= (smallDist << kDif)); + return; } - private void WriteEndMarker(UInt32 posState) - { - if (!_writeEndMark) - { - return; - } + _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].Encode( + _rangeEncoder, + 1 + ); + _isRep[_state._index].Encode(_rangeEncoder, 0); + _state.UpdateMatch(); + var len = Base.K_MATCH_MIN_LEN; + _lenEncoder.Encode(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState); + uint posSlot = (1 << Base.K_NUM_POS_SLOT_BITS) - 1; + var lenToPosState = Base.GetLenToPosState(len); + _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot); + var footerBits = 30; + var posReduced = (((uint)1) << footerBits) - 1; + _rangeEncoder.EncodeDirectBits( + posReduced >> Base.K_NUM_ALIGN_BITS, + footerBits - Base.K_NUM_ALIGN_BITS + ); + _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.K_ALIGN_MASK); + } - _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].Encode( - _rangeEncoder, - 1 - ); - _isRep[_state._index].Encode(_rangeEncoder, 0); - _state.UpdateMatch(); - UInt32 len = Base.K_MATCH_MIN_LEN; - _lenEncoder.Encode(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState); - UInt32 posSlot = (1 << Base.K_NUM_POS_SLOT_BITS) - 1; - UInt32 lenToPosState = Base.GetLenToPosState(len); - _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot); - int footerBits = 30; - UInt32 posReduced = (((UInt32)1) << footerBits) - 1; - _rangeEncoder.EncodeDirectBits( - posReduced >> Base.K_NUM_ALIGN_BITS, - footerBits - Base.K_NUM_ALIGN_BITS - ); - _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.K_ALIGN_MASK); + private void Flush(uint nowPos) + { + ReleaseMfStream(); + WriteEndMarker(nowPos & _posStateMask); + _rangeEncoder.FlushData(); + _rangeEncoder.FlushStream(); + } + + public void CodeOneBlock(out long inSize, out long outSize, out bool finished) + { + inSize = 0; + outSize = 0; + finished = true; + + if (_inStream != null) + { + _matchFinder.SetStream(_inStream); + _needReleaseMfStream = true; + _inStream = null; } - private void Flush(UInt32 nowPos) + if (_finished) { - ReleaseMfStream(); - WriteEndMarker(nowPos & _posStateMask); - _rangeEncoder.FlushData(); - _rangeEncoder.FlushStream(); + return; } + _finished = true; - public void CodeOneBlock(out Int64 inSize, out Int64 outSize, out bool finished) + var progressPosValuePrev = _nowPos64; + if (_nowPos64 == 0) { - inSize = 0; - outSize = 0; - finished = true; - - if (_inStream != null) + if (_trainSize > 0) { - _matchFinder.SetStream(_inStream); - _needReleaseMfStream = true; - _inStream = null; - } - - if (_finished) - { - return; - } - _finished = true; - - Int64 progressPosValuePrev = _nowPos64; - if (_nowPos64 == 0) - { - if (_trainSize > 0) + for ( + ; + _trainSize > 0 && (!_processingMode || !_matchFinder.IsDataStarved); + _trainSize-- + ) { - for ( - ; - _trainSize > 0 && (!_processingMode || !_matchFinder.IsDataStarved); - _trainSize-- - ) - { - _matchFinder.Skip(1); - } - if (_trainSize == 0) - { - _previousByte = _matchFinder.GetIndexByte(-1); - } + _matchFinder.Skip(1); } - if (_processingMode && _matchFinder.IsDataStarved) + if (_trainSize == 0) { - _finished = false; - return; + _previousByte = _matchFinder.GetIndexByte(-1); } - if (_matchFinder.GetNumAvailableBytes() == 0) - { - Flush((UInt32)_nowPos64); - return; - } - UInt32 len, - numDistancePairs; // it's not used - ReadMatchDistances(out len, out numDistancePairs); - UInt32 posState = (UInt32)(_nowPos64) & _posStateMask; - _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].Encode( - _rangeEncoder, - 0 - ); - _state.UpdateChar(); - Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset)); - _literalEncoder - .GetSubCoder((UInt32)(_nowPos64), _previousByte) - .Encode(_rangeEncoder, curByte); - _previousByte = curByte; - _additionalOffset--; - _nowPos64++; } if (_processingMode && _matchFinder.IsDataStarved) { @@ -1393,561 +1365,576 @@ namespace SharpCompress.Compressors.LZMA } if (_matchFinder.GetNumAvailableBytes() == 0) { - Flush((UInt32)_nowPos64); + Flush((uint)_nowPos64); return; } - while (true) + // it's not used + ReadMatchDistances(out var len, out var numDistancePairs); + var posState = (uint)(_nowPos64) & _posStateMask; + _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].Encode( + _rangeEncoder, + 0 + ); + _state.UpdateChar(); + var curByte = _matchFinder.GetIndexByte((int)(0 - _additionalOffset)); + _literalEncoder + .GetSubCoder((uint)(_nowPos64), _previousByte) + .Encode(_rangeEncoder, curByte); + _previousByte = curByte; + _additionalOffset--; + _nowPos64++; + } + if (_processingMode && _matchFinder.IsDataStarved) + { + _finished = false; + return; + } + if (_matchFinder.GetNumAvailableBytes() == 0) + { + Flush((uint)_nowPos64); + return; + } + while (true) + { + if (_processingMode && _matchFinder.IsDataStarved) { + _finished = false; + return; + } + + var len = GetOptimum((uint)_nowPos64, out var pos); + + var posState = ((uint)_nowPos64) & _posStateMask; + var complexState = (_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState; + if (len == 1 && pos == 0xFFFFFFFF) + { + _isMatch[complexState].Encode(_rangeEncoder, 0); + var curByte = _matchFinder.GetIndexByte((int)(0 - _additionalOffset)); + var subCoder = _literalEncoder.GetSubCoder( + (uint)_nowPos64, + _previousByte + ); + if (!_state.IsCharState()) + { + var matchByte = _matchFinder.GetIndexByte( + (int)(0 - _repDistances[0] - 1 - _additionalOffset) + ); + subCoder.EncodeMatched(_rangeEncoder, matchByte, curByte); + } + else + { + subCoder.Encode(_rangeEncoder, curByte); + } + _previousByte = curByte; + _state.UpdateChar(); + } + else + { + _isMatch[complexState].Encode(_rangeEncoder, 1); + if (pos < Base.K_NUM_REP_DISTANCES) + { + _isRep[_state._index].Encode(_rangeEncoder, 1); + if (pos == 0) + { + _isRepG0[_state._index].Encode(_rangeEncoder, 0); + if (len == 1) + { + _isRep0Long[complexState].Encode(_rangeEncoder, 0); + } + else + { + _isRep0Long[complexState].Encode(_rangeEncoder, 1); + } + } + else + { + _isRepG0[_state._index].Encode(_rangeEncoder, 1); + if (pos == 1) + { + _isRepG1[_state._index].Encode(_rangeEncoder, 0); + } + else + { + _isRepG1[_state._index].Encode(_rangeEncoder, 1); + _isRepG2[_state._index].Encode(_rangeEncoder, pos - 2); + } + } + if (len == 1) + { + _state.UpdateShortRep(); + } + else + { + _repMatchLenEncoder.Encode( + _rangeEncoder, + len - Base.K_MATCH_MIN_LEN, + posState + ); + _state.UpdateRep(); + } + var distance = _repDistances[pos]; + if (pos != 0) + { + for (var i = pos; i >= 1; i--) + { + _repDistances[i] = _repDistances[i - 1]; + } + _repDistances[0] = distance; + } + } + else + { + _isRep[_state._index].Encode(_rangeEncoder, 0); + _state.UpdateMatch(); + _lenEncoder.Encode(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState); + pos -= Base.K_NUM_REP_DISTANCES; + var posSlot = GetPosSlot(pos); + var lenToPosState = Base.GetLenToPosState(len); + _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot); + + if (posSlot >= Base.K_START_POS_MODEL_INDEX) + { + var footerBits = (int)((posSlot >> 1) - 1); + var baseVal = ((2 | (posSlot & 1)) << footerBits); + var posReduced = pos - baseVal; + + if (posSlot < Base.K_END_POS_MODEL_INDEX) + { + BitTreeEncoder.ReverseEncode( + _posEncoders, + baseVal - posSlot - 1, + _rangeEncoder, + footerBits, + posReduced + ); + } + else + { + _rangeEncoder.EncodeDirectBits( + posReduced >> Base.K_NUM_ALIGN_BITS, + footerBits - Base.K_NUM_ALIGN_BITS + ); + _posAlignEncoder.ReverseEncode( + _rangeEncoder, + posReduced & Base.K_ALIGN_MASK + ); + _alignPriceCount++; + } + } + var distance = pos; + for (var i = Base.K_NUM_REP_DISTANCES - 1; i >= 1; i--) + { + _repDistances[i] = _repDistances[i - 1]; + } + _repDistances[0] = distance; + _matchPriceCount++; + } + _previousByte = _matchFinder.GetIndexByte((int)(len - 1 - _additionalOffset)); + } + _additionalOffset -= len; + _nowPos64 += len; + if (_additionalOffset == 0) + { + // if (!_fastMode) + if (_matchPriceCount >= (1 << 7)) + { + FillDistancesPrices(); + } + if (_alignPriceCount >= Base.K_ALIGN_TABLE_SIZE) + { + FillAlignPrices(); + } + inSize = _nowPos64; + outSize = _rangeEncoder.GetProcessedSizeAdd(); if (_processingMode && _matchFinder.IsDataStarved) { _finished = false; return; } - - UInt32 pos; - UInt32 len = GetOptimum((UInt32)_nowPos64, out pos); - - UInt32 posState = ((UInt32)_nowPos64) & _posStateMask; - UInt32 complexState = (_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState; - if (len == 1 && pos == 0xFFFFFFFF) + if (_matchFinder.GetNumAvailableBytes() == 0) { - _isMatch[complexState].Encode(_rangeEncoder, 0); - Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset)); - LiteralEncoder.Encoder2 subCoder = _literalEncoder.GetSubCoder( - (UInt32)_nowPos64, - _previousByte - ); - if (!_state.IsCharState()) - { - Byte matchByte = _matchFinder.GetIndexByte( - (Int32)(0 - _repDistances[0] - 1 - _additionalOffset) - ); - subCoder.EncodeMatched(_rangeEncoder, matchByte, curByte); - } - else - { - subCoder.Encode(_rangeEncoder, curByte); - } - _previousByte = curByte; - _state.UpdateChar(); + Flush((uint)_nowPos64); + return; } - else + + if (_nowPos64 - progressPosValuePrev >= (1 << 12)) { - _isMatch[complexState].Encode(_rangeEncoder, 1); - if (pos < Base.K_NUM_REP_DISTANCES) - { - _isRep[_state._index].Encode(_rangeEncoder, 1); - if (pos == 0) - { - _isRepG0[_state._index].Encode(_rangeEncoder, 0); - if (len == 1) - { - _isRep0Long[complexState].Encode(_rangeEncoder, 0); - } - else - { - _isRep0Long[complexState].Encode(_rangeEncoder, 1); - } - } - else - { - _isRepG0[_state._index].Encode(_rangeEncoder, 1); - if (pos == 1) - { - _isRepG1[_state._index].Encode(_rangeEncoder, 0); - } - else - { - _isRepG1[_state._index].Encode(_rangeEncoder, 1); - _isRepG2[_state._index].Encode(_rangeEncoder, pos - 2); - } - } - if (len == 1) - { - _state.UpdateShortRep(); - } - else - { - _repMatchLenEncoder.Encode( - _rangeEncoder, - len - Base.K_MATCH_MIN_LEN, - posState - ); - _state.UpdateRep(); - } - UInt32 distance = _repDistances[pos]; - if (pos != 0) - { - for (UInt32 i = pos; i >= 1; i--) - { - _repDistances[i] = _repDistances[i - 1]; - } - _repDistances[0] = distance; - } - } - else - { - _isRep[_state._index].Encode(_rangeEncoder, 0); - _state.UpdateMatch(); - _lenEncoder.Encode(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState); - pos -= Base.K_NUM_REP_DISTANCES; - UInt32 posSlot = GetPosSlot(pos); - UInt32 lenToPosState = Base.GetLenToPosState(len); - _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot); - - if (posSlot >= Base.K_START_POS_MODEL_INDEX) - { - int footerBits = (int)((posSlot >> 1) - 1); - UInt32 baseVal = ((2 | (posSlot & 1)) << footerBits); - UInt32 posReduced = pos - baseVal; - - if (posSlot < Base.K_END_POS_MODEL_INDEX) - { - BitTreeEncoder.ReverseEncode( - _posEncoders, - baseVal - posSlot - 1, - _rangeEncoder, - footerBits, - posReduced - ); - } - else - { - _rangeEncoder.EncodeDirectBits( - posReduced >> Base.K_NUM_ALIGN_BITS, - footerBits - Base.K_NUM_ALIGN_BITS - ); - _posAlignEncoder.ReverseEncode( - _rangeEncoder, - posReduced & Base.K_ALIGN_MASK - ); - _alignPriceCount++; - } - } - UInt32 distance = pos; - for (UInt32 i = Base.K_NUM_REP_DISTANCES - 1; i >= 1; i--) - { - _repDistances[i] = _repDistances[i - 1]; - } - _repDistances[0] = distance; - _matchPriceCount++; - } - _previousByte = _matchFinder.GetIndexByte((Int32)(len - 1 - _additionalOffset)); - } - _additionalOffset -= len; - _nowPos64 += len; - if (_additionalOffset == 0) - { - // if (!_fastMode) - if (_matchPriceCount >= (1 << 7)) - { - FillDistancesPrices(); - } - if (_alignPriceCount >= Base.K_ALIGN_TABLE_SIZE) - { - FillAlignPrices(); - } - inSize = _nowPos64; - outSize = _rangeEncoder.GetProcessedSizeAdd(); - if (_processingMode && _matchFinder.IsDataStarved) - { - _finished = false; - return; - } - if (_matchFinder.GetNumAvailableBytes() == 0) - { - Flush((UInt32)_nowPos64); - return; - } - - if (_nowPos64 - progressPosValuePrev >= (1 << 12)) - { - _finished = false; - finished = false; - return; - } + _finished = false; + finished = false; + return; } } } + } - private void ReleaseMfStream() - { - if (_matchFinder != null && _needReleaseMfStream) - { - _matchFinder.ReleaseStream(); - _needReleaseMfStream = false; - } - } - - private void SetOutStream(Stream outStream) - { - _rangeEncoder.SetStream(outStream); - } - - private void ReleaseOutStream() - { - _rangeEncoder.ReleaseStream(); - } - - private void ReleaseStreams() - { - ReleaseMfStream(); - ReleaseOutStream(); - } - - public void SetStreams(Stream inStream, Stream outStream, Int64 inSize, Int64 outSize) - { - _inStream = inStream; - _finished = false; - Create(); - SetOutStream(outStream); - Init(); - _matchFinder.Init(); - - // if (!_fastMode) - { - FillDistancesPrices(); - FillAlignPrices(); - } - - _lenEncoder.SetTableSize(_numFastBytes + 1 - Base.K_MATCH_MIN_LEN); - _lenEncoder.UpdateTables((UInt32)1 << _posStateBits); - _repMatchLenEncoder.SetTableSize(_numFastBytes + 1 - Base.K_MATCH_MIN_LEN); - _repMatchLenEncoder.UpdateTables((UInt32)1 << _posStateBits); - - _nowPos64 = 0; - } - - public void Code( - Stream inStream, - Stream outStream, - Int64 inSize, - Int64 outSize, - ICodeProgress progress - ) + private void ReleaseMfStream() + { + if (_matchFinder != null && _needReleaseMfStream) { + _matchFinder.ReleaseStream(); _needReleaseMfStream = false; - _processingMode = false; - try + } + } + + private void SetOutStream(Stream outStream) + { + _rangeEncoder.SetStream(outStream); + } + + private void ReleaseOutStream() + { + _rangeEncoder.ReleaseStream(); + } + + private void ReleaseStreams() + { + ReleaseMfStream(); + ReleaseOutStream(); + } + + public void SetStreams(Stream inStream, Stream outStream, long inSize, long outSize) + { + _inStream = inStream; + _finished = false; + Create(); + SetOutStream(outStream); + Init(); + _matchFinder.Init(); + + // if (!_fastMode) + { + FillDistancesPrices(); + FillAlignPrices(); + } + + _lenEncoder.SetTableSize(_numFastBytes + 1 - Base.K_MATCH_MIN_LEN); + _lenEncoder.UpdateTables((uint)1 << _posStateBits); + _repMatchLenEncoder.SetTableSize(_numFastBytes + 1 - Base.K_MATCH_MIN_LEN); + _repMatchLenEncoder.UpdateTables((uint)1 << _posStateBits); + + _nowPos64 = 0; + } + + public void Code( + Stream inStream, + Stream outStream, + long inSize, + long outSize, + ICodeProgress progress + ) + { + _needReleaseMfStream = false; + _processingMode = false; + try + { + SetStreams(inStream, outStream, inSize, outSize); + while (true) { - SetStreams(inStream, outStream, inSize, outSize); - while (true) + CodeOneBlock(out var processedInSize, out var processedOutSize, out var finished); + if (finished) { - Int64 processedInSize; - Int64 processedOutSize; - bool finished; - CodeOneBlock(out processedInSize, out processedOutSize, out finished); - if (finished) - { - return; - } - if (progress != null) - { - progress.SetProgress(processedInSize, processedOutSize); - } + return; + } + progress?.SetProgress(processedInSize, processedOutSize); + } + } + finally + { + ReleaseStreams(); + } + } + + public long Code(Stream inStream, bool final) + { + _matchFinder.SetStream(inStream); + _processingMode = !final; + try + { + while (true) + { + CodeOneBlock(out var processedInSize, out var processedOutSize, out var finished); + if (finished) + { + return processedInSize; } } - finally + } + finally + { + _matchFinder.ReleaseStream(); + if (final) { ReleaseStreams(); } } + } - public long Code(Stream inStream, bool final) + public void Train(Stream trainStream) + { + if (_nowPos64 > 0) { - _matchFinder.SetStream(inStream); - _processingMode = !final; - try - { - while (true) - { - Int64 processedInSize; - Int64 processedOutSize; - bool finished; - CodeOneBlock(out processedInSize, out processedOutSize, out finished); - if (finished) - { - return processedInSize; - } - } - } - finally - { - _matchFinder.ReleaseStream(); - if (final) - { - ReleaseStreams(); - } - } + throw new InvalidOperationException(); } - - public void Train(Stream trainStream) + _trainSize = (uint)trainStream.Length; + if (_trainSize > 0) { - if (_nowPos64 > 0) + _matchFinder.SetStream(trainStream); + for (; _trainSize > 0 && !_matchFinder.IsDataStarved; _trainSize--) { - throw new InvalidOperationException(); + _matchFinder.Skip(1); } - _trainSize = (uint)trainStream.Length; - if (_trainSize > 0) + if (_trainSize == 0) { - _matchFinder.SetStream(trainStream); - for (; _trainSize > 0 && !_matchFinder.IsDataStarved; _trainSize--) - { - _matchFinder.Skip(1); - } - if (_trainSize == 0) - { - _previousByte = _matchFinder.GetIndexByte(-1); - } - _matchFinder.ReleaseStream(); + _previousByte = _matchFinder.GetIndexByte(-1); } + _matchFinder.ReleaseStream(); } + } - private const int K_PROP_SIZE = 5; - private readonly Byte[] _properties = new Byte[K_PROP_SIZE]; + private const int K_PROP_SIZE = 5; + private readonly byte[] _properties = new byte[K_PROP_SIZE]; - public void WriteCoderProperties(Stream outStream) + public void WriteCoderProperties(Stream outStream) + { + WriteCoderProperties(_properties); + outStream.Write(_properties, 0, K_PROP_SIZE); + } + + public void WriteCoderProperties(Span span) + { + span[0] = (byte)( + (((_posStateBits * 5) + _numLiteralPosStateBits) * 9) + _numLiteralContextBits + ); + for (var i = 0; i < 4; i++) { - WriteCoderProperties(_properties); - outStream.Write(_properties, 0, K_PROP_SIZE); + span[1 + i] = (byte)((_dictionarySize >> (8 * i)) & 0xFF); } + } - public void WriteCoderProperties(Span span) + private readonly uint[] _tempPrices = new uint[Base.K_NUM_FULL_DISTANCES]; + private uint _matchPriceCount; + + private void FillDistancesPrices() + { + for (var i = Base.K_START_POS_MODEL_INDEX; i < Base.K_NUM_FULL_DISTANCES; i++) { - span[0] = (byte)( - (_posStateBits * 5 + _numLiteralPosStateBits) * 9 + _numLiteralContextBits + var posSlot = GetPosSlot(i); + var footerBits = (int)((posSlot >> 1) - 1); + var baseVal = ((2 | (posSlot & 1)) << footerBits); + _tempPrices[i] = BitTreeEncoder.ReverseGetPrice( + _posEncoders, + baseVal - posSlot - 1, + footerBits, + i - baseVal ); - for (int i = 0; i < 4; i++) - { - span[1 + i] = (byte)((_dictionarySize >> (8 * i)) & 0xFF); - } } - private readonly UInt32[] _tempPrices = new UInt32[Base.K_NUM_FULL_DISTANCES]; - private UInt32 _matchPriceCount; - - private void FillDistancesPrices() + for ( + uint lenToPosState = 0; + lenToPosState < Base.K_NUM_LEN_TO_POS_STATES; + lenToPosState++ + ) { - for (UInt32 i = Base.K_START_POS_MODEL_INDEX; i < Base.K_NUM_FULL_DISTANCES; i++) + uint posSlot; + var encoder = _posSlotEncoder[lenToPosState]; + + var st = (lenToPosState << Base.K_NUM_POS_SLOT_BITS); + for (posSlot = 0; posSlot < _distTableSize; posSlot++) { - UInt32 posSlot = GetPosSlot(i); - int footerBits = (int)((posSlot >> 1) - 1); - UInt32 baseVal = ((2 | (posSlot & 1)) << footerBits); - _tempPrices[i] = BitTreeEncoder.ReverseGetPrice( - _posEncoders, - baseVal - posSlot - 1, - footerBits, - i - baseVal + _posSlotPrices[st + posSlot] = encoder.GetPrice(posSlot); + } + for (posSlot = Base.K_END_POS_MODEL_INDEX; posSlot < _distTableSize; posSlot++) + { + _posSlotPrices[st + posSlot] += ( + (((posSlot >> 1) - 1) - Base.K_NUM_ALIGN_BITS) + << BitEncoder.K_NUM_BIT_PRICE_SHIFT_BITS ); } - for ( - UInt32 lenToPosState = 0; - lenToPosState < Base.K_NUM_LEN_TO_POS_STATES; - lenToPosState++ - ) + var st2 = lenToPosState * Base.K_NUM_FULL_DISTANCES; + uint i; + for (i = 0; i < Base.K_START_POS_MODEL_INDEX; i++) { - UInt32 posSlot; - BitTreeEncoder encoder = _posSlotEncoder[lenToPosState]; - - UInt32 st = (lenToPosState << Base.K_NUM_POS_SLOT_BITS); - for (posSlot = 0; posSlot < _distTableSize; posSlot++) - { - _posSlotPrices[st + posSlot] = encoder.GetPrice(posSlot); - } - for (posSlot = Base.K_END_POS_MODEL_INDEX; posSlot < _distTableSize; posSlot++) - { - _posSlotPrices[st + posSlot] += ( - (((posSlot >> 1) - 1) - Base.K_NUM_ALIGN_BITS) - << BitEncoder.K_NUM_BIT_PRICE_SHIFT_BITS - ); - } - - UInt32 st2 = lenToPosState * Base.K_NUM_FULL_DISTANCES; - UInt32 i; - for (i = 0; i < Base.K_START_POS_MODEL_INDEX; i++) - { - _distancesPrices[st2 + i] = _posSlotPrices[st + i]; - } - for (; i < Base.K_NUM_FULL_DISTANCES; i++) - { - _distancesPrices[st2 + i] = _posSlotPrices[st + GetPosSlot(i)] + _tempPrices[i]; - } + _distancesPrices[st2 + i] = _posSlotPrices[st + i]; } - _matchPriceCount = 0; - } - - private void FillAlignPrices() - { - for (UInt32 i = 0; i < Base.K_ALIGN_TABLE_SIZE; i++) + for (; i < Base.K_NUM_FULL_DISTANCES; i++) { - _alignPrices[i] = _posAlignEncoder.ReverseGetPrice(i); + _distancesPrices[st2 + i] = _posSlotPrices[st + GetPosSlot(i)] + _tempPrices[i]; } - _alignPriceCount = 0; } + _matchPriceCount = 0; + } - private static readonly string[] K_MATCH_FINDER_I_DS = { "BT2", "BT4" }; - - private static int FindMatchFinder(string s) + private void FillAlignPrices() + { + for (uint i = 0; i < Base.K_ALIGN_TABLE_SIZE; i++) { - for (int m = 0; m < K_MATCH_FINDER_I_DS.Length; m++) + _alignPrices[i] = _posAlignEncoder.ReverseGetPrice(i); + } + _alignPriceCount = 0; + } + + private static readonly string[] K_MATCH_FINDER_I_DS = { "BT2", "BT4" }; + + private static int FindMatchFinder(string s) + { + for (var m = 0; m < K_MATCH_FINDER_I_DS.Length; m++) + { + if (string.Equals(s, K_MATCH_FINDER_I_DS[m], StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(s, K_MATCH_FINDER_I_DS[m], StringComparison.OrdinalIgnoreCase)) - { - return m; - } + return m; } - return -1; } + return -1; + } - public void SetCoderProperties(CoderPropId[] propIDs, object[] properties) + public void SetCoderProperties(CoderPropId[] propIDs, object[] properties) + { + for (uint i = 0; i < properties.Length; i++) { - for (UInt32 i = 0; i < properties.Length; i++) + var prop = properties[i]; + switch (propIDs[i]) { - object prop = properties[i]; - switch (propIDs[i]) + case CoderPropId.NumFastBytes: { - case CoderPropId.NumFastBytes: + if (prop is not int) { - if (!(prop is Int32)) - { - throw new InvalidParamException(); - } - Int32 numFastBytes = (Int32)prop; - if (numFastBytes < 5 || numFastBytes > Base.K_MATCH_MAX_LEN) - { - throw new InvalidParamException(); - } - _numFastBytes = (UInt32)numFastBytes; - break; - } - case CoderPropId.Algorithm: - { - /* - if (!(prop is Int32)) - throw new InvalidParamException(); - Int32 maximize = (Int32)prop; - _fastMode = (maximize == 0); - _maxMode = (maximize >= 2); - */ - break; - } - case CoderPropId.MatchFinder: - { - if (!(prop is String)) - { - throw new InvalidParamException(); - } - EMatchFinderType matchFinderIndexPrev = _matchFinderType; - int m = FindMatchFinder(((string)prop)); - if (m < 0) - { - throw new InvalidParamException(); - } - _matchFinderType = (EMatchFinderType)m; - if (_matchFinder != null && matchFinderIndexPrev != _matchFinderType) - { - _dictionarySizePrev = 0xFFFFFFFF; - _matchFinder = null; - } - break; - } - case CoderPropId.DictionarySize: - { - const int kDicLogSizeMaxCompress = 30; - if (!(prop is Int32)) - { - throw new InvalidParamException(); - } - ; - Int32 dictionarySize = (Int32)prop; - if ( - dictionarySize < (UInt32)(1 << Base.K_DIC_LOG_SIZE_MIN) - || dictionarySize > (UInt32)(1 << kDicLogSizeMaxCompress) - ) - { - throw new InvalidParamException(); - } - _dictionarySize = (UInt32)dictionarySize; - int dicLogSize; - for ( - dicLogSize = 0; - dicLogSize < (UInt32)kDicLogSizeMaxCompress; - dicLogSize++ - ) - { - if (dictionarySize <= ((UInt32)(1) << dicLogSize)) - { - break; - } - } - _distTableSize = (UInt32)dicLogSize * 2; - break; - } - case CoderPropId.PosStateBits: - { - if (!(prop is Int32)) - { - throw new InvalidParamException(); - } - Int32 v = (Int32)prop; - if (v < 0 || v > (UInt32)Base.K_NUM_POS_STATES_BITS_ENCODING_MAX) - { - throw new InvalidParamException(); - } - _posStateBits = v; - _posStateMask = (((UInt32)1) << _posStateBits) - 1; - break; - } - case CoderPropId.LitPosBits: - { - if (!(prop is Int32)) - { - throw new InvalidParamException(); - } - Int32 v = (Int32)prop; - if (v < 0 || v > Base.K_NUM_LIT_POS_STATES_BITS_ENCODING_MAX) - { - throw new InvalidParamException(); - } - _numLiteralPosStateBits = v; - break; - } - case CoderPropId.LitContextBits: - { - if (!(prop is Int32)) - { - throw new InvalidParamException(); - } - Int32 v = (Int32)prop; - if (v < 0 || v > Base.K_NUM_LIT_CONTEXT_BITS_MAX) - { - throw new InvalidParamException(); - } - ; - _numLiteralContextBits = v; - break; - } - case CoderPropId.EndMarker: - { - if (!(prop is Boolean)) - { - throw new InvalidParamException(); - } - SetWriteEndMarkerMode((Boolean)prop); - break; - } - default: throw new InvalidParamException(); + } + var numFastBytes = (int)prop; + if (numFastBytes < 5 || numFastBytes > Base.K_MATCH_MAX_LEN) + { + throw new InvalidParamException(); + } + _numFastBytes = (uint)numFastBytes; + break; } + case CoderPropId.Algorithm: + { + /* + if (!(prop is Int32)) + throw new InvalidParamException(); + Int32 maximize = (Int32)prop; + _fastMode = (maximize == 0); + _maxMode = (maximize >= 2); + */ + break; + } + case CoderPropId.MatchFinder: + { + if (prop is not string) + { + throw new InvalidParamException(); + } + var matchFinderIndexPrev = _matchFinderType; + var m = FindMatchFinder(((string)prop)); + if (m < 0) + { + throw new InvalidParamException(); + } + _matchFinderType = (EMatchFinderType)m; + if (_matchFinder != null && matchFinderIndexPrev != _matchFinderType) + { + _dictionarySizePrev = 0xFFFFFFFF; + _matchFinder = null; + } + break; + } + case CoderPropId.DictionarySize: + { + const int kDicLogSizeMaxCompress = 30; + if (prop is not int) + { + throw new InvalidParamException(); + } + ; + var dictionarySize = (int)prop; + if ( + dictionarySize < (uint)(1 << Base.K_DIC_LOG_SIZE_MIN) + || dictionarySize > (uint)(1 << kDicLogSizeMaxCompress) + ) + { + throw new InvalidParamException(); + } + _dictionarySize = (uint)dictionarySize; + int dicLogSize; + for ( + dicLogSize = 0; + dicLogSize < (uint)kDicLogSizeMaxCompress; + dicLogSize++ + ) + { + if (dictionarySize <= ((uint)(1) << dicLogSize)) + { + break; + } + } + _distTableSize = (uint)dicLogSize * 2; + break; + } + case CoderPropId.PosStateBits: + { + if (prop is not int) + { + throw new InvalidParamException(); + } + var v = (int)prop; + if (v < 0 || v > (uint)Base.K_NUM_POS_STATES_BITS_ENCODING_MAX) + { + throw new InvalidParamException(); + } + _posStateBits = v; + _posStateMask = (((uint)1) << _posStateBits) - 1; + break; + } + case CoderPropId.LitPosBits: + { + if (prop is not int) + { + throw new InvalidParamException(); + } + var v = (int)prop; + if (v < 0 || v > Base.K_NUM_LIT_POS_STATES_BITS_ENCODING_MAX) + { + throw new InvalidParamException(); + } + _numLiteralPosStateBits = v; + break; + } + case CoderPropId.LitContextBits: + { + if (prop is not int) + { + throw new InvalidParamException(); + } + var v = (int)prop; + if (v < 0 || v > Base.K_NUM_LIT_CONTEXT_BITS_MAX) + { + throw new InvalidParamException(); + } + ; + _numLiteralContextBits = v; + break; + } + case CoderPropId.EndMarker: + { + if (prop is not bool) + { + throw new InvalidParamException(); + } + SetWriteEndMarkerMode((bool)prop); + break; + } + default: + throw new InvalidParamException(); } } - - private uint _trainSize; - - public void SetTrainSize(uint trainSize) - { - _trainSize = trainSize; - } + } + + private uint _trainSize; + + public void SetTrainSize(uint trainSize) + { + _trainSize = trainSize; } } diff --git a/src/SharpCompress/Compressors/LZMA/LzmaEncoderProperties.cs b/src/SharpCompress/Compressors/LZMA/LzmaEncoderProperties.cs index 395b2492..17c43ed6 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaEncoderProperties.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaEncoderProperties.cs @@ -1,46 +1,45 @@ -namespace SharpCompress.Compressors.LZMA +namespace SharpCompress.Compressors.LZMA; + +public class LzmaEncoderProperties { - public class LzmaEncoderProperties + internal CoderPropId[] _propIDs; + internal object[] _properties; + + public LzmaEncoderProperties() : this(false) { } + + public LzmaEncoderProperties(bool eos) : this(eos, 1 << 20) { } + + public LzmaEncoderProperties(bool eos, int dictionary) : this(eos, dictionary, 32) { } + + public LzmaEncoderProperties(bool eos, int dictionary, int numFastBytes) { - internal CoderPropId[] _propIDs; - internal object[] _properties; + var posStateBits = 2; + var litContextBits = 3; + var litPosBits = 0; + var algorithm = 2; + var mf = "bt4"; - public LzmaEncoderProperties() : this(false) { } - - public LzmaEncoderProperties(bool eos) : this(eos, 1 << 20) { } - - public LzmaEncoderProperties(bool eos, int dictionary) : this(eos, dictionary, 32) { } - - public LzmaEncoderProperties(bool eos, int dictionary, int numFastBytes) + _propIDs = new[] { - int posStateBits = 2; - int litContextBits = 3; - int litPosBits = 0; - int algorithm = 2; - string mf = "bt4"; - - _propIDs = new[] - { - CoderPropId.DictionarySize, - CoderPropId.PosStateBits, - CoderPropId.LitContextBits, - CoderPropId.LitPosBits, - CoderPropId.Algorithm, - CoderPropId.NumFastBytes, - CoderPropId.MatchFinder, - CoderPropId.EndMarker - }; - _properties = new object[] - { - dictionary, - posStateBits, - litContextBits, - litPosBits, - algorithm, - numFastBytes, - mf, - eos - }; - } + CoderPropId.DictionarySize, + CoderPropId.PosStateBits, + CoderPropId.LitContextBits, + CoderPropId.LitPosBits, + CoderPropId.Algorithm, + CoderPropId.NumFastBytes, + CoderPropId.MatchFinder, + CoderPropId.EndMarker + }; + _properties = new object[] + { + dictionary, + posStateBits, + litContextBits, + litPosBits, + algorithm, + numFastBytes, + mf, + eos + }; } } diff --git a/src/SharpCompress/Compressors/LZMA/LzmaStream.cs b/src/SharpCompress/Compressors/LZMA/LzmaStream.cs index 80238cdf..108381e5 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaStream.cs @@ -1,329 +1,328 @@ -#nullable disable +#nullable disable using System; using System.Buffers.Binary; using System.IO; using SharpCompress.Compressors.LZMA.LZ; -namespace SharpCompress.Compressors.LZMA +namespace SharpCompress.Compressors.LZMA; + +public class LzmaStream : Stream { - public class LzmaStream : Stream + private readonly Stream _inputStream; + private readonly long _inputSize; + private readonly long _outputSize; + + private readonly int _dictionarySize; + private readonly OutWindow _outWindow = new OutWindow(); + private readonly RangeCoder.Decoder _rangeDecoder = new RangeCoder.Decoder(); + private Decoder _decoder; + + private long _position; + private bool _endReached; + private long _availableBytes; + private long _rangeDecoderLimit; + private long _inputPosition; + + // LZMA2 + private readonly bool _isLzma2; + private bool _uncompressedChunk; + private bool _needDictReset = true; + private bool _needProps = true; + + private readonly Encoder _encoder; + private bool _isDisposed; + + public LzmaStream(byte[] properties, Stream inputStream) + : this(properties, inputStream, -1, -1, null, properties.Length < 5) { } + + public LzmaStream(byte[] properties, Stream inputStream, long inputSize) + : this(properties, inputStream, inputSize, -1, null, properties.Length < 5) { } + + public LzmaStream(byte[] properties, Stream inputStream, long inputSize, long outputSize) + : this(properties, inputStream, inputSize, outputSize, null, properties.Length < 5) { } + + public LzmaStream( + byte[] properties, + Stream inputStream, + long inputSize, + long outputSize, + Stream presetDictionary, + bool isLzma2 + ) { - private readonly Stream _inputStream; - private readonly long _inputSize; - private readonly long _outputSize; + _inputStream = inputStream; + _inputSize = inputSize; + _outputSize = outputSize; + _isLzma2 = isLzma2; - private readonly int _dictionarySize; - private readonly OutWindow _outWindow = new OutWindow(); - private readonly RangeCoder.Decoder _rangeDecoder = new RangeCoder.Decoder(); - private Decoder _decoder; - - private long _position; - private bool _endReached; - private long _availableBytes; - private long _rangeDecoderLimit; - private long _inputPosition; - - // LZMA2 - private readonly bool _isLzma2; - private bool _uncompressedChunk; - private bool _needDictReset = true; - private bool _needProps = true; - - private readonly Encoder _encoder; - private bool _isDisposed; - - public LzmaStream(byte[] properties, Stream inputStream) - : this(properties, inputStream, -1, -1, null, properties.Length < 5) { } - - public LzmaStream(byte[] properties, Stream inputStream, long inputSize) - : this(properties, inputStream, inputSize, -1, null, properties.Length < 5) { } - - public LzmaStream(byte[] properties, Stream inputStream, long inputSize, long outputSize) - : this(properties, inputStream, inputSize, outputSize, null, properties.Length < 5) { } - - public LzmaStream( - byte[] properties, - Stream inputStream, - long inputSize, - long outputSize, - Stream presetDictionary, - bool isLzma2 - ) + if (!isLzma2) { - _inputStream = inputStream; - _inputSize = inputSize; - _outputSize = outputSize; - _isLzma2 = isLzma2; - - if (!isLzma2) - { - _dictionarySize = BinaryPrimitives.ReadInt32LittleEndian(properties.AsSpan(1)); - _outWindow.Create(_dictionarySize); - if (presetDictionary != null) - { - _outWindow.Train(presetDictionary); - } - - _rangeDecoder.Init(inputStream); - - _decoder = new Decoder(); - _decoder.SetDecoderProperties(properties); - Properties = properties; - - _availableBytes = outputSize < 0 ? long.MaxValue : outputSize; - _rangeDecoderLimit = inputSize; - } - else - { - _dictionarySize = 2 | (properties[0] & 1); - _dictionarySize <<= (properties[0] >> 1) + 11; - - _outWindow.Create(_dictionarySize); - if (presetDictionary != null) - { - _outWindow.Train(presetDictionary); - _needDictReset = false; - } - - Properties = new byte[1]; - _availableBytes = 0; - } - } - - public LzmaStream(LzmaEncoderProperties properties, bool isLzma2, Stream outputStream) - : this(properties, isLzma2, null, outputStream) { } - - public LzmaStream( - LzmaEncoderProperties properties, - bool isLzma2, - Stream presetDictionary, - Stream outputStream - ) - { - _isLzma2 = isLzma2; - _availableBytes = 0; - _endReached = true; - - if (isLzma2) - { - throw new NotImplementedException(); - } - - _encoder = new Encoder(); - _encoder.SetCoderProperties(properties._propIDs, properties._properties); - byte[] prop = new byte[5]; - _encoder.WriteCoderProperties(prop); - Properties = prop; - - _encoder.SetStreams(null, outputStream, -1, -1); + _dictionarySize = BinaryPrimitives.ReadInt32LittleEndian(properties.AsSpan(1)); + _outWindow.Create(_dictionarySize); if (presetDictionary != null) { - _encoder.Train(presetDictionary); + _outWindow.Train(presetDictionary); } + + _rangeDecoder.Init(inputStream); + + _decoder = new Decoder(); + _decoder.SetDecoderProperties(properties); + Properties = properties; + + _availableBytes = outputSize < 0 ? long.MaxValue : outputSize; + _rangeDecoderLimit = inputSize; } - - public override bool CanRead => _encoder == null; - - public override bool CanSeek => false; - - public override bool CanWrite => _encoder != null; - - public override void Flush() { } - - protected override void Dispose(bool disposing) + else { - if (_isDisposed) + _dictionarySize = 2 | (properties[0] & 1); + _dictionarySize <<= (properties[0] >> 1) + 11; + + _outWindow.Create(_dictionarySize); + if (presetDictionary != null) { - return; - } - _isDisposed = true; - if (disposing) - { - if (_encoder != null) - { - _position = _encoder.Code(null, true); - } - _inputStream?.Dispose(); - } - base.Dispose(disposing); - } - - public override long Length => _position + _availableBytes; - - public override long Position - { - get => _position; - set => throw new NotSupportedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - if (_endReached) - { - return 0; - } - - int total = 0; - while (total < count) - { - if (_availableBytes == 0) - { - if (_isLzma2) - { - DecodeChunkHeader(); - } - else - { - _endReached = true; - } - if (_endReached) - { - break; - } - } - - int toProcess = count - total; - if (toProcess > _availableBytes) - { - toProcess = (int)_availableBytes; - } - - _outWindow.SetLimit(toProcess); - if (_uncompressedChunk) - { - _inputPosition += _outWindow.CopyStream(_inputStream, toProcess); - } - else if ( - _decoder.Code(_dictionarySize, _outWindow, _rangeDecoder) && _outputSize < 0 - ) - { - _availableBytes = _outWindow.AvailableBytes; - } - - int read = _outWindow.Read(buffer, offset, toProcess); - total += read; - offset += read; - _position += read; - _availableBytes -= read; - - if (_availableBytes == 0 && !_uncompressedChunk) - { - _rangeDecoder.ReleaseStream(); - if ( - !_rangeDecoder.IsFinished - || (_rangeDecoderLimit >= 0 && _rangeDecoder._total != _rangeDecoderLimit) - ) - { - throw new DataErrorException(); - } - _inputPosition += _rangeDecoder._total; - if (_outWindow.HasPending) - { - throw new DataErrorException(); - } - } - } - - if (_endReached) - { - if (_inputSize >= 0 && _inputPosition != _inputSize) - { - throw new DataErrorException(); - } - if (_outputSize >= 0 && _position != _outputSize) - { - throw new DataErrorException(); - } - } - - return total; - } - - private void DecodeChunkHeader() - { - int control = _inputStream.ReadByte(); - _inputPosition++; - - if (control == 0x00) - { - _endReached = true; - return; - } - - if (control >= 0xE0 || control == 0x01) - { - _needProps = true; + _outWindow.Train(presetDictionary); _needDictReset = false; - _outWindow.Reset(); - } - else if (_needDictReset) - { - throw new DataErrorException(); } - if (control >= 0x80) - { - _uncompressedChunk = false; - - _availableBytes = (control & 0x1F) << 16; - _availableBytes += (_inputStream.ReadByte() << 8) + _inputStream.ReadByte() + 1; - _inputPosition += 2; - - _rangeDecoderLimit = (_inputStream.ReadByte() << 8) + _inputStream.ReadByte() + 1; - _inputPosition += 2; - - if (control >= 0xC0) - { - _needProps = false; - Properties[0] = (byte)_inputStream.ReadByte(); - _inputPosition++; - - _decoder = new Decoder(); - _decoder.SetDecoderProperties(Properties); - } - else if (_needProps) - { - throw new DataErrorException(); - } - else if (control >= 0xA0) - { - _decoder = new Decoder(); - _decoder.SetDecoderProperties(Properties); - } - - _rangeDecoder.Init(_inputStream); - } - else if (control > 0x02) - { - throw new DataErrorException(); - } - else - { - _uncompressedChunk = true; - _availableBytes = (_inputStream.ReadByte() << 8) + _inputStream.ReadByte() + 1; - _inputPosition += 2; - } + Properties = new byte[1]; + _availableBytes = 0; } + } - public override long Seek(long offset, SeekOrigin origin) + public LzmaStream(LzmaEncoderProperties properties, bool isLzma2, Stream outputStream) + : this(properties, isLzma2, null, outputStream) { } + + public LzmaStream( + LzmaEncoderProperties properties, + bool isLzma2, + Stream presetDictionary, + Stream outputStream + ) + { + _isLzma2 = isLzma2; + _availableBytes = 0; + _endReached = true; + + if (isLzma2) { - throw new NotSupportedException(); + throw new NotImplementedException(); } - public override void SetLength(long value) + _encoder = new Encoder(); + _encoder.SetCoderProperties(properties._propIDs, properties._properties); + var prop = new byte[5]; + _encoder.WriteCoderProperties(prop); + Properties = prop; + + _encoder.SetStreams(null, outputStream, -1, -1); + if (presetDictionary != null) { - throw new NotSupportedException(); + _encoder.Train(presetDictionary); } + } - public override void Write(byte[] buffer, int offset, int count) + public override bool CanRead => _encoder == null; + + public override bool CanSeek => false; + + public override bool CanWrite => _encoder != null; + + public override void Flush() { } + + protected override void Dispose(bool disposing) + { + if (_isDisposed) + { + return; + } + _isDisposed = true; + if (disposing) { if (_encoder != null) { - _position = _encoder.Code(new MemoryStream(buffer, offset, count), false); + _position = _encoder.Code(null, true); + } + _inputStream?.Dispose(); + } + base.Dispose(disposing); + } + + public override long Length => _position + _availableBytes; + + public override long Position + { + get => _position; + set => throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (_endReached) + { + return 0; + } + + var total = 0; + while (total < count) + { + if (_availableBytes == 0) + { + if (_isLzma2) + { + DecodeChunkHeader(); + } + else + { + _endReached = true; + } + if (_endReached) + { + break; + } + } + + var toProcess = count - total; + if (toProcess > _availableBytes) + { + toProcess = (int)_availableBytes; + } + + _outWindow.SetLimit(toProcess); + if (_uncompressedChunk) + { + _inputPosition += _outWindow.CopyStream(_inputStream, toProcess); + } + else if ( + _decoder.Code(_dictionarySize, _outWindow, _rangeDecoder) && _outputSize < 0 + ) + { + _availableBytes = _outWindow.AvailableBytes; + } + + var read = _outWindow.Read(buffer, offset, toProcess); + total += read; + offset += read; + _position += read; + _availableBytes -= read; + + if (_availableBytes == 0 && !_uncompressedChunk) + { + _rangeDecoder.ReleaseStream(); + if ( + !_rangeDecoder.IsFinished + || (_rangeDecoderLimit >= 0 && _rangeDecoder._total != _rangeDecoderLimit) + ) + { + throw new DataErrorException(); + } + _inputPosition += _rangeDecoder._total; + if (_outWindow.HasPending) + { + throw new DataErrorException(); + } } } - public byte[] Properties { get; } = new byte[5]; + if (_endReached) + { + if (_inputSize >= 0 && _inputPosition != _inputSize) + { + throw new DataErrorException(); + } + if (_outputSize >= 0 && _position != _outputSize) + { + throw new DataErrorException(); + } + } + + return total; } + + private void DecodeChunkHeader() + { + var control = _inputStream.ReadByte(); + _inputPosition++; + + if (control == 0x00) + { + _endReached = true; + return; + } + + if (control >= 0xE0 || control == 0x01) + { + _needProps = true; + _needDictReset = false; + _outWindow.Reset(); + } + else if (_needDictReset) + { + throw new DataErrorException(); + } + + if (control >= 0x80) + { + _uncompressedChunk = false; + + _availableBytes = (control & 0x1F) << 16; + _availableBytes += (_inputStream.ReadByte() << 8) + _inputStream.ReadByte() + 1; + _inputPosition += 2; + + _rangeDecoderLimit = (_inputStream.ReadByte() << 8) + _inputStream.ReadByte() + 1; + _inputPosition += 2; + + if (control >= 0xC0) + { + _needProps = false; + Properties[0] = (byte)_inputStream.ReadByte(); + _inputPosition++; + + _decoder = new Decoder(); + _decoder.SetDecoderProperties(Properties); + } + else if (_needProps) + { + throw new DataErrorException(); + } + else if (control >= 0xA0) + { + _decoder = new Decoder(); + _decoder.SetDecoderProperties(Properties); + } + + _rangeDecoder.Init(_inputStream); + } + else if (control > 0x02) + { + throw new DataErrorException(); + } + else + { + _uncompressedChunk = true; + _availableBytes = (_inputStream.ReadByte() << 8) + _inputStream.ReadByte() + 1; + _inputPosition += 2; + } + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + if (_encoder != null) + { + _position = _encoder.Code(new MemoryStream(buffer, offset, count), false); + } + } + + public byte[] Properties { get; } = new byte[5]; } diff --git a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs index 36ba6842..3694286c 100644 --- a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs +++ b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs @@ -1,253 +1,251 @@ #nullable disable -using System; using System.IO; -namespace SharpCompress.Compressors.LZMA.RangeCoder +namespace SharpCompress.Compressors.LZMA.RangeCoder; + +internal class Encoder { - internal class Encoder + public const uint K_TOP_VALUE = (1 << 24); + + private Stream _stream; + + public ulong _low; + public uint _range; + private uint _cacheSize; + private byte _cache; + + //long StartPosition; + + public void SetStream(Stream stream) { - public const uint K_TOP_VALUE = (1 << 24); + _stream = stream; + } - private Stream _stream; + public void ReleaseStream() + { + _stream = null; + } - public UInt64 _low; - public uint _range; - private uint _cacheSize; - private byte _cache; + public void Init() + { + //StartPosition = Stream.Position; - //long StartPosition; + _low = 0; + _range = 0xFFFFFFFF; + _cacheSize = 1; + _cache = 0; + } - public void SetStream(Stream stream) + public void FlushData() + { + for (var i = 0; i < 5; i++) { - _stream = stream; - } - - public void ReleaseStream() - { - _stream = null; - } - - public void Init() - { - //StartPosition = Stream.Position; - - _low = 0; - _range = 0xFFFFFFFF; - _cacheSize = 1; - _cache = 0; - } - - public void FlushData() - { - for (int i = 0; i < 5; i++) - { - ShiftLow(); - } - } - - public void FlushStream() - { - _stream.Flush(); - } - - public void CloseStream() - { - _stream.Dispose(); - } - - public void Encode(uint start, uint size, uint total) - { - _low += start * (_range /= total); - _range *= size; - while (_range < K_TOP_VALUE) - { - _range <<= 8; - ShiftLow(); - } - } - - public void ShiftLow() - { - if ((uint)_low < 0xFF000000 || (uint)(_low >> 32) == 1) - { - byte temp = _cache; - do - { - _stream.WriteByte((byte)(temp + (_low >> 32))); - temp = 0xFF; - } while (--_cacheSize != 0); - _cache = (byte)(((uint)_low) >> 24); - } - _cacheSize++; - _low = ((uint)_low) << 8; - } - - public void EncodeDirectBits(uint v, int numTotalBits) - { - for (int i = numTotalBits - 1; i >= 0; i--) - { - _range >>= 1; - if (((v >> i) & 1) == 1) - { - _low += _range; - } - if (_range < K_TOP_VALUE) - { - _range <<= 8; - ShiftLow(); - } - } - } - - public void EncodeBit(uint size0, int numTotalBits, uint symbol) - { - uint newBound = (_range >> numTotalBits) * size0; - if (symbol == 0) - { - _range = newBound; - } - else - { - _low += newBound; - _range -= newBound; - } - while (_range < K_TOP_VALUE) - { - _range <<= 8; - ShiftLow(); - } - } - - public long GetProcessedSizeAdd() - { - return -1; - - //return _cacheSize + Stream.Position - StartPosition + 4; - // (long)Stream.GetProcessedSize(); + ShiftLow(); } } - internal class Decoder + public void FlushStream() { - public const uint K_TOP_VALUE = (1 << 24); - public uint _range; - public uint _code; + _stream.Flush(); + } - // public Buffer.InBuffer Stream = new Buffer.InBuffer(1 << 16); - public Stream _stream; - public long _total; + public void CloseStream() + { + _stream.Dispose(); + } - public void Init(Stream stream) + public void Encode(uint start, uint size, uint total) + { + _low += start * (_range /= total); + _range *= size; + while (_range < K_TOP_VALUE) { - // Stream.Init(stream); - _stream = stream; + _range <<= 8; + ShiftLow(); + } + } - _code = 0; - _range = 0xFFFFFFFF; - for (int i = 0; i < 5; i++) + public void ShiftLow() + { + if ((uint)_low < 0xFF000000 || (uint)(_low >> 32) == 1) + { + var temp = _cache; + do { - _code = (_code << 8) | (byte)_stream.ReadByte(); - } - _total = 5; + _stream.WriteByte((byte)(temp + (_low >> 32))); + temp = 0xFF; + } while (--_cacheSize != 0); + _cache = (byte)(((uint)_low) >> 24); } + _cacheSize++; + _low = ((uint)_low) << 8; + } - public void ReleaseStream() + public void EncodeDirectBits(uint v, int numTotalBits) + { + for (var i = numTotalBits - 1; i >= 0; i--) { - // Stream.ReleaseStream(); - _stream = null; - } - - public void CloseStream() - { - _stream.Dispose(); - } - - public void Normalize() - { - while (_range < K_TOP_VALUE) + _range >>= 1; + if (((v >> i) & 1) == 1) { - _code = (_code << 8) | (byte)_stream.ReadByte(); - _range <<= 8; - _total++; + _low += _range; } - } - - public void Normalize2() - { if (_range < K_TOP_VALUE) { - _code = (_code << 8) | (byte)_stream.ReadByte(); _range <<= 8; + ShiftLow(); + } + } + } + + public void EncodeBit(uint size0, int numTotalBits, uint symbol) + { + var newBound = (_range >> numTotalBits) * size0; + if (symbol == 0) + { + _range = newBound; + } + else + { + _low += newBound; + _range -= newBound; + } + while (_range < K_TOP_VALUE) + { + _range <<= 8; + ShiftLow(); + } + } + + public long GetProcessedSizeAdd() + { + return -1; + + //return _cacheSize + Stream.Position - StartPosition + 4; + // (long)Stream.GetProcessedSize(); + } +} + +internal class Decoder +{ + public const uint K_TOP_VALUE = (1 << 24); + public uint _range; + public uint _code; + + // public Buffer.InBuffer Stream = new Buffer.InBuffer(1 << 16); + public Stream _stream; + public long _total; + + public void Init(Stream stream) + { + // Stream.Init(stream); + _stream = stream; + + _code = 0; + _range = 0xFFFFFFFF; + for (var i = 0; i < 5; i++) + { + _code = (_code << 8) | (byte)_stream.ReadByte(); + } + _total = 5; + } + + public void ReleaseStream() + { + // Stream.ReleaseStream(); + _stream = null; + } + + public void CloseStream() + { + _stream.Dispose(); + } + + public void Normalize() + { + while (_range < K_TOP_VALUE) + { + _code = (_code << 8) | (byte)_stream.ReadByte(); + _range <<= 8; + _total++; + } + } + + public void Normalize2() + { + if (_range < K_TOP_VALUE) + { + _code = (_code << 8) | (byte)_stream.ReadByte(); + _range <<= 8; + _total++; + } + } + + public uint GetThreshold(uint total) + { + return _code / (_range /= total); + } + + public void Decode(uint start, uint size) + { + _code -= start * _range; + _range *= size; + Normalize(); + } + + public uint DecodeDirectBits(int numTotalBits) + { + var range = _range; + var code = _code; + uint result = 0; + for (var i = numTotalBits; i > 0; i--) + { + range >>= 1; + /* + result <<= 1; + if (code >= range) + { + code -= range; + result |= 1; + } + */ + var t = (code - range) >> 31; + code -= range & (t - 1); + result = (result << 1) | (1 - t); + + if (range < K_TOP_VALUE) + { + code = (code << 8) | (byte)_stream.ReadByte(); + range <<= 8; _total++; } } - - public uint GetThreshold(uint total) - { - return _code / (_range /= total); - } - - public void Decode(uint start, uint size) - { - _code -= start * _range; - _range *= size; - Normalize(); - } - - public uint DecodeDirectBits(int numTotalBits) - { - uint range = _range; - uint code = _code; - uint result = 0; - for (int i = numTotalBits; i > 0; i--) - { - range >>= 1; - /* - result <<= 1; - if (code >= range) - { - code -= range; - result |= 1; - } - */ - uint t = (code - range) >> 31; - code -= range & (t - 1); - result = (result << 1) | (1 - t); - - if (range < K_TOP_VALUE) - { - code = (code << 8) | (byte)_stream.ReadByte(); - range <<= 8; - _total++; - } - } - _range = range; - _code = code; - return result; - } - - public uint DecodeBit(uint size0, int numTotalBits) - { - uint newBound = (_range >> numTotalBits) * size0; - uint symbol; - if (_code < newBound) - { - symbol = 0; - _range = newBound; - } - else - { - symbol = 1; - _code -= newBound; - _range -= newBound; - } - Normalize(); - return symbol; - } - - public bool IsFinished => _code == 0; - - // ulong GetProcessedSize() {return Stream.GetProcessedSize(); } + _range = range; + _code = code; + return result; } + + public uint DecodeBit(uint size0, int numTotalBits) + { + var newBound = (_range >> numTotalBits) * size0; + uint symbol; + if (_code < newBound) + { + symbol = 0; + _range = newBound; + } + else + { + symbol = 1; + _code -= newBound; + _range -= newBound; + } + Normalize(); + return symbol; + } + + public bool IsFinished => _code == 0; + + // ulong GetProcessedSize() {return Stream.GetProcessedSize(); } } diff --git a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBit.cs b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBit.cs index 80b30bd8..15827a54 100644 --- a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBit.cs +++ b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBit.cs @@ -1,140 +1,126 @@ -using System; +namespace SharpCompress.Compressors.LZMA.RangeCoder; -namespace SharpCompress.Compressors.LZMA.RangeCoder +internal struct BitEncoder { - internal struct BitEncoder + public const int K_NUM_BIT_MODEL_TOTAL_BITS = 11; + public const uint K_BIT_MODEL_TOTAL = (1 << K_NUM_BIT_MODEL_TOTAL_BITS); + private const int K_NUM_MOVE_BITS = 5; + private const int K_NUM_MOVE_REDUCING_BITS = 2; + public const int K_NUM_BIT_PRICE_SHIFT_BITS = 6; + + private uint _prob; + + public void Init() { - public const int K_NUM_BIT_MODEL_TOTAL_BITS = 11; - public const uint K_BIT_MODEL_TOTAL = (1 << K_NUM_BIT_MODEL_TOTAL_BITS); - private const int K_NUM_MOVE_BITS = 5; - private const int K_NUM_MOVE_REDUCING_BITS = 2; - public const int K_NUM_BIT_PRICE_SHIFT_BITS = 6; + _prob = K_BIT_MODEL_TOTAL >> 1; + } - private uint _prob; - - public void Init() + public void UpdateModel(uint symbol) + { + if (symbol == 0) { - _prob = K_BIT_MODEL_TOTAL >> 1; + _prob += (K_BIT_MODEL_TOTAL - _prob) >> K_NUM_MOVE_BITS; } - - public void UpdateModel(uint symbol) + else { - if (symbol == 0) - { - _prob += (K_BIT_MODEL_TOTAL - _prob) >> K_NUM_MOVE_BITS; - } - else - { - _prob -= (_prob) >> K_NUM_MOVE_BITS; - } - } - - public void Encode(Encoder encoder, uint symbol) - { - // encoder.EncodeBit(Prob, kNumBitModelTotalBits, symbol); - // UpdateModel(symbol); - uint newBound = (encoder._range >> K_NUM_BIT_MODEL_TOTAL_BITS) * _prob; - if (symbol == 0) - { - encoder._range = newBound; - _prob += (K_BIT_MODEL_TOTAL - _prob) >> K_NUM_MOVE_BITS; - } - else - { - encoder._low += newBound; - encoder._range -= newBound; - _prob -= (_prob) >> K_NUM_MOVE_BITS; - } - if (encoder._range < Encoder.K_TOP_VALUE) - { - encoder._range <<= 8; - encoder.ShiftLow(); - } - } - - private static readonly UInt32[] PROB_PRICES = new UInt32[ - K_BIT_MODEL_TOTAL >> K_NUM_MOVE_REDUCING_BITS - ]; - - static BitEncoder() - { - const int kNumBits = (K_NUM_BIT_MODEL_TOTAL_BITS - K_NUM_MOVE_REDUCING_BITS); - for (int i = kNumBits - 1; i >= 0; i--) - { - UInt32 start = (UInt32)1 << (kNumBits - i - 1); - UInt32 end = (UInt32)1 << (kNumBits - i); - for (UInt32 j = start; j < end; j++) - { - PROB_PRICES[j] = - ((UInt32)i << K_NUM_BIT_PRICE_SHIFT_BITS) - + (((end - j) << K_NUM_BIT_PRICE_SHIFT_BITS) >> (kNumBits - i - 1)); - } - } - } - - public uint GetPrice(uint symbol) - { - return PROB_PRICES[ - (((_prob - symbol) ^ ((-(int)symbol))) & (K_BIT_MODEL_TOTAL - 1)) - >> K_NUM_MOVE_REDUCING_BITS - ]; - } - - public uint GetPrice0() - { - return PROB_PRICES[_prob >> K_NUM_MOVE_REDUCING_BITS]; - } - - public uint GetPrice1() - { - return PROB_PRICES[(K_BIT_MODEL_TOTAL - _prob) >> K_NUM_MOVE_REDUCING_BITS]; + _prob -= (_prob) >> K_NUM_MOVE_BITS; } } - internal struct BitDecoder + public void Encode(Encoder encoder, uint symbol) { - public const int K_NUM_BIT_MODEL_TOTAL_BITS = 11; - public const uint K_BIT_MODEL_TOTAL = (1 << K_NUM_BIT_MODEL_TOTAL_BITS); - private const int K_NUM_MOVE_BITS = 5; - - private uint _prob; - - public void UpdateModel(int numMoveBits, uint symbol) + // encoder.EncodeBit(Prob, kNumBitModelTotalBits, symbol); + // UpdateModel(symbol); + var newBound = (encoder._range >> K_NUM_BIT_MODEL_TOTAL_BITS) * _prob; + if (symbol == 0) { - if (symbol == 0) - { - _prob += (K_BIT_MODEL_TOTAL - _prob) >> numMoveBits; - } - else - { - _prob -= (_prob) >> numMoveBits; - } + encoder._range = newBound; + _prob += (K_BIT_MODEL_TOTAL - _prob) >> K_NUM_MOVE_BITS; } - - public void Init() + else { - _prob = K_BIT_MODEL_TOTAL >> 1; - } - - public uint Decode(Decoder rangeDecoder) - { - uint newBound = (rangeDecoder._range >> K_NUM_BIT_MODEL_TOTAL_BITS) * _prob; - if (rangeDecoder._code < newBound) - { - rangeDecoder._range = newBound; - _prob += (K_BIT_MODEL_TOTAL - _prob) >> K_NUM_MOVE_BITS; - if (rangeDecoder._range < Decoder.K_TOP_VALUE) - { - rangeDecoder._code = - (rangeDecoder._code << 8) | (byte)rangeDecoder._stream.ReadByte(); - rangeDecoder._range <<= 8; - rangeDecoder._total++; - } - return 0; - } - rangeDecoder._range -= newBound; - rangeDecoder._code -= newBound; + encoder._low += newBound; + encoder._range -= newBound; _prob -= (_prob) >> K_NUM_MOVE_BITS; + } + if (encoder._range < Encoder.K_TOP_VALUE) + { + encoder._range <<= 8; + encoder.ShiftLow(); + } + } + + private static readonly uint[] PROB_PRICES = new uint[ + K_BIT_MODEL_TOTAL >> K_NUM_MOVE_REDUCING_BITS + ]; + + static BitEncoder() + { + const int kNumBits = (K_NUM_BIT_MODEL_TOTAL_BITS - K_NUM_MOVE_REDUCING_BITS); + for (var i = kNumBits - 1; i >= 0; i--) + { + var start = (uint)1 << (kNumBits - i - 1); + var end = (uint)1 << (kNumBits - i); + for (var j = start; j < end; j++) + { + PROB_PRICES[j] = + ((uint)i << K_NUM_BIT_PRICE_SHIFT_BITS) + + (((end - j) << K_NUM_BIT_PRICE_SHIFT_BITS) >> (kNumBits - i - 1)); + } + } + } + + public uint GetPrice(uint symbol) + { + return PROB_PRICES[ + (((_prob - symbol) ^ ((-(int)symbol))) & (K_BIT_MODEL_TOTAL - 1)) + >> K_NUM_MOVE_REDUCING_BITS + ]; + } + + public uint GetPrice0() + { + return PROB_PRICES[_prob >> K_NUM_MOVE_REDUCING_BITS]; + } + + public uint GetPrice1() + { + return PROB_PRICES[(K_BIT_MODEL_TOTAL - _prob) >> K_NUM_MOVE_REDUCING_BITS]; + } +} + +internal struct BitDecoder +{ + public const int K_NUM_BIT_MODEL_TOTAL_BITS = 11; + public const uint K_BIT_MODEL_TOTAL = (1 << K_NUM_BIT_MODEL_TOTAL_BITS); + private const int K_NUM_MOVE_BITS = 5; + + private uint _prob; + + public void UpdateModel(int numMoveBits, uint symbol) + { + if (symbol == 0) + { + _prob += (K_BIT_MODEL_TOTAL - _prob) >> numMoveBits; + } + else + { + _prob -= (_prob) >> numMoveBits; + } + } + + public void Init() + { + _prob = K_BIT_MODEL_TOTAL >> 1; + } + + public uint Decode(Decoder rangeDecoder) + { + var newBound = (rangeDecoder._range >> K_NUM_BIT_MODEL_TOTAL_BITS) * _prob; + if (rangeDecoder._code < newBound) + { + rangeDecoder._range = newBound; + _prob += (K_BIT_MODEL_TOTAL - _prob) >> K_NUM_MOVE_BITS; if (rangeDecoder._range < Decoder.K_TOP_VALUE) { rangeDecoder._code = @@ -142,7 +128,18 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder rangeDecoder._range <<= 8; rangeDecoder._total++; } - return 1; + return 0; } + rangeDecoder._range -= newBound; + rangeDecoder._code -= newBound; + _prob -= (_prob) >> K_NUM_MOVE_BITS; + if (rangeDecoder._range < Decoder.K_TOP_VALUE) + { + rangeDecoder._code = + (rangeDecoder._code << 8) | (byte)rangeDecoder._stream.ReadByte(); + rangeDecoder._range <<= 8; + rangeDecoder._total++; + } + return 1; } } diff --git a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBitTree.cs b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBitTree.cs index a00a5135..cef8872c 100644 --- a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBitTree.cs +++ b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBitTree.cs @@ -1,176 +1,173 @@ -using System; +namespace SharpCompress.Compressors.LZMA.RangeCoder; -namespace SharpCompress.Compressors.LZMA.RangeCoder +internal readonly struct BitTreeEncoder { - internal struct BitTreeEncoder + private readonly BitEncoder[] _models; + private readonly int _numBitLevels; + + public BitTreeEncoder(int numBitLevels) { - private readonly BitEncoder[] _models; - private readonly int _numBitLevels; + _numBitLevels = numBitLevels; + _models = new BitEncoder[1 << numBitLevels]; + } - public BitTreeEncoder(int numBitLevels) + public void Init() + { + for (uint i = 1; i < (1 << _numBitLevels); i++) { - _numBitLevels = numBitLevels; - _models = new BitEncoder[1 << numBitLevels]; - } - - public void Init() - { - for (uint i = 1; i < (1 << _numBitLevels); i++) - { - _models[i].Init(); - } - } - - public void Encode(Encoder rangeEncoder, UInt32 symbol) - { - UInt32 m = 1; - for (int bitIndex = _numBitLevels; bitIndex > 0; ) - { - bitIndex--; - UInt32 bit = (symbol >> bitIndex) & 1; - _models[m].Encode(rangeEncoder, bit); - m = (m << 1) | bit; - } - } - - public void ReverseEncode(Encoder rangeEncoder, UInt32 symbol) - { - UInt32 m = 1; - for (UInt32 i = 0; i < _numBitLevels; i++) - { - UInt32 bit = symbol & 1; - _models[m].Encode(rangeEncoder, bit); - m = (m << 1) | bit; - symbol >>= 1; - } - } - - public UInt32 GetPrice(UInt32 symbol) - { - UInt32 price = 0; - UInt32 m = 1; - for (int bitIndex = _numBitLevels; bitIndex > 0; ) - { - bitIndex--; - UInt32 bit = (symbol >> bitIndex) & 1; - price += _models[m].GetPrice(bit); - m = (m << 1) + bit; - } - return price; - } - - public UInt32 ReverseGetPrice(UInt32 symbol) - { - UInt32 price = 0; - UInt32 m = 1; - for (int i = _numBitLevels; i > 0; i--) - { - UInt32 bit = symbol & 1; - symbol >>= 1; - price += _models[m].GetPrice(bit); - m = (m << 1) | bit; - } - return price; - } - - public static UInt32 ReverseGetPrice( - BitEncoder[] models, - UInt32 startIndex, - int numBitLevels, - UInt32 symbol - ) - { - UInt32 price = 0; - UInt32 m = 1; - for (int i = numBitLevels; i > 0; i--) - { - UInt32 bit = symbol & 1; - symbol >>= 1; - price += models[startIndex + m].GetPrice(bit); - m = (m << 1) | bit; - } - return price; - } - - public static void ReverseEncode( - BitEncoder[] models, - UInt32 startIndex, - Encoder rangeEncoder, - int numBitLevels, - UInt32 symbol - ) - { - UInt32 m = 1; - for (int i = 0; i < numBitLevels; i++) - { - UInt32 bit = symbol & 1; - models[startIndex + m].Encode(rangeEncoder, bit); - m = (m << 1) | bit; - symbol >>= 1; - } + _models[i].Init(); } } - internal struct BitTreeDecoder + public void Encode(Encoder rangeEncoder, uint symbol) { - private readonly BitDecoder[] _models; - private readonly int _numBitLevels; - - public BitTreeDecoder(int numBitLevels) + uint m = 1; + for (var bitIndex = _numBitLevels; bitIndex > 0;) { - _numBitLevels = numBitLevels; - _models = new BitDecoder[1 << numBitLevels]; + bitIndex--; + var bit = (symbol >> bitIndex) & 1; + _models[m].Encode(rangeEncoder, bit); + m = (m << 1) | bit; } + } - public void Init() + public void ReverseEncode(Encoder rangeEncoder, uint symbol) + { + uint m = 1; + for (uint i = 0; i < _numBitLevels; i++) { - for (uint i = 1; i < (1 << _numBitLevels); i++) - { - _models[i].Init(); - } + var bit = symbol & 1; + _models[m].Encode(rangeEncoder, bit); + m = (m << 1) | bit; + symbol >>= 1; } + } - public uint Decode(Decoder rangeDecoder) + public uint GetPrice(uint symbol) + { + uint price = 0; + uint m = 1; + for (var bitIndex = _numBitLevels; bitIndex > 0;) { - uint m = 1; - for (int bitIndex = _numBitLevels; bitIndex > 0; bitIndex--) - { - m = (m << 1) + _models[m].Decode(rangeDecoder); - } - return m - ((uint)1 << _numBitLevels); + bitIndex--; + var bit = (symbol >> bitIndex) & 1; + price += _models[m].GetPrice(bit); + m = (m << 1) + bit; } + return price; + } - public uint ReverseDecode(Decoder rangeDecoder) + public uint ReverseGetPrice(uint symbol) + { + uint price = 0; + uint m = 1; + for (var i = _numBitLevels; i > 0; i--) { - uint m = 1; - uint symbol = 0; - for (int bitIndex = 0; bitIndex < _numBitLevels; bitIndex++) - { - uint bit = _models[m].Decode(rangeDecoder); - m <<= 1; - m += bit; - symbol |= (bit << bitIndex); - } - return symbol; + var bit = symbol & 1; + symbol >>= 1; + price += _models[m].GetPrice(bit); + m = (m << 1) | bit; } + return price; + } - public static uint ReverseDecode( - BitDecoder[] models, - UInt32 startIndex, - Decoder rangeDecoder, - int numBitLevels - ) + public static uint ReverseGetPrice( + BitEncoder[] models, + uint startIndex, + int numBitLevels, + uint symbol + ) + { + uint price = 0; + uint m = 1; + for (var i = numBitLevels; i > 0; i--) { - uint m = 1; - uint symbol = 0; - for (int bitIndex = 0; bitIndex < numBitLevels; bitIndex++) - { - uint bit = models[startIndex + m].Decode(rangeDecoder); - m <<= 1; - m += bit; - symbol |= (bit << bitIndex); - } - return symbol; + var bit = symbol & 1; + symbol >>= 1; + price += models[startIndex + m].GetPrice(bit); + m = (m << 1) | bit; + } + return price; + } + + public static void ReverseEncode( + BitEncoder[] models, + uint startIndex, + Encoder rangeEncoder, + int numBitLevels, + uint symbol + ) + { + uint m = 1; + for (var i = 0; i < numBitLevels; i++) + { + var bit = symbol & 1; + models[startIndex + m].Encode(rangeEncoder, bit); + m = (m << 1) | bit; + symbol >>= 1; } } } + +internal readonly struct BitTreeDecoder +{ + private readonly BitDecoder[] _models; + private readonly int _numBitLevels; + + public BitTreeDecoder(int numBitLevels) + { + _numBitLevels = numBitLevels; + _models = new BitDecoder[1 << numBitLevels]; + } + + public void Init() + { + for (uint i = 1; i < (1 << _numBitLevels); i++) + { + _models[i].Init(); + } + } + + public uint Decode(Decoder rangeDecoder) + { + uint m = 1; + for (var bitIndex = _numBitLevels; bitIndex > 0; bitIndex--) + { + m = (m << 1) + _models[m].Decode(rangeDecoder); + } + return m - ((uint)1 << _numBitLevels); + } + + public uint ReverseDecode(Decoder rangeDecoder) + { + uint m = 1; + uint symbol = 0; + for (var bitIndex = 0; bitIndex < _numBitLevels; bitIndex++) + { + var bit = _models[m].Decode(rangeDecoder); + m <<= 1; + m += bit; + symbol |= (bit << bitIndex); + } + return symbol; + } + + public static uint ReverseDecode( + BitDecoder[] models, + uint startIndex, + Decoder rangeDecoder, + int numBitLevels + ) + { + uint m = 1; + uint symbol = 0; + for (var bitIndex = 0; bitIndex < numBitLevels; bitIndex++) + { + var bit = models[startIndex + m].Decode(rangeDecoder); + m <<= 1; + m += bit; + symbol |= (bit << bitIndex); + } + return symbol; + } +} diff --git a/src/SharpCompress/Compressors/LZMA/Registry.cs b/src/SharpCompress/Compressors/LZMA/Registry.cs index 967d1fe7..6aba0c5d 100644 --- a/src/SharpCompress/Compressors/LZMA/Registry.cs +++ b/src/SharpCompress/Compressors/LZMA/Registry.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Linq; using SharpCompress.Common.SevenZip; @@ -8,54 +8,52 @@ using SharpCompress.Compressors.Filters; using SharpCompress.Compressors.LZMA.Utilites; using SharpCompress.Compressors.PPMd; -namespace SharpCompress.Compressors.LZMA -{ - internal static class DecoderRegistry - { - private const uint K_COPY = 0x0; - private const uint K_DELTA = 3; - private const uint K_LZMA2 = 0x21; - private const uint K_LZMA = 0x030101; - private const uint K_PPMD = 0x030401; - private const uint K_BCJ = 0x03030103; - private const uint K_BCJ2 = 0x0303011B; - private const uint K_DEFLATE = 0x040108; - private const uint K_B_ZIP2 = 0x040202; +namespace SharpCompress.Compressors.LZMA; - internal static Stream CreateDecoderStream( - CMethodId id, - Stream[] inStreams, - byte[] info, - IPasswordProvider pass, - long limit - ) +internal static class DecoderRegistry +{ + private const uint K_COPY = 0x0; + private const uint K_LZMA2 = 0x21; + private const uint K_LZMA = 0x030101; + private const uint K_PPMD = 0x030401; + private const uint K_BCJ = 0x03030103; + private const uint K_BCJ2 = 0x0303011B; + private const uint K_DEFLATE = 0x040108; + private const uint K_B_ZIP2 = 0x040202; + + internal static Stream CreateDecoderStream( + CMethodId id, + Stream[] inStreams, + byte[] info, + IPasswordProvider pass, + long limit + ) + { + switch (id._id) { - switch (id._id) - { - case K_COPY: - if (info != null) - { - throw new NotSupportedException(); - } - return inStreams.Single(); - case K_LZMA: - case K_LZMA2: - return new LzmaStream(info, inStreams.Single(), -1, limit); - case CMethodId.K_AES_ID: - return new AesDecoderStream(inStreams.Single(), info, pass, limit); - case K_BCJ: - return new BCJFilter(false, inStreams.Single()); - case K_BCJ2: - return new Bcj2DecoderStream(inStreams, info, limit); - case K_B_ZIP2: - return new BZip2Stream(inStreams.Single(), CompressionMode.Decompress, true); - case K_PPMD: - return new PpmdStream(new PpmdProperties(info), inStreams.Single(), false); - case K_DEFLATE: - return new DeflateStream(inStreams.Single(), CompressionMode.Decompress); - default: + case K_COPY: + if (info != null) + { throw new NotSupportedException(); - } + } + return inStreams.Single(); + case K_LZMA: + case K_LZMA2: + return new LzmaStream(info, inStreams.Single(), -1, limit); + case CMethodId.K_AES_ID: + return new AesDecoderStream(inStreams.Single(), info, pass, limit); + case K_BCJ: + return new BCJFilter(false, inStreams.Single()); + case K_BCJ2: + return new Bcj2DecoderStream(inStreams, info, limit); + case K_B_ZIP2: + return new BZip2Stream(inStreams.Single(), CompressionMode.Decompress, true); + case K_PPMD: + return new PpmdStream(new PpmdProperties(info), inStreams.Single(), false); + case K_DEFLATE: + return new DeflateStream(inStreams.Single(), CompressionMode.Decompress); + default: + throw new NotSupportedException(); } } } diff --git a/src/SharpCompress/Compressors/LZMA/Utilites/CrcBuilderStream.cs b/src/SharpCompress/Compressors/LZMA/Utilites/CrcBuilderStream.cs index e72c715a..6e8c608f 100644 --- a/src/SharpCompress/Compressors/LZMA/Utilites/CrcBuilderStream.cs +++ b/src/SharpCompress/Compressors/LZMA/Utilites/CrcBuilderStream.cs @@ -1,86 +1,85 @@ -using System; +using System; using System.IO; -namespace SharpCompress.Compressors.LZMA.Utilites +namespace SharpCompress.Compressors.LZMA.Utilites; + +internal class CrcBuilderStream : Stream { - internal class CrcBuilderStream : Stream + private readonly Stream _mTarget; + private uint _mCrc; + private bool _mFinished; + private bool _isDisposed; + + public CrcBuilderStream(Stream target) { - private readonly Stream _mTarget; - private uint _mCrc; - private bool _mFinished; - private bool _isDisposed; + _mTarget = target; + _mCrc = Crc.INIT_CRC; + } - public CrcBuilderStream(Stream target) + protected override void Dispose(bool disposing) + { + if (_isDisposed) { - _mTarget = target; - _mCrc = Crc.INIT_CRC; + return; + } + _isDisposed = true; + _mTarget.Dispose(); + base.Dispose(disposing); + } + + public long Processed { get; private set; } + + public uint Finish() + { + if (!_mFinished) + { + _mFinished = true; + _mCrc = Crc.Finish(_mCrc); } - protected override void Dispose(bool disposing) + return _mCrc; + } + + public override bool CanRead => false; + + public override bool CanSeek => false; + + public override bool CanWrite => true; + + public override void Flush() { } + + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new InvalidOperationException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + if (_mFinished) { - if (_isDisposed) - { - return; - } - _isDisposed = true; - _mTarget.Dispose(); - base.Dispose(disposing); + throw new InvalidOperationException("CRC calculation has been finished."); } - public long Processed { get; private set; } - - public uint Finish() - { - if (!_mFinished) - { - _mFinished = true; - _mCrc = Crc.Finish(_mCrc); - } - - return _mCrc; - } - - public override bool CanRead => false; - - public override bool CanSeek => false; - - public override bool CanWrite => true; - - public override void Flush() { } - - public override long Length => throw new NotSupportedException(); - - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - throw new InvalidOperationException(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - if (_mFinished) - { - throw new InvalidOperationException("CRC calculation has been finished."); - } - - Processed += count; - _mCrc = Crc.Update(_mCrc, buffer, offset, count); - _mTarget.Write(buffer, offset, count); - } + Processed += count; + _mCrc = Crc.Update(_mCrc, buffer, offset, count); + _mTarget.Write(buffer, offset, count); } } diff --git a/src/SharpCompress/Compressors/LZMA/Utilites/CrcCheckStream.cs b/src/SharpCompress/Compressors/LZMA/Utilites/CrcCheckStream.cs index 2f44bcc3..c81dbb1d 100644 --- a/src/SharpCompress/Compressors/LZMA/Utilites/CrcCheckStream.cs +++ b/src/SharpCompress/Compressors/LZMA/Utilites/CrcCheckStream.cs @@ -2,112 +2,111 @@ using System; using System.Diagnostics; using System.IO; -namespace SharpCompress.Compressors.LZMA.Utilites +namespace SharpCompress.Compressors.LZMA.Utilites; + +[CLSCompliant(false)] +public class CrcCheckStream : Stream { - [CLSCompliant(false)] - public class CrcCheckStream : Stream + private readonly uint _mExpectedCrc; + private uint _mCurrentCrc; + private bool _mClosed; + + private readonly long[] _mBytes = new long[256]; + private long _mLength; + + public CrcCheckStream(uint crc) { - private readonly uint _mExpectedCrc; - private uint _mCurrentCrc; - private bool _mClosed; + _mExpectedCrc = crc; + _mCurrentCrc = Crc.INIT_CRC; + } - private readonly long[] _mBytes = new long[256]; - private long _mLength; - - public CrcCheckStream(uint crc) + protected override void Dispose(bool disposing) + { + //Nanook - is not equal here - _mCurrentCrc is yet to be negated + //if (_mCurrentCrc != _mExpectedCrc) + //{ + // throw new InvalidOperationException(); + //} + try { - _mExpectedCrc = crc; - _mCurrentCrc = Crc.INIT_CRC; - } - - protected override void Dispose(bool disposing) - { - //Nanook - is not equal here - _mCurrentCrc is yet to be negated - //if (_mCurrentCrc != _mExpectedCrc) - //{ - // throw new InvalidOperationException(); - //} - try + if (disposing && !_mClosed) { - if (disposing && !_mClosed) - { - _mClosed = true; - _mCurrentCrc = Crc.Finish(_mCurrentCrc); //now becomes equal + _mClosed = true; + _mCurrentCrc = Crc.Finish(_mCurrentCrc); //now becomes equal #if DEBUG - if (_mCurrentCrc == _mExpectedCrc) - { - Debug.WriteLine("CRC ok: " + _mExpectedCrc.ToString("x8")); - } - else - { - Debugger.Break(); - Debug.WriteLine("bad CRC"); - } + if (_mCurrentCrc == _mExpectedCrc) + { + Debug.WriteLine("CRC ok: " + _mExpectedCrc.ToString("x8")); + } + else + { + Debugger.Break(); + Debug.WriteLine("bad CRC"); + } - double lengthInv = 1.0 / _mLength; - double entropy = 0; - for (int i = 0; i < 256; i++) + var lengthInv = 1.0 / _mLength; + double entropy = 0; + for (var i = 0; i < 256; i++) + { + if (_mBytes[i] != 0) { - if (_mBytes[i] != 0) - { - double p = lengthInv * _mBytes[i]; - entropy -= p * Math.Log(p, 256); - } - } - Debug.WriteLine("entropy: " + (int)(entropy * 100) + "%"); -#endif - if (_mCurrentCrc != _mExpectedCrc) //moved test to here - { - throw new InvalidOperationException(); + var p = lengthInv * _mBytes[i]; + entropy -= p * Math.Log(p, 256); } } - } - finally - { - base.Dispose(disposing); + Debug.WriteLine("entropy: " + (int)(entropy * 100) + "%"); +#endif + if (_mCurrentCrc != _mExpectedCrc) //moved test to here + { + throw new InvalidOperationException(); + } } } - - public override bool CanRead => false; - - public override bool CanSeek => false; - - public override bool CanWrite => true; - - public override void Flush() { } - - public override long Length => throw new NotSupportedException(); - - public override long Position + finally { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - throw new InvalidOperationException(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - _mLength += count; - for (int i = 0; i < count; i++) - { - _mBytes[buffer[offset + i]]++; - } - - _mCurrentCrc = Crc.Update(_mCurrentCrc, buffer, offset, count); + base.Dispose(disposing); } } + + public override bool CanRead => false; + + public override bool CanSeek => false; + + public override bool CanWrite => true; + + public override void Flush() { } + + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new InvalidOperationException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + _mLength += count; + for (var i = 0; i < count; i++) + { + _mBytes[buffer[offset + i]]++; + } + + _mCurrentCrc = Crc.Update(_mCurrentCrc, buffer, offset, count); + } } diff --git a/src/SharpCompress/Compressors/LZMA/Utilites/IPasswordProvider.cs b/src/SharpCompress/Compressors/LZMA/Utilites/IPasswordProvider.cs index 977c86bf..2f0eb904 100644 --- a/src/SharpCompress/Compressors/LZMA/Utilites/IPasswordProvider.cs +++ b/src/SharpCompress/Compressors/LZMA/Utilites/IPasswordProvider.cs @@ -1,7 +1,6 @@ -namespace SharpCompress.Compressors.LZMA.Utilites +namespace SharpCompress.Compressors.LZMA.Utilites; + +internal interface IPasswordProvider { - internal interface IPasswordProvider - { - string CryptoGetTextPassword(); - } + string CryptoGetTextPassword(); } diff --git a/src/SharpCompress/Compressors/LZMA/Utilites/Utils.cs b/src/SharpCompress/Compressors/LZMA/Utilites/Utils.cs index 1b64ccb6..19b0f374 100644 --- a/src/SharpCompress/Compressors/LZMA/Utilites/Utils.cs +++ b/src/SharpCompress/Compressors/LZMA/Utilites/Utils.cs @@ -1,92 +1,91 @@ -using System; +using System; using System.Diagnostics; using System.IO; -namespace SharpCompress.Compressors.LZMA.Utilites +namespace SharpCompress.Compressors.LZMA.Utilites; + +internal enum BlockType : byte { - internal enum BlockType : byte + #region Constants + + End = 0, + Header = 1, + ArchiveProperties = 2, + AdditionalStreamsInfo = 3, + MainStreamsInfo = 4, + FilesInfo = 5, + PackInfo = 6, + UnpackInfo = 7, + SubStreamsInfo = 8, + Size = 9, + Crc = 10, + Folder = 11, + CodersUnpackSize = 12, + NumUnpackStream = 13, + EmptyStream = 14, + EmptyFile = 15, + Anti = 16, + Name = 17, + CTime = 18, + ATime = 19, + MTime = 20, + WinAttributes = 21, + Comment = 22, + EncodedHeader = 23, + StartPos = 24, + Dummy = 25 + + #endregion +} + +internal static class Utils +{ + [Conditional("DEBUG")] + public static void Assert(bool expression) { - #region Constants + if (!expression) + { + if (Debugger.IsAttached) + { + Debugger.Break(); + } - End = 0, - Header = 1, - ArchiveProperties = 2, - AdditionalStreamsInfo = 3, - MainStreamsInfo = 4, - FilesInfo = 5, - PackInfo = 6, - UnpackInfo = 7, - SubStreamsInfo = 8, - Size = 9, - Crc = 10, - Folder = 11, - CodersUnpackSize = 12, - NumUnpackStream = 13, - EmptyStream = 14, - EmptyFile = 15, - Anti = 16, - Name = 17, - CTime = 18, - ATime = 19, - MTime = 20, - WinAttributes = 21, - Comment = 22, - EncodedHeader = 23, - StartPos = 24, - Dummy = 25 - - #endregion + throw new InvalidOperationException("Assertion failed."); + } } - internal static class Utils + public static void ReadExact(this Stream stream, byte[] buffer, int offset, int length) { - [Conditional("DEBUG")] - public static void Assert(bool expression) + if (stream is null) { - if (!expression) - { - if (Debugger.IsAttached) - { - Debugger.Break(); - } - - throw new InvalidOperationException("Assertion failed."); - } + throw new ArgumentNullException(nameof(stream)); } - public static void ReadExact(this Stream stream, byte[] buffer, int offset, int length) + if (buffer is null) { - if (stream is null) + throw new ArgumentNullException(nameof(buffer)); + } + + if (offset < 0 || offset > buffer.Length) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + + if (length < 0 || length > buffer.Length - offset) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + while (length > 0) + { + var fetched = stream.Read(buffer, offset, length); + if (fetched <= 0) { - throw new ArgumentNullException(nameof(stream)); + throw new EndOfStreamException(); } - if (buffer is null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (offset < 0 || offset > buffer.Length) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - - if (length < 0 || length > buffer.Length - offset) - { - throw new ArgumentOutOfRangeException(nameof(length)); - } - - while (length > 0) - { - int fetched = stream.Read(buffer, offset, length); - if (fetched <= 0) - { - throw new EndOfStreamException(); - } - - offset += fetched; - length -= fetched; - } + offset += fetched; + length -= fetched; } } } diff --git a/src/SharpCompress/Compressors/PPMd/H/FreqData.cs b/src/SharpCompress/Compressors/PPMd/H/FreqData.cs index a139a449..bb7a630d 100644 --- a/src/SharpCompress/Compressors/PPMd/H/FreqData.cs +++ b/src/SharpCompress/Compressors/PPMd/H/FreqData.cs @@ -2,65 +2,64 @@ using System; using System.Buffers.Binary; using System.Text; -namespace SharpCompress.Compressors.PPMd.H +namespace SharpCompress.Compressors.PPMd.H; + +internal class FreqData : Pointer { - internal class FreqData : Pointer + internal const int SIZE = 6; + + // struct FreqData + // { + // ushort SummFreq; + // STATE _PACK_ATTR * Stats; + // }; + + internal FreqData(byte[] memory) : base(memory) { } + + internal int SummFreq { - internal const int SIZE = 6; + get => BinaryPrimitives.ReadInt16LittleEndian(Memory.AsSpan(Address)) & 0xffff; + set => BinaryPrimitives.WriteInt16LittleEndian(Memory.AsSpan(Address), (short)value); + } - // struct FreqData - // { - // ushort SummFreq; - // STATE _PACK_ATTR * Stats; - // }; + internal FreqData Initialize(byte[] mem) + { + return Initialize(mem); + } - internal FreqData(byte[] memory) : base(memory) { } + internal void IncrementSummFreq(int dSummFreq) + { + SummFreq += (short)dSummFreq; + } - internal int SummFreq - { - get => BinaryPrimitives.ReadInt16LittleEndian(Memory.AsSpan(Address)) & 0xffff; - set => BinaryPrimitives.WriteInt16LittleEndian(Memory.AsSpan(Address), (short)value); - } + internal int GetStats() + { + return BinaryPrimitives.ReadInt32LittleEndian(Memory.AsSpan(Address + 2)); + } - internal FreqData Initialize(byte[] mem) - { - return base.Initialize(mem); - } + internal virtual void SetStats(State state) + { + SetStats(state.Address); + } - internal void IncrementSummFreq(int dSummFreq) - { - SummFreq += (short)dSummFreq; - } + internal void SetStats(int state) + { + BinaryPrimitives.WriteInt32LittleEndian(Memory.AsSpan(Address + 2), state); + } - internal int GetStats() - { - return BinaryPrimitives.ReadInt32LittleEndian(Memory.AsSpan(Address + 2)); - } - - internal virtual void SetStats(State state) - { - SetStats(state.Address); - } - - internal void SetStats(int state) - { - BinaryPrimitives.WriteInt32LittleEndian(Memory.AsSpan(Address + 2), state); - } - - public override String ToString() - { - StringBuilder buffer = new StringBuilder(); - buffer.Append("FreqData["); - buffer.Append("\n Address="); - buffer.Append(Address); - buffer.Append("\n size="); - buffer.Append(SIZE); - buffer.Append("\n summFreq="); - buffer.Append(SummFreq); - buffer.Append("\n stats="); - buffer.Append(GetStats()); - buffer.Append("\n]"); - return buffer.ToString(); - } + public override string ToString() + { + var buffer = new StringBuilder(); + buffer.Append("FreqData["); + buffer.Append("\n Address="); + buffer.Append(Address); + buffer.Append("\n size="); + buffer.Append(SIZE); + buffer.Append("\n summFreq="); + buffer.Append(SummFreq); + buffer.Append("\n stats="); + buffer.Append(GetStats()); + buffer.Append("\n]"); + return buffer.ToString(); } } diff --git a/src/SharpCompress/Compressors/PPMd/H/ModelPPM.cs b/src/SharpCompress/Compressors/PPMd/H/ModelPPM.cs index 974a61ea..1eb8ae3d 100644 --- a/src/SharpCompress/Compressors/PPMd/H/ModelPPM.cs +++ b/src/SharpCompress/Compressors/PPMd/H/ModelPPM.cs @@ -6,984 +6,982 @@ using System.Text; using SharpCompress.Compressors.Rar; using Decoder = SharpCompress.Compressors.LZMA.RangeCoder.Decoder; -namespace SharpCompress.Compressors.PPMd.H +namespace SharpCompress.Compressors.PPMd.H; + +internal class ModelPpm { - internal class ModelPpm + private void InitBlock() { - private void InitBlock() + for (var i = 0; i < 25; i++) { - for (int i = 0; i < 25; i++) + _see2Cont[i] = new See2Context[16]; + } + for (var i2 = 0; i2 < 128; i2++) + { + _binSumm[i2] = new int[64]; + } + } + + public SubAllocator SubAlloc { get; } = new SubAllocator(); + + public virtual See2Context DummySee2Cont => _dummySee2Cont; + + public virtual int InitRl => _initRl; + + public virtual int EscCount + { + get => _escCount; + set => _escCount = value & 0xff; + } + + public virtual int[] CharMask => _charMask; + + public virtual int NumMasked + { + get => _numMasked; + set => _numMasked = value; + } + + public virtual int PrevSuccess + { + get => _prevSuccess; + set => _prevSuccess = value & 0xff; + } + + public virtual int InitEsc + { + get => _initEsc; + set => _initEsc = value; + } + + public virtual int RunLength + { + get => _runLength; + set => _runLength = value; + } + + public virtual int HiBitsFlag + { + get => _hiBitsFlag; + set => _hiBitsFlag = value & 0xff; + } + + public virtual int[][] BinSumm => _binSumm; + + internal RangeCoder Coder { get; private set; } + + internal State FoundState { get; private set; } + + public virtual byte[] Heap => SubAlloc.Heap; + + public virtual int OrderFall => _orderFall; + + public const int MAX_O = 64; /* maximum allowed model order */ + + public const int INT_BITS = 7; + + public const int PERIOD_BITS = 7; + + //UPGRADE_NOTE: Final was removed from the declaration of 'TOT_BITS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly int TOT_BITS = INT_BITS + PERIOD_BITS; + + //UPGRADE_NOTE: Final was removed from the declaration of 'INTERVAL '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly int INTERVAL = 1 << INT_BITS; + + //UPGRADE_NOTE: Final was removed from the declaration of 'BIN_SCALE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly int BIN_SCALE = 1 << TOT_BITS; + + public const int MAX_FREQ = 124; + + private readonly See2Context[][] _see2Cont = new See2Context[25][]; + + private See2Context _dummySee2Cont; + + private PpmContext _minContext; //medContext + + private PpmContext _maxContext; + + private int _numMasked, + _initEsc, + _orderFall, + _maxOrder, + _runLength, + _initRl; + + private readonly int[] _charMask = new int[256]; + + private readonly int[] _ns2Indx = new int[256]; + + private readonly int[] _ns2BsIndx = new int[256]; + + private readonly int[] _hb2Flag = new int[256]; + + // byte EscCount, PrevSuccess, HiBitsFlag; + private int _escCount, + _prevSuccess, + _hiBitsFlag; + + private readonly int[][] _binSumm = new int[128][]; // binary SEE-contexts + + private static readonly int[] INIT_BIN_ESC = + { + 0x3CDD, + 0x1F3F, + 0x59BF, + 0x48F3, + 0x64A1, + 0x5ABC, + 0x6632, + 0x6051 + }; + + // Temp fields + //UPGRADE_NOTE: Final was removed from the declaration of 'tempState1 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly State _tempState1 = new State(null); + + //UPGRADE_NOTE: Final was removed from the declaration of 'tempState2 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly State _tempState2 = new State(null); + + //UPGRADE_NOTE: Final was removed from the declaration of 'tempState3 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly State _tempState3 = new State(null); + + //UPGRADE_NOTE: Final was removed from the declaration of 'tempState4 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly State _tempState4 = new State(null); + + //UPGRADE_NOTE: Final was removed from the declaration of 'tempStateRef1 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly StateRef _tempStateRef1 = new StateRef(); + + //UPGRADE_NOTE: Final was removed from the declaration of 'tempStateRef2 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly StateRef _tempStateRef2 = new StateRef(); + + //UPGRADE_NOTE: Final was removed from the declaration of 'tempPPMContext1 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly PpmContext _tempPpmContext1 = new PpmContext(null); + + //UPGRADE_NOTE: Final was removed from the declaration of 'tempPPMContext2 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly PpmContext _tempPpmContext2 = new PpmContext(null); + + //UPGRADE_NOTE: Final was removed from the declaration of 'tempPPMContext3 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly PpmContext _tempPpmContext3 = new PpmContext(null); + + //UPGRADE_NOTE: Final was removed from the declaration of 'tempPPMContext4 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly PpmContext _tempPpmContext4 = new PpmContext(null); + + //UPGRADE_NOTE: Final was removed from the declaration of 'ps '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly int[] _ps = new int[MAX_O]; + + public ModelPpm() + { + InitBlock(); + _minContext = null; + _maxContext = null; + + //medContext = null; + } + + private void RestartModelRare() + { + new Span(_charMask).Clear(); + SubAlloc.InitSubAllocator(); + _initRl = -(_maxOrder < 12 ? _maxOrder : 12) - 1; + var addr = SubAlloc.AllocContext(); + _minContext.Address = addr; + _maxContext.Address = addr; + _minContext.SetSuffix(0); + _orderFall = _maxOrder; + _minContext.NumStats = 256; + _minContext.FreqData.SummFreq = _minContext.NumStats + 1; + + addr = SubAlloc.AllocUnits(256 / 2); + FoundState.Address = addr; + _minContext.FreqData.SetStats(addr); + + var state = new State(SubAlloc.Heap); + addr = _minContext.FreqData.GetStats(); + _runLength = _initRl; + _prevSuccess = 0; + for (var i = 0; i < 256; i++) + { + state.Address = addr + (i * State.SIZE); + state.Symbol = i; + state.Freq = 1; + state.SetSuccessor(0); + } + + for (var i = 0; i < 128; i++) + { + for (var k = 0; k < 8; k++) { - _see2Cont[i] = new See2Context[16]; - } - for (int i2 = 0; i2 < 128; i2++) - { - _binSumm[i2] = new int[64]; - } - } - - public SubAllocator SubAlloc { get; } = new SubAllocator(); - - public virtual See2Context DummySee2Cont => _dummySee2Cont; - - public virtual int InitRl => _initRl; - - public virtual int EscCount - { - get => _escCount; - set => _escCount = value & 0xff; - } - - public virtual int[] CharMask => _charMask; - - public virtual int NumMasked - { - get => _numMasked; - set => _numMasked = value; - } - - public virtual int PrevSuccess - { - get => _prevSuccess; - set => _prevSuccess = value & 0xff; - } - - public virtual int InitEsc - { - get => _initEsc; - set => _initEsc = value; - } - - public virtual int RunLength - { - get => _runLength; - set => _runLength = value; - } - - public virtual int HiBitsFlag - { - get => _hiBitsFlag; - set => _hiBitsFlag = value & 0xff; - } - - public virtual int[][] BinSumm => _binSumm; - - internal RangeCoder Coder { get; private set; } - - internal State FoundState { get; private set; } - - public virtual byte[] Heap => SubAlloc.Heap; - - public virtual int OrderFall => _orderFall; - - public const int MAX_O = 64; /* maximum allowed model order */ - - public const int INT_BITS = 7; - - public const int PERIOD_BITS = 7; - - //UPGRADE_NOTE: Final was removed from the declaration of 'TOT_BITS '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - public static readonly int TOT_BITS = INT_BITS + PERIOD_BITS; - - //UPGRADE_NOTE: Final was removed from the declaration of 'INTERVAL '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - public static readonly int INTERVAL = 1 << INT_BITS; - - //UPGRADE_NOTE: Final was removed from the declaration of 'BIN_SCALE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - public static readonly int BIN_SCALE = 1 << TOT_BITS; - - public const int MAX_FREQ = 124; - - private readonly See2Context[][] _see2Cont = new See2Context[25][]; - - private See2Context _dummySee2Cont; - - private PpmContext _minContext; //medContext - - private PpmContext _maxContext; - - private int _numMasked, - _initEsc, - _orderFall, - _maxOrder, - _runLength, - _initRl; - - private readonly int[] _charMask = new int[256]; - - private readonly int[] _ns2Indx = new int[256]; - - private readonly int[] _ns2BsIndx = new int[256]; - - private readonly int[] _hb2Flag = new int[256]; - - // byte EscCount, PrevSuccess, HiBitsFlag; - private int _escCount, - _prevSuccess, - _hiBitsFlag; - - private readonly int[][] _binSumm = new int[128][]; // binary SEE-contexts - - private static readonly int[] INIT_BIN_ESC = - { - 0x3CDD, - 0x1F3F, - 0x59BF, - 0x48F3, - 0x64A1, - 0x5ABC, - 0x6632, - 0x6051 - }; - - // Temp fields - //UPGRADE_NOTE: Final was removed from the declaration of 'tempState1 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState1 = new State(null); - - //UPGRADE_NOTE: Final was removed from the declaration of 'tempState2 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState2 = new State(null); - - //UPGRADE_NOTE: Final was removed from the declaration of 'tempState3 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState3 = new State(null); - - //UPGRADE_NOTE: Final was removed from the declaration of 'tempState4 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState4 = new State(null); - - //UPGRADE_NOTE: Final was removed from the declaration of 'tempStateRef1 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly StateRef _tempStateRef1 = new StateRef(); - - //UPGRADE_NOTE: Final was removed from the declaration of 'tempStateRef2 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly StateRef _tempStateRef2 = new StateRef(); - - //UPGRADE_NOTE: Final was removed from the declaration of 'tempPPMContext1 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly PpmContext _tempPpmContext1 = new PpmContext(null); - - //UPGRADE_NOTE: Final was removed from the declaration of 'tempPPMContext2 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly PpmContext _tempPpmContext2 = new PpmContext(null); - - //UPGRADE_NOTE: Final was removed from the declaration of 'tempPPMContext3 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly PpmContext _tempPpmContext3 = new PpmContext(null); - - //UPGRADE_NOTE: Final was removed from the declaration of 'tempPPMContext4 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly PpmContext _tempPpmContext4 = new PpmContext(null); - - //UPGRADE_NOTE: Final was removed from the declaration of 'ps '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly int[] _ps = new int[MAX_O]; - - public ModelPpm() - { - InitBlock(); - _minContext = null; - _maxContext = null; - - //medContext = null; - } - - private void RestartModelRare() - { - new Span(_charMask).Clear(); - SubAlloc.InitSubAllocator(); - _initRl = -(_maxOrder < 12 ? _maxOrder : 12) - 1; - int addr = SubAlloc.AllocContext(); - _minContext.Address = addr; - _maxContext.Address = addr; - _minContext.SetSuffix(0); - _orderFall = _maxOrder; - _minContext.NumStats = 256; - _minContext.FreqData.SummFreq = _minContext.NumStats + 1; - - addr = SubAlloc.AllocUnits(256 / 2); - FoundState.Address = addr; - _minContext.FreqData.SetStats(addr); - - State state = new State(SubAlloc.Heap); - addr = _minContext.FreqData.GetStats(); - _runLength = _initRl; - _prevSuccess = 0; - for (int i = 0; i < 256; i++) - { - state.Address = addr + i * State.SIZE; - state.Symbol = i; - state.Freq = 1; - state.SetSuccessor(0); - } - - for (int i = 0; i < 128; i++) - { - for (int k = 0; k < 8; k++) + for (var m = 0; m < 64; m += 8) { - for (int m = 0; m < 64; m += 8) - { - _binSumm[i][k + m] = BIN_SCALE - INIT_BIN_ESC[k] / (i + 2); - } - } - } - for (int i = 0; i < 25; i++) - { - for (int k = 0; k < 16; k++) - { - _see2Cont[i][k].Initialize(5 * i + 10); + _binSumm[i][k + m] = BIN_SCALE - (INIT_BIN_ESC[k] / (i + 2)); } } } - - private void StartModelRare(int maxOrder) + for (var i = 0; i < 25; i++) { - int i, - k, - m, - step; - _escCount = 1; - _maxOrder = maxOrder; - RestartModelRare(); - - // Bug Fixed - _ns2BsIndx[0] = 0; - _ns2BsIndx[1] = 2; - for (int j = 0; j < 9; j++) + for (var k = 0; k < 16; k++) { - _ns2BsIndx[2 + j] = 4; + _see2Cont[i][k].Initialize((5 * i) + 10); } - for (int j = 0; j < 256 - 11; j++) - { - _ns2BsIndx[11 + j] = 6; - } - for (i = 0; i < 3; i++) - { - _ns2Indx[i] = i; - } - for (m = i, k = 1, step = 1; i < 256; i++) - { - _ns2Indx[i] = m; - if ((--k) == 0) - { - k = ++step; - m++; - } - } - for (int j = 0; j < 0x40; j++) - { - _hb2Flag[j] = 0; - } - for (int j = 0; j < 0x100 - 0x40; j++) - { - _hb2Flag[0x40 + j] = 0x08; - } - _dummySee2Cont.Shift = PERIOD_BITS; } + } - private void ClearMask() + private void StartModelRare(int maxOrder) + { + int i, + k, + m, + step; + _escCount = 1; + _maxOrder = maxOrder; + RestartModelRare(); + + // Bug Fixed + _ns2BsIndx[0] = 0; + _ns2BsIndx[1] = 2; + for (var j = 0; j < 9; j++) { - _escCount = 1; - new Span(_charMask).Clear(); + _ns2BsIndx[2 + j] = 4; } - - internal bool DecodeInit(IRarUnpack unpackRead, int escChar) + for (var j = 0; j < 256 - 11; j++) { - int maxOrder = unpackRead.Char & 0xff; - bool reset = ((maxOrder & 0x20) != 0); - - int maxMb = 0; - if (reset) - { - maxMb = unpackRead.Char; - } - else - { - if (SubAlloc.GetAllocatedMemory() == 0) - { - return (false); - } - } - if ((maxOrder & 0x40) != 0) - { - escChar = unpackRead.Char; - unpackRead.PpmEscChar = escChar; - } - Coder = new RangeCoder(unpackRead); - if (reset) - { - maxOrder = (maxOrder & 0x1f) + 1; - if (maxOrder > 16) - { - maxOrder = 16 + (maxOrder - 16) * 3; - } - if (maxOrder == 1) - { - SubAlloc.StopSubAllocator(); - return (false); - } - SubAlloc.StartSubAllocator((maxMb + 1) << 20); - _minContext = new PpmContext(Heap); - - //medContext = new PPMContext(Heap); - _maxContext = new PpmContext(Heap); - FoundState = new State(Heap); - _dummySee2Cont = new See2Context(); - for (int i = 0; i < 25; i++) - { - for (int j = 0; j < 16; j++) - { - _see2Cont[i][j] = new See2Context(); - } - } - StartModelRare(maxOrder); - } - return (_minContext.Address != 0); + _ns2BsIndx[11 + j] = 6; } - - public virtual int DecodeChar() + for (i = 0; i < 3; i++) { - // Debug - //subAlloc.dumpHeap(); - - if (_minContext.Address <= SubAlloc.PText || _minContext.Address > SubAlloc.HeapEnd) - { - return (-1); - } - - if (_minContext.NumStats != 1) - { - if ( - _minContext.FreqData.GetStats() <= SubAlloc.PText - || _minContext.FreqData.GetStats() > SubAlloc.HeapEnd - ) - { - return (-1); - } - if (!_minContext.DecodeSymbol1(this)) - { - return (-1); - } - } - else - { - _minContext.DecodeBinSymbol(this); - } - Coder.Decode(); - while (FoundState.Address == 0) - { - Coder.AriDecNormalize(); - do - { - _orderFall++; - _minContext.Address = _minContext.GetSuffix(); // =MinContext->Suffix; - if ( - _minContext.Address <= SubAlloc.PText - || _minContext.Address > SubAlloc.HeapEnd - ) - { - return (-1); - } - } while (_minContext.NumStats == _numMasked); - if (!_minContext.DecodeSymbol2(this)) - { - return (-1); - } - Coder.Decode(); - } - int symbol = FoundState.Symbol; - if ((_orderFall == 0) && FoundState.GetSuccessor() > SubAlloc.PText) - { - // MinContext=MaxContext=FoundState->Successor; - int addr = FoundState.GetSuccessor(); - _minContext.Address = addr; - _maxContext.Address = addr; - } - else - { - UpdateModel(); - - //this.foundState.Address=foundState.Address);//TODO just 4 debugging - if (_escCount == 0) - { - ClearMask(); - } - } - Coder.AriDecNormalize(); // ARI_DEC_NORMALIZE(Coder.code,Coder.low,Coder.range,Coder.UnpackRead); - return (symbol); + _ns2Indx[i] = i; } - - public virtual See2Context[][] GetSee2Cont() + for (m = i, k = 1, step = 1; i < 256; i++) { - return _see2Cont; + _ns2Indx[i] = m; + if ((--k) == 0) + { + k = ++step; + m++; + } } - - public virtual void IncEscCount(int dEscCount) + for (var j = 0; j < 0x40; j++) { - EscCount = EscCount + dEscCount; + _hb2Flag[j] = 0; } - - public virtual void IncRunLength(int dRunLength) + for (var j = 0; j < 0x100 - 0x40; j++) { - RunLength = RunLength + dRunLength; + _hb2Flag[0x40 + j] = 0x08; } + _dummySee2Cont.Shift = PERIOD_BITS; + } - public virtual int[] GetHb2Flag() + private void ClearMask() + { + _escCount = 1; + new Span(_charMask).Clear(); + } + + internal bool DecodeInit(IRarUnpack unpackRead, int escChar) + { + var maxOrder = unpackRead.Char & 0xff; + var reset = ((maxOrder & 0x20) != 0); + + var maxMb = 0; + if (reset) { - return _hb2Flag; + maxMb = unpackRead.Char; } - - public virtual int[] GetNs2BsIndx() + else { - return _ns2BsIndx; + if (SubAlloc.GetAllocatedMemory() == 0) + { + return (false); + } } - - public virtual int[] GetNs2Indx() + if ((maxOrder & 0x40) != 0) { - return _ns2Indx; + escChar = unpackRead.Char; + unpackRead.PpmEscChar = escChar; } - - private int CreateSuccessors(bool skip, State p1) + Coder = new RangeCoder(unpackRead); + if (reset) { - //State upState = tempState1.Initialize(null); - StateRef upState = _tempStateRef2; - State tempState = _tempState1.Initialize(Heap); - - // PPM_CONTEXT* pc=MinContext, * UpBranch=FoundState->Successor; - PpmContext pc = _tempPpmContext1.Initialize(Heap); - pc.Address = _minContext.Address; - PpmContext upBranch = _tempPpmContext2.Initialize(Heap); - upBranch.Address = FoundState.GetSuccessor(); - - // STATE * p, * ps[MAX_O], ** pps=ps; - State p = _tempState2.Initialize(Heap); - int pps = 0; - - bool noLoop = false; - - if (!skip) + maxOrder = (maxOrder & 0x1f) + 1; + if (maxOrder > 16) { - _ps[pps++] = FoundState.Address; // *pps++ = FoundState; - if (pc.GetSuffix() == 0) - { - noLoop = true; - } + maxOrder = 16 + ((maxOrder - 16) * 3); } - if (!noLoop) - { - bool loopEntry = false; - if (p1.Address != 0) - { - p.Address = p1.Address; - pc.Address = pc.GetSuffix(); // =pc->Suffix; - loopEntry = true; - } - do - { - if (!loopEntry) - { - pc.Address = pc.GetSuffix(); // pc=pc->Suffix; - if (pc.NumStats != 1) - { - p.Address = pc.FreqData.GetStats(); // p=pc->U.Stats - if (p.Symbol != FoundState.Symbol) - { - do - { - p.IncrementAddress(); - } while (p.Symbol != FoundState.Symbol); - } - } - else - { - p.Address = pc.GetOneState().Address; // p=&(pc->OneState); - } - } // LOOP_ENTRY: - loopEntry = false; - if (p.GetSuccessor() != upBranch.Address) - { - pc.Address = p.GetSuccessor(); // =p->Successor; - break; - } - _ps[pps++] = p.Address; - } while (pc.GetSuffix() != 0); - } // NO_LOOP: - if (pps == 0) - { - return pc.Address; - } - upState.Symbol = Heap[upBranch.Address]; // UpState.Symbol=*(byte*) - - // UpBranch; - // UpState.Successor=(PPM_CONTEXT*) (((byte*) UpBranch)+1); - upState.SetSuccessor(upBranch.Address + 1); //TODO check if +1 necessary - if (pc.NumStats != 1) - { - if (pc.Address <= SubAlloc.PText) - { - return (0); - } - p.Address = pc.FreqData.GetStats(); - if (p.Symbol != upState.Symbol) - { - do - { - p.IncrementAddress(); - } while (p.Symbol != upState.Symbol); - } - int cf = p.Freq - 1; - int s0 = pc.FreqData.SummFreq - pc.NumStats - cf; - - // UpState.Freq=1+((2*cf <= s0)?(5*cf > s0):((2*cf+3*s0-1)/(2*s0))); - upState.Freq = - 1 - + ((2 * cf <= s0) ? (5 * cf > s0 ? 1 : 0) : ((2 * cf + 3 * s0 - 1) / (2 * s0))); - } - else - { - upState.Freq = pc.GetOneState().Freq; // UpState.Freq=pc->OneState.Freq; - } - do - { - // pc = pc->createChild(this,*--pps,UpState); - tempState.Address = _ps[--pps]; - pc.Address = pc.CreateChild(this, tempState, upState); - if (pc.Address == 0) - { - return 0; - } - } while (pps != 0); - return pc.Address; - } - - private void UpdateModelRestart() - { - RestartModelRare(); - _escCount = 0; - } - - private void UpdateModel() - { - //System.out.println("ModelPPM.updateModel()"); - // STATE fs = *FoundState, *p = NULL; - StateRef fs = _tempStateRef1; - fs.Values = FoundState; - State p = _tempState3.Initialize(Heap); - State tempState = _tempState4.Initialize(Heap); - - PpmContext pc = _tempPpmContext3.Initialize(Heap); - PpmContext successor = _tempPpmContext4.Initialize(Heap); - - int ns1, - ns, - cf, - sf, - s0; - pc.Address = _minContext.GetSuffix(); - if (fs.Freq < MAX_FREQ / 4 && pc.Address != 0) - { - if (pc.NumStats != 1) - { - p.Address = pc.FreqData.GetStats(); - if (p.Symbol != fs.Symbol) - { - do - { - p.IncrementAddress(); - } while (p.Symbol != fs.Symbol); - tempState.Address = p.Address - State.SIZE; - if (p.Freq >= tempState.Freq) - { - State.PpmdSwap(p, tempState); - p.DecrementAddress(); - } - } - if (p.Freq < MAX_FREQ - 9) - { - p.IncrementFreq(2); - pc.FreqData.IncrementSummFreq(2); - } - } - else - { - p.Address = pc.GetOneState().Address; - if (p.Freq < 32) - { - p.IncrementFreq(1); - } - } - } - if (_orderFall == 0) - { - FoundState.SetSuccessor(CreateSuccessors(true, p)); - _minContext.Address = FoundState.GetSuccessor(); - _maxContext.Address = FoundState.GetSuccessor(); - if (_minContext.Address == 0) - { - UpdateModelRestart(); - return; - } - return; - } - SubAlloc.Heap[SubAlloc.PText] = (byte)fs.Symbol; - SubAlloc.IncPText(); - successor.Address = SubAlloc.PText; - if (SubAlloc.PText >= SubAlloc.FakeUnitsStart) - { - UpdateModelRestart(); - return; - } - - // // Debug - // subAlloc.dumpHeap(); - if (fs.GetSuccessor() != 0) - { - if (fs.GetSuccessor() <= SubAlloc.PText) - { - fs.SetSuccessor(CreateSuccessors(false, p)); - if (fs.GetSuccessor() == 0) - { - UpdateModelRestart(); - return; - } - } - if (--_orderFall == 0) - { - successor.Address = fs.GetSuccessor(); - if (_maxContext.Address != _minContext.Address) - { - SubAlloc.DecPText(1); - } - } - } - else - { - FoundState.SetSuccessor(successor.Address); - fs.SetSuccessor(_minContext); - } - - // // Debug - // subAlloc.dumpHeap(); - ns = _minContext.NumStats; - s0 = _minContext.FreqData.SummFreq - (ns) - (fs.Freq - 1); - for ( - pc.Address = _maxContext.Address; - pc.Address != _minContext.Address; - pc.Address = pc.GetSuffix() - ) - { - if ((ns1 = pc.NumStats) != 1) - { - if ((ns1 & 1) == 0) - { - //System.out.println(ns1); - pc.FreqData.SetStats( - SubAlloc.ExpandUnits(pc.FreqData.GetStats(), Utility.URShift(ns1, 1)) - ); - if (pc.FreqData.GetStats() == 0) - { - UpdateModelRestart(); - return; - } - } - - // bug fixed - // int sum = ((2 * ns1 < ns) ? 1 : 0) + - // 2 * ((4 * ((ns1 <= ns) ? 1 : 0)) & ((pc.getFreqData() - // .getSummFreq() <= 8 * ns1) ? 1 : 0)); - int sum = - ((2 * ns1 < ns) ? 1 : 0) - + 2 - * ( - ((4 * ns1 <= ns) ? 1 : 0) - & ((pc.FreqData.SummFreq <= 8 * ns1) ? 1 : 0) - ); - pc.FreqData.IncrementSummFreq(sum); - } - else - { - p.Address = SubAlloc.AllocUnits(1); - if (p.Address == 0) - { - UpdateModelRestart(); - return; - } - p.SetValues(pc.GetOneState()); - pc.FreqData.SetStats(p); - if (p.Freq < MAX_FREQ / 4 - 1) - { - p.IncrementFreq(p.Freq); - } - else - { - p.Freq = MAX_FREQ - 4; - } - pc.FreqData.SummFreq = (p.Freq + _initEsc + (ns > 3 ? 1 : 0)); - } - cf = 2 * fs.Freq * (pc.FreqData.SummFreq + 6); - sf = s0 + pc.FreqData.SummFreq; - if (cf < 6 * sf) - { - cf = 1 + (cf > sf ? 1 : 0) + (cf >= 4 * sf ? 1 : 0); - pc.FreqData.IncrementSummFreq(3); - } - else - { - cf = - 4 - + (cf >= 9 * sf ? 1 : 0) - + (cf >= 12 * sf ? 1 : 0) - + (cf >= 15 * sf ? 1 : 0); - pc.FreqData.IncrementSummFreq(cf); - } - p.Address = pc.FreqData.GetStats() + ns1 * State.SIZE; - p.SetSuccessor(successor); - p.Symbol = fs.Symbol; - p.Freq = cf; - pc.NumStats = ++ns1; - } - - int address = fs.GetSuccessor(); - _maxContext.Address = address; - _minContext.Address = address; - - //TODO-----debug - // int pos = minContext.getFreqData().getStats(); - // State a = new State(getHeap()); - // a.Address=pos); - // pos+=State.size; - // a.Address=pos); - //--dbg end - } - - // Debug - public override String ToString() - { - StringBuilder buffer = new StringBuilder(); - buffer.Append("ModelPPM["); - buffer.Append("\n numMasked="); - buffer.Append(_numMasked); - buffer.Append("\n initEsc="); - buffer.Append(_initEsc); - buffer.Append("\n orderFall="); - buffer.Append(_orderFall); - buffer.Append("\n maxOrder="); - buffer.Append(_maxOrder); - buffer.Append("\n runLength="); - buffer.Append(_runLength); - buffer.Append("\n initRL="); - buffer.Append(_initRl); - buffer.Append("\n escCount="); - buffer.Append(_escCount); - buffer.Append("\n prevSuccess="); - buffer.Append(_prevSuccess); - buffer.Append("\n foundState="); - buffer.Append(FoundState); - buffer.Append("\n coder="); - buffer.Append(Coder); - buffer.Append("\n subAlloc="); - buffer.Append(SubAlloc); - buffer.Append("\n]"); - return buffer.ToString(); - } - - // Debug - // public void dumpHeap() { - // subAlloc.dumpHeap(); - // } - - internal bool DecodeInit(Stream stream, int maxOrder, int maxMemory) - { - if (stream != null) - { - Coder = new RangeCoder(stream); - } - if (maxOrder == 1) { SubAlloc.StopSubAllocator(); return (false); } - SubAlloc.StartSubAllocator(maxMemory); + SubAlloc.StartSubAllocator((maxMb + 1) << 20); _minContext = new PpmContext(Heap); //medContext = new PPMContext(Heap); _maxContext = new PpmContext(Heap); FoundState = new State(Heap); _dummySee2Cont = new See2Context(); - for (int i = 0; i < 25; i++) + for (var i = 0; i < 25; i++) { - for (int j = 0; j < 16; j++) + for (var j = 0; j < 16; j++) { _see2Cont[i][j] = new See2Context(); } } StartModelRare(maxOrder); + } + return (_minContext.Address != 0); + } - return (_minContext.Address != 0); + public virtual int DecodeChar() + { + // Debug + //subAlloc.dumpHeap(); + + if (_minContext.Address <= SubAlloc.PText || _minContext.Address > SubAlloc.HeapEnd) + { + return (-1); } - internal void NextContext() + if (_minContext.NumStats != 1) { - int addr = FoundState.GetSuccessor(); - if (_orderFall == 0 && addr > SubAlloc.PText) + if ( + _minContext.FreqData.GetStats() <= SubAlloc.PText + || _minContext.FreqData.GetStats() > SubAlloc.HeapEnd + ) { - _minContext.Address = addr; - _maxContext.Address = addr; + return (-1); } - else + if (!_minContext.DecodeSymbol1(this)) { - UpdateModel(); + return (-1); } } - - public int DecodeChar(Decoder decoder) + else { - if (_minContext.NumStats != 1) + _minContext.DecodeBinSymbol(this); + } + Coder.Decode(); + while (FoundState.Address == 0) + { + Coder.AriDecNormalize(); + do { - State s = _tempState1.Initialize(Heap); - s.Address = _minContext.FreqData.GetStats(); - int i; - int count, - hiCnt; + _orderFall++; + _minContext.Address = _minContext.GetSuffix(); // =MinContext->Suffix; if ( - (count = (int)decoder.GetThreshold((uint)_minContext.FreqData.SummFreq)) - < (hiCnt = s.Freq) + _minContext.Address <= SubAlloc.PText + || _minContext.Address > SubAlloc.HeapEnd ) { - byte symbol; - decoder.Decode(0, (uint)s.Freq); - symbol = (byte)s.Symbol; - _minContext.update1_0(this, s.Address); - NextContext(); - return symbol; + return (-1); } - _prevSuccess = 0; - i = _minContext.NumStats - 1; - do + } while (_minContext.NumStats == _numMasked); + if (!_minContext.DecodeSymbol2(this)) + { + return (-1); + } + Coder.Decode(); + } + var symbol = FoundState.Symbol; + if ((_orderFall == 0) && FoundState.GetSuccessor() > SubAlloc.PText) + { + // MinContext=MaxContext=FoundState->Successor; + var addr = FoundState.GetSuccessor(); + _minContext.Address = addr; + _maxContext.Address = addr; + } + else + { + UpdateModel(); + + //this.foundState.Address=foundState.Address);//TODO just 4 debugging + if (_escCount == 0) + { + ClearMask(); + } + } + Coder.AriDecNormalize(); // ARI_DEC_NORMALIZE(Coder.code,Coder.low,Coder.range,Coder.UnpackRead); + return (symbol); + } + + public virtual See2Context[][] GetSee2Cont() + { + return _see2Cont; + } + + public virtual void IncEscCount(int dEscCount) + { + EscCount += dEscCount; + } + + public virtual void IncRunLength(int dRunLength) + { + RunLength += dRunLength; + } + + public virtual int[] GetHb2Flag() + { + return _hb2Flag; + } + + public virtual int[] GetNs2BsIndx() + { + return _ns2BsIndx; + } + + public virtual int[] GetNs2Indx() + { + return _ns2Indx; + } + + private int CreateSuccessors(bool skip, State p1) + { + //State upState = tempState1.Initialize(null); + var upState = _tempStateRef2; + var tempState = _tempState1.Initialize(Heap); + + // PPM_CONTEXT* pc=MinContext, * UpBranch=FoundState->Successor; + var pc = _tempPpmContext1.Initialize(Heap); + pc.Address = _minContext.Address; + var upBranch = _tempPpmContext2.Initialize(Heap); + upBranch.Address = FoundState.GetSuccessor(); + + // STATE * p, * ps[MAX_O], ** pps=ps; + var p = _tempState2.Initialize(Heap); + var pps = 0; + + var noLoop = false; + + if (!skip) + { + _ps[pps++] = FoundState.Address; // *pps++ = FoundState; + if (pc.GetSuffix() == 0) + { + noLoop = true; + } + } + if (!noLoop) + { + var loopEntry = false; + if (p1.Address != 0) + { + p.Address = p1.Address; + pc.Address = pc.GetSuffix(); // =pc->Suffix; + loopEntry = true; + } + do + { + if (!loopEntry) { - s.IncrementAddress(); - if ((hiCnt += s.Freq) > count) + pc.Address = pc.GetSuffix(); // pc=pc->Suffix; + if (pc.NumStats != 1) { - byte symbol; - decoder.Decode((uint)(hiCnt - s.Freq), (uint)s.Freq); - symbol = (byte)s.Symbol; - _minContext.Update1(this, s.Address); - NextContext(); - return symbol; + p.Address = pc.FreqData.GetStats(); // p=pc->U.Stats + if (p.Symbol != FoundState.Symbol) + { + do + { + p.IncrementAddress(); + } while (p.Symbol != FoundState.Symbol); + } } - } while (--i > 0); - if (count >= _minContext.FreqData.SummFreq) + else + { + p.Address = pc.GetOneState().Address; // p=&(pc->OneState); + } + } // LOOP_ENTRY: + loopEntry = false; + if (p.GetSuccessor() != upBranch.Address) { - return -2; + pc.Address = p.GetSuccessor(); // =p->Successor; + break; } - _hiBitsFlag = _hb2Flag[FoundState.Symbol]; - decoder.Decode((uint)hiCnt, (uint)(_minContext.FreqData.SummFreq - hiCnt)); - for (i = 0; i < 256; i++) - { - _charMask[i] = -1; - } - _charMask[s.Symbol] = 0; - i = _minContext.NumStats - 1; + _ps[pps++] = p.Address; + } while (pc.GetSuffix() != 0); + } // NO_LOOP: + if (pps == 0) + { + return pc.Address; + } + upState.Symbol = Heap[upBranch.Address]; // UpState.Symbol=*(byte*) + + // UpBranch; + // UpState.Successor=(PPM_CONTEXT*) (((byte*) UpBranch)+1); + upState.SetSuccessor(upBranch.Address + 1); //TODO check if +1 necessary + if (pc.NumStats != 1) + { + if (pc.Address <= SubAlloc.PText) + { + return (0); + } + p.Address = pc.FreqData.GetStats(); + if (p.Symbol != upState.Symbol) + { do { - s.DecrementAddress(); - _charMask[s.Symbol] = 0; - } while (--i > 0); + p.IncrementAddress(); + } while (p.Symbol != upState.Symbol); + } + var cf = p.Freq - 1; + var s0 = pc.FreqData.SummFreq - pc.NumStats - cf; + + // UpState.Freq=1+((2*cf <= s0)?(5*cf > s0):((2*cf+3*s0-1)/(2*s0))); + upState.Freq = + 1 + + ((2 * cf <= s0) ? (5 * cf > s0 ? 1 : 0) : (((2 * cf) + (3 * s0) - 1) / (2 * s0))); + } + else + { + upState.Freq = pc.GetOneState().Freq; // UpState.Freq=pc->OneState.Freq; + } + do + { + // pc = pc->createChild(this,*--pps,UpState); + tempState.Address = _ps[--pps]; + pc.Address = pc.CreateChild(this, tempState, upState); + if (pc.Address == 0) + { + return 0; + } + } while (pps != 0); + return pc.Address; + } + + private void UpdateModelRestart() + { + RestartModelRare(); + _escCount = 0; + } + + private void UpdateModel() + { + //System.out.println("ModelPPM.updateModel()"); + // STATE fs = *FoundState, *p = NULL; + var fs = _tempStateRef1; + fs.Values = FoundState; + var p = _tempState3.Initialize(Heap); + var tempState = _tempState4.Initialize(Heap); + + var pc = _tempPpmContext3.Initialize(Heap); + var successor = _tempPpmContext4.Initialize(Heap); + + int ns1, + ns, + cf, + sf, + s0; + pc.Address = _minContext.GetSuffix(); + if (fs.Freq < MAX_FREQ / 4 && pc.Address != 0) + { + if (pc.NumStats != 1) + { + p.Address = pc.FreqData.GetStats(); + if (p.Symbol != fs.Symbol) + { + do + { + p.IncrementAddress(); + } while (p.Symbol != fs.Symbol); + tempState.Address = p.Address - State.SIZE; + if (p.Freq >= tempState.Freq) + { + State.PpmdSwap(p, tempState); + p.DecrementAddress(); + } + } + if (p.Freq < MAX_FREQ - 9) + { + p.IncrementFreq(2); + pc.FreqData.IncrementSummFreq(2); + } } else { - State rs = _tempState1.Initialize(Heap); - rs.Address = _minContext.GetOneState().Address; - _hiBitsFlag = GetHb2Flag()[FoundState.Symbol]; - int off1 = rs.Freq - 1; - int off2 = _minContext.GetArrayIndex(this, rs); - int bs = _binSumm[off1][off2]; - if (decoder.DecodeBit((uint)bs, 14) == 0) + p.Address = pc.GetOneState().Address; + if (p.Freq < 32) + { + p.IncrementFreq(1); + } + } + } + if (_orderFall == 0) + { + FoundState.SetSuccessor(CreateSuccessors(true, p)); + _minContext.Address = FoundState.GetSuccessor(); + _maxContext.Address = FoundState.GetSuccessor(); + if (_minContext.Address == 0) + { + UpdateModelRestart(); + return; + } + return; + } + SubAlloc.Heap[SubAlloc.PText] = (byte)fs.Symbol; + SubAlloc.IncPText(); + successor.Address = SubAlloc.PText; + if (SubAlloc.PText >= SubAlloc.FakeUnitsStart) + { + UpdateModelRestart(); + return; + } + + // // Debug + // subAlloc.dumpHeap(); + if (fs.GetSuccessor() != 0) + { + if (fs.GetSuccessor() <= SubAlloc.PText) + { + fs.SetSuccessor(CreateSuccessors(false, p)); + if (fs.GetSuccessor() == 0) + { + UpdateModelRestart(); + return; + } + } + if (--_orderFall == 0) + { + successor.Address = fs.GetSuccessor(); + if (_maxContext.Address != _minContext.Address) + { + SubAlloc.DecPText(1); + } + } + } + else + { + FoundState.SetSuccessor(successor.Address); + fs.SetSuccessor(_minContext); + } + + // // Debug + // subAlloc.dumpHeap(); + ns = _minContext.NumStats; + s0 = _minContext.FreqData.SummFreq - (ns) - (fs.Freq - 1); + for ( + pc.Address = _maxContext.Address; + pc.Address != _minContext.Address; + pc.Address = pc.GetSuffix() + ) + { + if ((ns1 = pc.NumStats) != 1) + { + if ((ns1 & 1) == 0) + { + //System.out.println(ns1); + pc.FreqData.SetStats( + SubAlloc.ExpandUnits(pc.FreqData.GetStats(), Utility.URShift(ns1, 1)) + ); + if (pc.FreqData.GetStats() == 0) + { + UpdateModelRestart(); + return; + } + } + + // bug fixed + // int sum = ((2 * ns1 < ns) ? 1 : 0) + + // 2 * ((4 * ((ns1 <= ns) ? 1 : 0)) & ((pc.getFreqData() + // .getSummFreq() <= 8 * ns1) ? 1 : 0)); + var sum = + ((2 * ns1 < ns) ? 1 : 0) + + (2 + * ( + ((4 * ns1 <= ns) ? 1 : 0) + & ((pc.FreqData.SummFreq <= 8 * ns1) ? 1 : 0) + )); + pc.FreqData.IncrementSummFreq(sum); + } + else + { + p.Address = SubAlloc.AllocUnits(1); + if (p.Address == 0) + { + UpdateModelRestart(); + return; + } + p.SetValues(pc.GetOneState()); + pc.FreqData.SetStats(p); + if (p.Freq < (MAX_FREQ / 4) - 1) + { + p.IncrementFreq(p.Freq); + } + else + { + p.Freq = MAX_FREQ - 4; + } + pc.FreqData.SummFreq = (p.Freq + _initEsc + (ns > 3 ? 1 : 0)); + } + cf = 2 * fs.Freq * (pc.FreqData.SummFreq + 6); + sf = s0 + pc.FreqData.SummFreq; + if (cf < 6 * sf) + { + cf = 1 + (cf > sf ? 1 : 0) + (cf >= 4 * sf ? 1 : 0); + pc.FreqData.IncrementSummFreq(3); + } + else + { + cf = + 4 + + (cf >= 9 * sf ? 1 : 0) + + (cf >= 12 * sf ? 1 : 0) + + (cf >= 15 * sf ? 1 : 0); + pc.FreqData.IncrementSummFreq(cf); + } + p.Address = pc.FreqData.GetStats() + (ns1 * State.SIZE); + p.SetSuccessor(successor); + p.Symbol = fs.Symbol; + p.Freq = cf; + pc.NumStats = ++ns1; + } + + var address = fs.GetSuccessor(); + _maxContext.Address = address; + _minContext.Address = address; + + //TODO-----debug + // int pos = minContext.getFreqData().getStats(); + // State a = new State(getHeap()); + // a.Address=pos); + // pos+=State.size; + // a.Address=pos); + //--dbg end + } + + // Debug + public override string ToString() + { + var buffer = new StringBuilder(); + buffer.Append("ModelPPM["); + buffer.Append("\n numMasked="); + buffer.Append(_numMasked); + buffer.Append("\n initEsc="); + buffer.Append(_initEsc); + buffer.Append("\n orderFall="); + buffer.Append(_orderFall); + buffer.Append("\n maxOrder="); + buffer.Append(_maxOrder); + buffer.Append("\n runLength="); + buffer.Append(_runLength); + buffer.Append("\n initRL="); + buffer.Append(_initRl); + buffer.Append("\n escCount="); + buffer.Append(_escCount); + buffer.Append("\n prevSuccess="); + buffer.Append(_prevSuccess); + buffer.Append("\n foundState="); + buffer.Append(FoundState); + buffer.Append("\n coder="); + buffer.Append(Coder); + buffer.Append("\n subAlloc="); + buffer.Append(SubAlloc); + buffer.Append("\n]"); + return buffer.ToString(); + } + + // Debug + // public void dumpHeap() { + // subAlloc.dumpHeap(); + // } + + internal bool DecodeInit(Stream stream, int maxOrder, int maxMemory) + { + if (stream != null) + { + Coder = new RangeCoder(stream); + } + + if (maxOrder == 1) + { + SubAlloc.StopSubAllocator(); + return (false); + } + SubAlloc.StartSubAllocator(maxMemory); + _minContext = new PpmContext(Heap); + + //medContext = new PPMContext(Heap); + _maxContext = new PpmContext(Heap); + FoundState = new State(Heap); + _dummySee2Cont = new See2Context(); + for (var i = 0; i < 25; i++) + { + for (var j = 0; j < 16; j++) + { + _see2Cont[i][j] = new See2Context(); + } + } + StartModelRare(maxOrder); + + return (_minContext.Address != 0); + } + + internal void NextContext() + { + var addr = FoundState.GetSuccessor(); + if (_orderFall == 0 && addr > SubAlloc.PText) + { + _minContext.Address = addr; + _maxContext.Address = addr; + } + else + { + UpdateModel(); + } + } + + public int DecodeChar(Decoder decoder) + { + if (_minContext.NumStats != 1) + { + var s = _tempState1.Initialize(Heap); + s.Address = _minContext.FreqData.GetStats(); + int i; + int count, + hiCnt; + if ( + (count = (int)decoder.GetThreshold((uint)_minContext.FreqData.SummFreq)) + < (hiCnt = s.Freq) + ) + { + byte symbol; + decoder.Decode(0, (uint)s.Freq); + symbol = (byte)s.Symbol; + _minContext.update1_0(this, s.Address); + NextContext(); + return symbol; + } + _prevSuccess = 0; + i = _minContext.NumStats - 1; + do + { + s.IncrementAddress(); + if ((hiCnt += s.Freq) > count) { byte symbol; - _binSumm[off1][off2] = - (bs + INTERVAL - _minContext.GetMean(bs, PERIOD_BITS, 2)) & 0xFFFF; - FoundState.Address = rs.Address; - symbol = (byte)rs.Symbol; - rs.IncrementFreq((rs.Freq < 128) ? 1 : 0); - _prevSuccess = 1; - IncRunLength(1); + decoder.Decode((uint)(hiCnt - s.Freq), (uint)s.Freq); + symbol = (byte)s.Symbol; + _minContext.Update1(this, s.Address); NextContext(); return symbol; } - bs = (bs - _minContext.GetMean(bs, PERIOD_BITS, 2)) & 0xFFFF; - _binSumm[off1][off2] = bs; - _initEsc = PpmContext.EXP_ESCAPE[Utility.URShift(bs, 10)]; - int i; - for (i = 0; i < 256; i++) - { - _charMask[i] = -1; - } - _charMask[rs.Symbol] = 0; - _prevSuccess = 0; - } - for (; ; ) + } while (--i > 0); + if (count >= _minContext.FreqData.SummFreq) { - State s = _tempState1.Initialize(Heap); - int i; - int freqSum, - count, - hiCnt; - See2Context see; - int num, - numMasked = _minContext.NumStats; - do - { - _orderFall++; - _minContext.Address = _minContext.GetSuffix(); - if ( - _minContext.Address <= SubAlloc.PText - || _minContext.Address > SubAlloc.HeapEnd - ) - { - return -1; - } - } while (_minContext.NumStats == numMasked); - hiCnt = 0; - s.Address = _minContext.FreqData.GetStats(); - i = 0; - num = _minContext.NumStats - numMasked; - do - { - int k = _charMask[s.Symbol]; - hiCnt += s.Freq & k; - _minContext._ps[i] = s.Address; - s.IncrementAddress(); - i -= k; - } while (i != num); - - see = _minContext.MakeEscFreq(this, numMasked, out freqSum); - freqSum += hiCnt; - count = (int)decoder.GetThreshold((uint)freqSum); - - if (count < hiCnt) - { - byte symbol; - State ps = _tempState2.Initialize(Heap); - for ( - hiCnt = 0, i = 0, ps.Address = _minContext._ps[i]; - (hiCnt += ps.Freq) <= count; - i++, ps.Address = _minContext._ps[i] - ) - { - ; - } - s.Address = ps.Address; - decoder.Decode((uint)(hiCnt - s.Freq), (uint)s.Freq); - see.Update(); - symbol = (byte)s.Symbol; - _minContext.Update2(this, s.Address); - UpdateModel(); - return symbol; - } - if (count >= freqSum) - { - return -2; - } - decoder.Decode((uint)hiCnt, (uint)(freqSum - hiCnt)); - see.Summ = see.Summ + freqSum; - do - { - s.Address = _minContext._ps[--i]; - _charMask[s.Symbol] = 0; - } while (i != 0); + return -2; } + _hiBitsFlag = _hb2Flag[FoundState.Symbol]; + decoder.Decode((uint)hiCnt, (uint)(_minContext.FreqData.SummFreq - hiCnt)); + for (i = 0; i < 256; i++) + { + _charMask[i] = -1; + } + _charMask[s.Symbol] = 0; + i = _minContext.NumStats - 1; + do + { + s.DecrementAddress(); + _charMask[s.Symbol] = 0; + } while (--i > 0); + } + else + { + var rs = _tempState1.Initialize(Heap); + rs.Address = _minContext.GetOneState().Address; + _hiBitsFlag = GetHb2Flag()[FoundState.Symbol]; + var off1 = rs.Freq - 1; + var off2 = _minContext.GetArrayIndex(this, rs); + var bs = _binSumm[off1][off2]; + if (decoder.DecodeBit((uint)bs, 14) == 0) + { + byte symbol; + _binSumm[off1][off2] = + (bs + INTERVAL - _minContext.GetMean(bs, PERIOD_BITS, 2)) & 0xFFFF; + FoundState.Address = rs.Address; + symbol = (byte)rs.Symbol; + rs.IncrementFreq((rs.Freq < 128) ? 1 : 0); + _prevSuccess = 1; + IncRunLength(1); + NextContext(); + return symbol; + } + bs = (bs - _minContext.GetMean(bs, PERIOD_BITS, 2)) & 0xFFFF; + _binSumm[off1][off2] = bs; + _initEsc = PpmContext.EXP_ESCAPE[Utility.URShift(bs, 10)]; + int i; + for (i = 0; i < 256; i++) + { + _charMask[i] = -1; + } + _charMask[rs.Symbol] = 0; + _prevSuccess = 0; + } + for (; ; ) + { + var s = _tempState1.Initialize(Heap); + int i; + int count, + hiCnt; + See2Context see; + int num, + numMasked = _minContext.NumStats; + do + { + _orderFall++; + _minContext.Address = _minContext.GetSuffix(); + if ( + _minContext.Address <= SubAlloc.PText + || _minContext.Address > SubAlloc.HeapEnd + ) + { + return -1; + } + } while (_minContext.NumStats == numMasked); + hiCnt = 0; + s.Address = _minContext.FreqData.GetStats(); + i = 0; + num = _minContext.NumStats - numMasked; + do + { + var k = _charMask[s.Symbol]; + hiCnt += s.Freq & k; + _minContext._ps[i] = s.Address; + s.IncrementAddress(); + i -= k; + } while (i != num); + + see = _minContext.MakeEscFreq(this, numMasked, out var freqSum); + freqSum += hiCnt; + count = (int)decoder.GetThreshold((uint)freqSum); + + if (count < hiCnt) + { + byte symbol; + var ps = _tempState2.Initialize(Heap); + for ( + hiCnt = 0, i = 0, ps.Address = _minContext._ps[i]; + (hiCnt += ps.Freq) <= count; + i++, ps.Address = _minContext._ps[i] + ) + { + ; + } + s.Address = ps.Address; + decoder.Decode((uint)(hiCnt - s.Freq), (uint)s.Freq); + see.Update(); + symbol = (byte)s.Symbol; + _minContext.Update2(this, s.Address); + UpdateModel(); + return symbol; + } + if (count >= freqSum) + { + return -2; + } + decoder.Decode((uint)hiCnt, (uint)(freqSum - hiCnt)); + see.Summ += freqSum; + do + { + s.Address = _minContext._ps[--i]; + _charMask[s.Symbol] = 0; + } while (i != 0); } } } diff --git a/src/SharpCompress/Compressors/PPMd/H/PPMContext.cs b/src/SharpCompress/Compressors/PPMd/H/PPMContext.cs index 53d9cc7f..52768984 100644 --- a/src/SharpCompress/Compressors/PPMd/H/PPMContext.cs +++ b/src/SharpCompress/Compressors/PPMd/H/PPMContext.cs @@ -4,585 +4,581 @@ using System; using System.Buffers.Binary; using System.Text; -namespace SharpCompress.Compressors.PPMd.H +namespace SharpCompress.Compressors.PPMd.H; + +internal class PpmContext : Pointer { - internal class PpmContext : Pointer + internal FreqData FreqData { - internal FreqData FreqData + get => _freqData; + set { - get => _freqData; - set - { - _freqData.SummFreq = value.SummFreq; - _freqData.SetStats(value.GetStats()); - } - } - - public virtual int NumStats - { - get - { - if (Memory != null) - { - _numStats = - BinaryPrimitives.ReadInt16LittleEndian(Memory.AsSpan(Address)) & 0xffff; - } - return _numStats; - } - set - { - _numStats = value & 0xffff; - if (Memory != null) - { - BinaryPrimitives.WriteInt16LittleEndian(Memory.AsSpan(Address), (short)value); - } - } - } - - //UPGRADE_NOTE: Final was removed from the declaration of 'unionSize '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - //UPGRADE_NOTE: The initialization of 'unionSize' was moved to static method 'SharpCompress.Unpack.PPM.PPMContext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'" - private static readonly int UNION_SIZE; - - //UPGRADE_NOTE: Final was removed from the declaration of 'size '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - public static readonly int SIZE = 2 + UNION_SIZE + 4; // 12 - - // ushort NumStats; - private int _numStats; // determines if feqData or onstate is used - - // (1==onestate) - - //UPGRADE_NOTE: Final was removed from the declaration of 'freqData '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly FreqData _freqData; // -\ - - // |-> union - //UPGRADE_NOTE: Final was removed from the declaration of 'oneState '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _oneState; // -/ - - private int _suffix; // pointer ppmcontext - - //UPGRADE_NOTE: Final was removed from the declaration of 'ExpEscape'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - public static readonly int[] EXP_ESCAPE = - { - 25, - 14, - 9, - 7, - 5, - 5, - 4, - 4, - 4, - 3, - 3, - 3, - 2, - 2, - 2, - 2 - }; - - // Temp fields - //UPGRADE_NOTE: Final was removed from the declaration of 'tempState1 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState1 = new State(null); - - //UPGRADE_NOTE: Final was removed from the declaration of 'tempState2 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState2 = new State(null); - - //UPGRADE_NOTE: Final was removed from the declaration of 'tempState3 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState3 = new State(null); - - //UPGRADE_NOTE: Final was removed from the declaration of 'tempState4 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState4 = new State(null); - - //UPGRADE_NOTE: Final was removed from the declaration of 'tempState5 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State _tempState5 = new State(null); - private PpmContext _tempPpmContext; - - //UPGRADE_NOTE: Final was removed from the declaration of 'ps '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - internal int[] _ps = new int[256]; - - public PpmContext(byte[] memory) : base(memory) - { - _oneState = new State(memory); - _freqData = new FreqData(memory); - } - - internal PpmContext Initialize(byte[] mem) - { - _oneState.Initialize(mem); - _freqData.Initialize(mem); - return base.Initialize(mem); - } - - internal State GetOneState() - { - return _oneState; - } - - internal void SetOneState(StateRef oneState) - { - _oneState.SetValues(oneState); - } - - internal int GetSuffix() - { - if (Memory != null) - { - _suffix = BinaryPrimitives.ReadInt32LittleEndian(Memory.AsSpan(Address + 8)); - } - return _suffix; - } - - internal void SetSuffix(PpmContext suffix) - { - SetSuffix(suffix.Address); - } - - internal void SetSuffix(int suffix) - { - _suffix = suffix; - if (Memory != null) - { - BinaryPrimitives.WriteInt32LittleEndian(Memory.AsSpan(Address + 8), suffix); - } - } - - internal override int Address - { - get => base.Address; - set - { - base.Address = value; - _oneState.Address = value + 2; - _freqData.Address = value + 2; - } - } - - private PpmContext GetTempPpmContext(byte[] memory) - { - if (_tempPpmContext is null) - { - _tempPpmContext = new PpmContext(null); - } - return _tempPpmContext.Initialize(memory); - } - - internal int CreateChild(ModelPpm model, State pStats, StateRef firstState) - { - PpmContext pc = GetTempPpmContext(model.SubAlloc.Heap); - pc.Address = model.SubAlloc.AllocContext(); - if (pc != null) - { - pc.NumStats = 1; - pc.SetOneState(firstState); - pc.SetSuffix(this); - pStats.SetSuccessor(pc); - } - return pc.Address; - } - - internal void Rescale(ModelPpm model) - { - int oldNs = NumStats, - i = NumStats - 1, - adder, - escFreq; - - // STATE* p1, * p; - State p1 = new State(model.Heap); - State p = new State(model.Heap); - State temp = new State(model.Heap); - - for ( - p.Address = model.FoundState.Address; - p.Address != _freqData.GetStats(); - p.DecrementAddress() - ) - { - temp.Address = p.Address - State.SIZE; - State.PpmdSwap(p, temp); - } - temp.Address = _freqData.GetStats(); - temp.IncrementFreq(4); - _freqData.IncrementSummFreq(4); - escFreq = _freqData.SummFreq - p.Freq; - adder = (model.OrderFall != 0) ? 1 : 0; - p.Freq = Utility.URShift((p.Freq + adder), 1); - _freqData.SummFreq = p.Freq; - do - { - p.IncrementAddress(); - escFreq -= p.Freq; - p.Freq = Utility.URShift((p.Freq + adder), 1); - _freqData.IncrementSummFreq(p.Freq); - temp.Address = p.Address - State.SIZE; - if (p.Freq > temp.Freq) - { - p1.Address = p.Address; - StateRef tmp = new StateRef(); - tmp.Values = p1; - State temp2 = new State(model.Heap); - State temp3 = new State(model.Heap); - do - { - // p1[0]=p1[-1]; - temp2.Address = p1.Address - State.SIZE; - p1.SetValues(temp2); - p1.DecrementAddress(); - temp3.Address = p1.Address - State.SIZE; - } while (p1.Address != _freqData.GetStats() && tmp.Freq > temp3.Freq); - p1.SetValues(tmp); - } - } while (--i != 0); - if (p.Freq == 0) - { - do - { - i++; - p.DecrementAddress(); - } while (p.Freq == 0); - escFreq += i; - NumStats = NumStats - i; - if (NumStats == 1) - { - StateRef tmp = new StateRef(); - temp.Address = _freqData.GetStats(); - tmp.Values = temp; - - // STATE tmp=*U.Stats; - do - { - // tmp.Freq-=(tmp.Freq >> 1) - tmp.DecrementFreq(Utility.URShift(tmp.Freq, 1)); - escFreq = Utility.URShift(escFreq, 1); - } while (escFreq > 1); - model.SubAlloc.FreeUnits(_freqData.GetStats(), Utility.URShift((oldNs + 1), 1)); - _oneState.SetValues(tmp); - model.FoundState.Address = _oneState.Address; - return; - } - } - escFreq -= Utility.URShift(escFreq, 1); - _freqData.IncrementSummFreq(escFreq); - int n0 = Utility.URShift((oldNs + 1), 1), - n1 = Utility.URShift((NumStats + 1), 1); - if (n0 != n1) - { - _freqData.SetStats(model.SubAlloc.ShrinkUnits(_freqData.GetStats(), n0, n1)); - } - model.FoundState.Address = _freqData.GetStats(); - } - - internal int GetArrayIndex(ModelPpm model, State rs) - { - PpmContext tempSuffix = GetTempPpmContext(model.SubAlloc.Heap); - tempSuffix.Address = GetSuffix(); - int ret = 0; - ret += model.PrevSuccess; - ret += model.GetNs2BsIndx()[tempSuffix.NumStats - 1]; - ret += model.HiBitsFlag + 2 * model.GetHb2Flag()[rs.Symbol]; - ret += ((Utility.URShift(model.RunLength, 26)) & 0x20); - return ret; - } - - internal int GetMean(int summ, int shift, int round) - { - return (Utility.URShift((summ + (1 << (shift - round))), (shift))); - } - - internal void DecodeBinSymbol(ModelPpm model) - { - State rs = _tempState1.Initialize(model.Heap); - rs.Address = _oneState.Address; // State& - model.HiBitsFlag = model.GetHb2Flag()[model.FoundState.Symbol]; - int off1 = rs.Freq - 1; - int off2 = GetArrayIndex(model, rs); - int bs = model.BinSumm[off1][off2]; - if (model.Coder.GetCurrentShiftCount(ModelPpm.TOT_BITS) < bs) - { - model.FoundState.Address = rs.Address; - rs.IncrementFreq((rs.Freq < 128) ? 1 : 0); - model.Coder.SubRange.LowCount = 0; - model.Coder.SubRange.HighCount = bs; - bs = ((bs + ModelPpm.INTERVAL - GetMean(bs, ModelPpm.PERIOD_BITS, 2)) & 0xffff); - model.BinSumm[off1][off2] = bs; - model.PrevSuccess = 1; - model.IncRunLength(1); - } - else - { - model.Coder.SubRange.LowCount = bs; - bs = (bs - GetMean(bs, ModelPpm.PERIOD_BITS, 2)) & 0xFFFF; - model.BinSumm[off1][off2] = bs; - model.Coder.SubRange.HighCount = ModelPpm.BIN_SCALE; - model.InitEsc = EXP_ESCAPE[Utility.URShift(bs, 10)]; - model.NumMasked = 1; - model.CharMask[rs.Symbol] = model.EscCount; - model.PrevSuccess = 0; - model.FoundState.Address = 0; - } - - //int a = 0;//TODO just 4 debugging - } - - // public static void ppmdSwap(ModelPPM model, StatePtr state1, StatePtr state2) - // { - // byte[] bytes = model.getSubAlloc().getHeap(); - // int p1 = state1.Address; - // int p2 = state2.Address; - // - // for (int i = 0; i < StatePtr.size; i++) { - // byte temp = bytes[p1+i]; - // bytes[p1+i] = bytes[p2+i]; - // bytes[p2+i] = temp; - // } - // state1.Address=p1); - // state2.Address=p2); - // } - - internal void Update1(ModelPpm model, int p) - { - model.FoundState.Address = p; - model.FoundState.IncrementFreq(4); - _freqData.IncrementSummFreq(4); - State p0 = _tempState3.Initialize(model.Heap); - State p1 = _tempState4.Initialize(model.Heap); - p0.Address = p; - p1.Address = p - State.SIZE; - if (p0.Freq > p1.Freq) - { - State.PpmdSwap(p0, p1); - model.FoundState.Address = p1.Address; - if (p1.Freq > ModelPpm.MAX_FREQ) - { - Rescale(model); - } - } - } - - internal void update1_0(ModelPpm model, int p) - { - model.FoundState.Address = p; - model.PrevSuccess = 2 * model.FoundState.Freq > _freqData.SummFreq ? 1 : 0; - model.IncRunLength(model.PrevSuccess); - _freqData.IncrementSummFreq(4); - model.FoundState.IncrementFreq(4); - if (model.FoundState.Freq > ModelPpm.MAX_FREQ) - { - Rescale(model); - } - } - - internal bool DecodeSymbol2(ModelPpm model) - { - long count; - int hiCnt, - i = NumStats - model.NumMasked; - See2Context psee2C = MakeEscFreq2(model, i); - RangeCoder coder = model.Coder; - - // STATE* ps[256], ** pps=ps, * p=U.Stats-1; - State p = _tempState1.Initialize(model.Heap); - State temp = _tempState2.Initialize(model.Heap); - p.Address = _freqData.GetStats() - State.SIZE; - int pps = 0; - hiCnt = 0; - - do - { - do - { - p.IncrementAddress(); // p++; - } while (model.CharMask[p.Symbol] == model.EscCount); - hiCnt += p.Freq; - _ps[pps++] = p.Address; - } while (--i != 0); - coder.SubRange.IncScale(hiCnt); - count = coder.CurrentCount; - if (count >= coder.SubRange.Scale) - { - return false; - } - pps = 0; - p.Address = _ps[pps]; - if (count < hiCnt) - { - hiCnt = 0; - while ((hiCnt += p.Freq) <= count) - { - p.Address = _ps[++pps]; // p=*++pps; - } - coder.SubRange.HighCount = hiCnt; - coder.SubRange.LowCount = hiCnt - p.Freq; - psee2C.Update(); - Update2(model, p.Address); - } - else - { - coder.SubRange.LowCount = hiCnt; - coder.SubRange.HighCount = coder.SubRange.Scale; - i = NumStats - model.NumMasked; // ->NumMasked; - pps--; - do - { - temp.Address = _ps[++pps]; // (*++pps) - model.CharMask[temp.Symbol] = model.EscCount; - } while (--i != 0); - psee2C.IncSumm((int)coder.SubRange.Scale); - model.NumMasked = NumStats; - } - return (true); - } - - internal void Update2(ModelPpm model, int p) - { - State temp = _tempState5.Initialize(model.Heap); - temp.Address = p; - model.FoundState.Address = p; - model.FoundState.IncrementFreq(4); - _freqData.IncrementSummFreq(4); - if (temp.Freq > ModelPpm.MAX_FREQ) - { - Rescale(model); - } - model.IncEscCount(1); - model.RunLength = model.InitRl; - } - - private See2Context MakeEscFreq2(ModelPpm model, int diff) - { - See2Context psee2C; - int numStats = NumStats; - if (numStats != 256) - { - PpmContext suff = GetTempPpmContext(model.Heap); - suff.Address = GetSuffix(); - int idx1 = model.GetNs2Indx()[diff - 1]; - int idx2 = 0; - idx2 += ((diff < suff.NumStats - numStats) ? 1 : 0); - idx2 += 2 * ((_freqData.SummFreq < 11 * numStats) ? 1 : 0); - idx2 += 4 * ((model.NumMasked > diff) ? 1 : 0); - idx2 += model.HiBitsFlag; - psee2C = model.GetSee2Cont()[idx1][idx2]; - model.Coder.SubRange.Scale = psee2C.Mean; - } - else - { - psee2C = model.DummySee2Cont; - model.Coder.SubRange.Scale = 1; - } - return psee2C; - } - - internal See2Context MakeEscFreq(ModelPpm model, int numMasked, out int escFreq) - { - See2Context psee2C; - int numStats = NumStats; - int nonMasked = numStats - numMasked; - if (numStats != 256) - { - PpmContext suff = GetTempPpmContext(model.Heap); - suff.Address = GetSuffix(); - int idx1 = model.GetNs2Indx()[nonMasked - 1]; - int idx2 = 0; - idx2 += ((nonMasked < suff.NumStats - numStats) ? 1 : 0); - idx2 += 2 * ((_freqData.SummFreq < 11 * numStats) ? 1 : 0); - idx2 += 4 * ((numMasked > nonMasked) ? 1 : 0); - idx2 += model.HiBitsFlag; - psee2C = model.GetSee2Cont()[idx1][idx2]; - escFreq = psee2C.Mean; - } - else - { - psee2C = model.DummySee2Cont; - escFreq = 1; - } - return psee2C; - } - - internal bool DecodeSymbol1(ModelPpm model) - { - RangeCoder coder = model.Coder; - coder.SubRange.Scale = _freqData.SummFreq; - State p = new State(model.Heap); - p.Address = _freqData.GetStats(); - int i, - hiCnt; - long count = coder.CurrentCount; - if (count >= coder.SubRange.Scale) - { - return false; - } - if (count < (hiCnt = p.Freq)) - { - coder.SubRange.HighCount = hiCnt; - model.PrevSuccess = (2 * hiCnt > coder.SubRange.Scale) ? 1 : 0; - model.IncRunLength(model.PrevSuccess); - hiCnt += 4; - model.FoundState.Address = p.Address; - model.FoundState.Freq = hiCnt; - _freqData.IncrementSummFreq(4); - if (hiCnt > ModelPpm.MAX_FREQ) - { - Rescale(model); - } - coder.SubRange.LowCount = 0; - return true; - } - if (model.FoundState.Address == 0) - { - return (false); - } - model.PrevSuccess = 0; - int numStats = NumStats; - i = numStats - 1; - while ((hiCnt += p.IncrementAddress().Freq) <= count) - { - if (--i == 0) - { - model.HiBitsFlag = model.GetHb2Flag()[model.FoundState.Symbol]; - coder.SubRange.LowCount = hiCnt; - model.CharMask[p.Symbol] = model.EscCount; - model.NumMasked = numStats; - i = numStats - 1; - model.FoundState.Address = 0; - do - { - model.CharMask[p.DecrementAddress().Symbol] = model.EscCount; - } while (--i != 0); - coder.SubRange.HighCount = coder.SubRange.Scale; - return (true); - } - } - coder.SubRange.LowCount = hiCnt - p.Freq; - coder.SubRange.HighCount = hiCnt; - Update1(model, p.Address); - return (true); - } - - public override String ToString() - { - StringBuilder buffer = new StringBuilder(); - buffer.Append("PPMContext["); - buffer.Append("\n Address="); - buffer.Append(Address); - buffer.Append("\n size="); - buffer.Append(SIZE); - buffer.Append("\n numStats="); - buffer.Append(NumStats); - buffer.Append("\n Suffix="); - buffer.Append(GetSuffix()); - buffer.Append("\n freqData="); - buffer.Append(_freqData); - buffer.Append("\n oneState="); - buffer.Append(_oneState); - buffer.Append("\n]"); - return buffer.ToString(); - } - - static PpmContext() - { - UNION_SIZE = Math.Max(FreqData.SIZE, State.SIZE); + _freqData.SummFreq = value.SummFreq; + _freqData.SetStats(value.GetStats()); } } + + public virtual int NumStats + { + get + { + if (Memory != null) + { + _numStats = + BinaryPrimitives.ReadInt16LittleEndian(Memory.AsSpan(Address)) & 0xffff; + } + return _numStats; + } + set + { + _numStats = value & 0xffff; + if (Memory != null) + { + BinaryPrimitives.WriteInt16LittleEndian(Memory.AsSpan(Address), (short)value); + } + } + } + + //UPGRADE_NOTE: Final was removed from the declaration of 'unionSize '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + //UPGRADE_NOTE: The initialization of 'unionSize' was moved to static method 'SharpCompress.Unpack.PPM.PPMContext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'" + private static readonly int UNION_SIZE; + + //UPGRADE_NOTE: Final was removed from the declaration of 'size '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly int SIZE = 2 + UNION_SIZE + 4; // 12 + + // ushort NumStats; + private int _numStats; // determines if feqData or onstate is used + + // (1==onestate) + + //UPGRADE_NOTE: Final was removed from the declaration of 'freqData '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly FreqData _freqData; // -\ + + // |-> union + //UPGRADE_NOTE: Final was removed from the declaration of 'oneState '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly State _oneState; // -/ + + private int _suffix; // pointer ppmcontext + + //UPGRADE_NOTE: Final was removed from the declaration of 'ExpEscape'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly int[] EXP_ESCAPE = + { + 25, + 14, + 9, + 7, + 5, + 5, + 4, + 4, + 4, + 3, + 3, + 3, + 2, + 2, + 2, + 2 + }; + + // Temp fields + //UPGRADE_NOTE: Final was removed from the declaration of 'tempState1 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly State _tempState1 = new State(null); + + //UPGRADE_NOTE: Final was removed from the declaration of 'tempState2 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly State _tempState2 = new State(null); + + //UPGRADE_NOTE: Final was removed from the declaration of 'tempState3 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly State _tempState3 = new State(null); + + //UPGRADE_NOTE: Final was removed from the declaration of 'tempState4 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly State _tempState4 = new State(null); + + //UPGRADE_NOTE: Final was removed from the declaration of 'tempState5 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly State _tempState5 = new State(null); + private PpmContext _tempPpmContext; + + //UPGRADE_NOTE: Final was removed from the declaration of 'ps '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal int[] _ps = new int[256]; + + public PpmContext(byte[] memory) : base(memory) + { + _oneState = new State(memory); + _freqData = new FreqData(memory); + } + + internal PpmContext Initialize(byte[] mem) + { + _oneState.Initialize(mem); + _freqData.Initialize(mem); + return Initialize(mem); + } + + internal State GetOneState() + { + return _oneState; + } + + internal void SetOneState(StateRef oneState) + { + _oneState.SetValues(oneState); + } + + internal int GetSuffix() + { + if (Memory != null) + { + _suffix = BinaryPrimitives.ReadInt32LittleEndian(Memory.AsSpan(Address + 8)); + } + return _suffix; + } + + internal void SetSuffix(PpmContext suffix) + { + SetSuffix(suffix.Address); + } + + internal void SetSuffix(int suffix) + { + _suffix = suffix; + if (Memory != null) + { + BinaryPrimitives.WriteInt32LittleEndian(Memory.AsSpan(Address + 8), suffix); + } + } + + internal override int Address + { + get => base.Address; + set + { + base.Address = value; + _oneState.Address = value + 2; + _freqData.Address = value + 2; + } + } + + private PpmContext GetTempPpmContext(byte[] memory) + { + _tempPpmContext ??= new PpmContext(null); + return _tempPpmContext.Initialize(memory); + } + + internal int CreateChild(ModelPpm model, State pStats, StateRef firstState) + { + var pc = GetTempPpmContext(model.SubAlloc.Heap); + pc.Address = model.SubAlloc.AllocContext(); + if (pc != null) + { + pc.NumStats = 1; + pc.SetOneState(firstState); + pc.SetSuffix(this); + pStats.SetSuccessor(pc); + } + return pc.Address; + } + + internal void Rescale(ModelPpm model) + { + int oldNs = NumStats, + i = NumStats - 1, + adder, + escFreq; + + // STATE* p1, * p; + var p1 = new State(model.Heap); + var p = new State(model.Heap); + var temp = new State(model.Heap); + + for ( + p.Address = model.FoundState.Address; + p.Address != _freqData.GetStats(); + p.DecrementAddress() + ) + { + temp.Address = p.Address - State.SIZE; + State.PpmdSwap(p, temp); + } + temp.Address = _freqData.GetStats(); + temp.IncrementFreq(4); + _freqData.IncrementSummFreq(4); + escFreq = _freqData.SummFreq - p.Freq; + adder = (model.OrderFall != 0) ? 1 : 0; + p.Freq = Utility.URShift((p.Freq + adder), 1); + _freqData.SummFreq = p.Freq; + do + { + p.IncrementAddress(); + escFreq -= p.Freq; + p.Freq = Utility.URShift((p.Freq + adder), 1); + _freqData.IncrementSummFreq(p.Freq); + temp.Address = p.Address - State.SIZE; + if (p.Freq > temp.Freq) + { + p1.Address = p.Address; + var tmp = new StateRef(); + tmp.Values = p1; + var temp2 = new State(model.Heap); + var temp3 = new State(model.Heap); + do + { + // p1[0]=p1[-1]; + temp2.Address = p1.Address - State.SIZE; + p1.SetValues(temp2); + p1.DecrementAddress(); + temp3.Address = p1.Address - State.SIZE; + } while (p1.Address != _freqData.GetStats() && tmp.Freq > temp3.Freq); + p1.SetValues(tmp); + } + } while (--i != 0); + if (p.Freq == 0) + { + do + { + i++; + p.DecrementAddress(); + } while (p.Freq == 0); + escFreq += i; + NumStats -= i; + if (NumStats == 1) + { + var tmp = new StateRef(); + temp.Address = _freqData.GetStats(); + tmp.Values = temp; + + // STATE tmp=*U.Stats; + do + { + // tmp.Freq-=(tmp.Freq >> 1) + tmp.DecrementFreq(Utility.URShift(tmp.Freq, 1)); + escFreq = Utility.URShift(escFreq, 1); + } while (escFreq > 1); + model.SubAlloc.FreeUnits(_freqData.GetStats(), Utility.URShift((oldNs + 1), 1)); + _oneState.SetValues(tmp); + model.FoundState.Address = _oneState.Address; + return; + } + } + escFreq -= Utility.URShift(escFreq, 1); + _freqData.IncrementSummFreq(escFreq); + int n0 = Utility.URShift((oldNs + 1), 1), + n1 = Utility.URShift((NumStats + 1), 1); + if (n0 != n1) + { + _freqData.SetStats(model.SubAlloc.ShrinkUnits(_freqData.GetStats(), n0, n1)); + } + model.FoundState.Address = _freqData.GetStats(); + } + + internal int GetArrayIndex(ModelPpm model, State rs) + { + var tempSuffix = GetTempPpmContext(model.SubAlloc.Heap); + tempSuffix.Address = GetSuffix(); + var ret = 0; + ret += model.PrevSuccess; + ret += model.GetNs2BsIndx()[tempSuffix.NumStats - 1]; + ret += model.HiBitsFlag + (2 * model.GetHb2Flag()[rs.Symbol]); + ret += ((Utility.URShift(model.RunLength, 26)) & 0x20); + return ret; + } + + internal int GetMean(int summ, int shift, int round) + { + return (Utility.URShift((summ + (1 << (shift - round))), (shift))); + } + + internal void DecodeBinSymbol(ModelPpm model) + { + var rs = _tempState1.Initialize(model.Heap); + rs.Address = _oneState.Address; // State& + model.HiBitsFlag = model.GetHb2Flag()[model.FoundState.Symbol]; + var off1 = rs.Freq - 1; + var off2 = GetArrayIndex(model, rs); + var bs = model.BinSumm[off1][off2]; + if (model.Coder.GetCurrentShiftCount(ModelPpm.TOT_BITS) < bs) + { + model.FoundState.Address = rs.Address; + rs.IncrementFreq((rs.Freq < 128) ? 1 : 0); + model.Coder.SubRange.LowCount = 0; + model.Coder.SubRange.HighCount = bs; + bs = ((bs + ModelPpm.INTERVAL - GetMean(bs, ModelPpm.PERIOD_BITS, 2)) & 0xffff); + model.BinSumm[off1][off2] = bs; + model.PrevSuccess = 1; + model.IncRunLength(1); + } + else + { + model.Coder.SubRange.LowCount = bs; + bs = (bs - GetMean(bs, ModelPpm.PERIOD_BITS, 2)) & 0xFFFF; + model.BinSumm[off1][off2] = bs; + model.Coder.SubRange.HighCount = ModelPpm.BIN_SCALE; + model.InitEsc = EXP_ESCAPE[Utility.URShift(bs, 10)]; + model.NumMasked = 1; + model.CharMask[rs.Symbol] = model.EscCount; + model.PrevSuccess = 0; + model.FoundState.Address = 0; + } + + //int a = 0;//TODO just 4 debugging + } + + // public static void ppmdSwap(ModelPPM model, StatePtr state1, StatePtr state2) + // { + // byte[] bytes = model.getSubAlloc().getHeap(); + // int p1 = state1.Address; + // int p2 = state2.Address; + // + // for (int i = 0; i < StatePtr.size; i++) { + // byte temp = bytes[p1+i]; + // bytes[p1+i] = bytes[p2+i]; + // bytes[p2+i] = temp; + // } + // state1.Address=p1); + // state2.Address=p2); + // } + + internal void Update1(ModelPpm model, int p) + { + model.FoundState.Address = p; + model.FoundState.IncrementFreq(4); + _freqData.IncrementSummFreq(4); + var p0 = _tempState3.Initialize(model.Heap); + var p1 = _tempState4.Initialize(model.Heap); + p0.Address = p; + p1.Address = p - State.SIZE; + if (p0.Freq > p1.Freq) + { + State.PpmdSwap(p0, p1); + model.FoundState.Address = p1.Address; + if (p1.Freq > ModelPpm.MAX_FREQ) + { + Rescale(model); + } + } + } + + internal void update1_0(ModelPpm model, int p) + { + model.FoundState.Address = p; + model.PrevSuccess = 2 * model.FoundState.Freq > _freqData.SummFreq ? 1 : 0; + model.IncRunLength(model.PrevSuccess); + _freqData.IncrementSummFreq(4); + model.FoundState.IncrementFreq(4); + if (model.FoundState.Freq > ModelPpm.MAX_FREQ) + { + Rescale(model); + } + } + + internal bool DecodeSymbol2(ModelPpm model) + { + long count; + int hiCnt, + i = NumStats - model.NumMasked; + var psee2C = MakeEscFreq2(model, i); + var coder = model.Coder; + + // STATE* ps[256], ** pps=ps, * p=U.Stats-1; + var p = _tempState1.Initialize(model.Heap); + var temp = _tempState2.Initialize(model.Heap); + p.Address = _freqData.GetStats() - State.SIZE; + var pps = 0; + hiCnt = 0; + + do + { + do + { + p.IncrementAddress(); // p++; + } while (model.CharMask[p.Symbol] == model.EscCount); + hiCnt += p.Freq; + _ps[pps++] = p.Address; + } while (--i != 0); + coder.SubRange.IncScale(hiCnt); + count = coder.CurrentCount; + if (count >= coder.SubRange.Scale) + { + return false; + } + pps = 0; + p.Address = _ps[pps]; + if (count < hiCnt) + { + hiCnt = 0; + while ((hiCnt += p.Freq) <= count) + { + p.Address = _ps[++pps]; // p=*++pps; + } + coder.SubRange.HighCount = hiCnt; + coder.SubRange.LowCount = hiCnt - p.Freq; + psee2C.Update(); + Update2(model, p.Address); + } + else + { + coder.SubRange.LowCount = hiCnt; + coder.SubRange.HighCount = coder.SubRange.Scale; + i = NumStats - model.NumMasked; // ->NumMasked; + pps--; + do + { + temp.Address = _ps[++pps]; // (*++pps) + model.CharMask[temp.Symbol] = model.EscCount; + } while (--i != 0); + psee2C.IncSumm((int)coder.SubRange.Scale); + model.NumMasked = NumStats; + } + return (true); + } + + internal void Update2(ModelPpm model, int p) + { + var temp = _tempState5.Initialize(model.Heap); + temp.Address = p; + model.FoundState.Address = p; + model.FoundState.IncrementFreq(4); + _freqData.IncrementSummFreq(4); + if (temp.Freq > ModelPpm.MAX_FREQ) + { + Rescale(model); + } + model.IncEscCount(1); + model.RunLength = model.InitRl; + } + + private See2Context MakeEscFreq2(ModelPpm model, int diff) + { + See2Context psee2C; + var numStats = NumStats; + if (numStats != 256) + { + var suff = GetTempPpmContext(model.Heap); + suff.Address = GetSuffix(); + var idx1 = model.GetNs2Indx()[diff - 1]; + var idx2 = 0; + idx2 += ((diff < suff.NumStats - numStats) ? 1 : 0); + idx2 += 2 * ((_freqData.SummFreq < 11 * numStats) ? 1 : 0); + idx2 += 4 * ((model.NumMasked > diff) ? 1 : 0); + idx2 += model.HiBitsFlag; + psee2C = model.GetSee2Cont()[idx1][idx2]; + model.Coder.SubRange.Scale = psee2C.Mean; + } + else + { + psee2C = model.DummySee2Cont; + model.Coder.SubRange.Scale = 1; + } + return psee2C; + } + + internal See2Context MakeEscFreq(ModelPpm model, int numMasked, out int escFreq) + { + See2Context psee2C; + var numStats = NumStats; + var nonMasked = numStats - numMasked; + if (numStats != 256) + { + var suff = GetTempPpmContext(model.Heap); + suff.Address = GetSuffix(); + var idx1 = model.GetNs2Indx()[nonMasked - 1]; + var idx2 = 0; + idx2 += ((nonMasked < suff.NumStats - numStats) ? 1 : 0); + idx2 += 2 * ((_freqData.SummFreq < 11 * numStats) ? 1 : 0); + idx2 += 4 * ((numMasked > nonMasked) ? 1 : 0); + idx2 += model.HiBitsFlag; + psee2C = model.GetSee2Cont()[idx1][idx2]; + escFreq = psee2C.Mean; + } + else + { + psee2C = model.DummySee2Cont; + escFreq = 1; + } + return psee2C; + } + + internal bool DecodeSymbol1(ModelPpm model) + { + var coder = model.Coder; + coder.SubRange.Scale = _freqData.SummFreq; + var p = new State(model.Heap); + p.Address = _freqData.GetStats(); + int i, + hiCnt; + long count = coder.CurrentCount; + if (count >= coder.SubRange.Scale) + { + return false; + } + if (count < (hiCnt = p.Freq)) + { + coder.SubRange.HighCount = hiCnt; + model.PrevSuccess = (2 * hiCnt > coder.SubRange.Scale) ? 1 : 0; + model.IncRunLength(model.PrevSuccess); + hiCnt += 4; + model.FoundState.Address = p.Address; + model.FoundState.Freq = hiCnt; + _freqData.IncrementSummFreq(4); + if (hiCnt > ModelPpm.MAX_FREQ) + { + Rescale(model); + } + coder.SubRange.LowCount = 0; + return true; + } + if (model.FoundState.Address == 0) + { + return (false); + } + model.PrevSuccess = 0; + var numStats = NumStats; + i = numStats - 1; + while ((hiCnt += p.IncrementAddress().Freq) <= count) + { + if (--i == 0) + { + model.HiBitsFlag = model.GetHb2Flag()[model.FoundState.Symbol]; + coder.SubRange.LowCount = hiCnt; + model.CharMask[p.Symbol] = model.EscCount; + model.NumMasked = numStats; + i = numStats - 1; + model.FoundState.Address = 0; + do + { + model.CharMask[p.DecrementAddress().Symbol] = model.EscCount; + } while (--i != 0); + coder.SubRange.HighCount = coder.SubRange.Scale; + return (true); + } + } + coder.SubRange.LowCount = hiCnt - p.Freq; + coder.SubRange.HighCount = hiCnt; + Update1(model, p.Address); + return (true); + } + + public override string ToString() + { + var buffer = new StringBuilder(); + buffer.Append("PPMContext["); + buffer.Append("\n Address="); + buffer.Append(Address); + buffer.Append("\n size="); + buffer.Append(SIZE); + buffer.Append("\n numStats="); + buffer.Append(NumStats); + buffer.Append("\n Suffix="); + buffer.Append(GetSuffix()); + buffer.Append("\n freqData="); + buffer.Append(_freqData); + buffer.Append("\n oneState="); + buffer.Append(_oneState); + buffer.Append("\n]"); + return buffer.ToString(); + } + + static PpmContext() + { + UNION_SIZE = Math.Max(FreqData.SIZE, State.SIZE); + } } diff --git a/src/SharpCompress/Compressors/PPMd/H/Pointer.cs b/src/SharpCompress/Compressors/PPMd/H/Pointer.cs index 40a15c30..b7f04d48 100644 --- a/src/SharpCompress/Compressors/PPMd/H/Pointer.cs +++ b/src/SharpCompress/Compressors/PPMd/H/Pointer.cs @@ -1,26 +1,25 @@ #nullable disable -namespace SharpCompress.Compressors.PPMd.H +namespace SharpCompress.Compressors.PPMd.H; + +internal abstract class Pointer { - internal abstract class Pointer + /// Initialize the object with the array (may be null) + /// the byte array + /// + internal Pointer(byte[] mem) { - /// Initialize the object with the array (may be null) - /// the byte array - /// - internal Pointer(byte[] mem) - { - Memory = mem; - } + Memory = mem; + } - internal byte[] Memory { get; private set; } + internal byte[] Memory { get; private set; } - internal virtual int Address { get; set; } + internal virtual int Address { get; set; } - protected T Initialize(byte[] mem) where T : Pointer - { - Memory = mem; - Address = 0; - return this as T; - } + protected T Initialize(byte[] mem) where T : Pointer + { + Memory = mem; + Address = 0; + return this as T; } } diff --git a/src/SharpCompress/Compressors/PPMd/H/RangeCoder.cs b/src/SharpCompress/Compressors/PPMd/H/RangeCoder.cs index 1d87849b..67f437ad 100644 --- a/src/SharpCompress/Compressors/PPMd/H/RangeCoder.cs +++ b/src/SharpCompress/Compressors/PPMd/H/RangeCoder.cs @@ -1,173 +1,171 @@ #nullable disable -using System; using System.IO; using System.Text; using SharpCompress.Compressors.Rar; -namespace SharpCompress.Compressors.PPMd.H +namespace SharpCompress.Compressors.PPMd.H; + +internal class RangeCoder { - internal class RangeCoder + internal const int TOP = 1 << 24; + internal const int BOT = 1 << 15; + internal const long UINT_MASK = 0xFFFFffffL; + + // uint low, code, range; + private long _low, + _code, + _range; + private readonly IRarUnpack _unpackRead; + private readonly Stream _stream; + + internal RangeCoder(IRarUnpack unpackRead) { - internal const int TOP = 1 << 24; - internal const int BOT = 1 << 15; - internal const long UINT_MASK = 0xFFFFffffL; + _unpackRead = unpackRead; + Init(); + } - // uint low, code, range; - private long _low, - _code, - _range; - private readonly IRarUnpack _unpackRead; - private readonly Stream _stream; + internal RangeCoder(Stream stream) + { + _stream = stream; + Init(); + } - internal RangeCoder(IRarUnpack unpackRead) + private void Init() + { + SubRange = new SubRange(); + + _low = _code = 0L; + _range = 0xFFFFffffL; + for (var i = 0; i < 4; i++) { - _unpackRead = unpackRead; - Init(); - } - - internal RangeCoder(Stream stream) - { - _stream = stream; - Init(); - } - - private void Init() - { - SubRange = new SubRange(); - - _low = _code = 0L; - _range = 0xFFFFffffL; - for (int i = 0; i < 4; i++) - { - _code = ((_code << 8) | Char) & UINT_MASK; - } - } - - internal int CurrentCount - { - get - { - _range = (_range / SubRange.Scale) & UINT_MASK; - return (int)((_code - _low) / (_range)); - } - } - - private long Char - { - get - { - if (_unpackRead != null) - { - return (_unpackRead.Char); - } - if (_stream != null) - { - return _stream.ReadByte(); - } - return -1; - } - } - - internal SubRange SubRange { get; private set; } - - internal long GetCurrentShiftCount(int shift) - { - _range = Utility.URShift(_range, shift); - return ((_code - _low) / (_range)) & UINT_MASK; - } - - internal void Decode() - { - _low = (_low + (_range * SubRange.LowCount)) & UINT_MASK; - _range = (_range * (SubRange.HighCount - SubRange.LowCount)) & UINT_MASK; - } - - internal void AriDecNormalize() - { - // while ((low ^ (low + range)) < TOP || range < BOT && ((range = -low & (BOT - 1)) != 0 ? true : true)) - // { - // code = ((code << 8) | unpackRead.getChar()&0xff)&uintMask; - // range = (range << 8)&uintMask; - // low = (low << 8)&uintMask; - // } - - // Rewrote for clarity - bool c2 = false; - while ((_low ^ (_low + _range)) < TOP || (c2 = _range < BOT)) - { - if (c2) - { - _range = (-_low & (BOT - 1)) & UINT_MASK; - c2 = false; - } - _code = ((_code << 8) | Char) & UINT_MASK; - _range = (_range << 8) & UINT_MASK; - _low = (_low << 8) & UINT_MASK; - } - } - - // Debug - public override String ToString() - { - StringBuilder buffer = new StringBuilder(); - buffer.Append("RangeCoder["); - buffer.Append("\n low="); - buffer.Append(_low); - buffer.Append("\n code="); - buffer.Append(_code); - buffer.Append("\n range="); - buffer.Append(_range); - buffer.Append("\n subrange="); - buffer.Append(SubRange); - buffer.Append(']'); - return buffer.ToString(); + _code = ((_code << 8) | Char) & UINT_MASK; } } - internal class SubRange + internal int CurrentCount { - // uint LowCount, HighCount, scale; - private long _lowCount, - _highCount, - _scale; - - internal void IncScale(int dScale) + get { - Scale = Scale + dScale; + _range = (_range / SubRange.Scale) & UINT_MASK; + return (int)((_code - _low) / (_range)); } + } - internal long HighCount + private long Char + { + get { - get => _highCount; - set => _highCount = value & RangeCoder.UINT_MASK; + if (_unpackRead != null) + { + return (_unpackRead.Char); + } + if (_stream != null) + { + return _stream.ReadByte(); + } + return -1; } + } - internal long LowCount - { - get => _lowCount & RangeCoder.UINT_MASK; - set => _lowCount = value & RangeCoder.UINT_MASK; - } + internal SubRange SubRange { get; private set; } - internal long Scale - { - get => _scale; - set => _scale = value & RangeCoder.UINT_MASK; - } + internal long GetCurrentShiftCount(int shift) + { + _range = Utility.URShift(_range, shift); + return ((_code - _low) / (_range)) & UINT_MASK; + } - // Debug - public override String ToString() + internal void Decode() + { + _low = (_low + (_range * SubRange.LowCount)) & UINT_MASK; + _range = (_range * (SubRange.HighCount - SubRange.LowCount)) & UINT_MASK; + } + + internal void AriDecNormalize() + { + // while ((low ^ (low + range)) < TOP || range < BOT && ((range = -low & (BOT - 1)) != 0 ? true : true)) + // { + // code = ((code << 8) | unpackRead.getChar()&0xff)&uintMask; + // range = (range << 8)&uintMask; + // low = (low << 8)&uintMask; + // } + + // Rewrote for clarity + var c2 = false; + while ((_low ^ (_low + _range)) < TOP || (c2 = _range < BOT)) { - StringBuilder buffer = new StringBuilder(); - buffer.Append("SubRange["); - buffer.Append("\n lowCount="); - buffer.Append(_lowCount); - buffer.Append("\n highCount="); - buffer.Append(_highCount); - buffer.Append("\n scale="); - buffer.Append(_scale); - buffer.Append(']'); - return buffer.ToString(); + if (c2) + { + _range = (-_low & (BOT - 1)) & UINT_MASK; + c2 = false; + } + _code = ((_code << 8) | Char) & UINT_MASK; + _range = (_range << 8) & UINT_MASK; + _low = (_low << 8) & UINT_MASK; } } + + // Debug + public override string ToString() + { + var buffer = new StringBuilder(); + buffer.Append("RangeCoder["); + buffer.Append("\n low="); + buffer.Append(_low); + buffer.Append("\n code="); + buffer.Append(_code); + buffer.Append("\n range="); + buffer.Append(_range); + buffer.Append("\n subrange="); + buffer.Append(SubRange); + buffer.Append(']'); + return buffer.ToString(); + } +} + +internal class SubRange +{ + // uint LowCount, HighCount, scale; + private long _lowCount, + _highCount, + _scale; + + internal void IncScale(int dScale) + { + Scale += dScale; + } + + internal long HighCount + { + get => _highCount; + set => _highCount = value & RangeCoder.UINT_MASK; + } + + internal long LowCount + { + get => _lowCount & RangeCoder.UINT_MASK; + set => _lowCount = value & RangeCoder.UINT_MASK; + } + + internal long Scale + { + get => _scale; + set => _scale = value & RangeCoder.UINT_MASK; + } + + // Debug + public override string ToString() + { + var buffer = new StringBuilder(); + buffer.Append("SubRange["); + buffer.Append("\n lowCount="); + buffer.Append(_lowCount); + buffer.Append("\n highCount="); + buffer.Append(_highCount); + buffer.Append("\n scale="); + buffer.Append(_scale); + buffer.Append(']'); + return buffer.ToString(); + } } diff --git a/src/SharpCompress/Compressors/PPMd/H/RarMemBlock.cs b/src/SharpCompress/Compressors/PPMd/H/RarMemBlock.cs index b067fac8..f3f7b147 100644 --- a/src/SharpCompress/Compressors/PPMd/H/RarMemBlock.cs +++ b/src/SharpCompress/Compressors/PPMd/H/RarMemBlock.cs @@ -1,126 +1,125 @@ using System; using System.Buffers.Binary; -namespace SharpCompress.Compressors.PPMd.H +namespace SharpCompress.Compressors.PPMd.H; + +internal class RarMemBlock : Pointer { - internal class RarMemBlock : Pointer + public const int SIZE = 12; + + private int _stamp, + _nu; + + private int _next, + _prev; // Pointer RarMemBlock + + public RarMemBlock(byte[] memory) : base(memory) { } + + internal int Stamp { - public const int SIZE = 12; - - private int _stamp, - _nu; - - private int _next, - _prev; // Pointer RarMemBlock - - public RarMemBlock(byte[] memory) : base(memory) { } - - internal int Stamp - { - get - { - if (Memory != null) - { - _stamp = - BinaryPrimitives.ReadInt16LittleEndian(Memory.AsSpan(Address)) & 0xffff; - } - return _stamp; - } - set - { - _stamp = value; - if (Memory != null) - { - BinaryPrimitives.WriteInt16LittleEndian(Memory.AsSpan(Address), (short)value); - } - } - } - - internal void InsertAt(RarMemBlock p) - { - RarMemBlock temp = new RarMemBlock(Memory); - SetPrev(p.Address); - temp.Address = GetPrev(); - SetNext(temp.GetNext()); // prev.getNext(); - temp.SetNext(this); // prev.setNext(this); - temp.Address = GetNext(); - temp.SetPrev(this); // next.setPrev(this); - } - - internal void Remove() - { - RarMemBlock temp = new RarMemBlock(Memory); - temp.Address = GetPrev(); - temp.SetNext(GetNext()); // prev.setNext(next); - temp.Address = GetNext(); - temp.SetPrev(GetPrev()); // next.setPrev(prev); - - // next = -1; - // prev = -1; - } - - internal int GetNext() + get { if (Memory != null) { - _next = BinaryPrimitives.ReadInt32LittleEndian(Memory.AsSpan(Address + 4)); + _stamp = + BinaryPrimitives.ReadInt16LittleEndian(Memory.AsSpan(Address)) & 0xffff; } - return _next; + return _stamp; } - - internal void SetNext(RarMemBlock next) + set { - SetNext(next.Address); - } - - internal void SetNext(int next) - { - _next = next; + _stamp = value; if (Memory != null) { - BinaryPrimitives.WriteInt32LittleEndian(Memory.AsSpan(Address + 4), next); - } - } - - internal int GetNu() - { - if (Memory != null) - { - _nu = BinaryPrimitives.ReadInt16LittleEndian(Memory.AsSpan(Address + 2)) & 0xffff; - } - return _nu; - } - - internal void SetNu(int nu) - { - _nu = nu & 0xffff; - if (Memory != null) - { - BinaryPrimitives.WriteInt16LittleEndian(Memory.AsSpan(Address + 2), (short)nu); - } - } - - internal int GetPrev() - { - if (Memory != null) - { - _prev = BinaryPrimitives.ReadInt32LittleEndian(Memory.AsSpan(Address + 8)); - } - return _prev; - } - - internal void SetPrev(RarMemBlock prev) - { - SetPrev(prev.Address); - } - - internal void SetPrev(int prev) - { - _prev = prev; - if (Memory != null) - { - BinaryPrimitives.WriteInt32LittleEndian(Memory.AsSpan(Address + 8), prev); + BinaryPrimitives.WriteInt16LittleEndian(Memory.AsSpan(Address), (short)value); } } } + + internal void InsertAt(RarMemBlock p) + { + var temp = new RarMemBlock(Memory); + SetPrev(p.Address); + temp.Address = GetPrev(); + SetNext(temp.GetNext()); // prev.getNext(); + temp.SetNext(this); // prev.setNext(this); + temp.Address = GetNext(); + temp.SetPrev(this); // next.setPrev(this); + } + + internal void Remove() + { + var temp = new RarMemBlock(Memory); + temp.Address = GetPrev(); + temp.SetNext(GetNext()); // prev.setNext(next); + temp.Address = GetNext(); + temp.SetPrev(GetPrev()); // next.setPrev(prev); + + // next = -1; + // prev = -1; + } + + internal int GetNext() + { + if (Memory != null) + { + _next = BinaryPrimitives.ReadInt32LittleEndian(Memory.AsSpan(Address + 4)); + } + return _next; + } + + internal void SetNext(RarMemBlock next) + { + SetNext(next.Address); + } + + internal void SetNext(int next) + { + _next = next; + if (Memory != null) + { + BinaryPrimitives.WriteInt32LittleEndian(Memory.AsSpan(Address + 4), next); + } + } + + internal int GetNu() + { + if (Memory != null) + { + _nu = BinaryPrimitives.ReadInt16LittleEndian(Memory.AsSpan(Address + 2)) & 0xffff; + } + return _nu; + } + + internal void SetNu(int nu) + { + _nu = nu & 0xffff; + if (Memory != null) + { + BinaryPrimitives.WriteInt16LittleEndian(Memory.AsSpan(Address + 2), (short)nu); + } + } + + internal int GetPrev() + { + if (Memory != null) + { + _prev = BinaryPrimitives.ReadInt32LittleEndian(Memory.AsSpan(Address + 8)); + } + return _prev; + } + + internal void SetPrev(RarMemBlock prev) + { + SetPrev(prev.Address); + } + + internal void SetPrev(int prev) + { + _prev = prev; + if (Memory != null) + { + BinaryPrimitives.WriteInt32LittleEndian(Memory.AsSpan(Address + 8), prev); + } + } } diff --git a/src/SharpCompress/Compressors/PPMd/H/RarNode.cs b/src/SharpCompress/Compressors/PPMd/H/RarNode.cs index 15775229..5a9e5a29 100644 --- a/src/SharpCompress/Compressors/PPMd/H/RarNode.cs +++ b/src/SharpCompress/Compressors/PPMd/H/RarNode.cs @@ -2,51 +2,50 @@ using System; using System.Buffers.Binary; using System.Text; -namespace SharpCompress.Compressors.PPMd.H +namespace SharpCompress.Compressors.PPMd.H; + +internal class RarNode : Pointer { - internal class RarNode : Pointer + private int _next; //rarnode pointer + + public const int SIZE = 4; + + public RarNode(byte[] memory) : base(memory) { } + + internal int GetNext() { - private int _next; //rarnode pointer - - public const int SIZE = 4; - - public RarNode(byte[] memory) : base(memory) { } - - internal int GetNext() + if (Memory != null) { - if (Memory != null) - { - _next = BinaryPrimitives.ReadInt32LittleEndian(Memory.AsSpan(Address)); - } - return _next; + _next = BinaryPrimitives.ReadInt32LittleEndian(Memory.AsSpan(Address)); } + return _next; + } - internal void SetNext(RarNode next) - { - SetNext(next.Address); - } + internal void SetNext(RarNode next) + { + SetNext(next.Address); + } - internal void SetNext(int next) + internal void SetNext(int next) + { + _next = next; + if (Memory != null) { - _next = next; - if (Memory != null) - { - BinaryPrimitives.WriteInt32LittleEndian(Memory.AsSpan(Address), next); - } - } - - public override string ToString() - { - StringBuilder buffer = new StringBuilder(); - buffer.Append("State["); - buffer.Append("\n Address="); - buffer.Append(Address); - buffer.Append("\n size="); - buffer.Append(SIZE); - buffer.Append("\n next="); - buffer.Append(GetNext()); - buffer.Append("\n]"); - return buffer.ToString(); + BinaryPrimitives.WriteInt32LittleEndian(Memory.AsSpan(Address), next); } } + + public override string ToString() + { + var buffer = new StringBuilder(); + buffer.Append("State["); + buffer.Append("\n Address="); + buffer.Append(Address); + buffer.Append("\n size="); + buffer.Append(SIZE); + buffer.Append("\n next="); + buffer.Append(GetNext()); + buffer.Append("\n]"); + return buffer.ToString(); + } } diff --git a/src/SharpCompress/Compressors/PPMd/H/SEE2Context.cs b/src/SharpCompress/Compressors/PPMd/H/SEE2Context.cs index dd4fa7c6..742bd279 100644 --- a/src/SharpCompress/Compressors/PPMd/H/SEE2Context.cs +++ b/src/SharpCompress/Compressors/PPMd/H/SEE2Context.cs @@ -1,87 +1,85 @@ -using System; using System.Text; -namespace SharpCompress.Compressors.PPMd.H +namespace SharpCompress.Compressors.PPMd.H; + +internal class See2Context { - internal class See2Context + public virtual int Mean { - public virtual int Mean + get { - get - { - int retVal = Utility.URShift(_summ, _shift); - _summ -= retVal; - return retVal + ((retVal == 0) ? 1 : 0); - } - } - - public virtual int Count - { - get => _count; - set => _count = value & 0xff; - } - - public virtual int Shift - { - get => _shift; - set => _shift = value & 0xff; - } - - public virtual int Summ - { - get => _summ; - set => _summ = value & 0xffff; - } - - public const int SIZE = 4; - - // ushort Summ; - private int _summ; - - // byte Shift; - private int _shift; - - // byte Count; - private int _count; - - public void Initialize(int initVal) - { - _shift = (ModelPpm.PERIOD_BITS - 4) & 0xff; - _summ = (initVal << _shift) & 0xffff; - _count = 4; - } - - public virtual void Update() - { - if (_shift < ModelPpm.PERIOD_BITS && --_count == 0) - { - _summ += _summ; - _count = (3 << _shift++); - } - _summ &= 0xffff; - _count &= 0xff; - _shift &= 0xff; - } - - public virtual void IncSumm(int dSumm) - { - Summ = Summ + dSumm; - } - - public override String ToString() - { - StringBuilder buffer = new StringBuilder(); - buffer.Append("SEE2Context["); - buffer.Append("\n size="); - buffer.Append(SIZE); - buffer.Append("\n summ="); - buffer.Append(_summ); - buffer.Append("\n shift="); - buffer.Append(_shift); - buffer.Append("\n count="); - buffer.Append(_count); - buffer.Append("\n]"); - return buffer.ToString(); + var retVal = Utility.URShift(_summ, _shift); + _summ -= retVal; + return retVal + ((retVal == 0) ? 1 : 0); } } + + public virtual int Count + { + get => _count; + set => _count = value & 0xff; + } + + public virtual int Shift + { + get => _shift; + set => _shift = value & 0xff; + } + + public virtual int Summ + { + get => _summ; + set => _summ = value & 0xffff; + } + + public const int SIZE = 4; + + // ushort Summ; + private int _summ; + + // byte Shift; + private int _shift; + + // byte Count; + private int _count; + + public void Initialize(int initVal) + { + _shift = (ModelPpm.PERIOD_BITS - 4) & 0xff; + _summ = (initVal << _shift) & 0xffff; + _count = 4; + } + + public virtual void Update() + { + if (_shift < ModelPpm.PERIOD_BITS && --_count == 0) + { + _summ += _summ; + _count = (3 << _shift++); + } + _summ &= 0xffff; + _count &= 0xff; + _shift &= 0xff; + } + + public virtual void IncSumm(int dSumm) + { + Summ += dSumm; + } + + public override string ToString() + { + var buffer = new StringBuilder(); + buffer.Append("SEE2Context["); + buffer.Append("\n size="); + buffer.Append(SIZE); + buffer.Append("\n summ="); + buffer.Append(_summ); + buffer.Append("\n shift="); + buffer.Append(_shift); + buffer.Append("\n count="); + buffer.Append(_count); + buffer.Append("\n]"); + return buffer.ToString(); + } } diff --git a/src/SharpCompress/Compressors/PPMd/H/State.cs b/src/SharpCompress/Compressors/PPMd/H/State.cs index 6ce6d2d0..3aa9b20f 100644 --- a/src/SharpCompress/Compressors/PPMd/H/State.cs +++ b/src/SharpCompress/Compressors/PPMd/H/State.cs @@ -2,74 +2,74 @@ using System; using System.Buffers.Binary; using System.Text; -namespace SharpCompress.Compressors.PPMd.H +namespace SharpCompress.Compressors.PPMd.H; + +internal sealed class State : Pointer { - internal sealed class State : Pointer + internal const int SIZE = 6; + + internal State(byte[]? memory) : base(memory) { } + + internal int Symbol { - internal const int SIZE = 6; + get => Memory[Address] & 0xff; + set => Memory[Address] = (byte)value; + } - internal State(byte[]? memory) : base(memory) { } + internal int Freq + { + get => Memory[Address + 1] & 0xff; + set => Memory[Address + 1] = (byte)value; + } - internal int Symbol - { - get => Memory[Address] & 0xff; - set => Memory[Address] = (byte)value; - } + internal State Initialize(byte[] mem) + { + return Initialize(mem); + } - internal int Freq - { - get => Memory[Address + 1] & 0xff; - set => Memory[Address + 1] = (byte)value; - } + internal void IncrementFreq(int dFreq) + { + Memory[Address + 1] = (byte)(Memory[Address + 1] + dFreq); + } - internal State Initialize(byte[] mem) - { - return base.Initialize(mem); - } + internal int GetSuccessor() + { + return BinaryPrimitives.ReadInt32LittleEndian(Memory.AsSpan(Address + 2)); + } - internal void IncrementFreq(int dFreq) - { - Memory[Address + 1] = (byte)(Memory[Address + 1] + dFreq); - } + internal void SetSuccessor(PpmContext successor) + { + SetSuccessor(successor.Address); + } - internal int GetSuccessor() - { - return BinaryPrimitives.ReadInt32LittleEndian(Memory.AsSpan(Address + 2)); - } + internal void SetSuccessor(int successor) + { + BinaryPrimitives.WriteInt32LittleEndian(Memory.AsSpan(Address + 2), successor); + } - internal void SetSuccessor(PpmContext successor) - { - SetSuccessor(successor.Address); - } + internal void SetValues(StateRef state) + { + Symbol = state.Symbol; + Freq = state.Freq; + SetSuccessor(state.GetSuccessor()); + } - internal void SetSuccessor(int successor) - { - BinaryPrimitives.WriteInt32LittleEndian(Memory.AsSpan(Address + 2), successor); - } + internal void SetValues(State ptr) + { + Array.Copy(ptr.Memory, ptr.Address, Memory, Address, SIZE); + } - internal void SetValues(StateRef state) - { - Symbol = state.Symbol; - Freq = state.Freq; - SetSuccessor(state.GetSuccessor()); - } + internal State DecrementAddress() + { + Address -= SIZE; + return this; + } - internal void SetValues(State ptr) - { - Array.Copy(ptr.Memory, ptr.Address, Memory, Address, SIZE); - } - - internal State DecrementAddress() - { - Address = Address - SIZE; - return this; - } - - internal State IncrementAddress() - { - Address = Address + SIZE; - return this; - } + internal State IncrementAddress() + { + Address += SIZE; + return this; + } internal static void PpmdSwap(State ptr1, State ptr2) { @@ -77,28 +77,27 @@ namespace SharpCompress.Compressors.PPMd.H mem2 = ptr2.Memory; for (int i = 0, pos1 = ptr1.Address, pos2 = ptr2.Address; i < SIZE; i++, pos1++, pos2++) { - byte temp = mem1[pos1]; + var temp = mem1[pos1]; mem1[pos1] = mem2[pos2]; mem2[pos2] = temp; } } - public override String ToString() - { - StringBuilder buffer = new StringBuilder(); - buffer.Append("State["); - buffer.Append("\n Address="); - buffer.Append(Address); - buffer.Append("\n size="); - buffer.Append(SIZE); - buffer.Append("\n symbol="); - buffer.Append(Symbol); - buffer.Append("\n freq="); - buffer.Append(Freq); - buffer.Append("\n successor="); - buffer.Append(GetSuccessor()); - buffer.Append("\n]"); - return buffer.ToString(); - } + public override string ToString() + { + var buffer = new StringBuilder(); + buffer.Append("State["); + buffer.Append("\n Address="); + buffer.Append(Address); + buffer.Append("\n size="); + buffer.Append(SIZE); + buffer.Append("\n symbol="); + buffer.Append(Symbol); + buffer.Append("\n freq="); + buffer.Append(Freq); + buffer.Append("\n successor="); + buffer.Append(GetSuccessor()); + buffer.Append("\n]"); + return buffer.ToString(); } } diff --git a/src/SharpCompress/Compressors/PPMd/H/StateRef.cs b/src/SharpCompress/Compressors/PPMd/H/StateRef.cs index 686c1128..9df43814 100644 --- a/src/SharpCompress/Compressors/PPMd/H/StateRef.cs +++ b/src/SharpCompress/Compressors/PPMd/H/StateRef.cs @@ -1,75 +1,73 @@ -using System; using System.Text; -namespace SharpCompress.Compressors.PPMd.H +namespace SharpCompress.Compressors.PPMd.H; + +internal class StateRef { - internal class StateRef + private int _symbol; + + private int _freq; + + private int _successor; // pointer ppmcontext + + internal int Symbol { - private int _symbol; + get => _symbol; + set => _symbol = value & 0xff; + } - private int _freq; + internal int Freq + { + get => _freq; + set => _freq = value & 0xff; + } - private int _successor; // pointer ppmcontext - - internal int Symbol + internal State Values + { + set { - get => _symbol; - set => _symbol = value & 0xff; - } - - internal int Freq - { - get => _freq; - set => _freq = value & 0xff; - } - - internal State Values - { - set - { - Freq = value.Freq; - SetSuccessor(value.GetSuccessor()); - Symbol = value.Symbol; - } - } - - public virtual void IncrementFreq(int dFreq) - { - _freq = (_freq + dFreq) & 0xff; - } - - public virtual void DecrementFreq(int dFreq) - { - _freq = (_freq - dFreq) & 0xff; - } - - public virtual int GetSuccessor() - { - return _successor; - } - - public virtual void SetSuccessor(PpmContext successor) - { - SetSuccessor(successor.Address); - } - - public virtual void SetSuccessor(int successor) - { - _successor = successor; - } - - public override String ToString() - { - StringBuilder buffer = new StringBuilder(); - buffer.Append("State["); - buffer.Append("\n symbol="); - buffer.Append(Symbol); - buffer.Append("\n freq="); - buffer.Append(Freq); - buffer.Append("\n successor="); - buffer.Append(GetSuccessor()); - buffer.Append("\n]"); - return buffer.ToString(); + Freq = value.Freq; + SetSuccessor(value.GetSuccessor()); + Symbol = value.Symbol; } } + + public virtual void IncrementFreq(int dFreq) + { + _freq = (_freq + dFreq) & 0xff; + } + + public virtual void DecrementFreq(int dFreq) + { + _freq = (_freq - dFreq) & 0xff; + } + + public virtual int GetSuccessor() + { + return _successor; + } + + public virtual void SetSuccessor(PpmContext successor) + { + SetSuccessor(successor.Address); + } + + public virtual void SetSuccessor(int successor) + { + _successor = successor; + } + + public override string ToString() + { + var buffer = new StringBuilder(); + buffer.Append("State["); + buffer.Append("\n symbol="); + buffer.Append(Symbol); + buffer.Append("\n freq="); + buffer.Append(Freq); + buffer.Append("\n successor="); + buffer.Append(GetSuccessor()); + buffer.Append("\n]"); + return buffer.ToString(); + } } diff --git a/src/SharpCompress/Compressors/PPMd/H/SubAllocator.cs b/src/SharpCompress/Compressors/PPMd/H/SubAllocator.cs index dba09739..a3757715 100644 --- a/src/SharpCompress/Compressors/PPMd/H/SubAllocator.cs +++ b/src/SharpCompress/Compressors/PPMd/H/SubAllocator.cs @@ -3,475 +3,474 @@ using System; using System.Text; -namespace SharpCompress.Compressors.PPMd.H +namespace SharpCompress.Compressors.PPMd.H; + +internal class SubAllocator { - internal class SubAllocator + public virtual int FakeUnitsStart { - public virtual int FakeUnitsStart + get => _fakeUnitsStart; + set => _fakeUnitsStart = value; + } + + public virtual int HeapEnd => _heapEnd; + + public virtual int PText + { + get => _pText; + set => _pText = value; + } + + public virtual int UnitsStart + { + get => _unitsStart; + set => _unitsStart = value; + } + + public virtual byte[] Heap => _heap; + + //UPGRADE_NOTE: Final was removed from the declaration of 'N4 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public const int N1 = 4; + public const int N2 = 4; + public const int N3 = 4; + public static readonly int N4 = (128 + 3 - (1 * N1) - (2 * N2) - (3 * N3)) / 4; + + //UPGRADE_NOTE: Final was removed from the declaration of 'N_INDEXES '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly int N_INDEXES = N1 + N2 + N3 + N4; + + //UPGRADE_NOTE: Final was removed from the declaration of 'UNIT_SIZE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + //UPGRADE_NOTE: The initialization of 'UNIT_SIZE' was moved to static method 'SharpCompress.Unpack.PPM.SubAllocator'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'" + public static readonly int UNIT_SIZE; + + public const int FIXED_UNIT_SIZE = 12; + + private int _subAllocatorSize; + + // byte Indx2Units[N_INDEXES], Units2Indx[128], GlueCount; + private readonly int[] _indx2Units = new int[N_INDEXES]; + private readonly int[] _units2Indx = new int[128]; + private int _glueCount; + + // byte *HeapStart,*LoUnit, *HiUnit; + private int _heapStart, + _loUnit, + _hiUnit; + + //UPGRADE_NOTE: Final was removed from the declaration of 'freeList '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + private readonly RarNode[] _freeList = new RarNode[N_INDEXES]; + + // byte *pText, *UnitsStart,*HeapEnd,*FakeUnitsStart; + private int _pText, + _unitsStart, + _heapEnd, + _fakeUnitsStart; + + private byte[] _heap; + + private int _freeListPos; + + private int _tempMemBlockPos; + + // Temp fields + private RarNode _tempRarNode; + private RarMemBlock _tempRarMemBlock1; + private RarMemBlock _tempRarMemBlock2; + private RarMemBlock _tempRarMemBlock3; + + public SubAllocator() + { + Clean(); + } + + public virtual void Clean() + { + _subAllocatorSize = 0; + } + + private void InsertNode(int p, int indx) + { + var temp = _tempRarNode; + temp.Address = p; + temp.SetNext(_freeList[indx].GetNext()); + _freeList[indx].SetNext(temp); + } + + public virtual void IncPText() + { + _pText++; + } + + private int RemoveNode(int indx) + { + var retVal = _freeList[indx].GetNext(); + var temp = _tempRarNode; + temp.Address = retVal; + _freeList[indx].SetNext(temp.GetNext()); + return retVal; + } + + private int U2B(int nu) + { + return UNIT_SIZE * nu; + } + + /* memblockptr */ + + private int MbPtr(int basePtr, int items) + { + return (basePtr + U2B(items)); + } + + private void SplitBlock(int pv, int oldIndx, int newIndx) + { + int i, + uDiff = _indx2Units[oldIndx] - _indx2Units[newIndx]; + var p = pv + U2B(_indx2Units[newIndx]); + if (_indx2Units[i = _units2Indx[uDiff - 1]] != uDiff) { - get => _fakeUnitsStart; - set => _fakeUnitsStart = value; + InsertNode(p, --i); + p += U2B(i = _indx2Units[i]); + uDiff -= i; } + InsertNode(p, _units2Indx[uDiff - 1]); + } - public virtual int HeapEnd => _heapEnd; - - public virtual int PText - { - get => _pText; - set => _pText = value; - } - - public virtual int UnitsStart - { - get => _unitsStart; - set => _unitsStart = value; - } - - public virtual byte[] Heap => _heap; - - //UPGRADE_NOTE: Final was removed from the declaration of 'N4 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - public const int N1 = 4; - public const int N2 = 4; - public const int N3 = 4; - public static readonly int N4 = (128 + 3 - 1 * N1 - 2 * N2 - 3 * N3) / 4; - - //UPGRADE_NOTE: Final was removed from the declaration of 'N_INDEXES '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - public static readonly int N_INDEXES = N1 + N2 + N3 + N4; - - //UPGRADE_NOTE: Final was removed from the declaration of 'UNIT_SIZE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - //UPGRADE_NOTE: The initialization of 'UNIT_SIZE' was moved to static method 'SharpCompress.Unpack.PPM.SubAllocator'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'" - public static readonly int UNIT_SIZE; - - public const int FIXED_UNIT_SIZE = 12; - - private int _subAllocatorSize; - - // byte Indx2Units[N_INDEXES], Units2Indx[128], GlueCount; - private readonly int[] _indx2Units = new int[N_INDEXES]; - private readonly int[] _units2Indx = new int[128]; - private int _glueCount; - - // byte *HeapStart,*LoUnit, *HiUnit; - private int _heapStart, - _loUnit, - _hiUnit; - - //UPGRADE_NOTE: Final was removed from the declaration of 'freeList '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly RarNode[] _freeList = new RarNode[N_INDEXES]; - - // byte *pText, *UnitsStart,*HeapEnd,*FakeUnitsStart; - private int _pText, - _unitsStart, - _heapEnd, - _fakeUnitsStart; - - private byte[] _heap; - - private int _freeListPos; - - private int _tempMemBlockPos; - - // Temp fields - private RarNode _tempRarNode; - private RarMemBlock _tempRarMemBlock1; - private RarMemBlock _tempRarMemBlock2; - private RarMemBlock _tempRarMemBlock3; - - public SubAllocator() - { - Clean(); - } - - public virtual void Clean() + public virtual void StopSubAllocator() + { + if (_subAllocatorSize != 0) { _subAllocatorSize = 0; - } - private void InsertNode(int p, int indx) - { - RarNode temp = _tempRarNode; - temp.Address = p; - temp.SetNext(_freeList[indx].GetNext()); - _freeList[indx].SetNext(temp); - } - - public virtual void IncPText() - { - _pText++; - } - - private int RemoveNode(int indx) - { - int retVal = _freeList[indx].GetNext(); - RarNode temp = _tempRarNode; - temp.Address = retVal; - _freeList[indx].SetNext(temp.GetNext()); - return retVal; - } - - private int U2B(int nu) - { - return UNIT_SIZE * nu; - } - - /* memblockptr */ - - private int MbPtr(int basePtr, int items) - { - return (basePtr + U2B(items)); - } - - private void SplitBlock(int pv, int oldIndx, int newIndx) - { - int i, - uDiff = _indx2Units[oldIndx] - _indx2Units[newIndx]; - int p = pv + U2B(_indx2Units[newIndx]); - if (_indx2Units[i = _units2Indx[uDiff - 1]] != uDiff) - { - InsertNode(p, --i); - p += U2B(i = _indx2Units[i]); - uDiff -= i; - } - InsertNode(p, _units2Indx[uDiff - 1]); - } - - public virtual void StopSubAllocator() - { - if (_subAllocatorSize != 0) - { - _subAllocatorSize = 0; - - //ArrayFactory.BYTES_FACTORY.recycle(heap); - _heap = null; - _heapStart = 1; - - // rarfree(HeapStart); - // Free temp fields - _tempRarNode = null; - _tempRarMemBlock1 = null; - _tempRarMemBlock2 = null; - _tempRarMemBlock3 = null; - } - } - - public virtual int GetAllocatedMemory() - { - return _subAllocatorSize; - } - - public virtual bool StartSubAllocator(int saSize) - { - int t = saSize; - if (_subAllocatorSize == t) - { - return true; - } - StopSubAllocator(); - int allocSize = t / FIXED_UNIT_SIZE * UNIT_SIZE + UNIT_SIZE; - - // adding space for freelist (needed for poiters) - // 1+ for null pointer - int realAllocSize = 1 + allocSize + 4 * N_INDEXES; - - // adding space for an additional memblock - _tempMemBlockPos = realAllocSize; - realAllocSize += RarMemBlock.SIZE; - - _heap = new byte[realAllocSize]; + //ArrayFactory.BYTES_FACTORY.recycle(heap); + _heap = null; _heapStart = 1; - _heapEnd = _heapStart + allocSize - UNIT_SIZE; - _subAllocatorSize = t; - // Bug fixed - _freeListPos = _heapStart + allocSize; + // rarfree(HeapStart); + // Free temp fields + _tempRarNode = null; + _tempRarMemBlock1 = null; + _tempRarMemBlock2 = null; + _tempRarMemBlock3 = null; + } + } - //UPGRADE_ISSUE: The following fragment of code could not be parsed and was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1156'" - //assert(realAllocSize - tempMemBlockPos == RarMemBlock.size): realAllocSize - //+ + tempMemBlockPos + + RarMemBlock.size; - - // Init freeList - for (int i = 0, pos = _freeListPos; i < _freeList.Length; i++, pos += RarNode.SIZE) - { - _freeList[i] = new RarNode(_heap); - _freeList[i].Address = pos; - } - - // Init temp fields - _tempRarNode = new RarNode(_heap); - _tempRarMemBlock1 = new RarMemBlock(_heap); - _tempRarMemBlock2 = new RarMemBlock(_heap); - _tempRarMemBlock3 = new RarMemBlock(_heap); + public virtual int GetAllocatedMemory() + { + return _subAllocatorSize; + } + public virtual bool StartSubAllocator(int saSize) + { + var t = saSize; + if (_subAllocatorSize == t) + { return true; } + StopSubAllocator(); + var allocSize = (t / FIXED_UNIT_SIZE * UNIT_SIZE) + UNIT_SIZE; - private void GlueFreeBlocks() + // adding space for freelist (needed for poiters) + // 1+ for null pointer + var realAllocSize = 1 + allocSize + (4 * N_INDEXES); + + // adding space for an additional memblock + _tempMemBlockPos = realAllocSize; + realAllocSize += RarMemBlock.SIZE; + + _heap = new byte[realAllocSize]; + _heapStart = 1; + _heapEnd = _heapStart + allocSize - UNIT_SIZE; + _subAllocatorSize = t; + + // Bug fixed + _freeListPos = _heapStart + allocSize; + + //UPGRADE_ISSUE: The following fragment of code could not be parsed and was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1156'" + //assert(realAllocSize - tempMemBlockPos == RarMemBlock.size): realAllocSize + //+ + tempMemBlockPos + + RarMemBlock.size; + + // Init freeList + for (int i = 0, pos = _freeListPos; i < _freeList.Length; i++, pos += RarNode.SIZE) { - RarMemBlock s0 = _tempRarMemBlock1; - s0.Address = _tempMemBlockPos; - RarMemBlock p = _tempRarMemBlock2; - RarMemBlock p1 = _tempRarMemBlock3; - int i, - k, - sz; - if (_loUnit != _hiUnit) - { - _heap[_loUnit] = 0; - } - for (i = 0, s0.SetPrev(s0), s0.SetNext(s0); i < N_INDEXES; i++) - { - while (_freeList[i].GetNext() != 0) - { - p.Address = RemoveNode(i); // =(RAR_MEM_BLK*)RemoveNode(i); - p.InsertAt(s0); // p->insertAt(&s0); - p.Stamp = 0xFFFF; // p->Stamp=0xFFFF; - p.SetNu(_indx2Units[i]); // p->NU=Indx2Units[i]; - } - } - for (p.Address = s0.GetNext(); p.Address != s0.Address; p.Address = p.GetNext()) - { - // while ((p1=MBPtr(p,p->NU))->Stamp == 0xFFFF && int(p->NU)+p1->NU - // < 0x10000) - // Bug fixed - p1.Address = MbPtr(p.Address, p.GetNu()); - while (p1.Stamp == 0xFFFF && p.GetNu() + p1.GetNu() < 0x10000) - { - p1.Remove(); - p.SetNu(p.GetNu() + p1.GetNu()); // ->NU += p1->NU; - p1.Address = MbPtr(p.Address, p.GetNu()); - } - } + _freeList[i] = new RarNode(_heap); + _freeList[i].Address = pos; + } - // while ((p=s0.next) != &s0) + // Init temp fields + _tempRarNode = new RarNode(_heap); + _tempRarMemBlock1 = new RarMemBlock(_heap); + _tempRarMemBlock2 = new RarMemBlock(_heap); + _tempRarMemBlock3 = new RarMemBlock(_heap); + + return true; + } + + private void GlueFreeBlocks() + { + var s0 = _tempRarMemBlock1; + s0.Address = _tempMemBlockPos; + var p = _tempRarMemBlock2; + var p1 = _tempRarMemBlock3; + int i, + k, + sz; + if (_loUnit != _hiUnit) + { + _heap[_loUnit] = 0; + } + for (i = 0, s0.SetPrev(s0), s0.SetNext(s0); i < N_INDEXES; i++) + { + while (_freeList[i].GetNext() != 0) + { + p.Address = RemoveNode(i); // =(RAR_MEM_BLK*)RemoveNode(i); + p.InsertAt(s0); // p->insertAt(&s0); + p.Stamp = 0xFFFF; // p->Stamp=0xFFFF; + p.SetNu(_indx2Units[i]); // p->NU=Indx2Units[i]; + } + } + for (p.Address = s0.GetNext(); p.Address != s0.Address; p.Address = p.GetNext()) + { + // while ((p1=MBPtr(p,p->NU))->Stamp == 0xFFFF && int(p->NU)+p1->NU + // < 0x10000) // Bug fixed + p1.Address = MbPtr(p.Address, p.GetNu()); + while (p1.Stamp == 0xFFFF && p.GetNu() + p1.GetNu() < 0x10000) + { + p1.Remove(); + p.SetNu(p.GetNu() + p1.GetNu()); // ->NU += p1->NU; + p1.Address = MbPtr(p.Address, p.GetNu()); + } + } + + // while ((p=s0.next) != &s0) + // Bug fixed + p.Address = s0.GetNext(); + while (p.Address != s0.Address) + { + for ( + p.Remove(), sz = p.GetNu(); + sz > 128; + sz -= 128, p.Address = MbPtr(p.Address, 128) + ) + { + InsertNode(p.Address, N_INDEXES - 1); + } + if (_indx2Units[i = _units2Indx[sz - 1]] != sz) + { + k = sz - _indx2Units[--i]; + InsertNode(MbPtr(p.Address, sz - k), k - 1); + } + InsertNode(p.Address, i); p.Address = s0.GetNext(); - while (p.Address != s0.Address) - { - for ( - p.Remove(), sz = p.GetNu(); - sz > 128; - sz -= 128, p.Address = MbPtr(p.Address, 128) - ) - { - InsertNode(p.Address, N_INDEXES - 1); - } - if (_indx2Units[i = _units2Indx[sz - 1]] != sz) - { - k = sz - _indx2Units[--i]; - InsertNode(MbPtr(p.Address, sz - k), k - 1); - } - InsertNode(p.Address, i); - p.Address = s0.GetNext(); - } } + } - private int AllocUnitsRare(int indx) + private int AllocUnitsRare(int indx) + { + if (_glueCount == 0) { - if (_glueCount == 0) - { - _glueCount = 255; - GlueFreeBlocks(); - if (_freeList[indx].GetNext() != 0) - { - return RemoveNode(indx); - } - } - int i = indx; - do - { - if (++i == N_INDEXES) - { - _glueCount--; - i = U2B(_indx2Units[indx]); - int j = FIXED_UNIT_SIZE * _indx2Units[indx]; - if (_fakeUnitsStart - _pText > j) - { - _fakeUnitsStart -= j; - _unitsStart -= i; - return _unitsStart; - } - return (0); - } - } while (_freeList[i].GetNext() == 0); - int retVal = RemoveNode(i); - SplitBlock(retVal, i, indx); - return retVal; - } - - public virtual int AllocUnits(int nu) - { - int indx = _units2Indx[nu - 1]; + _glueCount = 255; + GlueFreeBlocks(); if (_freeList[indx].GetNext() != 0) { return RemoveNode(indx); } - int retVal = _loUnit; - _loUnit += U2B(_indx2Units[indx]); - if (_loUnit <= _hiUnit) - { - return retVal; - } - _loUnit -= U2B(_indx2Units[indx]); - return AllocUnitsRare(indx); } - - public virtual int AllocContext() + var i = indx; + do { - if (_hiUnit != _loUnit) + if (++i == N_INDEXES) { - return (_hiUnit -= UNIT_SIZE); + _glueCount--; + i = U2B(_indx2Units[indx]); + var j = FIXED_UNIT_SIZE * _indx2Units[indx]; + if (_fakeUnitsStart - _pText > j) + { + _fakeUnitsStart -= j; + _unitsStart -= i; + return _unitsStart; + } + return (0); } - if (_freeList[0].GetNext() != 0) - { - return RemoveNode(0); - } - return AllocUnitsRare(0); + } while (_freeList[i].GetNext() == 0); + var retVal = RemoveNode(i); + SplitBlock(retVal, i, indx); + return retVal; + } + + public virtual int AllocUnits(int nu) + { + var indx = _units2Indx[nu - 1]; + if (_freeList[indx].GetNext() != 0) + { + return RemoveNode(indx); } - - public virtual int ExpandUnits(int oldPtr, int oldNu) + var retVal = _loUnit; + _loUnit += U2B(_indx2Units[indx]); + if (_loUnit <= _hiUnit) { - int i0 = _units2Indx[oldNu - 1]; - int i1 = _units2Indx[oldNu - 1 + 1]; - if (i0 == i1) - { - return oldPtr; - } - int ptr = AllocUnits(oldNu + 1); - if (ptr != 0) - { - // memcpy(ptr,OldPtr,U2B(OldNU)); - Array.Copy(_heap, oldPtr, _heap, ptr, U2B(oldNu)); - InsertNode(oldPtr, i0); - } - return ptr; + return retVal; } + _loUnit -= U2B(_indx2Units[indx]); + return AllocUnitsRare(indx); + } - public virtual int ShrinkUnits(int oldPtr, int oldNu, int newNu) + public virtual int AllocContext() + { + if (_hiUnit != _loUnit) { - // System.out.println("SubAllocator.shrinkUnits(" + OldPtr + ", " + - // OldNU + ", " + NewNU + ")"); - int i0 = _units2Indx[oldNu - 1]; - int i1 = _units2Indx[newNu - 1]; - if (i0 == i1) - { - return oldPtr; - } - if (_freeList[i1].GetNext() != 0) - { - int ptr = RemoveNode(i1); + return (_hiUnit -= UNIT_SIZE); + } + if (_freeList[0].GetNext() != 0) + { + return RemoveNode(0); + } + return AllocUnitsRare(0); + } - // memcpy(ptr,OldPtr,U2B(NewNU)); - // for (int i = 0; i < U2B(NewNU); i++) { - // heap[ptr + i] = heap[OldPtr + i]; - // } - Array.Copy(_heap, oldPtr, _heap, ptr, U2B(newNu)); - InsertNode(oldPtr, i0); - return ptr; - } - SplitBlock(oldPtr, i0, i1); + public virtual int ExpandUnits(int oldPtr, int oldNu) + { + var i0 = _units2Indx[oldNu - 1]; + var i1 = _units2Indx[oldNu - 1 + 1]; + if (i0 == i1) + { return oldPtr; } - - public virtual void FreeUnits(int ptr, int oldNu) + var ptr = AllocUnits(oldNu + 1); + if (ptr != 0) { - InsertNode(ptr, _units2Indx[oldNu - 1]); + // memcpy(ptr,OldPtr,U2B(OldNU)); + Array.Copy(_heap, oldPtr, _heap, ptr, U2B(oldNu)); + InsertNode(oldPtr, i0); + } + return ptr; + } + + public virtual int ShrinkUnits(int oldPtr, int oldNu, int newNu) + { + // System.out.println("SubAllocator.shrinkUnits(" + OldPtr + ", " + + // OldNU + ", " + NewNU + ")"); + var i0 = _units2Indx[oldNu - 1]; + var i1 = _units2Indx[newNu - 1]; + if (i0 == i1) + { + return oldPtr; + } + if (_freeList[i1].GetNext() != 0) + { + var ptr = RemoveNode(i1); + + // memcpy(ptr,OldPtr,U2B(NewNU)); + // for (int i = 0; i < U2B(NewNU); i++) { + // heap[ptr + i] = heap[OldPtr + i]; + // } + Array.Copy(_heap, oldPtr, _heap, ptr, U2B(newNu)); + InsertNode(oldPtr, i0); + return ptr; + } + SplitBlock(oldPtr, i0, i1); + return oldPtr; + } + + public virtual void FreeUnits(int ptr, int oldNu) + { + InsertNode(ptr, _units2Indx[oldNu - 1]); + } + + public virtual void DecPText(int dPText) + { + PText -= dPText; + } + + public virtual void InitSubAllocator() + { + int i, + k; + new Span(_heap, _freeListPos, SizeOfFreeList()).Clear(); + + _pText = _heapStart; + + var size2 = FIXED_UNIT_SIZE * (_subAllocatorSize / 8 / FIXED_UNIT_SIZE * 7); + var realSize2 = size2 / FIXED_UNIT_SIZE * UNIT_SIZE; + var size1 = _subAllocatorSize - size2; + var realSize1 = (size1 / FIXED_UNIT_SIZE * UNIT_SIZE) + (size1 % FIXED_UNIT_SIZE); + _hiUnit = _heapStart + _subAllocatorSize; + _loUnit = _unitsStart = _heapStart + realSize1; + _fakeUnitsStart = _heapStart + size1; + _hiUnit = _loUnit + realSize2; + + for (i = 0, k = 1; i < N1; i++, k += 1) + { + _indx2Units[i] = k & 0xff; + } + for (k++; i < N1 + N2; i++, k += 2) + { + _indx2Units[i] = k & 0xff; + } + for (k++; i < N1 + N2 + N3; i++, k += 3) + { + _indx2Units[i] = k & 0xff; + } + for (k++; i < (N1 + N2 + N3 + N4); i++, k += 4) + { + _indx2Units[i] = k & 0xff; } - public virtual void DecPText(int dPText) + for (_glueCount = 0, k = 0, i = 0; k < 128; k++) { - PText = PText - dPText; - } - - public virtual void InitSubAllocator() - { - int i, - k; - new Span(_heap, _freeListPos, SizeOfFreeList()).Clear(); - - _pText = _heapStart; - - int size2 = FIXED_UNIT_SIZE * (_subAllocatorSize / 8 / FIXED_UNIT_SIZE * 7); - int realSize2 = size2 / FIXED_UNIT_SIZE * UNIT_SIZE; - int size1 = _subAllocatorSize - size2; - int realSize1 = size1 / FIXED_UNIT_SIZE * UNIT_SIZE + size1 % FIXED_UNIT_SIZE; - _hiUnit = _heapStart + _subAllocatorSize; - _loUnit = _unitsStart = _heapStart + realSize1; - _fakeUnitsStart = _heapStart + size1; - _hiUnit = _loUnit + realSize2; - - for (i = 0, k = 1; i < N1; i++, k += 1) - { - _indx2Units[i] = k & 0xff; - } - for (k++; i < N1 + N2; i++, k += 2) - { - _indx2Units[i] = k & 0xff; - } - for (k++; i < N1 + N2 + N3; i++, k += 3) - { - _indx2Units[i] = k & 0xff; - } - for (k++; i < (N1 + N2 + N3 + N4); i++, k += 4) - { - _indx2Units[i] = k & 0xff; - } - - for (_glueCount = 0, k = 0, i = 0; k < 128; k++) - { - i += ((_indx2Units[i] < (k + 1)) ? 1 : 0); - _units2Indx[k] = i & 0xff; - } - } - - private int SizeOfFreeList() - { - return _freeList.Length * RarNode.SIZE; - } - - // Debug - // public void dumpHeap() { - // File file = new File("P:\\test\\heapdumpj"); - // OutputStream out = null; - // try { - // out = new FileOutputStream(file); - // out.write(heap, heapStart, heapEnd - heapStart); - // out.flush(); - // System.out.println("Heap dumped to " + file.getAbsolutePath()); - // } - // catch (IOException e) { - // e.printStackTrace(); - // } - // finally { - // FileUtil.close(out); - // } - // } - - // Debug - public override String ToString() - { - StringBuilder buffer = new StringBuilder(); - buffer.Append("SubAllocator["); - buffer.Append("\n subAllocatorSize="); - buffer.Append(_subAllocatorSize); - buffer.Append("\n glueCount="); - buffer.Append(_glueCount); - buffer.Append("\n heapStart="); - buffer.Append(_heapStart); - buffer.Append("\n loUnit="); - buffer.Append(_loUnit); - buffer.Append("\n hiUnit="); - buffer.Append(_hiUnit); - buffer.Append("\n pText="); - buffer.Append(_pText); - buffer.Append("\n unitsStart="); - buffer.Append(_unitsStart); - buffer.Append("\n]"); - return buffer.ToString(); - } - - static SubAllocator() - { - UNIT_SIZE = Math.Max(PpmContext.SIZE, RarMemBlock.SIZE); + i += ((_indx2Units[i] < (k + 1)) ? 1 : 0); + _units2Indx[k] = i & 0xff; } } + + private int SizeOfFreeList() + { + return _freeList.Length * RarNode.SIZE; + } + + // Debug + // public void dumpHeap() { + // File file = new File("P:\\test\\heapdumpj"); + // OutputStream out = null; + // try { + // out = new FileOutputStream(file); + // out.write(heap, heapStart, heapEnd - heapStart); + // out.flush(); + // System.out.println("Heap dumped to " + file.getAbsolutePath()); + // } + // catch (IOException e) { + // e.printStackTrace(); + // } + // finally { + // FileUtil.close(out); + // } + // } + + // Debug + public override string ToString() + { + var buffer = new StringBuilder(); + buffer.Append("SubAllocator["); + buffer.Append("\n subAllocatorSize="); + buffer.Append(_subAllocatorSize); + buffer.Append("\n glueCount="); + buffer.Append(_glueCount); + buffer.Append("\n heapStart="); + buffer.Append(_heapStart); + buffer.Append("\n loUnit="); + buffer.Append(_loUnit); + buffer.Append("\n hiUnit="); + buffer.Append(_hiUnit); + buffer.Append("\n pText="); + buffer.Append(_pText); + buffer.Append("\n unitsStart="); + buffer.Append(_unitsStart); + buffer.Append("\n]"); + return buffer.ToString(); + } + + static SubAllocator() + { + UNIT_SIZE = Math.Max(PpmContext.SIZE, RarMemBlock.SIZE); + } } diff --git a/src/SharpCompress/Compressors/PPMd/I1/Allocator.cs b/src/SharpCompress/Compressors/PPMd/I1/Allocator.cs index 39a0c94d..6f66e310 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/Allocator.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/Allocator.cs @@ -1,504 +1,503 @@ #nullable disable -namespace SharpCompress.Compressors.PPMd.I1 +namespace SharpCompress.Compressors.PPMd.I1; + +/// Allocate a single, large array and then provide sections of this array to callers. Callers are provided with +/// instances of (which simply contain a single address value, representing a location +/// in the large array). Callers can then cast to one of the following structures (all +/// of which also simply contain a single address value): +internal class Allocator { - /// Allocate a single, large array and then provide sections of this array to callers. Callers are provided with - /// instances of (which simply contain a single address value, representing a location - /// in the large array). Callers can then cast to one of the following structures (all - /// of which also simply contain a single address value): - internal class Allocator + private const uint UNIT_SIZE = 12; + private const uint LOCAL_OFFSET = 4; // reserve the first four bytes for Pointer.Zero + private const uint NODE_OFFSET = LOCAL_OFFSET + MemoryNode.SIZE; // reserve space for a single memory node + + private const uint HEAP_OFFSET = NODE_OFFSET + (INDEX_COUNT * MemoryNode.SIZE); + + // reserve space for the array of memory nodes + + private const uint N1 = 4; + private const uint N2 = 4; + private const uint N3 = 4; + private const uint N4 = (128 + 3 - (1 * N1) - (2 * N2) - (3 * N3)) / 4; + private const uint INDEX_COUNT = N1 + N2 + N3 + N4; + + private static readonly byte[] INDEX_TO_UNITS; + private static readonly byte[] UNITS_TO_INDEX; + + public uint _allocatorSize; + public uint _glueCount; + public Pointer _baseUnit; + public Pointer _lowUnit; + public Pointer _highUnit; + public Pointer _text; + public Pointer _heap; + public MemoryNode[] _memoryNodes; + + public byte[] _memory; + + /// + /// Initializes static read-only arrays used by the . + /// + static Allocator() { - private const uint UNIT_SIZE = 12; - private const uint LOCAL_OFFSET = 4; // reserve the first four bytes for Pointer.Zero - private const uint NODE_OFFSET = LOCAL_OFFSET + MemoryNode.SIZE; // reserve space for a single memory node + // Construct the static index to units lookup array. It will contain the following values. + // + // 1 2 3 4 6 8 10 12 15 18 21 24 28 32 36 40 44 48 52 56 60 64 68 72 76 80 84 88 92 96 100 104 108 + // 112 116 120 124 128 - private const uint HEAP_OFFSET = NODE_OFFSET + INDEX_COUNT * MemoryNode.SIZE; + uint index; + uint unitCount; - // reserve space for the array of memory nodes + INDEX_TO_UNITS = new byte[INDEX_COUNT]; - private const uint N1 = 4; - private const uint N2 = 4; - private const uint N3 = 4; - private const uint N4 = (128 + 3 - 1 * N1 - 2 * N2 - 3 * N3) / 4; - private const uint INDEX_COUNT = N1 + N2 + N3 + N4; - - private static readonly byte[] INDEX_TO_UNITS; - private static readonly byte[] UNITS_TO_INDEX; - - public uint _allocatorSize; - public uint _glueCount; - public Pointer _baseUnit; - public Pointer _lowUnit; - public Pointer _highUnit; - public Pointer _text; - public Pointer _heap; - public MemoryNode[] _memoryNodes; - - public byte[] _memory; - - /// - /// Initializes static read-only arrays used by the . - /// - static Allocator() + for (index = 0, unitCount = 1; index < N1; index++, unitCount += 1) { - // Construct the static index to units lookup array. It will contain the following values. - // - // 1 2 3 4 6 8 10 12 15 18 21 24 28 32 36 40 44 48 52 56 60 64 68 72 76 80 84 88 92 96 100 104 108 - // 112 116 120 124 128 - - uint index; - uint unitCount; - - INDEX_TO_UNITS = new byte[INDEX_COUNT]; - - for (index = 0, unitCount = 1; index < N1; index++, unitCount += 1) - { - INDEX_TO_UNITS[index] = (byte)unitCount; - } - - for (unitCount++; index < N1 + N2; index++, unitCount += 2) - { - INDEX_TO_UNITS[index] = (byte)unitCount; - } - - for (unitCount++; index < N1 + N2 + N3; index++, unitCount += 3) - { - INDEX_TO_UNITS[index] = (byte)unitCount; - } - - for (unitCount++; index < N1 + N2 + N3 + N4; index++, unitCount += 4) - { - INDEX_TO_UNITS[index] = (byte)unitCount; - } - - // Construct the static units to index lookup array. It will contain the following values. - // - // 00 01 02 03 04 04 05 05 06 06 07 07 08 08 08 09 09 09 10 10 10 11 11 11 12 12 12 12 13 13 13 13 - // 14 14 14 14 15 15 15 15 16 16 16 16 17 17 17 17 18 18 18 18 19 19 19 19 20 20 20 20 21 21 21 21 - // 22 22 22 22 23 23 23 23 24 24 24 24 25 25 25 25 26 26 26 26 27 27 27 27 28 28 28 28 29 29 29 29 - // 30 30 30 30 31 31 31 31 32 32 32 32 33 33 33 33 34 34 34 34 35 35 35 35 36 36 36 36 37 37 37 37 - - UNITS_TO_INDEX = new byte[128]; - - for (unitCount = index = 0; unitCount < 128; unitCount++) - { - index += (uint)((INDEX_TO_UNITS[index] < unitCount + 1) ? 1 : 0); - UNITS_TO_INDEX[unitCount] = (byte)index; - } + INDEX_TO_UNITS[index] = (byte)unitCount; } - #region Public Methods - - public Allocator() + for (unitCount++; index < N1 + N2; index++, unitCount += 2) { - _memoryNodes = new MemoryNode[INDEX_COUNT]; + INDEX_TO_UNITS[index] = (byte)unitCount; } - /// - /// Initialize or reset the memory allocator (so that the single, large array can be re-used without destroying - /// and re-creating it). - /// - public void Initialize() + for (unitCount++; index < N1 + N2 + N3; index++, unitCount += 3) { - for (int index = 0; index < INDEX_COUNT; index++) - { - _memoryNodes[index] = new MemoryNode( - (uint)(NODE_OFFSET + index * MemoryNode.SIZE), - _memory - ); - _memoryNodes[index].Stamp = 0; - _memoryNodes[index].Next = MemoryNode.ZERO; - _memoryNodes[index].UnitCount = 0; - } - - _text = _heap; - - uint difference = UNIT_SIZE * (_allocatorSize / 8 / UNIT_SIZE * 7); - - _highUnit = _heap + _allocatorSize; - _lowUnit = _highUnit - difference; - _baseUnit = _highUnit - difference; - - _glueCount = 0; + INDEX_TO_UNITS[index] = (byte)unitCount; } - /// - /// Start the allocator (create a single, large array of bytes). - /// - /// - /// Note that .NET will create that array on the large object heap (because it is so large). - /// - /// - public void Start(int allocatorSize) + for (unitCount++; index < N1 + N2 + N3 + N4; index++, unitCount += 4) { - uint size = (uint)allocatorSize; - if (_allocatorSize != size) + INDEX_TO_UNITS[index] = (byte)unitCount; + } + + // Construct the static units to index lookup array. It will contain the following values. + // + // 00 01 02 03 04 04 05 05 06 06 07 07 08 08 08 09 09 09 10 10 10 11 11 11 12 12 12 12 13 13 13 13 + // 14 14 14 14 15 15 15 15 16 16 16 16 17 17 17 17 18 18 18 18 19 19 19 19 20 20 20 20 21 21 21 21 + // 22 22 22 22 23 23 23 23 24 24 24 24 25 25 25 25 26 26 26 26 27 27 27 27 28 28 28 28 29 29 29 29 + // 30 30 30 30 31 31 31 31 32 32 32 32 33 33 33 33 34 34 34 34 35 35 35 35 36 36 36 36 37 37 37 37 + + UNITS_TO_INDEX = new byte[128]; + + for (unitCount = index = 0; unitCount < 128; unitCount++) + { + index += (uint)((INDEX_TO_UNITS[index] < unitCount + 1) ? 1 : 0); + UNITS_TO_INDEX[unitCount] = (byte)index; + } + } + + #region Public Methods + + public Allocator() + { + _memoryNodes = new MemoryNode[INDEX_COUNT]; + } + + /// + /// Initialize or reset the memory allocator (so that the single, large array can be re-used without destroying + /// and re-creating it). + /// + public void Initialize() + { + for (var index = 0; index < INDEX_COUNT; index++) + { + _memoryNodes[index] = new MemoryNode( + (uint)(NODE_OFFSET + (index * MemoryNode.SIZE)), + _memory + ); + _memoryNodes[index].Stamp = 0; + _memoryNodes[index].Next = MemoryNode.ZERO; + _memoryNodes[index].UnitCount = 0; + } + + _text = _heap; + + var difference = UNIT_SIZE * (_allocatorSize / 8 / UNIT_SIZE * 7); + + _highUnit = _heap + _allocatorSize; + _lowUnit = _highUnit - difference; + _baseUnit = _highUnit - difference; + + _glueCount = 0; + } + + /// + /// Start the allocator (create a single, large array of bytes). + /// + /// + /// Note that .NET will create that array on the large object heap (because it is so large). + /// + /// + public void Start(int allocatorSize) + { + var size = (uint)allocatorSize; + if (_allocatorSize != size) + { + Stop(); + _memory = new byte[HEAP_OFFSET + size]; // the single, large array of bytes + _heap = new Pointer(HEAP_OFFSET, _memory); // reserve bytes in the range 0 .. HeapOffset - 1 + _allocatorSize = size; + } + } + + /// + /// Stop the allocator (free the single, large array of bytes). This can safely be called multiple times (without + /// intervening calls to ). + /// + /// + /// Because the array is on the large object heap it may not be freed immediately. + /// + public void Stop() + { + if (_allocatorSize != 0) + { + _allocatorSize = 0; + _memory = null; + _heap = Pointer.ZERO; + } + } + + /// + /// Determine how much memory (from the single, large array) is currenly in use. + /// + /// + public uint GetMemoryUsed() + { + var memoryUsed = _allocatorSize - (_highUnit - _lowUnit) - (_baseUnit - _text); + for (uint index = 0; index < INDEX_COUNT; index++) + { + memoryUsed -= UNIT_SIZE * INDEX_TO_UNITS[index] * _memoryNodes[index].Stamp; + } + return memoryUsed; + } + + /// + /// Allocate a given number of units from the single, large array. Each unit is bytes + /// in size. + /// + /// + /// + public Pointer AllocateUnits(uint unitCount) + { + uint index = UNITS_TO_INDEX[unitCount - 1]; + if (_memoryNodes[index].Available) + { + return _memoryNodes[index].Remove(); + } + + var allocatedBlock = _lowUnit; + _lowUnit += INDEX_TO_UNITS[index] * UNIT_SIZE; + if (_lowUnit <= _highUnit) + { + return allocatedBlock; + } + + _lowUnit -= INDEX_TO_UNITS[index] * UNIT_SIZE; + return AllocateUnitsRare(index); + } + + /// + /// Allocate enough space for a PpmContext instance in the single, large array. + /// + /// + public Pointer AllocateContext() + { + if (_highUnit != _lowUnit) + { + return (_highUnit -= UNIT_SIZE); + } + if (_memoryNodes[0].Available) + { + return _memoryNodes[0].Remove(); + } + return AllocateUnitsRare(0); + } + + /// + /// Increase the size of an existing allocation (represented by a ). + /// + /// + /// + /// + public Pointer ExpandUnits(Pointer oldPointer, uint oldUnitCount) + { + uint oldIndex = UNITS_TO_INDEX[oldUnitCount - 1]; + uint newIndex = UNITS_TO_INDEX[oldUnitCount]; + + if (oldIndex == newIndex) + { + return oldPointer; + } + + var pointer = AllocateUnits(oldUnitCount + 1); + + if (pointer != Pointer.ZERO) + { + CopyUnits(pointer, oldPointer, oldUnitCount); + _memoryNodes[oldIndex].Insert(oldPointer, oldUnitCount); + } + + return pointer; + } + + /// + /// Decrease the size of an existing allocation (represented by a ). + /// + /// + /// + /// + /// + public Pointer ShrinkUnits(Pointer oldPointer, uint oldUnitCount, uint newUnitCount) + { + uint oldIndex = UNITS_TO_INDEX[oldUnitCount - 1]; + uint newIndex = UNITS_TO_INDEX[newUnitCount - 1]; + + if (oldIndex == newIndex) + { + return oldPointer; + } + + if (_memoryNodes[newIndex].Available) + { + Pointer pointer = _memoryNodes[newIndex].Remove(); + CopyUnits(pointer, oldPointer, newUnitCount); + _memoryNodes[oldIndex].Insert(oldPointer, INDEX_TO_UNITS[oldIndex]); + return pointer; + } + SplitBlock(oldPointer, oldIndex, newIndex); + return oldPointer; + } + + /// + /// Free previously allocated space (the location and amount of space to free must be specified by using + /// a to indicate the location and a number of units to indicate the amount). + /// + /// + /// + public void FreeUnits(Pointer pointer, uint unitCount) + { + uint index = UNITS_TO_INDEX[unitCount - 1]; + _memoryNodes[index].Insert(pointer, INDEX_TO_UNITS[index]); + } + + public void SpecialFreeUnits(Pointer pointer) + { + if (pointer != _baseUnit) + { + _memoryNodes[0].Insert(pointer, 1); + } + else + { + MemoryNode memoryNode = pointer; + memoryNode.Stamp = uint.MaxValue; + _baseUnit += UNIT_SIZE; + } + } + + public Pointer MoveUnitsUp(Pointer oldPointer, uint unitCount) + { + uint index = UNITS_TO_INDEX[unitCount - 1]; + + if (oldPointer > _baseUnit + (16 * 1024) || oldPointer > _memoryNodes[index].Next) + { + return oldPointer; + } + + Pointer pointer = _memoryNodes[index].Remove(); + CopyUnits(pointer, oldPointer, unitCount); + unitCount = INDEX_TO_UNITS[index]; + + if (oldPointer != _baseUnit) + { + _memoryNodes[index].Insert(oldPointer, unitCount); + } + else + { + _baseUnit += unitCount * UNIT_SIZE; + } + + return pointer; + } + + /// + /// Expand the space allocated (in the single, large array) for the bytes of the data (ie. the "text") that is + /// being encoded or decoded. + /// + public void ExpandText() + { + MemoryNode memoryNode; + var counts = new uint[INDEX_COUNT]; + + while ((memoryNode = _baseUnit).Stamp == uint.MaxValue) + { + _baseUnit = memoryNode + memoryNode.UnitCount; + counts[UNITS_TO_INDEX[memoryNode.UnitCount - 1]]++; + memoryNode.Stamp = 0; + } + + for (uint index = 0; index < INDEX_COUNT; index++) + { + for ( + memoryNode = _memoryNodes[index]; + counts[index] != 0; + memoryNode = memoryNode.Next + ) { - Stop(); - _memory = new byte[HEAP_OFFSET + size]; // the single, large array of bytes - _heap = new Pointer(HEAP_OFFSET, _memory); // reserve bytes in the range 0 .. HeapOffset - 1 - _allocatorSize = size; + while (memoryNode.Next.Stamp == 0) + { + memoryNode.Unlink(); + _memoryNodes[index].Stamp--; + if (--counts[index] == 0) + { + break; + } + } } } + } - /// - /// Stop the allocator (free the single, large array of bytes). This can safely be called multiple times (without - /// intervening calls to ). - /// - /// - /// Because the array is on the large object heap it may not be freed immediately. - /// - public void Stop() - { - if (_allocatorSize != 0) - { - _allocatorSize = 0; - _memory = null; - _heap = Pointer.ZERO; - } - } + #endregion - /// - /// Determine how much memory (from the single, large array) is currenly in use. - /// - /// - public uint GetMemoryUsed() - { - uint memoryUsed = _allocatorSize - (_highUnit - _lowUnit) - (_baseUnit - _text); - for (uint index = 0; index < INDEX_COUNT; index++) - { - memoryUsed -= UNIT_SIZE * INDEX_TO_UNITS[index] * _memoryNodes[index].Stamp; - } - return memoryUsed; - } + #region Private Methods - /// - /// Allocate a given number of units from the single, large array. Each unit is bytes - /// in size. - /// - /// - /// - public Pointer AllocateUnits(uint unitCount) + private Pointer AllocateUnitsRare(uint index) + { + if (_glueCount == 0) { - uint index = UNITS_TO_INDEX[unitCount - 1]; + GlueFreeBlocks(); if (_memoryNodes[index].Available) { return _memoryNodes[index].Remove(); } - - Pointer allocatedBlock = _lowUnit; - _lowUnit += INDEX_TO_UNITS[index] * UNIT_SIZE; - if (_lowUnit <= _highUnit) - { - return allocatedBlock; - } - - _lowUnit -= INDEX_TO_UNITS[index] * UNIT_SIZE; - return AllocateUnitsRare(index); } - /// - /// Allocate enough space for a PpmContext instance in the single, large array. - /// - /// - public Pointer AllocateContext() + var oldIndex = index; + do { - if (_highUnit != _lowUnit) + if (++oldIndex == INDEX_COUNT) { - return (_highUnit -= UNIT_SIZE); + _glueCount--; + oldIndex = INDEX_TO_UNITS[index] * UNIT_SIZE; + return (_baseUnit - _text > oldIndex) ? (_baseUnit -= oldIndex) : Pointer.ZERO; } - if (_memoryNodes[0].Available) - { - return _memoryNodes[0].Remove(); - } - return AllocateUnitsRare(0); - } + } while (!_memoryNodes[oldIndex].Available); - /// - /// Increase the size of an existing allocation (represented by a ). - /// - /// - /// - /// - public Pointer ExpandUnits(Pointer oldPointer, uint oldUnitCount) - { - uint oldIndex = UNITS_TO_INDEX[oldUnitCount - 1]; - uint newIndex = UNITS_TO_INDEX[oldUnitCount]; - - if (oldIndex == newIndex) - { - return oldPointer; - } - - Pointer pointer = AllocateUnits(oldUnitCount + 1); - - if (pointer != Pointer.ZERO) - { - CopyUnits(pointer, oldPointer, oldUnitCount); - _memoryNodes[oldIndex].Insert(oldPointer, oldUnitCount); - } - - return pointer; - } - - /// - /// Decrease the size of an existing allocation (represented by a ). - /// - /// - /// - /// - /// - public Pointer ShrinkUnits(Pointer oldPointer, uint oldUnitCount, uint newUnitCount) - { - uint oldIndex = UNITS_TO_INDEX[oldUnitCount - 1]; - uint newIndex = UNITS_TO_INDEX[newUnitCount - 1]; - - if (oldIndex == newIndex) - { - return oldPointer; - } - - if (_memoryNodes[newIndex].Available) - { - Pointer pointer = _memoryNodes[newIndex].Remove(); - CopyUnits(pointer, oldPointer, newUnitCount); - _memoryNodes[oldIndex].Insert(oldPointer, INDEX_TO_UNITS[oldIndex]); - return pointer; - } - SplitBlock(oldPointer, oldIndex, newIndex); - return oldPointer; - } - - /// - /// Free previously allocated space (the location and amount of space to free must be specified by using - /// a to indicate the location and a number of units to indicate the amount). - /// - /// - /// - public void FreeUnits(Pointer pointer, uint unitCount) - { - uint index = UNITS_TO_INDEX[unitCount - 1]; - _memoryNodes[index].Insert(pointer, INDEX_TO_UNITS[index]); - } - - public void SpecialFreeUnits(Pointer pointer) - { - if (pointer != _baseUnit) - { - _memoryNodes[0].Insert(pointer, 1); - } - else - { - MemoryNode memoryNode = pointer; - memoryNode.Stamp = uint.MaxValue; - _baseUnit += UNIT_SIZE; - } - } - - public Pointer MoveUnitsUp(Pointer oldPointer, uint unitCount) - { - uint index = UNITS_TO_INDEX[unitCount - 1]; - - if (oldPointer > _baseUnit + 16 * 1024 || oldPointer > _memoryNodes[index].Next) - { - return oldPointer; - } - - Pointer pointer = _memoryNodes[index].Remove(); - CopyUnits(pointer, oldPointer, unitCount); - unitCount = INDEX_TO_UNITS[index]; - - if (oldPointer != _baseUnit) - { - _memoryNodes[index].Insert(oldPointer, unitCount); - } - else - { - _baseUnit += unitCount * UNIT_SIZE; - } - - return pointer; - } - - /// - /// Expand the space allocated (in the single, large array) for the bytes of the data (ie. the "text") that is - /// being encoded or decoded. - /// - public void ExpandText() - { - MemoryNode memoryNode; - uint[] counts = new uint[INDEX_COUNT]; - - while ((memoryNode = _baseUnit).Stamp == uint.MaxValue) - { - _baseUnit = memoryNode + memoryNode.UnitCount; - counts[UNITS_TO_INDEX[memoryNode.UnitCount - 1]]++; - memoryNode.Stamp = 0; - } - - for (uint index = 0; index < INDEX_COUNT; index++) - { - for ( - memoryNode = _memoryNodes[index]; - counts[index] != 0; - memoryNode = memoryNode.Next - ) - { - while (memoryNode.Next.Stamp == 0) - { - memoryNode.Unlink(); - _memoryNodes[index].Stamp--; - if (--counts[index] == 0) - { - break; - } - } - } - } - } - - #endregion - - #region Private Methods - - private Pointer AllocateUnitsRare(uint index) - { - if (_glueCount == 0) - { - GlueFreeBlocks(); - if (_memoryNodes[index].Available) - { - return _memoryNodes[index].Remove(); - } - } - - uint oldIndex = index; - do - { - if (++oldIndex == INDEX_COUNT) - { - _glueCount--; - oldIndex = INDEX_TO_UNITS[index] * UNIT_SIZE; - return (_baseUnit - _text > oldIndex) ? (_baseUnit -= oldIndex) : Pointer.ZERO; - } - } while (!_memoryNodes[oldIndex].Available); - - Pointer allocatedBlock = _memoryNodes[oldIndex].Remove(); - SplitBlock(allocatedBlock, oldIndex, index); - return allocatedBlock; - } - - private void SplitBlock(Pointer pointer, uint oldIndex, uint newIndex) - { - uint unitCountDifference = (uint)(INDEX_TO_UNITS[oldIndex] - INDEX_TO_UNITS[newIndex]); - Pointer newPointer = pointer + INDEX_TO_UNITS[newIndex] * UNIT_SIZE; - - uint index = UNITS_TO_INDEX[unitCountDifference - 1]; - if (INDEX_TO_UNITS[index] != unitCountDifference) - { - uint unitCount = INDEX_TO_UNITS[--index]; - _memoryNodes[index].Insert(newPointer, unitCount); - newPointer += unitCount * UNIT_SIZE; - unitCountDifference -= unitCount; - } - - _memoryNodes[UNITS_TO_INDEX[unitCountDifference - 1]].Insert( - newPointer, - unitCountDifference - ); - } - - private void GlueFreeBlocks() - { - MemoryNode memoryNode = new MemoryNode(LOCAL_OFFSET, _memory); - memoryNode.Stamp = 0; - memoryNode.Next = MemoryNode.ZERO; - memoryNode.UnitCount = 0; - - MemoryNode memoryNode0; - MemoryNode memoryNode1; - MemoryNode memoryNode2; - - if (_lowUnit != _highUnit) - { - _lowUnit[0] = 0; - } - - // Find all unused memory nodes. - - memoryNode1 = memoryNode; - for (uint index = 0; index < INDEX_COUNT; index++) - { - while (_memoryNodes[index].Available) - { - memoryNode0 = _memoryNodes[index].Remove(); - if (memoryNode0.UnitCount != 0) - { - while ( - (memoryNode2 = memoryNode0 + memoryNode0.UnitCount).Stamp - == uint.MaxValue - ) - { - memoryNode0.UnitCount = memoryNode0.UnitCount + memoryNode2.UnitCount; - memoryNode2.UnitCount = 0; - } - memoryNode1.Link(memoryNode0); - memoryNode1 = memoryNode0; - } - } - } - - // Coalesce the memory represented by the unused memory nodes. - - while (memoryNode.Available) - { - memoryNode0 = memoryNode.Remove(); - uint unitCount = memoryNode0.UnitCount; - if (unitCount != 0) - { - for (; unitCount > 128; unitCount -= 128, memoryNode0 += 128) - { - _memoryNodes[INDEX_COUNT - 1].Insert(memoryNode0, 128); - } - - uint index = UNITS_TO_INDEX[unitCount - 1]; - if (INDEX_TO_UNITS[index] != unitCount) - { - uint unitCountDifference = unitCount - INDEX_TO_UNITS[--index]; - _memoryNodes[unitCountDifference - 1].Insert( - memoryNode0 + (unitCount - unitCountDifference), - unitCountDifference - ); - } - - _memoryNodes[index].Insert(memoryNode0, INDEX_TO_UNITS[index]); - } - } - - _glueCount = 1 << 13; - } - - private void CopyUnits(Pointer target, Pointer source, uint unitCount) - { - do - { - target[0] = source[0]; - target[1] = source[1]; - target[2] = source[2]; - target[3] = source[3]; - target[4] = source[4]; - target[5] = source[5]; - target[6] = source[6]; - target[7] = source[7]; - target[8] = source[8]; - target[9] = source[9]; - target[10] = source[10]; - target[11] = source[11]; - target += UNIT_SIZE; - source += UNIT_SIZE; - } while (--unitCount != 0); - } - - #endregion + Pointer allocatedBlock = _memoryNodes[oldIndex].Remove(); + SplitBlock(allocatedBlock, oldIndex, index); + return allocatedBlock; } + + private void SplitBlock(Pointer pointer, uint oldIndex, uint newIndex) + { + var unitCountDifference = (uint)(INDEX_TO_UNITS[oldIndex] - INDEX_TO_UNITS[newIndex]); + var newPointer = pointer + (INDEX_TO_UNITS[newIndex] * UNIT_SIZE); + + uint index = UNITS_TO_INDEX[unitCountDifference - 1]; + if (INDEX_TO_UNITS[index] != unitCountDifference) + { + uint unitCount = INDEX_TO_UNITS[--index]; + _memoryNodes[index].Insert(newPointer, unitCount); + newPointer += unitCount * UNIT_SIZE; + unitCountDifference -= unitCount; + } + + _memoryNodes[UNITS_TO_INDEX[unitCountDifference - 1]].Insert( + newPointer, + unitCountDifference + ); + } + + private void GlueFreeBlocks() + { + var memoryNode = new MemoryNode(LOCAL_OFFSET, _memory); + memoryNode.Stamp = 0; + memoryNode.Next = MemoryNode.ZERO; + memoryNode.UnitCount = 0; + + MemoryNode memoryNode0; + MemoryNode memoryNode1; + MemoryNode memoryNode2; + + if (_lowUnit != _highUnit) + { + _lowUnit[0] = 0; + } + + // Find all unused memory nodes. + + memoryNode1 = memoryNode; + for (uint index = 0; index < INDEX_COUNT; index++) + { + while (_memoryNodes[index].Available) + { + memoryNode0 = _memoryNodes[index].Remove(); + if (memoryNode0.UnitCount != 0) + { + while ( + (memoryNode2 = memoryNode0 + memoryNode0.UnitCount).Stamp + == uint.MaxValue + ) + { + memoryNode0.UnitCount += memoryNode2.UnitCount; + memoryNode2.UnitCount = 0; + } + memoryNode1.Link(memoryNode0); + memoryNode1 = memoryNode0; + } + } + } + + // Coalesce the memory represented by the unused memory nodes. + + while (memoryNode.Available) + { + memoryNode0 = memoryNode.Remove(); + var unitCount = memoryNode0.UnitCount; + if (unitCount != 0) + { + for (; unitCount > 128; unitCount -= 128, memoryNode0 += 128) + { + _memoryNodes[INDEX_COUNT - 1].Insert(memoryNode0, 128); + } + + uint index = UNITS_TO_INDEX[unitCount - 1]; + if (INDEX_TO_UNITS[index] != unitCount) + { + var unitCountDifference = unitCount - INDEX_TO_UNITS[--index]; + _memoryNodes[unitCountDifference - 1].Insert( + memoryNode0 + (unitCount - unitCountDifference), + unitCountDifference + ); + } + + _memoryNodes[index].Insert(memoryNode0, INDEX_TO_UNITS[index]); + } + } + + _glueCount = 1 << 13; + } + + private void CopyUnits(Pointer target, Pointer source, uint unitCount) + { + do + { + target[0] = source[0]; + target[1] = source[1]; + target[2] = source[2]; + target[3] = source[3]; + target[4] = source[4]; + target[5] = source[5]; + target[6] = source[6]; + target[7] = source[7]; + target[8] = source[8]; + target[9] = source[9]; + target[10] = source[10]; + target[11] = source[11]; + target += UNIT_SIZE; + source += UNIT_SIZE; + } while (--unitCount != 0); + } + + #endregion } diff --git a/src/SharpCompress/Compressors/PPMd/I1/Coder.cs b/src/SharpCompress/Compressors/PPMd/I1/Coder.cs index 7f398284..c1644521 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/Coder.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/Coder.cs @@ -4,107 +4,106 @@ using System.IO; #endregion -namespace SharpCompress.Compressors.PPMd.I1 +namespace SharpCompress.Compressors.PPMd.I1; + +/// +/// A simple range coder. +/// +/// +/// Note that in most cases fields are used rather than properties for performance reasons (for example, +/// is a field rather than a property). +/// +internal class Coder { - /// - /// A simple range coder. - /// - /// - /// Note that in most cases fields are used rather than properties for performance reasons (for example, - /// is a field rather than a property). - /// - internal class Coder + private const uint RANGE_TOP = 1 << 24; + private const uint RANGE_BOTTOM = 1 << 15; + private uint _low; + private uint _code; + private uint _range; + + public uint _lowCount; + public uint _highCount; + public uint _scale; + + public void RangeEncoderInitialize() { - private const uint RANGE_TOP = 1 << 24; - private const uint RANGE_BOTTOM = 1 << 15; - private uint _low; - private uint _code; - private uint _range; + _low = 0; + _range = uint.MaxValue; + } - public uint _lowCount; - public uint _highCount; - public uint _scale; - - public void RangeEncoderInitialize() + public void RangeEncoderNormalize(Stream stream) + { + while ( + (_low ^ (_low + _range)) < RANGE_TOP + || _range < RANGE_BOTTOM + && ((_range = (uint)-_low & (RANGE_BOTTOM - 1)) != 0 || true) + ) { - _low = 0; - _range = uint.MaxValue; - } - - public void RangeEncoderNormalize(Stream stream) - { - while ( - (_low ^ (_low + _range)) < RANGE_TOP - || _range < RANGE_BOTTOM - && ((_range = (uint)-_low & (RANGE_BOTTOM - 1)) != 0 || true) - ) - { - stream.WriteByte((byte)(_low >> 24)); - _range <<= 8; - _low <<= 8; - } - } - - public void RangeEncodeSymbol() - { - _low += _lowCount * (_range /= _scale); - _range *= _highCount - _lowCount; - } - - public void RangeShiftEncodeSymbol(int rangeShift) - { - _low += _lowCount * (_range >>= rangeShift); - _range *= _highCount - _lowCount; - } - - public void RangeEncoderFlush(Stream stream) - { - for (uint index = 0; index < 4; index++) - { - stream.WriteByte((byte)(_low >> 24)); - _low <<= 8; - } - } - - public void RangeDecoderInitialize(Stream stream) - { - _low = 0; - _code = 0; - _range = uint.MaxValue; - for (uint index = 0; index < 4; index++) - { - _code = (_code << 8) | (byte)stream.ReadByte(); - } - } - - public void RangeDecoderNormalize(Stream stream) - { - while ( - (_low ^ (_low + _range)) < RANGE_TOP - || _range < RANGE_BOTTOM - && ((_range = (uint)-_low & (RANGE_BOTTOM - 1)) != 0 || true) - ) - { - _code = (_code << 8) | (byte)stream.ReadByte(); - _range <<= 8; - _low <<= 8; - } - } - - public uint RangeGetCurrentCount() - { - return (_code - _low) / (_range /= _scale); - } - - public uint RangeGetCurrentShiftCount(int rangeShift) - { - return (_code - _low) / (_range >>= rangeShift); - } - - public void RangeRemoveSubrange() - { - _low += _range * _lowCount; - _range *= _highCount - _lowCount; + stream.WriteByte((byte)(_low >> 24)); + _range <<= 8; + _low <<= 8; } } + + public void RangeEncodeSymbol() + { + _low += _lowCount * (_range /= _scale); + _range *= _highCount - _lowCount; + } + + public void RangeShiftEncodeSymbol(int rangeShift) + { + _low += _lowCount * (_range >>= rangeShift); + _range *= _highCount - _lowCount; + } + + public void RangeEncoderFlush(Stream stream) + { + for (uint index = 0; index < 4; index++) + { + stream.WriteByte((byte)(_low >> 24)); + _low <<= 8; + } + } + + public void RangeDecoderInitialize(Stream stream) + { + _low = 0; + _code = 0; + _range = uint.MaxValue; + for (uint index = 0; index < 4; index++) + { + _code = (_code << 8) | (byte)stream.ReadByte(); + } + } + + public void RangeDecoderNormalize(Stream stream) + { + while ( + (_low ^ (_low + _range)) < RANGE_TOP + || _range < RANGE_BOTTOM + && ((_range = (uint)-_low & (RANGE_BOTTOM - 1)) != 0 || true) + ) + { + _code = (_code << 8) | (byte)stream.ReadByte(); + _range <<= 8; + _low <<= 8; + } + } + + public uint RangeGetCurrentCount() + { + return (_code - _low) / (_range /= _scale); + } + + public uint RangeGetCurrentShiftCount(int rangeShift) + { + return (_code - _low) / (_range >>= rangeShift); + } + + public void RangeRemoveSubrange() + { + _low += _range * _lowCount; + _range *= _highCount - _lowCount; + } } diff --git a/src/SharpCompress/Compressors/PPMd/I1/MemoryNode.cs b/src/SharpCompress/Compressors/PPMd/I1/MemoryNode.cs index 1c6429ad..e9c64e53 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/MemoryNode.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/MemoryNode.cs @@ -1,254 +1,252 @@ #nullable disable -namespace SharpCompress.Compressors.PPMd.I1 +namespace SharpCompress.Compressors.PPMd.I1; + +/// +/// A structure containing a single address. The address represents a location in the +/// array. That location in the array contains information itself describing a section +/// of the array (ie. a block of memory). +/// +/// +/// +/// This must be a structure rather than a class because several places in the associated code assume that +/// is a value type (meaning that assignment creates a completely new copy of +/// the instance rather than just copying a reference to the same instance). +/// +/// +/// MemoryNode +/// 4 Stamp +/// 4 Next +/// 4 UnitCount +/// +/// +/// Note that is a field rather than a property for performance reasons. +/// +/// +internal struct MemoryNode { + public uint _address; + public byte[] _memory; + public static readonly MemoryNode ZERO = new MemoryNode(0, null); + public const int SIZE = 12; + /// - /// A structure containing a single address. The address represents a location in the - /// array. That location in the array contains information itself describing a section - /// of the array (ie. a block of memory). + /// Initializes a new instance of the structure. /// - /// - /// - /// This must be a structure rather than a class because several places in the associated code assume that - /// is a value type (meaning that assignment creates a completely new copy of - /// the instance rather than just copying a reference to the same instance). - /// - /// - /// MemoryNode - /// 4 Stamp - /// 4 Next - /// 4 UnitCount - /// - /// - /// Note that is a field rather than a property for performance reasons. - /// - /// - internal struct MemoryNode + public MemoryNode(uint address, byte[] memory) { - public uint _address; - public byte[] _memory; - public static readonly MemoryNode ZERO = new MemoryNode(0, null); - public const int SIZE = 12; + _address = address; + _memory = memory; + } - /// - /// Initializes a new instance of the structure. - /// - public MemoryNode(uint address, byte[] memory) + /// + /// Gets or sets the stamp. + /// + public uint Stamp + { + get => + _memory[_address] + | (((uint)_memory[_address + 1]) << 8) + | (((uint)_memory[_address + 2]) << 16) + | (((uint)_memory[_address + 3]) << 24); + set { - _address = address; - _memory = memory; - } - - /// - /// Gets or sets the stamp. - /// - public uint Stamp - { - get => - _memory[_address] - | ((uint)_memory[_address + 1]) << 8 - | ((uint)_memory[_address + 2]) << 16 - | ((uint)_memory[_address + 3]) << 24; - set - { - _memory[_address] = (byte)value; - _memory[_address + 1] = (byte)(value >> 8); - _memory[_address + 2] = (byte)(value >> 16); - _memory[_address + 3] = (byte)(value >> 24); - } - } - - /// - /// Gets or sets the next memory node. - /// - public MemoryNode Next - { - get => - new MemoryNode( - _memory[_address + 4] - | ((uint)_memory[_address + 5]) << 8 - | ((uint)_memory[_address + 6]) << 16 - | ((uint)_memory[_address + 7]) << 24, - _memory - ); - set - { - _memory[_address + 4] = (byte)value._address; - _memory[_address + 5] = (byte)(value._address >> 8); - _memory[_address + 6] = (byte)(value._address >> 16); - _memory[_address + 7] = (byte)(value._address >> 24); - } - } - - /// - /// Gets or sets the unit count. - /// - public uint UnitCount - { - get => - _memory[_address + 8] - | ((uint)_memory[_address + 9]) << 8 - | ((uint)_memory[_address + 10]) << 16 - | ((uint)_memory[_address + 11]) << 24; - set - { - _memory[_address + 8] = (byte)value; - _memory[_address + 9] = (byte)(value >> 8); - _memory[_address + 10] = (byte)(value >> 16); - _memory[_address + 11] = (byte)(value >> 24); - } - } - - /// - /// Gets whether there is a next memory node available. - /// - public bool Available => Next._address != 0; - - /// - /// Link in the provided memory node. - /// - /// - public void Link(MemoryNode memoryNode) - { - memoryNode.Next = Next; - Next = memoryNode; - } - - /// - /// Unlink this memory node. - /// - public void Unlink() - { - Next = Next.Next; - } - - /// - /// Insert the memory node into the linked list. - /// - /// - /// - public void Insert(MemoryNode memoryNode, uint unitCount) - { - Link(memoryNode); - memoryNode.Stamp = uint.MaxValue; - memoryNode.UnitCount = unitCount; - Stamp++; - } - - /// - /// Remove this memory node from the linked list. - /// - /// - public MemoryNode Remove() - { - MemoryNode next = Next; - Unlink(); - Stamp--; - return next; - } - - /// - /// Allow a pointer to be implicitly converted to a memory node. - /// - /// - /// - public static implicit operator MemoryNode(Pointer pointer) - { - return new MemoryNode(pointer._address, pointer._memory); - } - - /// - /// Allow pointer-like addition on a memory node. - /// - /// - /// - /// - public static MemoryNode operator +(MemoryNode memoryNode, int offset) - { - memoryNode._address = (uint)(memoryNode._address + offset * SIZE); - return memoryNode; - } - - /// - /// Allow pointer-like addition on a memory node. - /// - /// - /// - /// - public static MemoryNode operator +(MemoryNode memoryNode, uint offset) - { - memoryNode._address += offset * SIZE; - return memoryNode; - } - - /// - /// Allow pointer-like subtraction on a memory node. - /// - /// - /// - /// - public static MemoryNode operator -(MemoryNode memoryNode, int offset) - { - memoryNode._address = (uint)(memoryNode._address - offset * SIZE); - return memoryNode; - } - - /// - /// Allow pointer-like subtraction on a memory node. - /// - /// - /// - /// - public static MemoryNode operator -(MemoryNode memoryNode, uint offset) - { - memoryNode._address -= offset * SIZE; - return memoryNode; - } - - /// - /// Compare two memory nodes. - /// - /// - /// - /// - public static bool operator ==(MemoryNode memoryNode1, MemoryNode memoryNode2) - { - return memoryNode1._address == memoryNode2._address; - } - - /// - /// Compare two memory nodes. - /// - /// - /// - /// - public static bool operator !=(MemoryNode memoryNode1, MemoryNode memoryNode2) - { - return memoryNode1._address != memoryNode2._address; - } - - /// - /// Indicates whether this instance and a specified object are equal. - /// - /// true if obj and this instance are the same type and represent the same value; otherwise, false. - /// Another object to compare to. - public override bool Equals(object obj) - { - if (obj is MemoryNode) - { - MemoryNode memoryNode = (MemoryNode)obj; - return memoryNode._address == _address; - } - return base.Equals(obj); - } - - /// - /// Returns the hash code for this instance. - /// - /// A 32-bit signed integer that is the hash code for this instance. - public override int GetHashCode() - { - return _address.GetHashCode(); + _memory[_address] = (byte)value; + _memory[_address + 1] = (byte)(value >> 8); + _memory[_address + 2] = (byte)(value >> 16); + _memory[_address + 3] = (byte)(value >> 24); } } + + /// + /// Gets or sets the next memory node. + /// + public MemoryNode Next + { + get => + new MemoryNode( + _memory[_address + 4] + | (((uint)_memory[_address + 5]) << 8) + | (((uint)_memory[_address + 6]) << 16) + | (((uint)_memory[_address + 7]) << 24), + _memory + ); + set + { + _memory[_address + 4] = (byte)value._address; + _memory[_address + 5] = (byte)(value._address >> 8); + _memory[_address + 6] = (byte)(value._address >> 16); + _memory[_address + 7] = (byte)(value._address >> 24); + } + } + + /// + /// Gets or sets the unit count. + /// + public uint UnitCount + { + get => + _memory[_address + 8] + | (((uint)_memory[_address + 9]) << 8) + | (((uint)_memory[_address + 10]) << 16) + | (((uint)_memory[_address + 11]) << 24); + set + { + _memory[_address + 8] = (byte)value; + _memory[_address + 9] = (byte)(value >> 8); + _memory[_address + 10] = (byte)(value >> 16); + _memory[_address + 11] = (byte)(value >> 24); + } + } + + /// + /// Gets whether there is a next memory node available. + /// + public bool Available => Next._address != 0; + + /// + /// Link in the provided memory node. + /// + /// + public void Link(MemoryNode memoryNode) + { + memoryNode.Next = Next; + Next = memoryNode; + } + + /// + /// Unlink this memory node. + /// + public void Unlink() + { + Next = Next.Next; + } + + /// + /// Insert the memory node into the linked list. + /// + /// + /// + public void Insert(MemoryNode memoryNode, uint unitCount) + { + Link(memoryNode); + memoryNode.Stamp = uint.MaxValue; + memoryNode.UnitCount = unitCount; + Stamp++; + } + + /// + /// Remove this memory node from the linked list. + /// + /// + public MemoryNode Remove() + { + var next = Next; + Unlink(); + Stamp--; + return next; + } + + /// + /// Allow a pointer to be implicitly converted to a memory node. + /// + /// + /// + public static implicit operator MemoryNode(Pointer pointer) + { + return new MemoryNode(pointer._address, pointer._memory); + } + + /// + /// Allow pointer-like addition on a memory node. + /// + /// + /// + /// + public static MemoryNode operator +(MemoryNode memoryNode, int offset) + { + memoryNode._address = (uint)(memoryNode._address + (offset * SIZE)); + return memoryNode; + } + + /// + /// Allow pointer-like addition on a memory node. + /// + /// + /// + /// + public static MemoryNode operator +(MemoryNode memoryNode, uint offset) + { + memoryNode._address += offset * SIZE; + return memoryNode; + } + + /// + /// Allow pointer-like subtraction on a memory node. + /// + /// + /// + /// + public static MemoryNode operator -(MemoryNode memoryNode, int offset) + { + memoryNode._address = (uint)(memoryNode._address - (offset * SIZE)); + return memoryNode; + } + + /// + /// Allow pointer-like subtraction on a memory node. + /// + /// + /// + /// + public static MemoryNode operator -(MemoryNode memoryNode, uint offset) + { + memoryNode._address -= offset * SIZE; + return memoryNode; + } + + /// + /// Compare two memory nodes. + /// + /// + /// + /// + public static bool operator ==(MemoryNode memoryNode1, MemoryNode memoryNode2) + { + return memoryNode1._address == memoryNode2._address; + } + + /// + /// Compare two memory nodes. + /// + /// + /// + /// + public static bool operator !=(MemoryNode memoryNode1, MemoryNode memoryNode2) + { + return memoryNode1._address != memoryNode2._address; + } + + /// + /// Indicates whether this instance and a specified object are equal. + /// + /// true if obj and this instance are the same type and represent the same value; otherwise, false. + /// Another object to compare to. + public override bool Equals(object obj) + { + if (obj is MemoryNode memoryNode) + { + return memoryNode._address == _address; + } + return base.Equals(obj); + } + + /// + /// Returns the hash code for this instance. + /// + /// A 32-bit signed integer that is the hash code for this instance. + public override int GetHashCode() + { + return _address.GetHashCode(); + } } diff --git a/src/SharpCompress/Compressors/PPMd/I1/Model.cs b/src/SharpCompress/Compressors/PPMd/I1/Model.cs index d7f67b8b..aebfcae8 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/Model.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/Model.cs @@ -6,960 +6,959 @@ using System.IO; // This is a port of Dmitry Shkarin's PPMd Variant I Revision 1. // Ported by Michael Bone (mjbone03@yahoo.com.au). -namespace SharpCompress.Compressors.PPMd.I1 +namespace SharpCompress.Compressors.PPMd.I1; + +/// +/// The model. +/// +internal partial class Model { - /// - /// The model. - /// - internal partial class Model + public const uint SIGNATURE = 0x84acaf8fU; + public const char VARIANT = 'I'; + public const int MAXIMUM_ORDER = 16; // maximum allowed model order + + private const byte UPPER_FREQUENCY = 5; + private const byte INTERVAL_BIT_COUNT = 7; + private const byte PERIOD_BIT_COUNT = 7; + private const byte TOTAL_BIT_COUNT = INTERVAL_BIT_COUNT + PERIOD_BIT_COUNT; + private const uint INTERVAL = 1 << INTERVAL_BIT_COUNT; + private const uint BINARY_SCALE = 1 << TOTAL_BIT_COUNT; + private const uint MAXIMUM_FREQUENCY = 124; + private const uint ORDER_BOUND = 9; + + private readonly See2Context[,] _see2Contexts; + private readonly See2Context _emptySee2Context; + private PpmContext _maximumContext; + private readonly ushort[,] _binarySummary = new ushort[25, 64]; // binary SEE-contexts + private readonly byte[] _numberStatisticsToBinarySummaryIndex = new byte[256]; + private readonly byte[] _probabilities = new byte[260]; + private readonly byte[] _characterMask = new byte[256]; + private byte _escapeCount; + private int _modelOrder; + private int _orderFall; + private int _initialEscape; + private int _initialRunLength; + private int _runLength; + private byte _previousSuccess; + private byte _numberMasked; + private ModelRestorationMethod _method; + private PpmState _foundState; // found next state transition + + private Allocator _allocator; + private Coder _coder; + private PpmContext _minimumContext; + private byte _numberStatistics; + private readonly PpmState[] _decodeStates = new PpmState[256]; + + private static readonly ushort[] INITIAL_BINARY_ESCAPES = { - public const uint SIGNATURE = 0x84acaf8fU; - public const char VARIANT = 'I'; - public const int MAXIMUM_ORDER = 16; // maximum allowed model order + 0x3CDD, + 0x1F3F, + 0x59BF, + 0x48F3, + 0x64A1, + 0x5ABC, + 0x6632, + 0x6051 + }; - private const byte UPPER_FREQUENCY = 5; - private const byte INTERVAL_BIT_COUNT = 7; - private const byte PERIOD_BIT_COUNT = 7; - private const byte TOTAL_BIT_COUNT = INTERVAL_BIT_COUNT + PERIOD_BIT_COUNT; - private const uint INTERVAL = 1 << INTERVAL_BIT_COUNT; - private const uint BINARY_SCALE = 1 << TOTAL_BIT_COUNT; - private const uint MAXIMUM_FREQUENCY = 124; - private const uint ORDER_BOUND = 9; + private static ReadOnlySpan EXPONENTIAL_ESCAPES => + new byte[] { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 }; - private readonly See2Context[,] _see2Contexts; - private readonly See2Context _emptySee2Context; - private PpmContext _maximumContext; - private readonly ushort[,] _binarySummary = new ushort[25, 64]; // binary SEE-contexts - private readonly byte[] _numberStatisticsToBinarySummaryIndex = new byte[256]; - private readonly byte[] _probabilities = new byte[260]; - private readonly byte[] _characterMask = new byte[256]; - private byte _escapeCount; - private int _modelOrder; - private int _orderFall; - private int _initialEscape; - private int _initialRunLength; - private int _runLength; - private byte _previousSuccess; - private byte _numberMasked; - private ModelRestorationMethod _method; - private PpmState _foundState; // found next state transition + #region Public Methods - private Allocator _allocator; - private Coder _coder; - private PpmContext _minimumContext; - private byte _numberStatistics; - private readonly PpmState[] _decodeStates = new PpmState[256]; + public Model() + { + // Construct the conversion table for number statistics. Initially it will contain the following values. + // + // 0 2 4 4 4 4 4 4 4 4 4 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 + // 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 + // 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 + // 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 + // 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 + // 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 + // 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 + // 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 - private static readonly ushort[] INITIAL_BINARY_ESCAPES = + _numberStatisticsToBinarySummaryIndex[0] = 2 * 0; + _numberStatisticsToBinarySummaryIndex[1] = 2 * 1; + for (var index = 2; index < 11; index++) { - 0x3CDD, - 0x1F3F, - 0x59BF, - 0x48F3, - 0x64A1, - 0x5ABC, - 0x6632, - 0x6051 - }; - - private static ReadOnlySpan EXPONENTIAL_ESCAPES => - new byte[] { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 }; - - #region Public Methods - - public Model() + _numberStatisticsToBinarySummaryIndex[index] = 2 * 2; + } + for (var index = 11; index < 256; index++) { - // Construct the conversion table for number statistics. Initially it will contain the following values. - // - // 0 2 4 4 4 4 4 4 4 4 4 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 - // 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 - // 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 - // 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 - // 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 - // 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 - // 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 - // 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 - - _numberStatisticsToBinarySummaryIndex[0] = 2 * 0; - _numberStatisticsToBinarySummaryIndex[1] = 2 * 1; - for (int index = 2; index < 11; index++) - { - _numberStatisticsToBinarySummaryIndex[index] = 2 * 2; - } - for (int index = 11; index < 256; index++) - { - _numberStatisticsToBinarySummaryIndex[index] = 2 * 3; - } - - // Construct the probability table. Initially it will contain the following values (depending on the value of - // the upper frequency). - // - // 00 01 02 03 04 05 06 06 07 07 07 08 08 08 08 09 09 09 09 09 10 10 10 10 10 10 11 11 11 11 11 11 - // 11 12 12 12 12 12 12 12 12 13 13 13 13 13 13 13 13 13 14 14 14 14 14 14 14 14 14 14 15 15 15 15 - // 15 15 15 15 15 15 15 16 16 16 16 16 16 16 16 16 16 16 16 17 17 17 17 17 17 17 17 17 17 17 17 17 - // 18 18 18 18 18 18 18 18 18 18 18 18 18 18 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 20 20 20 - // 20 20 20 20 20 20 20 20 20 20 20 20 20 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 22 22 - // 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 - // 23 23 23 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 25 25 25 25 25 25 25 25 25 - // 25 25 25 25 25 25 25 25 25 25 25 25 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 - // 26 26 27 27 - - uint count = 1; - uint step = 1; - uint probability = UPPER_FREQUENCY; - - for (int index = 0; index < UPPER_FREQUENCY; index++) - { - _probabilities[index] = (byte)index; - } - - for (int index = UPPER_FREQUENCY; index < 260; index++) - { - _probabilities[index] = (byte)probability; - count--; - if (count == 0) - { - step++; - count = step; - probability++; - } - } - - // Create the context array. - - _see2Contexts = new See2Context[24, 32]; - for (int index1 = 0; index1 < 24; index1++) - { - for (int index2 = 0; index2 < 32; index2++) - { - _see2Contexts[index1, index2] = new See2Context(); - } - } - - // Set the signature (identifying the algorithm). - - _emptySee2Context = new See2Context(); - _emptySee2Context._summary = (ushort)(SIGNATURE & 0x0000ffff); - _emptySee2Context._shift = (byte)((SIGNATURE >> 16) & 0x000000ff); - _emptySee2Context._count = (byte)(SIGNATURE >> 24); + _numberStatisticsToBinarySummaryIndex[index] = 2 * 3; } - /// - /// Encode (ie. compress) a given source stream, writing the encoded result to the target stream. - /// - public void Encode(Stream target, Stream source, PpmdProperties properties) + // Construct the probability table. Initially it will contain the following values (depending on the value of + // the upper frequency). + // + // 00 01 02 03 04 05 06 06 07 07 07 08 08 08 08 09 09 09 09 09 10 10 10 10 10 10 11 11 11 11 11 11 + // 11 12 12 12 12 12 12 12 12 13 13 13 13 13 13 13 13 13 14 14 14 14 14 14 14 14 14 14 15 15 15 15 + // 15 15 15 15 15 15 15 16 16 16 16 16 16 16 16 16 16 16 16 17 17 17 17 17 17 17 17 17 17 17 17 17 + // 18 18 18 18 18 18 18 18 18 18 18 18 18 18 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 20 20 20 + // 20 20 20 20 20 20 20 20 20 20 20 20 20 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 21 22 22 + // 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 + // 23 23 23 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 24 25 25 25 25 25 25 25 25 25 + // 25 25 25 25 25 25 25 25 25 25 25 25 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 + // 26 26 27 27 + + uint count = 1; + uint step = 1; + uint probability = UPPER_FREQUENCY; + + for (var index = 0; index < UPPER_FREQUENCY; index++) { - if (target is null) - { - throw new ArgumentNullException(nameof(target)); - } - - if (source is null) - { - throw new ArgumentNullException(nameof(source)); - } - - EncodeStart(properties); - EncodeBlock(target, source, true); + _probabilities[index] = (byte)index; } - internal Coder EncodeStart(PpmdProperties properties) + for (int index = UPPER_FREQUENCY; index < 260; index++) { - _allocator = properties._allocator; - _coder = new Coder(); - _coder.RangeEncoderInitialize(); - StartModel(properties.ModelOrder, properties.RestorationMethod); - return _coder; - } - - internal void EncodeBlock(Stream target, Stream source, bool final) - { - while (true) + _probabilities[index] = (byte)probability; + count--; + if (count == 0) { - _minimumContext = _maximumContext; - _numberStatistics = _minimumContext.NumberStatistics; - - int c = source.ReadByte(); - if (c < 0 && !final) - { - return; - } - - if (_numberStatistics != 0) - { - EncodeSymbol1(c, _minimumContext); - _coder.RangeEncodeSymbol(); - } - else - { - EncodeBinarySymbol(c, _minimumContext); - _coder.RangeShiftEncodeSymbol(TOTAL_BIT_COUNT); - } - - while (_foundState == PpmState.ZERO) - { - _coder.RangeEncoderNormalize(target); - do - { - _orderFall++; - _minimumContext = _minimumContext.Suffix; - if (_minimumContext == PpmContext.ZERO) - { - goto StopEncoding; - } - } while (_minimumContext.NumberStatistics == _numberMasked); - EncodeSymbol2(c, _minimumContext); - _coder.RangeEncodeSymbol(); - } - - if (_orderFall == 0 && (Pointer)_foundState.Successor >= _allocator._baseUnit) - { - _maximumContext = _foundState.Successor; - } - else - { - UpdateModel(_minimumContext); - if (_escapeCount == 0) - { - ClearMask(); - } - } - - _coder.RangeEncoderNormalize(target); - } - - StopEncoding: - _coder.RangeEncoderFlush(target); - } - - /// - /// Dencode (ie. decompress) a given source stream, writing the decoded result to the target stream. - /// - public void Decode(Stream target, Stream source, PpmdProperties properties) - { - if (target is null) - { - throw new ArgumentNullException(nameof(target)); - } - - if (source is null) - { - throw new ArgumentNullException(nameof(source)); - } - - DecodeStart(source, properties); - byte[] buffer = new byte[65536]; - int read; - while ((read = DecodeBlock(source, buffer, 0, buffer.Length)) != 0) - { - target.Write(buffer, 0, read); + step++; + count = step; + probability++; } } - internal Coder DecodeStart(Stream source, PpmdProperties properties) + // Create the context array. + + _see2Contexts = new See2Context[24, 32]; + for (var index1 = 0; index1 < 24; index1++) + { + for (var index2 = 0; index2 < 32; index2++) + { + _see2Contexts[index1, index2] = new See2Context(); + } + } + + // Set the signature (identifying the algorithm). + + _emptySee2Context = new See2Context(); + _emptySee2Context._summary = (ushort)(SIGNATURE & 0x0000ffff); + _emptySee2Context._shift = (byte)((SIGNATURE >> 16) & 0x000000ff); + _emptySee2Context._count = (byte)(SIGNATURE >> 24); + } + + /// + /// Encode (ie. compress) a given source stream, writing the encoded result to the target stream. + /// + public void Encode(Stream target, Stream source, PpmdProperties properties) + { + if (target is null) + { + throw new ArgumentNullException(nameof(target)); + } + + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + + EncodeStart(properties); + EncodeBlock(target, source, true); + } + + internal Coder EncodeStart(PpmdProperties properties) + { + _allocator = properties._allocator; + _coder = new Coder(); + _coder.RangeEncoderInitialize(); + StartModel(properties.ModelOrder, properties.RestorationMethod); + return _coder; + } + + internal void EncodeBlock(Stream target, Stream source, bool final) + { + while (true) { - _allocator = properties._allocator; - _coder = new Coder(); - _coder.RangeDecoderInitialize(source); - StartModel(properties.ModelOrder, properties.RestorationMethod); _minimumContext = _maximumContext; _numberStatistics = _minimumContext.NumberStatistics; - return _coder; - } - internal int DecodeBlock(Stream source, byte[] buffer, int offset, int count) - { - if (_minimumContext == PpmContext.ZERO) + var c = source.ReadByte(); + if (c < 0 && !final) { - return 0; - } - - int total = 0; - while (total < count) - { - if (_numberStatistics != 0) - { - DecodeSymbol1(_minimumContext); - } - else - { - DecodeBinarySymbol(_minimumContext); - } - - _coder.RangeRemoveSubrange(); - - while (_foundState == PpmState.ZERO) - { - _coder.RangeDecoderNormalize(source); - do - { - _orderFall++; - _minimumContext = _minimumContext.Suffix; - if (_minimumContext == PpmContext.ZERO) - { - goto StopDecoding; - } - } while (_minimumContext.NumberStatistics == _numberMasked); - DecodeSymbol2(_minimumContext); - _coder.RangeRemoveSubrange(); - } - - buffer[offset] = _foundState.Symbol; - offset++; - total++; - - if (_orderFall == 0 && (Pointer)_foundState.Successor >= _allocator._baseUnit) - { - _maximumContext = _foundState.Successor; - } - else - { - UpdateModel(_minimumContext); - if (_escapeCount == 0) - { - ClearMask(); - } - } - - _minimumContext = _maximumContext; - _numberStatistics = _minimumContext.NumberStatistics; - _coder.RangeDecoderNormalize(source); - } - - StopDecoding: - return total; - } - - #endregion - - #region Private Methods - - /// - /// Initialise the model (unless the model order is set to 1 in which case the model should be cleared so that - /// the statistics are carried over, allowing "solid" mode compression). - /// - private void StartModel(int modelOrder, ModelRestorationMethod modelRestorationMethod) - { - Array.Clear(_characterMask, 0, _characterMask.Length); - _escapeCount = 1; - - // Compress in "solid" mode if the model order value is set to 1 (this will examine the current PPM context - // structures to determine the value of orderFall). - - if (modelOrder < 2) - { - _orderFall = _modelOrder; - for ( - PpmContext context = _maximumContext; - context.Suffix != PpmContext.ZERO; - context = context.Suffix - ) - { - _orderFall--; - } return; } - _modelOrder = modelOrder; - _orderFall = modelOrder; - _method = modelRestorationMethod; - _allocator.Initialize(); - _initialRunLength = -((modelOrder < 12) ? modelOrder : 12) - 1; - _runLength = _initialRunLength; - - // Allocate the context structure. - - _maximumContext = _allocator.AllocateContext(); - _maximumContext.Suffix = PpmContext.ZERO; - _maximumContext.NumberStatistics = 255; - _maximumContext.SummaryFrequency = (ushort)(_maximumContext.NumberStatistics + 2); - _maximumContext.Statistics = _allocator.AllocateUnits(256 / 2); - - // allocates enough space for 256 PPM states (each is 6 bytes) - - _previousSuccess = 0; - for (int index = 0; index < 256; index++) + if (_numberStatistics != 0) { - PpmState state = _maximumContext.Statistics[index]; - state.Symbol = (byte)index; - state.Frequency = 1; - state.Successor = PpmContext.ZERO; - } - - uint probability = 0; - for (int index1 = 0; probability < 25; probability++) - { - while (_probabilities[index1] == probability) - { - index1++; - } - for (int index2 = 0; index2 < 8; index2++) - { - _binarySummary[probability, index2] = (ushort)( - BINARY_SCALE - INITIAL_BINARY_ESCAPES[index2] / (index1 + 1) - ); - } - for (int index2 = 8; index2 < 64; index2 += 8) - { - for (int index3 = 0; index3 < 8; index3++) - { - _binarySummary[probability, index2 + index3] = _binarySummary[ - probability, - index3 - ]; - } - } - } - - probability = 0; - for (uint index1 = 0; probability < 24; probability++) - { - while (_probabilities[index1 + 3] == probability + 3) - { - index1++; - } - for (int index2 = 0; index2 < 32; index2++) - { - _see2Contexts[probability, index2].Initialize(2 * index1 + 5); - } - } - } - - private void UpdateModel(PpmContext minimumContext) - { - PpmState state = PpmState.ZERO; - PpmContext successor; - PpmContext currentContext = _maximumContext; - uint numberStatistics; - uint ns1; - uint cf; - uint sf; - uint s0; - uint foundStateFrequency = _foundState.Frequency; - byte foundStateSymbol = _foundState.Symbol; - byte symbol; - byte flag; - - PpmContext foundStateSuccessor = _foundState.Successor; - PpmContext context = minimumContext.Suffix; - - if ((foundStateFrequency < MAXIMUM_FREQUENCY / 4) && (context != PpmContext.ZERO)) - { - if (context.NumberStatistics != 0) - { - state = context.Statistics; - if (state.Symbol != foundStateSymbol) - { - do - { - symbol = state[1].Symbol; - state++; - } while (symbol != foundStateSymbol); - if (state[0].Frequency >= state[-1].Frequency) - { - Swap(state[0], state[-1]); - state--; - } - } - cf = (uint)((state.Frequency < MAXIMUM_FREQUENCY - 9) ? 2 : 0); - state.Frequency += (byte)cf; - context.SummaryFrequency += (byte)cf; - } - else - { - state = context.FirstState; - state.Frequency += (byte)((state.Frequency < 32) ? 1 : 0); - } - } - - if (_orderFall == 0 && foundStateSuccessor != PpmContext.ZERO) - { - _foundState.Successor = CreateSuccessors(true, state, minimumContext); - if (_foundState.Successor == PpmContext.ZERO) - { - goto RestartModel; - } - _maximumContext = _foundState.Successor; - return; - } - - _allocator._text[0] = foundStateSymbol; - _allocator._text++; - successor = _allocator._text; - - if (_allocator._text >= _allocator._baseUnit) - { - goto RestartModel; - } - - if (foundStateSuccessor != PpmContext.ZERO) - { - if (foundStateSuccessor < _allocator._baseUnit) - { - foundStateSuccessor = CreateSuccessors(false, state, minimumContext); - } + EncodeSymbol1(c, _minimumContext); + _coder.RangeEncodeSymbol(); } else { - foundStateSuccessor = ReduceOrder(state, minimumContext); + EncodeBinarySymbol(c, _minimumContext); + _coder.RangeShiftEncodeSymbol(TOTAL_BIT_COUNT); } - if (foundStateSuccessor == PpmContext.ZERO) + while (_foundState == PpmState.ZERO) + { + _coder.RangeEncoderNormalize(target); + do + { + _orderFall++; + _minimumContext = _minimumContext.Suffix; + if (_minimumContext == PpmContext.ZERO) + { + goto StopEncoding; + } + } while (_minimumContext.NumberStatistics == _numberMasked); + EncodeSymbol2(c, _minimumContext); + _coder.RangeEncodeSymbol(); + } + + if (_orderFall == 0 && (Pointer)_foundState.Successor >= _allocator._baseUnit) + { + _maximumContext = _foundState.Successor; + } + else + { + UpdateModel(_minimumContext); + if (_escapeCount == 0) + { + ClearMask(); + } + } + + _coder.RangeEncoderNormalize(target); + } + + StopEncoding: + _coder.RangeEncoderFlush(target); + } + + /// + /// Dencode (ie. decompress) a given source stream, writing the decoded result to the target stream. + /// + public void Decode(Stream target, Stream source, PpmdProperties properties) + { + if (target is null) + { + throw new ArgumentNullException(nameof(target)); + } + + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + + DecodeStart(source, properties); + var buffer = new byte[65536]; + int read; + while ((read = DecodeBlock(source, buffer, 0, buffer.Length)) != 0) + { + target.Write(buffer, 0, read); + } + } + + internal Coder DecodeStart(Stream source, PpmdProperties properties) + { + _allocator = properties._allocator; + _coder = new Coder(); + _coder.RangeDecoderInitialize(source); + StartModel(properties.ModelOrder, properties.RestorationMethod); + _minimumContext = _maximumContext; + _numberStatistics = _minimumContext.NumberStatistics; + return _coder; + } + + internal int DecodeBlock(Stream source, byte[] buffer, int offset, int count) + { + if (_minimumContext == PpmContext.ZERO) + { + return 0; + } + + var total = 0; + while (total < count) + { + if (_numberStatistics != 0) + { + DecodeSymbol1(_minimumContext); + } + else + { + DecodeBinarySymbol(_minimumContext); + } + + _coder.RangeRemoveSubrange(); + + while (_foundState == PpmState.ZERO) + { + _coder.RangeDecoderNormalize(source); + do + { + _orderFall++; + _minimumContext = _minimumContext.Suffix; + if (_minimumContext == PpmContext.ZERO) + { + goto StopDecoding; + } + } while (_minimumContext.NumberStatistics == _numberMasked); + DecodeSymbol2(_minimumContext); + _coder.RangeRemoveSubrange(); + } + + buffer[offset] = _foundState.Symbol; + offset++; + total++; + + if (_orderFall == 0 && (Pointer)_foundState.Successor >= _allocator._baseUnit) + { + _maximumContext = _foundState.Successor; + } + else + { + UpdateModel(_minimumContext); + if (_escapeCount == 0) + { + ClearMask(); + } + } + + _minimumContext = _maximumContext; + _numberStatistics = _minimumContext.NumberStatistics; + _coder.RangeDecoderNormalize(source); + } + + StopDecoding: + return total; + } + + #endregion + + #region Private Methods + + /// + /// Initialise the model (unless the model order is set to 1 in which case the model should be cleared so that + /// the statistics are carried over, allowing "solid" mode compression). + /// + private void StartModel(int modelOrder, ModelRestorationMethod modelRestorationMethod) + { + Array.Clear(_characterMask, 0, _characterMask.Length); + _escapeCount = 1; + + // Compress in "solid" mode if the model order value is set to 1 (this will examine the current PPM context + // structures to determine the value of orderFall). + + if (modelOrder < 2) + { + _orderFall = _modelOrder; + for ( + var context = _maximumContext; + context.Suffix != PpmContext.ZERO; + context = context.Suffix + ) + { + _orderFall--; + } + return; + } + + _modelOrder = modelOrder; + _orderFall = modelOrder; + _method = modelRestorationMethod; + _allocator.Initialize(); + _initialRunLength = -((modelOrder < 12) ? modelOrder : 12) - 1; + _runLength = _initialRunLength; + + // Allocate the context structure. + + _maximumContext = _allocator.AllocateContext(); + _maximumContext.Suffix = PpmContext.ZERO; + _maximumContext.NumberStatistics = 255; + _maximumContext.SummaryFrequency = (ushort)(_maximumContext.NumberStatistics + 2); + _maximumContext.Statistics = _allocator.AllocateUnits(256 / 2); + + // allocates enough space for 256 PPM states (each is 6 bytes) + + _previousSuccess = 0; + for (var index = 0; index < 256; index++) + { + var state = _maximumContext.Statistics[index]; + state.Symbol = (byte)index; + state.Frequency = 1; + state.Successor = PpmContext.ZERO; + } + + uint probability = 0; + for (var index1 = 0; probability < 25; probability++) + { + while (_probabilities[index1] == probability) + { + index1++; + } + for (var index2 = 0; index2 < 8; index2++) + { + _binarySummary[probability, index2] = (ushort)( + BINARY_SCALE - (INITIAL_BINARY_ESCAPES[index2] / (index1 + 1)) + ); + } + for (var index2 = 8; index2 < 64; index2 += 8) + { + for (var index3 = 0; index3 < 8; index3++) + { + _binarySummary[probability, index2 + index3] = _binarySummary[ + probability, + index3 + ]; + } + } + } + + probability = 0; + for (uint index1 = 0; probability < 24; probability++) + { + while (_probabilities[index1 + 3] == probability + 3) + { + index1++; + } + for (var index2 = 0; index2 < 32; index2++) + { + _see2Contexts[probability, index2].Initialize((2 * index1) + 5); + } + } + } + + private void UpdateModel(PpmContext minimumContext) + { + var state = PpmState.ZERO; + PpmContext successor; + var currentContext = _maximumContext; + uint numberStatistics; + uint ns1; + uint cf; + uint sf; + uint s0; + uint foundStateFrequency = _foundState.Frequency; + var foundStateSymbol = _foundState.Symbol; + byte symbol; + byte flag; + + var foundStateSuccessor = _foundState.Successor; + var context = minimumContext.Suffix; + + if ((foundStateFrequency < MAXIMUM_FREQUENCY / 4) && (context != PpmContext.ZERO)) + { + if (context.NumberStatistics != 0) + { + state = context.Statistics; + if (state.Symbol != foundStateSymbol) + { + do + { + symbol = state[1].Symbol; + state++; + } while (symbol != foundStateSymbol); + if (state[0].Frequency >= state[-1].Frequency) + { + Swap(state[0], state[-1]); + state--; + } + } + cf = (uint)((state.Frequency < MAXIMUM_FREQUENCY - 9) ? 2 : 0); + state.Frequency += (byte)cf; + context.SummaryFrequency += (byte)cf; + } + else + { + state = context.FirstState; + state.Frequency += (byte)((state.Frequency < 32) ? 1 : 0); + } + } + + if (_orderFall == 0 && foundStateSuccessor != PpmContext.ZERO) + { + _foundState.Successor = CreateSuccessors(true, state, minimumContext); + if (_foundState.Successor == PpmContext.ZERO) { goto RestartModel; } + _maximumContext = _foundState.Successor; + return; + } - if (--_orderFall == 0) - { - successor = foundStateSuccessor; - _allocator._text -= (_maximumContext != minimumContext) ? 1 : 0; - } - else if (_method > ModelRestorationMethod.Freeze) - { - successor = foundStateSuccessor; - _allocator._text = _allocator._heap; - _orderFall = 0; - } + _allocator._text[0] = foundStateSymbol; + _allocator._text++; + successor = _allocator._text; - numberStatistics = minimumContext.NumberStatistics; - s0 = minimumContext.SummaryFrequency - numberStatistics - foundStateFrequency; - flag = (byte)((foundStateSymbol >= 0x40) ? 0x08 : 0x00); - for (; currentContext != minimumContext; currentContext = currentContext.Suffix) + if (_allocator._text >= _allocator._baseUnit) + { + goto RestartModel; + } + + if (foundStateSuccessor != PpmContext.ZERO) + { + if (foundStateSuccessor < _allocator._baseUnit) { - ns1 = currentContext.NumberStatistics; - if (ns1 != 0) + foundStateSuccessor = CreateSuccessors(false, state, minimumContext); + } + } + else + { + foundStateSuccessor = ReduceOrder(state, minimumContext); + } + + if (foundStateSuccessor == PpmContext.ZERO) + { + goto RestartModel; + } + + if (--_orderFall == 0) + { + successor = foundStateSuccessor; + _allocator._text -= (_maximumContext != minimumContext) ? 1 : 0; + } + else if (_method > ModelRestorationMethod.Freeze) + { + successor = foundStateSuccessor; + _allocator._text = _allocator._heap; + _orderFall = 0; + } + + numberStatistics = minimumContext.NumberStatistics; + s0 = minimumContext.SummaryFrequency - numberStatistics - foundStateFrequency; + flag = (byte)((foundStateSymbol >= 0x40) ? 0x08 : 0x00); + for (; currentContext != minimumContext; currentContext = currentContext.Suffix) + { + ns1 = currentContext.NumberStatistics; + if (ns1 != 0) + { + if ((ns1 & 1) != 0) { - if ((ns1 & 1) != 0) - { - state = _allocator.ExpandUnits(currentContext.Statistics, (ns1 + 1) >> 1); - if (state == PpmState.ZERO) - { - goto RestartModel; - } - currentContext.Statistics = state; - } - currentContext.SummaryFrequency += (ushort)( - (3 * ns1 + 1 < numberStatistics) ? 1 : 0 - ); - } - else - { - state = _allocator.AllocateUnits(1); + state = _allocator.ExpandUnits(currentContext.Statistics, (ns1 + 1) >> 1); if (state == PpmState.ZERO) { goto RestartModel; } - Copy(state, currentContext.FirstState); currentContext.Statistics = state; - if (state.Frequency < MAXIMUM_FREQUENCY / 4 - 1) - { - state.Frequency += state.Frequency; - } - else - { - state.Frequency = (byte)(MAXIMUM_FREQUENCY - 4); - } - currentContext.SummaryFrequency = (ushort)( - state.Frequency + _initialEscape + ((numberStatistics > 2) ? 1 : 0) - ); } - - cf = (uint)(2 * foundStateFrequency * (currentContext.SummaryFrequency + 6)); - sf = s0 + currentContext.SummaryFrequency; - - if (cf < 6 * sf) + currentContext.SummaryFrequency += (ushort)( + ((3 * ns1) + 1 < numberStatistics) ? 1 : 0 + ); + } + else + { + state = _allocator.AllocateUnits(1); + if (state == PpmState.ZERO) { - cf = (uint)(1 + ((cf > sf) ? 1 : 0) + ((cf >= 4 * sf) ? 1 : 0)); - currentContext.SummaryFrequency += 4; + goto RestartModel; + } + Copy(state, currentContext.FirstState); + currentContext.Statistics = state; + if (state.Frequency < (MAXIMUM_FREQUENCY / 4) - 1) + { + state.Frequency += state.Frequency; } else { - cf = (uint)( - 4 - + ((cf > 9 * sf) ? 1 : 0) - + ((cf > 12 * sf) ? 1 : 0) - + ((cf > 15 * sf) ? 1 : 0) - ); - currentContext.SummaryFrequency += (ushort)cf; + state.Frequency = (byte)(MAXIMUM_FREQUENCY - 4); } - - state = currentContext.Statistics + (++currentContext.NumberStatistics); - state.Successor = successor; - state.Symbol = foundStateSymbol; - state.Frequency = (byte)cf; - currentContext.Flags |= flag; + currentContext.SummaryFrequency = (ushort)( + state.Frequency + _initialEscape + ((numberStatistics > 2) ? 1 : 0) + ); } - _maximumContext = foundStateSuccessor; - return; + cf = (uint)(2 * foundStateFrequency * (currentContext.SummaryFrequency + 6)); + sf = s0 + currentContext.SummaryFrequency; - RestartModel: - RestoreModel(currentContext, minimumContext, foundStateSuccessor); + if (cf < 6 * sf) + { + cf = (uint)(1 + ((cf > sf) ? 1 : 0) + ((cf >= 4 * sf) ? 1 : 0)); + currentContext.SummaryFrequency += 4; + } + else + { + cf = (uint)( + 4 + + ((cf > 9 * sf) ? 1 : 0) + + ((cf > 12 * sf) ? 1 : 0) + + ((cf > 15 * sf) ? 1 : 0) + ); + currentContext.SummaryFrequency += (ushort)cf; + } + + state = currentContext.Statistics + (++currentContext.NumberStatistics); + state.Successor = successor; + state.Symbol = foundStateSymbol; + state.Frequency = (byte)cf; + currentContext.Flags |= flag; } - private PpmContext CreateSuccessors(bool skip, PpmState state, PpmContext context) + _maximumContext = foundStateSuccessor; + return; + + RestartModel: + RestoreModel(currentContext, minimumContext, foundStateSuccessor); + } + + private PpmContext CreateSuccessors(bool skip, PpmState state, PpmContext context) + { + var upBranch = _foundState.Successor; + var states = new PpmState[MAXIMUM_ORDER]; + uint stateIndex = 0; + var symbol = _foundState.Symbol; + + if (!skip) { - PpmContext upBranch = _foundState.Successor; - PpmState[] states = new PpmState[MAXIMUM_ORDER]; - uint stateIndex = 0; - byte symbol = _foundState.Symbol; - - if (!skip) + states[stateIndex++] = _foundState; + if (context.Suffix == PpmContext.ZERO) { - states[stateIndex++] = _foundState; - if (context.Suffix == PpmContext.ZERO) - { - goto NoLoop; - } + goto NoLoop; + } + } + + var gotoLoopEntry = false; + if (state != PpmState.ZERO) + { + context = context.Suffix; + gotoLoopEntry = true; + } + + do + { + if (gotoLoopEntry) + { + gotoLoopEntry = false; + goto LoopEntry; } - bool gotoLoopEntry = false; - if (state != PpmState.ZERO) - { - context = context.Suffix; - gotoLoopEntry = true; - } - - do - { - if (gotoLoopEntry) - { - gotoLoopEntry = false; - goto LoopEntry; - } - - context = context.Suffix; - if (context.NumberStatistics != 0) - { - byte temporary; - state = context.Statistics; - if (state.Symbol != symbol) - { - do - { - temporary = state[1].Symbol; - state++; - } while (temporary != symbol); - } - temporary = (byte)((state.Frequency < MAXIMUM_FREQUENCY - 9) ? 1 : 0); - state.Frequency += temporary; - context.SummaryFrequency += temporary; - } - else - { - state = context.FirstState; - state.Frequency += (byte)( - ((context.Suffix.NumberStatistics == 0) ? 1 : 0) - & ((state.Frequency < 24) ? 1 : 0) - ); - } - - LoopEntry: - if (state.Successor != upBranch) - { - context = state.Successor; - break; - } - states[stateIndex++] = state; - } while (context.Suffix != PpmContext.ZERO); - - NoLoop: - if (stateIndex == 0) - { - return context; - } - - byte localNumberStatistics = 0; - byte localFlags = (byte)((symbol >= 0x40) ? 0x10 : 0x00); - symbol = upBranch.NumberStatistics; - byte localSymbol = symbol; - byte localFrequency; - PpmContext localSuccessor = ((Pointer)upBranch) + 1; - localFlags |= (byte)((symbol >= 0x40) ? 0x08 : 0x00); - + context = context.Suffix; if (context.NumberStatistics != 0) { + byte temporary; state = context.Statistics; if (state.Symbol != symbol) { - byte temporary; do { temporary = state[1].Symbol; state++; } while (temporary != symbol); } - uint cf = (uint)(state.Frequency - 1); - uint s0 = (uint)(context.SummaryFrequency - context.NumberStatistics - cf); - localFrequency = (byte)( - 1 + ((2 * cf <= s0) ? (uint)((5 * cf > s0) ? 1 : 0) : ((cf + 2 * s0 - 3) / s0)) + temporary = (byte)((state.Frequency < MAXIMUM_FREQUENCY - 9) ? 1 : 0); + state.Frequency += temporary; + context.SummaryFrequency += temporary; + } + else + { + state = context.FirstState; + state.Frequency += (byte)( + ((context.Suffix.NumberStatistics == 0) ? 1 : 0) + & ((state.Frequency < 24) ? 1 : 0) + ); + } + + LoopEntry: + if (state.Successor != upBranch) + { + context = state.Successor; + break; + } + states[stateIndex++] = state; + } while (context.Suffix != PpmContext.ZERO); + + NoLoop: + if (stateIndex == 0) + { + return context; + } + + byte localNumberStatistics = 0; + var localFlags = (byte)((symbol >= 0x40) ? 0x10 : 0x00); + symbol = upBranch.NumberStatistics; + var localSymbol = symbol; + byte localFrequency; + PpmContext localSuccessor = ((Pointer)upBranch) + 1; + localFlags |= (byte)((symbol >= 0x40) ? 0x08 : 0x00); + + if (context.NumberStatistics != 0) + { + state = context.Statistics; + if (state.Symbol != symbol) + { + byte temporary; + do + { + temporary = state[1].Symbol; + state++; + } while (temporary != symbol); + } + var cf = (uint)(state.Frequency - 1); + var s0 = (uint)(context.SummaryFrequency - context.NumberStatistics - cf); + localFrequency = (byte)( + 1 + ((2 * cf <= s0) ? (uint)((5 * cf > s0) ? 1 : 0) : ((cf + (2 * s0) - 3) / s0)) + ); + } + else + { + localFrequency = context.FirstStateFrequency; + } + + do + { + PpmContext currentContext = _allocator.AllocateContext(); + if (currentContext == PpmContext.ZERO) + { + return PpmContext.ZERO; + } + currentContext.NumberStatistics = localNumberStatistics; + currentContext.Flags = localFlags; + currentContext.FirstStateSymbol = localSymbol; + currentContext.FirstStateFrequency = localFrequency; + currentContext.FirstStateSuccessor = localSuccessor; + currentContext.Suffix = context; + context = currentContext; + states[--stateIndex].Successor = context; + } while (stateIndex != 0); + + return context; + } + + private PpmContext ReduceOrder(PpmState state, PpmContext context) + { + PpmState currentState; + var states = new PpmState[MAXIMUM_ORDER]; + uint stateIndex = 0; + var currentContext = context; + PpmContext upBranch = _allocator._text; + byte temporary; + var symbol = _foundState.Symbol; + + states[stateIndex++] = _foundState; + _foundState.Successor = upBranch; + _orderFall++; + + var gotoLoopEntry = false; + if (state != PpmState.ZERO) + { + context = context.Suffix; + gotoLoopEntry = true; + } + + while (true) + { + if (gotoLoopEntry) + { + gotoLoopEntry = false; + goto LoopEntry; + } + + if (context.Suffix == PpmContext.ZERO) + { + if (_method > ModelRestorationMethod.Freeze) + { + do + { + states[--stateIndex].Successor = context; + } while (stateIndex != 0); + _allocator._text = _allocator._heap + 1; + _orderFall = 1; + } + return context; + } + + context = context.Suffix; + if (context.NumberStatistics != 0) + { + state = context.Statistics; + if (state.Symbol != symbol) + { + do + { + temporary = state[1].Symbol; + state++; + } while (temporary != symbol); + } + temporary = (byte)((state.Frequency < MAXIMUM_FREQUENCY - 9) ? 2 : 0); + state.Frequency += temporary; + context.SummaryFrequency += temporary; + } + else + { + state = context.FirstState; + state.Frequency += (byte)((state.Frequency < 32) ? 1 : 0); + } + + LoopEntry: + if (state.Successor != PpmContext.ZERO) + { + break; + } + states[stateIndex++] = state; + state.Successor = upBranch; + _orderFall++; + } + + if (_method > ModelRestorationMethod.Freeze) + { + context = state.Successor; + do + { + states[--stateIndex].Successor = context; + } while (stateIndex != 0); + _allocator._text = _allocator._heap + 1; + _orderFall = 1; + return context; + } + if (state.Successor <= upBranch) + { + currentState = _foundState; + _foundState = state; + state.Successor = CreateSuccessors(false, PpmState.ZERO, context); + _foundState = currentState; + } + + if (_orderFall == 1 && currentContext == _maximumContext) + { + _foundState.Successor = state.Successor; + _allocator._text--; + } + + return state.Successor; + } + + private void RestoreModel( + PpmContext context, + PpmContext minimumContext, + PpmContext foundStateSuccessor + ) + { + PpmContext currentContext; + + _allocator._text = _allocator._heap; + for ( + currentContext = _maximumContext; + currentContext != context; + currentContext = currentContext.Suffix + ) + { + if (--currentContext.NumberStatistics == 0) + { + currentContext.Flags = (byte)( + (currentContext.Flags & 0x10) + + ((currentContext.Statistics.Symbol >= 0x40) ? 0x08 : 0x00) + ); + var state = currentContext.Statistics; + Copy(currentContext.FirstState, state); + _allocator.SpecialFreeUnits(state); + currentContext.FirstStateFrequency = (byte)( + (currentContext.FirstStateFrequency + 11) >> 3 ); } else { - localFrequency = context.FirstStateFrequency; + Refresh( + (uint)((currentContext.NumberStatistics + 3) >> 1), + false, + currentContext + ); + } + } + + for (; currentContext != minimumContext; currentContext = currentContext.Suffix) + { + if (currentContext.NumberStatistics == 0) + { + currentContext.FirstStateFrequency -= (byte)( + currentContext.FirstStateFrequency >> 1 + ); + } + else if ( + (currentContext.SummaryFrequency += 4) + > 128 + (4 * currentContext.NumberStatistics) + ) + { + Refresh( + (uint)((currentContext.NumberStatistics + 2) >> 1), + true, + currentContext + ); + } + } + + if (_method > ModelRestorationMethod.Freeze) + { + _maximumContext = foundStateSuccessor; + _allocator._glueCount += (uint)( + ((_allocator._memoryNodes[1].Stamp & 1) == 0) ? 1 : 0 + ); + } + else if (_method == ModelRestorationMethod.Freeze) + { + while (_maximumContext.Suffix != PpmContext.ZERO) + { + _maximumContext = _maximumContext.Suffix; + } + + RemoveBinaryContexts(0, _maximumContext); + _method++; + _allocator._glueCount = 0; + _orderFall = _modelOrder; + } + else if ( + _method == ModelRestorationMethod.Restart + || _allocator.GetMemoryUsed() < (_allocator._allocatorSize >> 1) + ) + { + StartModel(_modelOrder, _method); + _escapeCount = 0; + } + else + { + while (_maximumContext.Suffix != PpmContext.ZERO) + { + _maximumContext = _maximumContext.Suffix; } do { - PpmContext currentContext = _allocator.AllocateContext(); - if (currentContext == PpmContext.ZERO) - { - return PpmContext.ZERO; - } - currentContext.NumberStatistics = localNumberStatistics; - currentContext.Flags = localFlags; - currentContext.FirstStateSymbol = localSymbol; - currentContext.FirstStateFrequency = localFrequency; - currentContext.FirstStateSuccessor = localSuccessor; - currentContext.Suffix = context; - context = currentContext; - states[--stateIndex].Successor = context; - } while (stateIndex != 0); + CutOff(0, _maximumContext); + _allocator.ExpandText(); + } while (_allocator.GetMemoryUsed() > 3 * (_allocator._allocatorSize >> 2)); - return context; + _allocator._glueCount = 0; + _orderFall = _modelOrder; } - - private PpmContext ReduceOrder(PpmState state, PpmContext context) - { - PpmState currentState; - PpmState[] states = new PpmState[MAXIMUM_ORDER]; - uint stateIndex = 0; - PpmContext currentContext = context; - PpmContext upBranch = _allocator._text; - byte temporary; - byte symbol = _foundState.Symbol; - - states[stateIndex++] = _foundState; - _foundState.Successor = upBranch; - _orderFall++; - - bool gotoLoopEntry = false; - if (state != PpmState.ZERO) - { - context = context.Suffix; - gotoLoopEntry = true; - } - - while (true) - { - if (gotoLoopEntry) - { - gotoLoopEntry = false; - goto LoopEntry; - } - - if (context.Suffix == PpmContext.ZERO) - { - if (_method > ModelRestorationMethod.Freeze) - { - do - { - states[--stateIndex].Successor = context; - } while (stateIndex != 0); - _allocator._text = _allocator._heap + 1; - _orderFall = 1; - } - return context; - } - - context = context.Suffix; - if (context.NumberStatistics != 0) - { - state = context.Statistics; - if (state.Symbol != symbol) - { - do - { - temporary = state[1].Symbol; - state++; - } while (temporary != symbol); - } - temporary = (byte)((state.Frequency < MAXIMUM_FREQUENCY - 9) ? 2 : 0); - state.Frequency += temporary; - context.SummaryFrequency += temporary; - } - else - { - state = context.FirstState; - state.Frequency += (byte)((state.Frequency < 32) ? 1 : 0); - } - - LoopEntry: - if (state.Successor != PpmContext.ZERO) - { - break; - } - states[stateIndex++] = state; - state.Successor = upBranch; - _orderFall++; - } - - if (_method > ModelRestorationMethod.Freeze) - { - context = state.Successor; - do - { - states[--stateIndex].Successor = context; - } while (stateIndex != 0); - _allocator._text = _allocator._heap + 1; - _orderFall = 1; - return context; - } - if (state.Successor <= upBranch) - { - currentState = _foundState; - _foundState = state; - state.Successor = CreateSuccessors(false, PpmState.ZERO, context); - _foundState = currentState; - } - - if (_orderFall == 1 && currentContext == _maximumContext) - { - _foundState.Successor = state.Successor; - _allocator._text--; - } - - return state.Successor; - } - - private void RestoreModel( - PpmContext context, - PpmContext minimumContext, - PpmContext foundStateSuccessor - ) - { - PpmContext currentContext; - - _allocator._text = _allocator._heap; - for ( - currentContext = _maximumContext; - currentContext != context; - currentContext = currentContext.Suffix - ) - { - if (--currentContext.NumberStatistics == 0) - { - currentContext.Flags = (byte)( - (currentContext.Flags & 0x10) - + ((currentContext.Statistics.Symbol >= 0x40) ? 0x08 : 0x00) - ); - PpmState state = currentContext.Statistics; - Copy(currentContext.FirstState, state); - _allocator.SpecialFreeUnits(state); - currentContext.FirstStateFrequency = (byte)( - (currentContext.FirstStateFrequency + 11) >> 3 - ); - } - else - { - Refresh( - (uint)((currentContext.NumberStatistics + 3) >> 1), - false, - currentContext - ); - } - } - - for (; currentContext != minimumContext; currentContext = currentContext.Suffix) - { - if (currentContext.NumberStatistics == 0) - { - currentContext.FirstStateFrequency -= (byte)( - currentContext.FirstStateFrequency >> 1 - ); - } - else if ( - (currentContext.SummaryFrequency += 4) - > 128 + 4 * currentContext.NumberStatistics - ) - { - Refresh( - (uint)((currentContext.NumberStatistics + 2) >> 1), - true, - currentContext - ); - } - } - - if (_method > ModelRestorationMethod.Freeze) - { - _maximumContext = foundStateSuccessor; - _allocator._glueCount += (uint)( - ((_allocator._memoryNodes[1].Stamp & 1) == 0) ? 1 : 0 - ); - } - else if (_method == ModelRestorationMethod.Freeze) - { - while (_maximumContext.Suffix != PpmContext.ZERO) - { - _maximumContext = _maximumContext.Suffix; - } - - RemoveBinaryContexts(0, _maximumContext); - _method = _method + 1; - _allocator._glueCount = 0; - _orderFall = _modelOrder; - } - else if ( - _method == ModelRestorationMethod.Restart - || _allocator.GetMemoryUsed() < (_allocator._allocatorSize >> 1) - ) - { - StartModel(_modelOrder, _method); - _escapeCount = 0; - } - else - { - while (_maximumContext.Suffix != PpmContext.ZERO) - { - _maximumContext = _maximumContext.Suffix; - } - - do - { - CutOff(0, _maximumContext); - _allocator.ExpandText(); - } while (_allocator.GetMemoryUsed() > 3 * (_allocator._allocatorSize >> 2)); - - _allocator._glueCount = 0; - _orderFall = _modelOrder; - } - } - - private static void Swap(PpmState state1, PpmState state2) - { - byte swapSymbol = state1.Symbol; - byte swapFrequency = state1.Frequency; - PpmContext swapSuccessor = state1.Successor; - - state1.Symbol = state2.Symbol; - state1.Frequency = state2.Frequency; - state1.Successor = state2.Successor; - - state2.Symbol = swapSymbol; - state2.Frequency = swapFrequency; - state2.Successor = swapSuccessor; - } - - private static void Copy(PpmState state1, PpmState state2) - { - state1.Symbol = state2.Symbol; - state1.Frequency = state2.Frequency; - state1.Successor = state2.Successor; - } - - private static int Mean(int sum, int shift, int round) - { - return (sum + (1 << (shift - round))) >> shift; - } - - private void ClearMask() - { - _escapeCount = 1; - Array.Clear(_characterMask, 0, _characterMask.Length); - } - - #endregion } + + private static void Swap(PpmState state1, PpmState state2) + { + var swapSymbol = state1.Symbol; + var swapFrequency = state1.Frequency; + var swapSuccessor = state1.Successor; + + state1.Symbol = state2.Symbol; + state1.Frequency = state2.Frequency; + state1.Successor = state2.Successor; + + state2.Symbol = swapSymbol; + state2.Frequency = swapFrequency; + state2.Successor = swapSuccessor; + } + + private static void Copy(PpmState state1, PpmState state2) + { + state1.Symbol = state2.Symbol; + state1.Frequency = state2.Frequency; + state1.Successor = state2.Successor; + } + + private static int Mean(int sum, int shift, int round) + { + return (sum + (1 << (shift - round))) >> shift; + } + + private void ClearMask() + { + _escapeCount = 1; + Array.Clear(_characterMask, 0, _characterMask.Length); + } + + #endregion } diff --git a/src/SharpCompress/Compressors/PPMd/I1/ModelRestorationMethod.cs b/src/SharpCompress/Compressors/PPMd/I1/ModelRestorationMethod.cs index 9e89d9da..0a504c99 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/ModelRestorationMethod.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/ModelRestorationMethod.cs @@ -4,26 +4,25 @@ #endregion -namespace SharpCompress.Compressors.PPMd.I1 +namespace SharpCompress.Compressors.PPMd.I1; + +/// +/// The method used to adjust the model when the memory limit is reached. +/// +internal enum ModelRestorationMethod { /// - /// The method used to adjust the model when the memory limit is reached. + /// Restart the model from scratch (this is the default). /// - internal enum ModelRestorationMethod - { - /// - /// Restart the model from scratch (this is the default). - /// - Restart = 0, + Restart = 0, - /// - /// Cut off the model (nearly twice as slow). - /// - CutOff = 1, + /// + /// Cut off the model (nearly twice as slow). + /// + CutOff = 1, - /// - /// Freeze the context tree (in some cases may result in poor compression). - /// - Freeze = 2 - } + /// + /// Freeze the context tree (in some cases may result in poor compression). + /// + Freeze = 2 } diff --git a/src/SharpCompress/Compressors/PPMd/I1/Pointer.cs b/src/SharpCompress/Compressors/PPMd/I1/Pointer.cs index 05748848..791a556d 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/Pointer.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/Pointer.cs @@ -2,380 +2,378 @@ using System; -namespace SharpCompress.Compressors.PPMd.I1 +namespace SharpCompress.Compressors.PPMd.I1; + +/// +/// A structure containing a single address representing a position in the array. This +/// is intended to mimic the behaviour of a pointer in C/C++. +/// +/// +/// +/// This must be a structure rather than a class because several places in the associated code assume that +/// is a value type (meaning that assignment creates a completely new copy of the +/// instance rather than just copying a reference to the same instance). +/// +/// +/// Note that is a field rather than a property for performance reasons. +/// +/// +internal struct Pointer { + public uint _address; + public byte[] _memory; + public static readonly Pointer ZERO = new Pointer(0, null); + public const int SIZE = 1; + /// - /// A structure containing a single address representing a position in the array. This - /// is intended to mimic the behaviour of a pointer in C/C++. + /// Initializes a new instance of the structure. /// - /// - /// - /// This must be a structure rather than a class because several places in the associated code assume that - /// is a value type (meaning that assignment creates a completely new copy of the - /// instance rather than just copying a reference to the same instance). - /// - /// - /// Note that is a field rather than a property for performance reasons. - /// - /// - internal struct Pointer + public Pointer(uint address, byte[] memory) { - public uint _address; - public byte[] _memory; - public static readonly Pointer ZERO = new Pointer(0, null); - public const int SIZE = 1; + _address = address; + _memory = memory; + } - /// - /// Initializes a new instance of the structure. - /// - public Pointer(uint address, byte[] memory) - { - _address = address; - _memory = memory; - } - - /// - /// Gets or sets the byte at the given . - /// - /// - /// - public byte this[int offset] - { - get - { -#if DEBUG - if (_address == 0) - { - throw new InvalidOperationException( - "The pointer being indexed is a null pointer." - ); - } -#endif - return _memory[_address + offset]; - } - set - { -#if DEBUG - if (_address == 0) - { - throw new InvalidOperationException( - "The pointer being indexed is a null pointer." - ); - } -#endif - _memory[_address + offset] = value; - } - } - - /// - /// Allow a to be implicitly converted to a . - /// - /// - /// - public static implicit operator Pointer(MemoryNode memoryNode) - { - return new Pointer(memoryNode._address, memoryNode._memory); - } - - /// - /// Allow a to be implicitly converted to a . - /// - /// - /// - public static implicit operator Pointer(Model.PpmContext context) - { - return new Pointer(context._address, context._memory); - } - - /// - /// Allow a to be implicitly converted to a . - /// - /// - /// - public static implicit operator Pointer(PpmState state) - { - return new Pointer(state._address, state._memory); - } - - /// - /// Increase the address of a pointer by the given number of bytes. - /// - /// - /// - /// - public static Pointer operator +(Pointer pointer, int offset) + /// + /// Gets or sets the byte at the given . + /// + /// + /// + public byte this[int offset] + { + get { #if DEBUG - if (pointer._address == 0) - { - throw new InvalidOperationException("The pointer is a null pointer."); - } -#endif - pointer._address = (uint)(pointer._address + offset); - return pointer; - } - - /// - /// Increase the address of a pointer by the given number of bytes. - /// - /// - /// - /// - public static Pointer operator +(Pointer pointer, uint offset) - { -#if DEBUG - if (pointer._address == 0) - { - throw new InvalidOperationException("The pointer is a null pointer."); - } -#endif - pointer._address += offset; - return pointer; - } - - /// - /// Increment the address of a pointer. - /// - /// - /// - public static Pointer operator ++(Pointer pointer) - { -#if DEBUG - if (pointer._address == 0) + if (_address == 0) { throw new InvalidOperationException( - "The pointer being incremented is a null pointer." + "The pointer being indexed is a null pointer." ); } #endif - pointer._address++; - return pointer; + return _memory[_address + offset]; } - - /// - /// Decrease the address of a pointer by the given number of bytes. - /// - /// - /// - /// - public static Pointer operator -(Pointer pointer, int offset) + set { #if DEBUG - if (pointer._address == 0) - { - throw new InvalidOperationException("The pointer is a null pointer."); - } -#endif - pointer._address = (uint)(pointer._address - offset); - return pointer; - } - - /// - /// Decrease the address of a pointer by the given number of bytes. - /// - /// - /// - /// - public static Pointer operator -(Pointer pointer, uint offset) - { -#if DEBUG - if (pointer._address == 0) - { - throw new InvalidOperationException("The pointer is a null pointer."); - } -#endif - pointer._address -= offset; - return pointer; - } - - /// - /// Decrement the address of a pointer. - /// - /// - /// - public static Pointer operator --(Pointer pointer) - { -#if DEBUG - if (pointer._address == 0) + if (_address == 0) { throw new InvalidOperationException( - "The pointer being decremented is a null pointer." + "The pointer being indexed is a null pointer." ); } #endif - pointer._address--; - return pointer; - } - - /// - /// Subtract two pointers. - /// - /// - /// - /// The number of bytes between the two pointers. - public static uint operator -(Pointer pointer1, Pointer pointer2) - { -#if DEBUG - if (pointer1._address == 0) - { - throw new InvalidOperationException( - "The pointer to the left of the subtraction operator is a null pointer." - ); - } - if (pointer2._address == 0) - { - throw new InvalidOperationException( - "The pointer to the right of the subtraction operator is a null pointer." - ); - } -#endif - return pointer1._address - pointer2._address; - } - - /// - /// Compare pointers. - /// - /// - /// - /// - public static bool operator <(Pointer pointer1, Pointer pointer2) - { -#if DEBUG - if (pointer1._address == 0) - { - throw new InvalidOperationException( - "The pointer to the left of the less than operator is a null pointer." - ); - } - if (pointer2._address == 0) - { - throw new InvalidOperationException( - "The pointer to the right of the less than operator is a null pointer." - ); - } -#endif - return pointer1._address < pointer2._address; - } - - /// - /// Compare two pointers. - /// - /// - /// - /// - public static bool operator <=(Pointer pointer1, Pointer pointer2) - { -#if DEBUG - if (pointer1._address == 0) - { - throw new InvalidOperationException( - "The pointer to the left of the less than or equal to operator is a null pointer." - ); - } - if (pointer2._address == 0) - { - throw new InvalidOperationException( - "The pointer to the right of the less than or equal to operator is a null pointer." - ); - } -#endif - return pointer1._address <= pointer2._address; - } - - /// - /// Compare two pointers. - /// - /// - /// - /// - public static bool operator >(Pointer pointer1, Pointer pointer2) - { -#if DEBUG - if (pointer1._address == 0) - { - throw new InvalidOperationException( - "The pointer to the left of the greater than operator is a null pointer." - ); - } - if (pointer2._address == 0) - { - throw new InvalidOperationException( - "The pointer to the right of the greater than operator is a null pointer." - ); - } -#endif - return pointer1._address > pointer2._address; - } - - /// - /// Compare two pointers. - /// - /// - /// - /// - public static bool operator >=(Pointer pointer1, Pointer pointer2) - { -#if DEBUG - if (pointer1._address == 0) - { - throw new InvalidOperationException( - "The pointer to the left of the greater than or equal to operator is a null pointer." - ); - } - if (pointer2._address == 0) - { - throw new InvalidOperationException( - "The pointer to the right of the greater than or equal to operator is a null pointer." - ); - } -#endif - return pointer1._address >= pointer2._address; - } - - /// - /// Compare two pointers. - /// - /// - /// - /// - public static bool operator ==(Pointer pointer1, Pointer pointer2) - { - return pointer1._address == pointer2._address; - } - - /// - /// Compare two pointers. - /// - /// - /// - /// - public static bool operator !=(Pointer pointer1, Pointer pointer2) - { - return pointer1._address != pointer2._address; - } - - /// - /// Indicates whether this instance and a specified object are equal. - /// - /// true if obj and this instance are the same type and represent the same value; otherwise, false. - /// Another object to compare to. - public override bool Equals(object obj) - { - if (obj is Pointer) - { - Pointer pointer = (Pointer)obj; - return pointer._address == _address; - } - return base.Equals(obj); - } - - /// - /// Returns the hash code for this instance. - /// - /// A 32-bit signed integer that is the hash code for this instance. - public override int GetHashCode() - { - return _address.GetHashCode(); + _memory[_address + offset] = value; } } + + /// + /// Allow a to be implicitly converted to a . + /// + /// + /// + public static implicit operator Pointer(MemoryNode memoryNode) + { + return new Pointer(memoryNode._address, memoryNode._memory); + } + + /// + /// Allow a to be implicitly converted to a . + /// + /// + /// + public static implicit operator Pointer(Model.PpmContext context) + { + return new Pointer(context._address, context._memory); + } + + /// + /// Allow a to be implicitly converted to a . + /// + /// + /// + public static implicit operator Pointer(PpmState state) + { + return new Pointer(state._address, state._memory); + } + + /// + /// Increase the address of a pointer by the given number of bytes. + /// + /// + /// + /// + public static Pointer operator +(Pointer pointer, int offset) + { +#if DEBUG + if (pointer._address == 0) + { + throw new InvalidOperationException("The pointer is a null pointer."); + } +#endif + pointer._address = (uint)(pointer._address + offset); + return pointer; + } + + /// + /// Increase the address of a pointer by the given number of bytes. + /// + /// + /// + /// + public static Pointer operator +(Pointer pointer, uint offset) + { +#if DEBUG + if (pointer._address == 0) + { + throw new InvalidOperationException("The pointer is a null pointer."); + } +#endif + pointer._address += offset; + return pointer; + } + + /// + /// Increment the address of a pointer. + /// + /// + /// + public static Pointer operator ++(Pointer pointer) + { +#if DEBUG + if (pointer._address == 0) + { + throw new InvalidOperationException( + "The pointer being incremented is a null pointer." + ); + } +#endif + pointer._address++; + return pointer; + } + + /// + /// Decrease the address of a pointer by the given number of bytes. + /// + /// + /// + /// + public static Pointer operator -(Pointer pointer, int offset) + { +#if DEBUG + if (pointer._address == 0) + { + throw new InvalidOperationException("The pointer is a null pointer."); + } +#endif + pointer._address = (uint)(pointer._address - offset); + return pointer; + } + + /// + /// Decrease the address of a pointer by the given number of bytes. + /// + /// + /// + /// + public static Pointer operator -(Pointer pointer, uint offset) + { +#if DEBUG + if (pointer._address == 0) + { + throw new InvalidOperationException("The pointer is a null pointer."); + } +#endif + pointer._address -= offset; + return pointer; + } + + /// + /// Decrement the address of a pointer. + /// + /// + /// + public static Pointer operator --(Pointer pointer) + { +#if DEBUG + if (pointer._address == 0) + { + throw new InvalidOperationException( + "The pointer being decremented is a null pointer." + ); + } +#endif + pointer._address--; + return pointer; + } + + /// + /// Subtract two pointers. + /// + /// + /// + /// The number of bytes between the two pointers. + public static uint operator -(Pointer pointer1, Pointer pointer2) + { +#if DEBUG + if (pointer1._address == 0) + { + throw new InvalidOperationException( + "The pointer to the left of the subtraction operator is a null pointer." + ); + } + if (pointer2._address == 0) + { + throw new InvalidOperationException( + "The pointer to the right of the subtraction operator is a null pointer." + ); + } +#endif + return pointer1._address - pointer2._address; + } + + /// + /// Compare pointers. + /// + /// + /// + /// + public static bool operator <(Pointer pointer1, Pointer pointer2) + { +#if DEBUG + if (pointer1._address == 0) + { + throw new InvalidOperationException( + "The pointer to the left of the less than operator is a null pointer." + ); + } + if (pointer2._address == 0) + { + throw new InvalidOperationException( + "The pointer to the right of the less than operator is a null pointer." + ); + } +#endif + return pointer1._address < pointer2._address; + } + + /// + /// Compare two pointers. + /// + /// + /// + /// + public static bool operator <=(Pointer pointer1, Pointer pointer2) + { +#if DEBUG + if (pointer1._address == 0) + { + throw new InvalidOperationException( + "The pointer to the left of the less than or equal to operator is a null pointer." + ); + } + if (pointer2._address == 0) + { + throw new InvalidOperationException( + "The pointer to the right of the less than or equal to operator is a null pointer." + ); + } +#endif + return pointer1._address <= pointer2._address; + } + + /// + /// Compare two pointers. + /// + /// + /// + /// + public static bool operator >(Pointer pointer1, Pointer pointer2) + { +#if DEBUG + if (pointer1._address == 0) + { + throw new InvalidOperationException( + "The pointer to the left of the greater than operator is a null pointer." + ); + } + if (pointer2._address == 0) + { + throw new InvalidOperationException( + "The pointer to the right of the greater than operator is a null pointer." + ); + } +#endif + return pointer1._address > pointer2._address; + } + + /// + /// Compare two pointers. + /// + /// + /// + /// + public static bool operator >=(Pointer pointer1, Pointer pointer2) + { +#if DEBUG + if (pointer1._address == 0) + { + throw new InvalidOperationException( + "The pointer to the left of the greater than or equal to operator is a null pointer." + ); + } + if (pointer2._address == 0) + { + throw new InvalidOperationException( + "The pointer to the right of the greater than or equal to operator is a null pointer." + ); + } +#endif + return pointer1._address >= pointer2._address; + } + + /// + /// Compare two pointers. + /// + /// + /// + /// + public static bool operator ==(Pointer pointer1, Pointer pointer2) + { + return pointer1._address == pointer2._address; + } + + /// + /// Compare two pointers. + /// + /// + /// + /// + public static bool operator !=(Pointer pointer1, Pointer pointer2) + { + return pointer1._address != pointer2._address; + } + + /// + /// Indicates whether this instance and a specified object are equal. + /// + /// true if obj and this instance are the same type and represent the same value; otherwise, false. + /// Another object to compare to. + public override bool Equals(object obj) + { + if (obj is Pointer pointer) + { + return pointer._address == _address; + } + return base.Equals(obj); + } + + /// + /// Returns the hash code for this instance. + /// + /// A 32-bit signed integer that is the hash code for this instance. + public override int GetHashCode() + { + return _address.GetHashCode(); + } } diff --git a/src/SharpCompress/Compressors/PPMd/I1/PpmContext.cs b/src/SharpCompress/Compressors/PPMd/I1/PpmContext.cs index 35d020b6..3e122032 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/PpmContext.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/PpmContext.cs @@ -1,796 +1,764 @@ #nullable disable -namespace SharpCompress.Compressors.PPMd.I1 +namespace SharpCompress.Compressors.PPMd.I1; + +/// +/// The PPM context structure. This is tightly coupled with . +/// +/// +/// +/// This must be a structure rather than a class because several places in the associated code assume that +/// is a value type (meaning that assignment creates a completely new copy of +/// the instance rather than just copying a reference to the same instance). +/// +/// +internal partial class Model { /// - /// The PPM context structure. This is tightly coupled with . + /// The structure which represents the current PPM context. This is 12 bytes in size. /// - /// - /// - /// This must be a structure rather than a class because several places in the associated code assume that - /// is a value type (meaning that assignment creates a completely new copy of - /// the instance rather than just copying a reference to the same instance). - /// - /// - internal partial class Model + internal struct PpmContext { + public uint _address; + public byte[] _memory; + public static readonly PpmContext ZERO = new PpmContext(0, null); + public const int SIZE = 12; + /// - /// The structure which represents the current PPM context. This is 12 bytes in size. + /// Initializes a new instance of the structure. /// - internal struct PpmContext + public PpmContext(uint address, byte[] memory) { - public uint _address; - public byte[] _memory; - public static readonly PpmContext ZERO = new PpmContext(0, null); - public const int SIZE = 12; + _address = address; + _memory = memory; + } - /// - /// Initializes a new instance of the structure. - /// - public PpmContext(uint address, byte[] memory) + /// + /// Gets or sets the number statistics. + /// + public byte NumberStatistics + { + get => _memory[_address]; + set => _memory[_address] = value; + } + + /// + /// Gets or sets the flags. + /// + public byte Flags + { + get => _memory[_address + 1]; + set => _memory[_address + 1] = value; + } + + /// + /// Gets or sets the summary frequency. + /// + public ushort SummaryFrequency + { + get => (ushort)(_memory[_address + 2] | (_memory[_address + 3] << 8)); + set { - _address = address; - _memory = memory; - } - - /// - /// Gets or sets the number statistics. - /// - public byte NumberStatistics - { - get => _memory[_address]; - set => _memory[_address] = value; - } - - /// - /// Gets or sets the flags. - /// - public byte Flags - { - get => _memory[_address + 1]; - set => _memory[_address + 1] = value; - } - - /// - /// Gets or sets the summary frequency. - /// - public ushort SummaryFrequency - { - get => (ushort)(_memory[_address + 2] | _memory[_address + 3] << 8); - set - { - _memory[_address + 2] = (byte)value; - _memory[_address + 3] = (byte)(value >> 8); - } - } - - /// - /// Gets or sets the statistics. - /// - public PpmState Statistics - { - get => - new PpmState( - _memory[_address + 4] - | ((uint)_memory[_address + 5]) << 8 - | ((uint)_memory[_address + 6]) << 16 - | ((uint)_memory[_address + 7]) << 24, - _memory - ); - set - { - _memory[_address + 4] = (byte)value._address; - _memory[_address + 5] = (byte)(value._address >> 8); - _memory[_address + 6] = (byte)(value._address >> 16); - _memory[_address + 7] = (byte)(value._address >> 24); - } - } - - /// - /// Gets or sets the suffix. - /// - public PpmContext Suffix - { - get => - new PpmContext( - _memory[_address + 8] - | ((uint)_memory[_address + 9]) << 8 - | ((uint)_memory[_address + 10]) << 16 - | ((uint)_memory[_address + 11]) << 24, - _memory - ); - set - { - _memory[_address + 8] = (byte)value._address; - _memory[_address + 9] = (byte)(value._address >> 8); - _memory[_address + 10] = (byte)(value._address >> 16); - _memory[_address + 11] = (byte)(value._address >> 24); - } - } - - /// - /// The first PPM state associated with the PPM context. - /// - /// - /// - /// The first PPM state overlaps this PPM context instance (the context.SummaryFrequency and context.Statistics members - /// of PpmContext use 6 bytes and so can therefore fit into the space used by the Symbol, Frequency and - /// Successor members of PpmState, since they also add up to 6 bytes). - /// - /// - /// PpmContext (context.SummaryFrequency and context.Statistics use 6 bytes) - /// 1 context.NumberStatistics - /// 1 context.Flags - /// 2 context.SummaryFrequency - /// 4 context.Statistics (pointer to PpmState) - /// 4 context.Suffix (pointer to PpmContext) - /// - /// - /// PpmState (total of 6 bytes) - /// 1 Symbol - /// 1 Frequency - /// 4 Successor (pointer to PpmContext) - /// - /// - /// - public PpmState FirstState => new PpmState(_address + 2, _memory); - - /// - /// Gets or sets the symbol of the first PPM state. This is provided for convenience. The same - /// information can be obtained using the Symbol property on the PPM state provided by the - /// property. - /// - public byte FirstStateSymbol - { - get => _memory[_address + 2]; - set => _memory[_address + 2] = value; - } - - /// - /// Gets or sets the frequency of the first PPM state. This is provided for convenience. The same - /// information can be obtained using the Frequency property on the PPM state provided by the - ///context.FirstState property. - /// - public byte FirstStateFrequency - { - get => _memory[_address + 3]; - set => _memory[_address + 3] = value; - } - - /// - /// Gets or sets the successor of the first PPM state. This is provided for convenience. The same - /// information can be obtained using the Successor property on the PPM state provided by the - /// - public PpmContext FirstStateSuccessor - { - get => - new PpmContext( - _memory[_address + 4] - | ((uint)_memory[_address + 5]) << 8 - | ((uint)_memory[_address + 6]) << 16 - | ((uint)_memory[_address + 7]) << 24, - _memory - ); - set - { - _memory[_address + 4] = (byte)value._address; - _memory[_address + 5] = (byte)(value._address >> 8); - _memory[_address + 6] = (byte)(value._address >> 16); - _memory[_address + 7] = (byte)(value._address >> 24); - } - } - - /// - /// Allow a pointer to be implicitly converted to a PPM context. - /// - /// - /// - public static implicit operator PpmContext(Pointer pointer) - { - return new PpmContext(pointer._address, pointer._memory); - } - - /// - /// Allow pointer-like addition on a PPM context. - /// - /// - /// - /// - public static PpmContext operator +(PpmContext context, int offset) - { - context._address = (uint)(context._address + offset * SIZE); - return context; - } - - /// - /// Allow pointer-like subtraction on a PPM context. - /// - /// - /// - /// - public static PpmContext operator -(PpmContext context, int offset) - { - context._address = (uint)(context._address - offset * SIZE); - return context; - } - - /// - /// Compare two PPM contexts. - /// - /// - /// - /// - public static bool operator <=(PpmContext context1, PpmContext context2) - { - return context1._address <= context2._address; - } - - /// - /// Compare two PPM contexts. - /// - /// - /// - /// - public static bool operator >=(PpmContext context1, PpmContext context2) - { - return context1._address >= context2._address; - } - - /// - /// Compare two PPM contexts. - /// - /// - /// - /// - public static bool operator ==(PpmContext context1, PpmContext context2) - { - return context1._address == context2._address; - } - - /// - /// Compare two PPM contexts. - /// - /// - /// - /// - public static bool operator !=(PpmContext context1, PpmContext context2) - { - return context1._address != context2._address; - } - - /// - /// Indicates whether this instance and a specified object are equal. - /// - /// true if obj and this instance are the same type and represent the same value; otherwise, false. - /// Another object to compare to. - public override bool Equals(object obj) - { - if (obj is PpmContext) - { - PpmContext context = (PpmContext)obj; - return context._address == _address; - } - return base.Equals(obj); - } - - /// - /// Returns the hash code for this instance. - /// - /// A 32-bit signed integer that is the hash code for this instance. - public override int GetHashCode() - { - return _address.GetHashCode(); + _memory[_address + 2] = (byte)value; + _memory[_address + 3] = (byte)(value >> 8); } } - private void EncodeBinarySymbol(int symbol, PpmContext context) + /// + /// Gets or sets the statistics. + /// + public PpmState Statistics { - PpmState state = context.FirstState; - int index1 = _probabilities[state.Frequency - 1]; - int index2 = - _numberStatisticsToBinarySummaryIndex[context.Suffix.NumberStatistics] - + _previousSuccess - + context.Flags - + ((_runLength >> 26) & 0x20); - - if (state.Symbol == symbol) - { - _foundState = state; - state.Frequency += (byte)((state.Frequency < 196) ? 1 : 0); - _coder._lowCount = 0; - _coder._highCount = _binarySummary[index1, index2]; - _binarySummary[index1, index2] += (ushort)( - INTERVAL - Mean(_binarySummary[index1, index2], PERIOD_BIT_COUNT, 2) + get => + new PpmState( + _memory[_address + 4] + | (((uint)_memory[_address + 5]) << 8) + | (((uint)_memory[_address + 6]) << 16) + | (((uint)_memory[_address + 7]) << 24), + _memory ); - _previousSuccess = 1; - _runLength++; - } - else + set { - _coder._lowCount = _binarySummary[index1, index2]; - _binarySummary[index1, index2] -= (ushort)Mean( - _binarySummary[index1, index2], - PERIOD_BIT_COUNT, - 2 - ); - _coder._highCount = BINARY_SCALE; - _initialEscape = EXPONENTIAL_ESCAPES[_binarySummary[index1, index2] >> 10]; - _characterMask[state.Symbol] = _escapeCount; - _previousSuccess = 0; - _numberMasked = 0; - _foundState = PpmState.ZERO; + _memory[_address + 4] = (byte)value._address; + _memory[_address + 5] = (byte)(value._address >> 8); + _memory[_address + 6] = (byte)(value._address >> 16); + _memory[_address + 7] = (byte)(value._address >> 24); } } - private void EncodeSymbol1(int symbol, PpmContext context) + /// + /// Gets or sets the suffix. + /// + public PpmContext Suffix { - uint lowCount; - uint index = context.Statistics.Symbol; - PpmState state = context.Statistics; - _coder._scale = context.SummaryFrequency; - if (index == symbol) + get => + new PpmContext( + _memory[_address + 8] + | (((uint)_memory[_address + 9]) << 8) + | (((uint)_memory[_address + 10]) << 16) + | (((uint)_memory[_address + 11]) << 24), + _memory + ); + set { - _coder._highCount = state.Frequency; - _previousSuccess = (byte)((2 * _coder._highCount >= _coder._scale) ? 1 : 0); - _foundState = state; - _foundState.Frequency += 4; - context.SummaryFrequency += 4; - _runLength += _previousSuccess; - if (state.Frequency > MAXIMUM_FREQUENCY) - { - Rescale(context); - } - _coder._lowCount = 0; - return; + _memory[_address + 8] = (byte)value._address; + _memory[_address + 9] = (byte)(value._address >> 8); + _memory[_address + 10] = (byte)(value._address >> 16); + _memory[_address + 11] = (byte)(value._address >> 24); } + } - lowCount = state.Frequency; - index = context.NumberStatistics; + /// + /// The first PPM state associated with the PPM context. + /// + /// + /// + /// The first PPM state overlaps this PPM context instance (the context.SummaryFrequency and context.Statistics members + /// of PpmContext use 6 bytes and so can therefore fit into the space used by the Symbol, Frequency and + /// Successor members of PpmState, since they also add up to 6 bytes). + /// + /// + /// PpmContext (context.SummaryFrequency and context.Statistics use 6 bytes) + /// 1 context.NumberStatistics + /// 1 context.Flags + /// 2 context.SummaryFrequency + /// 4 context.Statistics (pointer to PpmState) + /// 4 context.Suffix (pointer to PpmContext) + /// + /// + /// PpmState (total of 6 bytes) + /// 1 Symbol + /// 1 Frequency + /// 4 Successor (pointer to PpmContext) + /// + /// + /// + public PpmState FirstState => new PpmState(_address + 2, _memory); + + /// + /// Gets or sets the symbol of the first PPM state. This is provided for convenience. The same + /// information can be obtained using the Symbol property on the PPM state provided by the + /// property. + /// + public byte FirstStateSymbol + { + get => _memory[_address + 2]; + set => _memory[_address + 2] = value; + } + + /// + /// Gets or sets the frequency of the first PPM state. This is provided for convenience. The same + /// information can be obtained using the Frequency property on the PPM state provided by the + ///context.FirstState property. + /// + public byte FirstStateFrequency + { + get => _memory[_address + 3]; + set => _memory[_address + 3] = value; + } + + /// + /// Gets or sets the successor of the first PPM state. This is provided for convenience. The same + /// information can be obtained using the Successor property on the PPM state provided by the + /// + public PpmContext FirstStateSuccessor + { + get => + new PpmContext( + _memory[_address + 4] + | (((uint)_memory[_address + 5]) << 8) + | (((uint)_memory[_address + 6]) << 16) + | (((uint)_memory[_address + 7]) << 24), + _memory + ); + set + { + _memory[_address + 4] = (byte)value._address; + _memory[_address + 5] = (byte)(value._address >> 8); + _memory[_address + 6] = (byte)(value._address >> 16); + _memory[_address + 7] = (byte)(value._address >> 24); + } + } + + /// + /// Allow a pointer to be implicitly converted to a PPM context. + /// + /// + /// + public static implicit operator PpmContext(Pointer pointer) + { + return new PpmContext(pointer._address, pointer._memory); + } + + /// + /// Allow pointer-like addition on a PPM context. + /// + /// + /// + /// + public static PpmContext operator +(PpmContext context, int offset) + { + context._address = (uint)(context._address + (offset * SIZE)); + return context; + } + + /// + /// Allow pointer-like subtraction on a PPM context. + /// + /// + /// + /// + public static PpmContext operator -(PpmContext context, int offset) + { + context._address = (uint)(context._address - (offset * SIZE)); + return context; + } + + /// + /// Compare two PPM contexts. + /// + /// + /// + /// + public static bool operator <=(PpmContext context1, PpmContext context2) + { + return context1._address <= context2._address; + } + + /// + /// Compare two PPM contexts. + /// + /// + /// + /// + public static bool operator >=(PpmContext context1, PpmContext context2) + { + return context1._address >= context2._address; + } + + /// + /// Compare two PPM contexts. + /// + /// + /// + /// + public static bool operator ==(PpmContext context1, PpmContext context2) + { + return context1._address == context2._address; + } + + /// + /// Compare two PPM contexts. + /// + /// + /// + /// + public static bool operator !=(PpmContext context1, PpmContext context2) + { + return context1._address != context2._address; + } + + /// + /// Indicates whether this instance and a specified object are equal. + /// + /// true if obj and this instance are the same type and represent the same value; otherwise, false. + /// Another object to compare to. + public override bool Equals(object obj) + { + if (obj is PpmContext context) + { + return context._address == _address; + } + return base.Equals(obj); + } + + /// + /// Returns the hash code for this instance. + /// + /// A 32-bit signed integer that is the hash code for this instance. + public override int GetHashCode() + { + return _address.GetHashCode(); + } + } + + private void EncodeBinarySymbol(int symbol, PpmContext context) + { + var state = context.FirstState; + int index1 = _probabilities[state.Frequency - 1]; + var index2 = + _numberStatisticsToBinarySummaryIndex[context.Suffix.NumberStatistics] + + _previousSuccess + + context.Flags + + ((_runLength >> 26) & 0x20); + + if (state.Symbol == symbol) + { + _foundState = state; + state.Frequency += (byte)((state.Frequency < 196) ? 1 : 0); + _coder._lowCount = 0; + _coder._highCount = _binarySummary[index1, index2]; + _binarySummary[index1, index2] += (ushort)( + INTERVAL - Mean(_binarySummary[index1, index2], PERIOD_BIT_COUNT, 2) + ); + _previousSuccess = 1; + _runLength++; + } + else + { + _coder._lowCount = _binarySummary[index1, index2]; + _binarySummary[index1, index2] -= (ushort)Mean( + _binarySummary[index1, index2], + PERIOD_BIT_COUNT, + 2 + ); + _coder._highCount = BINARY_SCALE; + _initialEscape = EXPONENTIAL_ESCAPES[_binarySummary[index1, index2] >> 10]; + _characterMask[state.Symbol] = _escapeCount; _previousSuccess = 0; - while ((++state).Symbol != symbol) - { - lowCount += state.Frequency; - if (--index == 0) - { - _coder._lowCount = lowCount; - _characterMask[state.Symbol] = _escapeCount; - _numberMasked = context.NumberStatistics; - index = context.NumberStatistics; - _foundState = PpmState.ZERO; - do - { - _characterMask[(--state).Symbol] = _escapeCount; - } while (--index != 0); - _coder._highCount = _coder._scale; - return; - } - } - _coder._highCount = (_coder._lowCount = lowCount) + state.Frequency; - Update1(state, context); + _numberMasked = 0; + _foundState = PpmState.ZERO; } + } - private void EncodeSymbol2(int symbol, PpmContext context) - { - See2Context see2Context = MakeEscapeFrequency(context); - uint currentSymbol; - uint lowCount = 0; - uint index = (uint)(context.NumberStatistics - _numberMasked); - PpmState state = context.Statistics - 1; - - do - { - do - { - currentSymbol = state[1].Symbol; - state++; - } while (_characterMask[currentSymbol] == _escapeCount); - _characterMask[currentSymbol] = _escapeCount; - if (currentSymbol == symbol) - { - goto SymbolFound; - } - lowCount += state.Frequency; - } while (--index != 0); - - _coder._lowCount = lowCount; - _coder._scale += _coder._lowCount; - _coder._highCount = _coder._scale; - see2Context._summary += (ushort)_coder._scale; - _numberMasked = context.NumberStatistics; - return; - - SymbolFound: - _coder._lowCount = lowCount; - lowCount += state.Frequency; - _coder._highCount = lowCount; - for (PpmState p1 = state; --index != 0; ) - { - do - { - currentSymbol = p1[1].Symbol; - p1++; - } while (_characterMask[currentSymbol] == _escapeCount); - lowCount += p1.Frequency; - } - _coder._scale += lowCount; - see2Context.Update(); - Update2(state, context); - } - - private void DecodeBinarySymbol(PpmContext context) - { - PpmState state = context.FirstState; - int index1 = _probabilities[state.Frequency - 1]; - int index2 = - _numberStatisticsToBinarySummaryIndex[context.Suffix.NumberStatistics] - + _previousSuccess - + context.Flags - + ((_runLength >> 26) & 0x20); - - if (_coder.RangeGetCurrentShiftCount(TOTAL_BIT_COUNT) < _binarySummary[index1, index2]) - { - _foundState = state; - state.Frequency += (byte)((state.Frequency < 196) ? 1 : 0); - _coder._lowCount = 0; - _coder._highCount = _binarySummary[index1, index2]; - _binarySummary[index1, index2] += (ushort)( - INTERVAL - Mean(_binarySummary[index1, index2], PERIOD_BIT_COUNT, 2) - ); - _previousSuccess = 1; - _runLength++; - } - else - { - _coder._lowCount = _binarySummary[index1, index2]; - _binarySummary[index1, index2] -= (ushort)Mean( - _binarySummary[index1, index2], - PERIOD_BIT_COUNT, - 2 - ); - _coder._highCount = BINARY_SCALE; - _initialEscape = EXPONENTIAL_ESCAPES[_binarySummary[index1, index2] >> 10]; - _characterMask[state.Symbol] = _escapeCount; - _previousSuccess = 0; - _numberMasked = 0; - _foundState = PpmState.ZERO; - } - } - - private void DecodeSymbol1(PpmContext context) - { - uint index; - uint count; - uint highCount = context.Statistics.Frequency; - PpmState state = context.Statistics; - _coder._scale = context.SummaryFrequency; - - count = _coder.RangeGetCurrentCount(); - if (count < highCount) - { - _coder._highCount = highCount; - _previousSuccess = (byte)((2 * _coder._highCount >= _coder._scale) ? 1 : 0); - _foundState = state; - highCount += 4; - _foundState.Frequency = (byte)highCount; - context.SummaryFrequency += 4; - _runLength += _previousSuccess; - if (highCount > MAXIMUM_FREQUENCY) - { - Rescale(context); - } - _coder._lowCount = 0; - return; - } - - index = context.NumberStatistics; - _previousSuccess = 0; - while ((highCount += (++state).Frequency) <= count) - { - if (--index == 0) - { - _coder._lowCount = highCount; - _characterMask[state.Symbol] = _escapeCount; - _numberMasked = context.NumberStatistics; - index = context.NumberStatistics; - _foundState = PpmState.ZERO; - do - { - _characterMask[(--state).Symbol] = _escapeCount; - } while (--index != 0); - _coder._highCount = _coder._scale; - return; - } - } - _coder._highCount = highCount; - _coder._lowCount = _coder._highCount - state.Frequency; - Update1(state, context); - } - - private void DecodeSymbol2(PpmContext context) - { - See2Context see2Context = MakeEscapeFrequency(context); - uint currentSymbol; - uint count; - uint highCount = 0; - uint index = (uint)(context.NumberStatistics - _numberMasked); - uint stateIndex = 0; - PpmState state = context.Statistics - 1; - - do - { - do - { - currentSymbol = state[1].Symbol; - state++; - } while (_characterMask[currentSymbol] == _escapeCount); - highCount += state.Frequency; - _decodeStates[stateIndex++] = state; - - // note that decodeStates is a static array that is re-used on each call to this method (for performance reasons) - } while (--index != 0); - - _coder._scale += highCount; - count = _coder.RangeGetCurrentCount(); - stateIndex = 0; - state = _decodeStates[stateIndex]; - if (count < highCount) - { - highCount = 0; - while ((highCount += state.Frequency) <= count) - { - state = _decodeStates[++stateIndex]; - } - _coder._highCount = highCount; - _coder._lowCount = _coder._highCount - state.Frequency; - see2Context.Update(); - Update2(state, context); - } - else - { - _coder._lowCount = highCount; - _coder._highCount = _coder._scale; - index = (uint)(context.NumberStatistics - _numberMasked); - _numberMasked = context.NumberStatistics; - do - { - _characterMask[_decodeStates[stateIndex].Symbol] = _escapeCount; - stateIndex++; - } while (--index != 0); - see2Context._summary += (ushort)_coder._scale; - } - } - - private void Update1(PpmState state, PpmContext context) - { - _foundState = state; - _foundState.Frequency += 4; - context.SummaryFrequency += 4; - if (state[0].Frequency > state[-1].Frequency) - { - Swap(state[0], state[-1]); - _foundState = --state; - if (state.Frequency > MAXIMUM_FREQUENCY) - { - Rescale(context); - } - } - } - - private void Update2(PpmState state, PpmContext context) + private void EncodeSymbol1(int symbol, PpmContext context) + { + uint lowCount; + uint index = context.Statistics.Symbol; + var state = context.Statistics; + _coder._scale = context.SummaryFrequency; + if (index == symbol) { + _coder._highCount = state.Frequency; + _previousSuccess = (byte)((2 * _coder._highCount >= _coder._scale) ? 1 : 0); _foundState = state; _foundState.Frequency += 4; context.SummaryFrequency += 4; + _runLength += _previousSuccess; if (state.Frequency > MAXIMUM_FREQUENCY) { Rescale(context); } - _escapeCount++; - _runLength = _initialRunLength; + _coder._lowCount = 0; + return; } - private See2Context MakeEscapeFrequency(PpmContext context) + lowCount = state.Frequency; + index = context.NumberStatistics; + _previousSuccess = 0; + while ((++state).Symbol != symbol) { - uint numberStatistics = (uint)2 * context.NumberStatistics; - See2Context see2Context; - - if (context.NumberStatistics != 0xff) + lowCount += state.Frequency; + if (--index == 0) { - // Note that context.Flags is always in the range 0 .. 28 (this ensures that the index used for the second - // dimension of the see2Contexts array is always in the range 0 .. 31). - - numberStatistics = context.Suffix.NumberStatistics; - int index1 = _probabilities[context.NumberStatistics + 2] - 3; - int index2 = - ((context.SummaryFrequency > 11 * (context.NumberStatistics + 1)) ? 1 : 0) - + ((2 * context.NumberStatistics < numberStatistics + _numberMasked) ? 2 : 0) - + context.Flags; - see2Context = _see2Contexts[index1, index2]; - _coder._scale = see2Context.Mean(); + _coder._lowCount = lowCount; + _characterMask[state.Symbol] = _escapeCount; + _numberMasked = context.NumberStatistics; + index = context.NumberStatistics; + _foundState = PpmState.ZERO; + do + { + _characterMask[(--state).Symbol] = _escapeCount; + } while (--index != 0); + _coder._highCount = _coder._scale; + return; } - else - { - see2Context = _emptySee2Context; - _coder._scale = 1; - } - - return see2Context; } + _coder._highCount = (_coder._lowCount = lowCount) + state.Frequency; + Update1(state, context); + } - private void Rescale(PpmContext context) + private void EncodeSymbol2(int symbol, PpmContext context) + { + var see2Context = MakeEscapeFrequency(context); + uint currentSymbol; + uint lowCount = 0; + var index = (uint)(context.NumberStatistics - _numberMasked); + var state = context.Statistics - 1; + + do { - uint oldUnitCount; - int adder; - uint escapeFrequency; - uint index = context.NumberStatistics; - - byte localSymbol; - byte localFrequency; - PpmContext localSuccessor; - PpmState p1; - PpmState state; - - for (state = _foundState; state != context.Statistics; state--) - { - Swap(state[0], state[-1]); - } - - state.Frequency += 4; - context.SummaryFrequency += 4; - escapeFrequency = (uint)(context.SummaryFrequency - state.Frequency); - adder = (_orderFall != 0 || _method > ModelRestorationMethod.Freeze) ? 1 : 0; - state.Frequency = (byte)((state.Frequency + adder) >> 1); - context.SummaryFrequency = state.Frequency; - do { - escapeFrequency -= (++state).Frequency; - state.Frequency = (byte)((state.Frequency + adder) >> 1); - context.SummaryFrequency += state.Frequency; - if (state[0].Frequency > state[-1].Frequency) - { - p1 = state; - localSymbol = p1.Symbol; - localFrequency = p1.Frequency; - localSuccessor = p1.Successor; - do - { - Copy(p1[0], p1[-1]); - } while (localFrequency > (--p1)[-1].Frequency); - p1.Symbol = localSymbol; - p1.Frequency = localFrequency; - p1.Successor = localSuccessor; - } - } while (--index != 0); - - if (state.Frequency == 0) + currentSymbol = state[1].Symbol; + state++; + } while (_characterMask[currentSymbol] == _escapeCount); + _characterMask[currentSymbol] = _escapeCount; + if (currentSymbol == symbol) { - do - { - index++; - } while ((--state).Frequency == 0); - - escapeFrequency += index; - oldUnitCount = (uint)((context.NumberStatistics + 2) >> 1); - context.NumberStatistics -= (byte)index; - if (context.NumberStatistics == 0) - { - localSymbol = context.Statistics.Symbol; - localFrequency = context.Statistics.Frequency; - localSuccessor = context.Statistics.Successor; - localFrequency = (byte)( - (2 * localFrequency + escapeFrequency - 1) / escapeFrequency - ); - if (localFrequency > MAXIMUM_FREQUENCY / 3) - { - localFrequency = (byte)(MAXIMUM_FREQUENCY / 3); - } - _allocator.FreeUnits(context.Statistics, oldUnitCount); - context.FirstStateSymbol = localSymbol; - context.FirstStateFrequency = localFrequency; - context.FirstStateSuccessor = localSuccessor; - context.Flags = (byte)( - (context.Flags & 0x10) + ((localSymbol >= 0x40) ? 0x08 : 0x00) - ); - _foundState = context.FirstState; - return; - } - - context.Statistics = _allocator.ShrinkUnits( - context.Statistics, - oldUnitCount, - (uint)((context.NumberStatistics + 2) >> 1) - ); - context.Flags &= 0xf7; - index = context.NumberStatistics; - state = context.Statistics; - context.Flags |= (byte)((state.Symbol >= 0x40) ? 0x08 : 0x00); - do - { - context.Flags |= (byte)(((++state).Symbol >= 0x40) ? 0x08 : 0x00); - } while (--index != 0); + goto SymbolFound; } + lowCount += state.Frequency; + } while (--index != 0); - escapeFrequency -= (escapeFrequency >> 1); - context.SummaryFrequency += (ushort)escapeFrequency; - context.Flags |= 0x04; - _foundState = context.Statistics; + _coder._lowCount = lowCount; + _coder._scale += _coder._lowCount; + _coder._highCount = _coder._scale; + see2Context._summary += (ushort)_coder._scale; + _numberMasked = context.NumberStatistics; + return; + + SymbolFound: + _coder._lowCount = lowCount; + lowCount += state.Frequency; + _coder._highCount = lowCount; + for (var p1 = state; --index != 0;) + { + do + { + currentSymbol = p1[1].Symbol; + p1++; + } while (_characterMask[currentSymbol] == _escapeCount); + lowCount += p1.Frequency; + } + _coder._scale += lowCount; + see2Context.Update(); + Update2(state, context); + } + + private void DecodeBinarySymbol(PpmContext context) + { + var state = context.FirstState; + int index1 = _probabilities[state.Frequency - 1]; + var index2 = + _numberStatisticsToBinarySummaryIndex[context.Suffix.NumberStatistics] + + _previousSuccess + + context.Flags + + ((_runLength >> 26) & 0x20); + + if (_coder.RangeGetCurrentShiftCount(TOTAL_BIT_COUNT) < _binarySummary[index1, index2]) + { + _foundState = state; + state.Frequency += (byte)((state.Frequency < 196) ? 1 : 0); + _coder._lowCount = 0; + _coder._highCount = _binarySummary[index1, index2]; + _binarySummary[index1, index2] += (ushort)( + INTERVAL - Mean(_binarySummary[index1, index2], PERIOD_BIT_COUNT, 2) + ); + _previousSuccess = 1; + _runLength++; + } + else + { + _coder._lowCount = _binarySummary[index1, index2]; + _binarySummary[index1, index2] -= (ushort)Mean( + _binarySummary[index1, index2], + PERIOD_BIT_COUNT, + 2 + ); + _coder._highCount = BINARY_SCALE; + _initialEscape = EXPONENTIAL_ESCAPES[_binarySummary[index1, index2] >> 10]; + _characterMask[state.Symbol] = _escapeCount; + _previousSuccess = 0; + _numberMasked = 0; + _foundState = PpmState.ZERO; + } + } + + private void DecodeSymbol1(PpmContext context) + { + uint index; + uint count; + uint highCount = context.Statistics.Frequency; + var state = context.Statistics; + _coder._scale = context.SummaryFrequency; + + count = _coder.RangeGetCurrentCount(); + if (count < highCount) + { + _coder._highCount = highCount; + _previousSuccess = (byte)((2 * _coder._highCount >= _coder._scale) ? 1 : 0); + _foundState = state; + highCount += 4; + _foundState.Frequency = (byte)highCount; + context.SummaryFrequency += 4; + _runLength += _previousSuccess; + if (highCount > MAXIMUM_FREQUENCY) + { + Rescale(context); + } + _coder._lowCount = 0; + return; } - private void Refresh(uint oldUnitCount, bool scale, PpmContext context) + index = context.NumberStatistics; + _previousSuccess = 0; + while ((highCount += (++state).Frequency) <= count) { - int index = context.NumberStatistics; - int escapeFrequency; - int scaleValue = (scale ? 1 : 0); + if (--index == 0) + { + _coder._lowCount = highCount; + _characterMask[state.Symbol] = _escapeCount; + _numberMasked = context.NumberStatistics; + index = context.NumberStatistics; + _foundState = PpmState.ZERO; + do + { + _characterMask[(--state).Symbol] = _escapeCount; + } while (--index != 0); + _coder._highCount = _coder._scale; + return; + } + } + _coder._highCount = highCount; + _coder._lowCount = _coder._highCount - state.Frequency; + Update1(state, context); + } + + private void DecodeSymbol2(PpmContext context) + { + var see2Context = MakeEscapeFrequency(context); + uint currentSymbol; + uint count; + uint highCount = 0; + var index = (uint)(context.NumberStatistics - _numberMasked); + uint stateIndex = 0; + var state = context.Statistics - 1; + + do + { + do + { + currentSymbol = state[1].Symbol; + state++; + } while (_characterMask[currentSymbol] == _escapeCount); + highCount += state.Frequency; + _decodeStates[stateIndex++] = state; + + // note that decodeStates is a static array that is re-used on each call to this method (for performance reasons) + } while (--index != 0); + + _coder._scale += highCount; + count = _coder.RangeGetCurrentCount(); + stateIndex = 0; + state = _decodeStates[stateIndex]; + if (count < highCount) + { + highCount = 0; + while ((highCount += state.Frequency) <= count) + { + state = _decodeStates[++stateIndex]; + } + _coder._highCount = highCount; + _coder._lowCount = _coder._highCount - state.Frequency; + see2Context.Update(); + Update2(state, context); + } + else + { + _coder._lowCount = highCount; + _coder._highCount = _coder._scale; + index = (uint)(context.NumberStatistics - _numberMasked); + _numberMasked = context.NumberStatistics; + do + { + _characterMask[_decodeStates[stateIndex].Symbol] = _escapeCount; + stateIndex++; + } while (--index != 0); + see2Context._summary += (ushort)_coder._scale; + } + } + + private void Update1(PpmState state, PpmContext context) + { + _foundState = state; + _foundState.Frequency += 4; + context.SummaryFrequency += 4; + if (state[0].Frequency > state[-1].Frequency) + { + Swap(state[0], state[-1]); + _foundState = --state; + if (state.Frequency > MAXIMUM_FREQUENCY) + { + Rescale(context); + } + } + } + + private void Update2(PpmState state, PpmContext context) + { + _foundState = state; + _foundState.Frequency += 4; + context.SummaryFrequency += 4; + if (state.Frequency > MAXIMUM_FREQUENCY) + { + Rescale(context); + } + _escapeCount++; + _runLength = _initialRunLength; + } + + private See2Context MakeEscapeFrequency(PpmContext context) + { + var numberStatistics = (uint)2 * context.NumberStatistics; + See2Context see2Context; + + if (context.NumberStatistics != 0xff) + { + // Note that context.Flags is always in the range 0 .. 28 (this ensures that the index used for the second + // dimension of the see2Contexts array is always in the range 0 .. 31). + + numberStatistics = context.Suffix.NumberStatistics; + var index1 = _probabilities[context.NumberStatistics + 2] - 3; + var index2 = + ((context.SummaryFrequency > 11 * (context.NumberStatistics + 1)) ? 1 : 0) + + ((2 * context.NumberStatistics < numberStatistics + _numberMasked) ? 2 : 0) + + context.Flags; + see2Context = _see2Contexts[index1, index2]; + _coder._scale = see2Context.Mean(); + } + else + { + see2Context = _emptySee2Context; + _coder._scale = 1; + } + + return see2Context; + } + + private void Rescale(PpmContext context) + { + uint oldUnitCount; + int adder; + uint escapeFrequency; + uint index = context.NumberStatistics; + + byte localSymbol; + byte localFrequency; + PpmContext localSuccessor; + PpmState p1; + PpmState state; + + for (state = _foundState; state != context.Statistics; state--) + { + Swap(state[0], state[-1]); + } + + state.Frequency += 4; + context.SummaryFrequency += 4; + escapeFrequency = (uint)(context.SummaryFrequency - state.Frequency); + adder = (_orderFall != 0 || _method > ModelRestorationMethod.Freeze) ? 1 : 0; + state.Frequency = (byte)((state.Frequency + adder) >> 1); + context.SummaryFrequency = state.Frequency; + + do + { + escapeFrequency -= (++state).Frequency; + state.Frequency = (byte)((state.Frequency + adder) >> 1); + context.SummaryFrequency += state.Frequency; + if (state[0].Frequency > state[-1].Frequency) + { + p1 = state; + localSymbol = p1.Symbol; + localFrequency = p1.Frequency; + localSuccessor = p1.Successor; + do + { + Copy(p1[0], p1[-1]); + } while (localFrequency > (--p1)[-1].Frequency); + p1.Symbol = localSymbol; + p1.Frequency = localFrequency; + p1.Successor = localSuccessor; + } + } while (--index != 0); + + if (state.Frequency == 0) + { + do + { + index++; + } while ((--state).Frequency == 0); + + escapeFrequency += index; + oldUnitCount = (uint)((context.NumberStatistics + 2) >> 1); + context.NumberStatistics -= (byte)index; + if (context.NumberStatistics == 0) + { + localSymbol = context.Statistics.Symbol; + localFrequency = context.Statistics.Frequency; + localSuccessor = context.Statistics.Successor; + localFrequency = (byte)( + ((2 * localFrequency) + escapeFrequency - 1) / escapeFrequency + ); + if (localFrequency > MAXIMUM_FREQUENCY / 3) + { + localFrequency = (byte)(MAXIMUM_FREQUENCY / 3); + } + _allocator.FreeUnits(context.Statistics, oldUnitCount); + context.FirstStateSymbol = localSymbol; + context.FirstStateFrequency = localFrequency; + context.FirstStateSuccessor = localSuccessor; + context.Flags = (byte)( + (context.Flags & 0x10) + ((localSymbol >= 0x40) ? 0x08 : 0x00) + ); + _foundState = context.FirstState; + return; + } context.Statistics = _allocator.ShrinkUnits( context.Statistics, oldUnitCount, - (uint)((index + 2) >> 1) + (uint)((context.NumberStatistics + 2) >> 1) ); - PpmState statistics = context.Statistics; - context.Flags = (byte)( - (context.Flags & (0x10 + (scale ? 0x04 : 0x00))) - + ((statistics.Symbol >= 0x40) ? 0x08 : 0x00) - ); - escapeFrequency = context.SummaryFrequency - statistics.Frequency; - statistics.Frequency = (byte)((statistics.Frequency + scaleValue) >> scaleValue); - context.SummaryFrequency = statistics.Frequency; - + context.Flags &= 0xf7; + index = context.NumberStatistics; + state = context.Statistics; + context.Flags |= (byte)((state.Symbol >= 0x40) ? 0x08 : 0x00); do { - escapeFrequency -= (++statistics).Frequency; - statistics.Frequency = (byte)((statistics.Frequency + scaleValue) >> scaleValue); - context.SummaryFrequency += statistics.Frequency; - context.Flags |= (byte)((statistics.Symbol >= 0x40) ? 0x08 : 0x00); + context.Flags |= (byte)(((++state).Symbol >= 0x40) ? 0x08 : 0x00); } while (--index != 0); - - escapeFrequency = (escapeFrequency + scaleValue) >> scaleValue; - context.SummaryFrequency += (ushort)escapeFrequency; } - private PpmContext CutOff(int order, PpmContext context) + escapeFrequency -= (escapeFrequency >> 1); + context.SummaryFrequency += (ushort)escapeFrequency; + context.Flags |= 0x04; + _foundState = context.Statistics; + } + + private void Refresh(uint oldUnitCount, bool scale, PpmContext context) + { + int index = context.NumberStatistics; + int escapeFrequency; + var scaleValue = (scale ? 1 : 0); + + context.Statistics = _allocator.ShrinkUnits( + context.Statistics, + oldUnitCount, + (uint)((index + 2) >> 1) + ); + var statistics = context.Statistics; + context.Flags = (byte)( + (context.Flags & (0x10 + (scale ? 0x04 : 0x00))) + + ((statistics.Symbol >= 0x40) ? 0x08 : 0x00) + ); + escapeFrequency = context.SummaryFrequency - statistics.Frequency; + statistics.Frequency = (byte)((statistics.Frequency + scaleValue) >> scaleValue); + context.SummaryFrequency = statistics.Frequency; + + do { - int index; - PpmState state; + escapeFrequency -= (++statistics).Frequency; + statistics.Frequency = (byte)((statistics.Frequency + scaleValue) >> scaleValue); + context.SummaryFrequency += statistics.Frequency; + context.Flags |= (byte)((statistics.Symbol >= 0x40) ? 0x08 : 0x00); + } while (--index != 0); - if (context.NumberStatistics == 0) + escapeFrequency = (escapeFrequency + scaleValue) >> scaleValue; + context.SummaryFrequency += (ushort)escapeFrequency; + } + + private PpmContext CutOff(int order, PpmContext context) + { + int index; + PpmState state; + + if (context.NumberStatistics == 0) + { + state = context.FirstState; + if ((Pointer)state.Successor >= _allocator._baseUnit) { - state = context.FirstState; - if ((Pointer)state.Successor >= _allocator._baseUnit) - { - if (order < _modelOrder) - { - state.Successor = CutOff(order + 1, state.Successor); - } - else - { - state.Successor = PpmContext.ZERO; - } - - if (state.Successor == PpmContext.ZERO && order > ORDER_BOUND) - { - _allocator.SpecialFreeUnits(context); - return PpmContext.ZERO; - } - - return context; - } - _allocator.SpecialFreeUnits(context); - return PpmContext.ZERO; - } - - uint unitCount = (uint)((context.NumberStatistics + 2) >> 1); - context.Statistics = _allocator.MoveUnitsUp(context.Statistics, unitCount); - index = context.NumberStatistics; - for (state = context.Statistics + index; state >= context.Statistics; state--) - { - if (state.Successor < _allocator._baseUnit) - { - state.Successor = PpmContext.ZERO; - Swap(state, context.Statistics[index--]); - } - else if (order < _modelOrder) + if (order < _modelOrder) { state.Successor = CutOff(order + 1, state.Successor); } @@ -798,77 +766,107 @@ namespace SharpCompress.Compressors.PPMd.I1 { state.Successor = PpmContext.ZERO; } - } - if (index != context.NumberStatistics && order != 0) - { - context.NumberStatistics = (byte)index; - state = context.Statistics; - if (index < 0) + if (state.Successor == PpmContext.ZERO && order > ORDER_BOUND) { - _allocator.FreeUnits(state, unitCount); _allocator.SpecialFreeUnits(context); return PpmContext.ZERO; } - if (index == 0) - { - context.Flags = (byte)( - (context.Flags & 0x10) + ((state.Symbol >= 0x40) ? 0x08 : 0x00) - ); - Copy(context.FirstState, state); - _allocator.FreeUnits(state, unitCount); - context.FirstStateFrequency = (byte)((context.FirstStateFrequency + 11) >> 3); - } - else - { - Refresh(unitCount, context.SummaryFrequency > 16 * index, context); - } - } - return context; - } - - private PpmContext RemoveBinaryContexts(int order, PpmContext context) - { - if (context.NumberStatistics == 0) - { - PpmState state = context.FirstState; - if ((Pointer)state.Successor >= _allocator._baseUnit && order < _modelOrder) - { - state.Successor = RemoveBinaryContexts(order + 1, state.Successor); - } - else - { - state.Successor = PpmContext.ZERO; - } - if ( - (state.Successor == PpmContext.ZERO) - && (context.Suffix.NumberStatistics == 0 || context.Suffix.Flags == 0xff) - ) - { - _allocator.FreeUnits(context, 1); - return PpmContext.ZERO; - } return context; } + _allocator.SpecialFreeUnits(context); + return PpmContext.ZERO; + } - for ( - PpmState state = context.Statistics + context.NumberStatistics; - state >= context.Statistics; - state-- + var unitCount = (uint)((context.NumberStatistics + 2) >> 1); + context.Statistics = _allocator.MoveUnitsUp(context.Statistics, unitCount); + index = context.NumberStatistics; + for (state = context.Statistics + index; state >= context.Statistics; state--) + { + if (state.Successor < _allocator._baseUnit) + { + state.Successor = PpmContext.ZERO; + Swap(state, context.Statistics[index--]); + } + else if (order < _modelOrder) + { + state.Successor = CutOff(order + 1, state.Successor); + } + else + { + state.Successor = PpmContext.ZERO; + } + } + + if (index != context.NumberStatistics && order != 0) + { + context.NumberStatistics = (byte)index; + state = context.Statistics; + if (index < 0) + { + _allocator.FreeUnits(state, unitCount); + _allocator.SpecialFreeUnits(context); + return PpmContext.ZERO; + } + if (index == 0) + { + context.Flags = (byte)( + (context.Flags & 0x10) + ((state.Symbol >= 0x40) ? 0x08 : 0x00) + ); + Copy(context.FirstState, state); + _allocator.FreeUnits(state, unitCount); + context.FirstStateFrequency = (byte)((context.FirstStateFrequency + 11) >> 3); + } + else + { + Refresh(unitCount, context.SummaryFrequency > 16 * index, context); + } + } + + return context; + } + + private PpmContext RemoveBinaryContexts(int order, PpmContext context) + { + if (context.NumberStatistics == 0) + { + var state = context.FirstState; + if ((Pointer)state.Successor >= _allocator._baseUnit && order < _modelOrder) + { + state.Successor = RemoveBinaryContexts(order + 1, state.Successor); + } + else + { + state.Successor = PpmContext.ZERO; + } + if ( + (state.Successor == PpmContext.ZERO) + && (context.Suffix.NumberStatistics == 0 || context.Suffix.Flags == 0xff) ) { - if ((Pointer)state.Successor >= _allocator._baseUnit && order < _modelOrder) - { - state.Successor = RemoveBinaryContexts(order + 1, state.Successor); - } - else - { - state.Successor = PpmContext.ZERO; - } + _allocator.FreeUnits(context, 1); + return PpmContext.ZERO; } - return context; } + + for ( + var state = context.Statistics + context.NumberStatistics; + state >= context.Statistics; + state-- + ) + { + if ((Pointer)state.Successor >= _allocator._baseUnit && order < _modelOrder) + { + state.Successor = RemoveBinaryContexts(order + 1, state.Successor); + } + else + { + state.Successor = PpmContext.ZERO; + } + } + + return context; } } diff --git a/src/SharpCompress/Compressors/PPMd/I1/PpmState.cs b/src/SharpCompress/Compressors/PPMd/I1/PpmState.cs index de88fea1..6ed4f9a5 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/PpmState.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/PpmState.cs @@ -1,206 +1,204 @@ #nullable disable -namespace SharpCompress.Compressors.PPMd.I1 +namespace SharpCompress.Compressors.PPMd.I1; + +/// +/// PPM state. +/// +/// +/// +/// This must be a structure rather than a class because several places in the associated code assume that +/// is a value type (meaning that assignment creates a completely new copy of the +/// instance rather than just copying a reference to the same instance). +/// +/// +/// Note that is a field rather than a property for performance reasons. +/// +/// +internal struct PpmState { + public uint _address; + public byte[] _memory; + public static readonly PpmState ZERO = new PpmState(0, null); + public const int SIZE = 6; + /// - /// PPM state. + /// Initializes a new instance of the structure. /// - /// - /// - /// This must be a structure rather than a class because several places in the associated code assume that - /// is a value type (meaning that assignment creates a completely new copy of the - /// instance rather than just copying a reference to the same instance). - /// - /// - /// Note that is a field rather than a property for performance reasons. - /// - /// - internal struct PpmState + public PpmState(uint address, byte[] memory) { - public uint _address; - public byte[] _memory; - public static readonly PpmState ZERO = new PpmState(0, null); - public const int SIZE = 6; + _address = address; + _memory = memory; + } - /// - /// Initializes a new instance of the structure. - /// - public PpmState(uint address, byte[] memory) + /// + /// Gets or sets the symbol. + /// + public byte Symbol + { + get => _memory[_address]; + set => _memory[_address] = value; + } + + /// + /// Gets or sets the frequency. + /// + public byte Frequency + { + get => _memory[_address + 1]; + set => _memory[_address + 1] = value; + } + + /// + /// Gets or sets the successor. + /// + public Model.PpmContext Successor + { + get => + new Model.PpmContext( + _memory[_address + 2] + | (((uint)_memory[_address + 3]) << 8) + | (((uint)_memory[_address + 4]) << 16) + | (((uint)_memory[_address + 5]) << 24), + _memory + ); + set { - _address = address; - _memory = memory; - } - - /// - /// Gets or sets the symbol. - /// - public byte Symbol - { - get => _memory[_address]; - set => _memory[_address] = value; - } - - /// - /// Gets or sets the frequency. - /// - public byte Frequency - { - get => _memory[_address + 1]; - set => _memory[_address + 1] = value; - } - - /// - /// Gets or sets the successor. - /// - public Model.PpmContext Successor - { - get => - new Model.PpmContext( - _memory[_address + 2] - | ((uint)_memory[_address + 3]) << 8 - | ((uint)_memory[_address + 4]) << 16 - | ((uint)_memory[_address + 5]) << 24, - _memory - ); - set - { - _memory[_address + 2] = (byte)value._address; - _memory[_address + 3] = (byte)(value._address >> 8); - _memory[_address + 4] = (byte)(value._address >> 16); - _memory[_address + 5] = (byte)(value._address >> 24); - } - } - - /// - /// Gets the at the relative to this - /// . - /// - /// - /// - public PpmState this[int offset] => new PpmState((uint)(_address + offset * SIZE), _memory); - - /// - /// Allow a pointer to be implicitly converted to a PPM state. - /// - /// - /// - public static implicit operator PpmState(Pointer pointer) - { - return new PpmState(pointer._address, pointer._memory); - } - - /// - /// Allow pointer-like addition on a PPM state. - /// - /// - /// - /// - public static PpmState operator +(PpmState state, int offset) - { - state._address = (uint)(state._address + offset * SIZE); - return state; - } - - /// - /// Allow pointer-like incrementing on a PPM state. - /// - /// - /// - public static PpmState operator ++(PpmState state) - { - state._address += SIZE; - return state; - } - - /// - /// Allow pointer-like subtraction on a PPM state. - /// - /// - /// - /// - public static PpmState operator -(PpmState state, int offset) - { - state._address = (uint)(state._address - offset * SIZE); - return state; - } - - /// - /// Allow pointer-like decrementing on a PPM state. - /// - /// - /// - public static PpmState operator --(PpmState state) - { - state._address -= SIZE; - return state; - } - - /// - /// Compare two PPM states. - /// - /// - /// - /// - public static bool operator <=(PpmState state1, PpmState state2) - { - return state1._address <= state2._address; - } - - /// - /// Compare two PPM states. - /// - /// - /// - /// - public static bool operator >=(PpmState state1, PpmState state2) - { - return state1._address >= state2._address; - } - - /// - /// Compare two PPM states. - /// - /// - /// - /// - public static bool operator ==(PpmState state1, PpmState state2) - { - return state1._address == state2._address; - } - - /// - /// Compare two PPM states. - /// - /// - /// - /// - public static bool operator !=(PpmState state1, PpmState state2) - { - return state1._address != state2._address; - } - - /// - /// Indicates whether this instance and a specified object are equal. - /// - /// true if obj and this instance are the same type and represent the same value; otherwise, false. - /// Another object to compare to. - public override bool Equals(object obj) - { - if (obj is PpmState) - { - PpmState state = (PpmState)obj; - return state._address == _address; - } - return base.Equals(obj); - } - - /// - /// Returns the hash code for this instance. - /// - /// A 32-bit signed integer that is the hash code for this instance. - public override int GetHashCode() - { - return _address.GetHashCode(); + _memory[_address + 2] = (byte)value._address; + _memory[_address + 3] = (byte)(value._address >> 8); + _memory[_address + 4] = (byte)(value._address >> 16); + _memory[_address + 5] = (byte)(value._address >> 24); } } + + /// + /// Gets the at the relative to this + /// . + /// + /// + /// + public PpmState this[int offset] => new PpmState((uint)(_address + (offset * SIZE)), _memory); + + /// + /// Allow a pointer to be implicitly converted to a PPM state. + /// + /// + /// + public static implicit operator PpmState(Pointer pointer) + { + return new PpmState(pointer._address, pointer._memory); + } + + /// + /// Allow pointer-like addition on a PPM state. + /// + /// + /// + /// + public static PpmState operator +(PpmState state, int offset) + { + state._address = (uint)(state._address + (offset * SIZE)); + return state; + } + + /// + /// Allow pointer-like incrementing on a PPM state. + /// + /// + /// + public static PpmState operator ++(PpmState state) + { + state._address += SIZE; + return state; + } + + /// + /// Allow pointer-like subtraction on a PPM state. + /// + /// + /// + /// + public static PpmState operator -(PpmState state, int offset) + { + state._address = (uint)(state._address - (offset * SIZE)); + return state; + } + + /// + /// Allow pointer-like decrementing on a PPM state. + /// + /// + /// + public static PpmState operator --(PpmState state) + { + state._address -= SIZE; + return state; + } + + /// + /// Compare two PPM states. + /// + /// + /// + /// + public static bool operator <=(PpmState state1, PpmState state2) + { + return state1._address <= state2._address; + } + + /// + /// Compare two PPM states. + /// + /// + /// + /// + public static bool operator >=(PpmState state1, PpmState state2) + { + return state1._address >= state2._address; + } + + /// + /// Compare two PPM states. + /// + /// + /// + /// + public static bool operator ==(PpmState state1, PpmState state2) + { + return state1._address == state2._address; + } + + /// + /// Compare two PPM states. + /// + /// + /// + /// + public static bool operator !=(PpmState state1, PpmState state2) + { + return state1._address != state2._address; + } + + /// + /// Indicates whether this instance and a specified object are equal. + /// + /// true if obj and this instance are the same type and represent the same value; otherwise, false. + /// Another object to compare to. + public override bool Equals(object obj) + { + if (obj is PpmState state) + { + return state._address == _address; + } + return base.Equals(obj); + } + + /// + /// Returns the hash code for this instance. + /// + /// A 32-bit signed integer that is the hash code for this instance. + public override int GetHashCode() + { + return _address.GetHashCode(); + } } diff --git a/src/SharpCompress/Compressors/PPMd/I1/See2Context.cs b/src/SharpCompress/Compressors/PPMd/I1/See2Context.cs index 68e09cca..bc52e432 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/See2Context.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/See2Context.cs @@ -4,52 +4,51 @@ #endregion -namespace SharpCompress.Compressors.PPMd.I1 +namespace SharpCompress.Compressors.PPMd.I1; + +/// +/// SEE2 (secondary escape estimation) contexts for PPM contexts with masked symbols. +/// +/// +/// +/// This must be a class rather than a structure because MakeEscapeFrequency returns a See2Context +/// instance from the see2Contexts array. The caller (for example, EncodeSymbol2) then updates the +/// returned See2Context instance and expects the updates to be reflected in the see2Contexts array. +/// This would not happen if this were a structure. +/// +/// +/// Note that in most cases fields are used rather than properties for performance reasons (for example, +/// is a field rather than a property). +/// +/// +internal class See2Context { - /// - /// SEE2 (secondary escape estimation) contexts for PPM contexts with masked symbols. - /// - /// - /// - /// This must be a class rather than a structure because MakeEscapeFrequency returns a See2Context - /// instance from the see2Contexts array. The caller (for example, EncodeSymbol2) then updates the - /// returned See2Context instance and expects the updates to be reflected in the see2Contexts array. - /// This would not happen if this were a structure. - /// - /// - /// Note that in most cases fields are used rather than properties for performance reasons (for example, - /// is a field rather than a property). - /// - /// - internal class See2Context + private const byte PERIOD_BIT_COUNT = 7; + + public ushort _summary; + public byte _shift; + public byte _count; + + public void Initialize(uint initialValue) { - private const byte PERIOD_BIT_COUNT = 7; + _shift = PERIOD_BIT_COUNT - 4; + _summary = (ushort)(initialValue << _shift); + _count = 7; + } - public ushort _summary; - public byte _shift; - public byte _count; + public uint Mean() + { + var value = (uint)(_summary >> _shift); + _summary = (ushort)(_summary - value); + return (uint)(value + ((value == 0) ? 1 : 0)); + } - public void Initialize(uint initialValue) + public void Update() + { + if (_shift < PERIOD_BIT_COUNT && --_count == 0) { - _shift = PERIOD_BIT_COUNT - 4; - _summary = (ushort)(initialValue << _shift); - _count = 7; - } - - public uint Mean() - { - uint value = (uint)(_summary >> _shift); - _summary = (ushort)(_summary - value); - return (uint)(value + ((value == 0) ? 1 : 0)); - } - - public void Update() - { - if (_shift < PERIOD_BIT_COUNT && --_count == 0) - { - _summary += _summary; - _count = (byte)(3 << _shift++); - } + _summary += _summary; + _count = (byte)(3 << _shift++); } } } diff --git a/src/SharpCompress/Compressors/PPMd/PpmdProperties.cs b/src/SharpCompress/Compressors/PPMd/PpmdProperties.cs index 326b6019..e75d88b8 100644 --- a/src/SharpCompress/Compressors/PPMd/PpmdProperties.cs +++ b/src/SharpCompress/Compressors/PPMd/PpmdProperties.cs @@ -1,83 +1,82 @@ -using System; +using System; using System.Buffers.Binary; using SharpCompress.Compressors.PPMd.I1; -namespace SharpCompress.Compressors.PPMd +namespace SharpCompress.Compressors.PPMd; + +public class PpmdProperties { - public class PpmdProperties + private int _allocatorSize; + internal Allocator? _allocator; + + public PpmdProperties() : this(16 << 20, 6) { } + + public PpmdProperties(int allocatorSize, int modelOrder) + : this(allocatorSize, modelOrder, ModelRestorationMethod.Restart) { } + + internal PpmdProperties( + int allocatorSize, + int modelOrder, + ModelRestorationMethod modelRestorationMethod + ) { - private int _allocatorSize; - internal Allocator? _allocator; + AllocatorSize = allocatorSize; + ModelOrder = modelOrder; + RestorationMethod = modelRestorationMethod; + } - public PpmdProperties() : this(16 << 20, 6) { } + public int ModelOrder { get; } + public PpmdVersion Version { get; } = PpmdVersion.I1; + internal ModelRestorationMethod RestorationMethod { get; } - public PpmdProperties(int allocatorSize, int modelOrder) - : this(allocatorSize, modelOrder, ModelRestorationMethod.Restart) { } + public PpmdProperties(byte[] properties) : this(properties.AsSpan()) { } - internal PpmdProperties( - int allocatorSize, - int modelOrder, - ModelRestorationMethod modelRestorationMethod - ) + public PpmdProperties(ReadOnlySpan properties) + { + if (properties.Length == 2) { - AllocatorSize = allocatorSize; - ModelOrder = modelOrder; - RestorationMethod = modelRestorationMethod; + var props = BinaryPrimitives.ReadUInt16LittleEndian(properties); + AllocatorSize = (((props >> 4) & 0xff) + 1) << 20; + ModelOrder = (props & 0x0f) + 1; + RestorationMethod = (ModelRestorationMethod)(props >> 12); } - - public int ModelOrder { get; } - public PpmdVersion Version { get; } = PpmdVersion.I1; - internal ModelRestorationMethod RestorationMethod { get; } - - public PpmdProperties(byte[] properties) : this(properties.AsSpan()) { } - - public PpmdProperties(ReadOnlySpan properties) + else if (properties.Length == 5) { - if (properties.Length == 2) - { - ushort props = BinaryPrimitives.ReadUInt16LittleEndian(properties); - AllocatorSize = (((props >> 4) & 0xff) + 1) << 20; - ModelOrder = (props & 0x0f) + 1; - RestorationMethod = (ModelRestorationMethod)(props >> 12); - } - else if (properties.Length == 5) - { - Version = PpmdVersion.H7Z; - AllocatorSize = BinaryPrimitives.ReadInt32LittleEndian(properties.Slice(1)); - ModelOrder = properties[0]; - } + Version = PpmdVersion.H7Z; + AllocatorSize = BinaryPrimitives.ReadInt32LittleEndian(properties.Slice(1)); + ModelOrder = properties[0]; } + } - public int AllocatorSize + public int AllocatorSize + { + get => _allocatorSize; + set { - get => _allocatorSize; - set + _allocatorSize = value; + if (Version == PpmdVersion.I1) { - _allocatorSize = value; - if (Version == PpmdVersion.I1) - { - _allocator ??= new Allocator(); + _allocator ??= new Allocator(); - _allocator.Start(_allocatorSize); - } - } - } - - public byte[] Properties - { - get - { - byte[] bytes = new byte[2]; - BinaryPrimitives.WriteUInt16LittleEndian( - bytes, - (ushort)( - (ModelOrder - 1) - + (((AllocatorSize >> 20) - 1) << 4) - + ((ushort)RestorationMethod << 12) - ) - ); - return bytes; + _allocator.Start(_allocatorSize); } } } + + public byte[] Properties + { + get + { + var bytes = new byte[2]; + BinaryPrimitives.WriteUInt16LittleEndian( + bytes, + (ushort)( + (ModelOrder - 1) + + (((AllocatorSize >> 20) - 1) << 4) + + ((ushort)RestorationMethod << 12) + ) + ); + return bytes; + } + } } diff --git a/src/SharpCompress/Compressors/PPMd/PpmdStream.cs b/src/SharpCompress/Compressors/PPMd/PpmdStream.cs index 4e48d71a..43c9757f 100644 --- a/src/SharpCompress/Compressors/PPMd/PpmdStream.cs +++ b/src/SharpCompress/Compressors/PPMd/PpmdStream.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable using System; using System.IO; @@ -6,141 +6,140 @@ using SharpCompress.Compressors.LZMA.RangeCoder; using SharpCompress.Compressors.PPMd.H; using SharpCompress.Compressors.PPMd.I1; -namespace SharpCompress.Compressors.PPMd +namespace SharpCompress.Compressors.PPMd; + +public class PpmdStream : Stream { - public class PpmdStream : Stream + private readonly PpmdProperties _properties; + private readonly Stream _stream; + private readonly bool _compress; + private readonly Model _model; + private readonly ModelPpm _modelH; + private readonly Decoder _decoder; + private long _position; + private bool _isDisposed; + + public PpmdStream(PpmdProperties properties, Stream stream, bool compress) { - private readonly PpmdProperties _properties; - private readonly Stream _stream; - private readonly bool _compress; - private readonly Model _model; - private readonly ModelPpm _modelH; - private readonly Decoder _decoder; - private long _position; - private bool _isDisposed; + _properties = properties; + _stream = stream; + _compress = compress; - public PpmdStream(PpmdProperties properties, Stream stream, bool compress) + if (properties.Version == PpmdVersion.I1) { - _properties = properties; - _stream = stream; - _compress = compress; - - if (properties.Version == PpmdVersion.I1) + _model = new Model(); + if (compress) { - _model = new Model(); - if (compress) - { - _model.EncodeStart(properties); - } - else - { - _model.DecodeStart(stream, properties); - } + _model.EncodeStart(properties); } - if (properties.Version == PpmdVersion.H) + else { - _modelH = new ModelPpm(); - if (compress) - { - throw new NotImplementedException(); - } - _modelH.DecodeInit(stream, properties.ModelOrder, properties.AllocatorSize); - } - if (properties.Version == PpmdVersion.H7Z) - { - _modelH = new ModelPpm(); - if (compress) - { - throw new NotImplementedException(); - } - _modelH.DecodeInit(null, properties.ModelOrder, properties.AllocatorSize); - _decoder = new Decoder(); - _decoder.Init(stream); + _model.DecodeStart(stream, properties); } } - - public override bool CanRead => !_compress; - - public override bool CanSeek => false; - - public override bool CanWrite => _compress; - - public override void Flush() { } - - protected override void Dispose(bool isDisposing) + if (properties.Version == PpmdVersion.H) { - if (_isDisposed) + _modelH = new ModelPpm(); + if (compress) { - return; + throw new NotImplementedException(); } - _isDisposed = true; - if (isDisposing) - { - if (_compress) - { - _model.EncodeBlock(_stream, new MemoryStream(), true); - } - } - base.Dispose(isDisposing); + _modelH.DecodeInit(stream, properties.ModelOrder, properties.AllocatorSize); } - - public override long Length => throw new NotSupportedException(); - - public override long Position + if (properties.Version == PpmdVersion.H7Z) { - get => _position; - set => throw new NotSupportedException(); + _modelH = new ModelPpm(); + if (compress) + { + throw new NotImplementedException(); + } + _modelH.DecodeInit(null, properties.ModelOrder, properties.AllocatorSize); + _decoder = new Decoder(); + _decoder.Init(stream); } + } - public override int Read(byte[] buffer, int offset, int count) + public override bool CanRead => !_compress; + + public override bool CanSeek => false; + + public override bool CanWrite => _compress; + + public override void Flush() { } + + protected override void Dispose(bool isDisposing) + { + if (_isDisposed) + { + return; + } + _isDisposed = true; + if (isDisposing) { if (_compress) { - return 0; + _model.EncodeBlock(_stream, new MemoryStream(), true); } - int size = 0; - if (_properties.Version == PpmdVersion.I1) - { - size = _model.DecodeBlock(_stream, buffer, offset, count); - } - if (_properties.Version == PpmdVersion.H) - { - int c; - while (size < count && (c = _modelH.DecodeChar()) >= 0) - { - buffer[offset++] = (byte)c; - size++; - } - } - if (_properties.Version == PpmdVersion.H7Z) - { - int c; - while (size < count && (c = _modelH.DecodeChar(_decoder)) >= 0) - { - buffer[offset++] = (byte)c; - size++; - } - } - _position += size; - return size; } + base.Dispose(isDisposing); + } - public override long Seek(long offset, SeekOrigin origin) + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => _position; + set => throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (_compress) { - throw new NotSupportedException(); + return 0; } - - public override void SetLength(long value) + var size = 0; + if (_properties.Version == PpmdVersion.I1) { - throw new NotSupportedException(); + size = _model.DecodeBlock(_stream, buffer, offset, count); } - - public override void Write(byte[] buffer, int offset, int count) + if (_properties.Version == PpmdVersion.H) { - if (_compress) + int c; + while (size < count && (c = _modelH.DecodeChar()) >= 0) { - _model.EncodeBlock(_stream, new MemoryStream(buffer, offset, count), false); + buffer[offset++] = (byte)c; + size++; } } + if (_properties.Version == PpmdVersion.H7Z) + { + int c; + while (size < count && (c = _modelH.DecodeChar(_decoder)) >= 0) + { + buffer[offset++] = (byte)c; + size++; + } + } + _position += size; + return size; + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + if (_compress) + { + _model.EncodeBlock(_stream, new MemoryStream(buffer, offset, count), false); + } } } diff --git a/src/SharpCompress/Compressors/PPMd/PpmdVersion.cs b/src/SharpCompress/Compressors/PPMd/PpmdVersion.cs index cd0d6f46..791034ac 100644 --- a/src/SharpCompress/Compressors/PPMd/PpmdVersion.cs +++ b/src/SharpCompress/Compressors/PPMd/PpmdVersion.cs @@ -1,9 +1,8 @@ -namespace SharpCompress.Compressors.PPMd +namespace SharpCompress.Compressors.PPMd; + +public enum PpmdVersion { - public enum PpmdVersion - { - H, - H7Z, - I1 - } + H, + H7Z, + I1 } diff --git a/src/SharpCompress/Compressors/Rar/IRarUnpack.cs b/src/SharpCompress/Compressors/Rar/IRarUnpack.cs index 0cc86a78..651e2c8a 100644 --- a/src/SharpCompress/Compressors/Rar/IRarUnpack.cs +++ b/src/SharpCompress/Compressors/Rar/IRarUnpack.cs @@ -1,18 +1,17 @@ -using System.IO; +using System.IO; using SharpCompress.Common.Rar.Headers; -namespace SharpCompress.Compressors.Rar +namespace SharpCompress.Compressors.Rar; + +internal interface IRarUnpack { - internal interface IRarUnpack - { - void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream); - void DoUnpack(); + void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream); + void DoUnpack(); - // eg u/i pause/resume button - bool Suspended { get; set; } + // eg u/i pause/resume button + bool Suspended { get; set; } - long DestSize { get; } - int Char { get; } - int PpmEscChar { get; set; } - } + long DestSize { get; } + int Char { get; } + int PpmEscChar { get; set; } } diff --git a/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs b/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs index db406df9..11a3f515 100644 --- a/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs +++ b/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs @@ -6,156 +6,155 @@ using System.IO; using SharpCompress.Common; using SharpCompress.Common.Rar; -namespace SharpCompress.Compressors.Rar +namespace SharpCompress.Compressors.Rar; + +internal sealed class MultiVolumeReadOnlyStream : Stream { - internal sealed class MultiVolumeReadOnlyStream : Stream + private long currentPosition; + private long maxPosition; + + private IEnumerator filePartEnumerator; + private Stream currentStream; + + private readonly IExtractionListener streamListener; + + private long currentPartTotalReadBytes; + private long currentEntryTotalReadBytes; + + internal MultiVolumeReadOnlyStream( + IEnumerable parts, + IExtractionListener streamListener + ) { - private long currentPosition; - private long maxPosition; + this.streamListener = streamListener; - private IEnumerator filePartEnumerator; - private Stream currentStream; + filePartEnumerator = parts.GetEnumerator(); + filePartEnumerator.MoveNext(); + InitializeNextFilePart(); + } - private readonly IExtractionListener streamListener; - - private long currentPartTotalReadBytes; - private long currentEntryTotalReadBytes; - - internal MultiVolumeReadOnlyStream( - IEnumerable parts, - IExtractionListener streamListener - ) + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) { - this.streamListener = streamListener; - - filePartEnumerator = parts.GetEnumerator(); - filePartEnumerator.MoveNext(); - InitializeNextFilePart(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) + if (filePartEnumerator != null) { - if (filePartEnumerator != null) - { - filePartEnumerator.Dispose(); - filePartEnumerator = null; - } - currentStream = null; + filePartEnumerator.Dispose(); + filePartEnumerator = null; } - } - - private void InitializeNextFilePart() - { - maxPosition = filePartEnumerator.Current.FileHeader.CompressedSize; - currentPosition = 0; - currentStream = filePartEnumerator.Current.GetCompressedStream(); - - currentPartTotalReadBytes = 0; - - CurrentCrc = filePartEnumerator.Current.FileHeader.FileCrc; - - streamListener.FireFilePartExtractionBegin( - filePartEnumerator.Current.FilePartName, - filePartEnumerator.Current.FileHeader.CompressedSize, - filePartEnumerator.Current.FileHeader.UncompressedSize - ); - } - - public override int Read(byte[] buffer, int offset, int count) - { - int totalRead = 0; - int currentOffset = offset; - int currentCount = count; - while (currentCount > 0) - { - int readSize = currentCount; - if (currentCount > maxPosition - currentPosition) - { - readSize = (int)(maxPosition - currentPosition); - } - - int read = currentStream.Read(buffer, currentOffset, readSize); - if (read < 0) - { - throw new EndOfStreamException(); - } - - currentPosition += read; - currentOffset += read; - currentCount -= read; - totalRead += read; - if ( - ((maxPosition - currentPosition) == 0) - && filePartEnumerator.Current.FileHeader.IsSplitAfter - ) - { - if (filePartEnumerator.Current.FileHeader.R4Salt != null) - { - throw new InvalidFormatException( - "Sharpcompress currently does not support multi-volume decryption." - ); - } - string fileName = filePartEnumerator.Current.FileHeader.FileName; - if (!filePartEnumerator.MoveNext()) - { - throw new InvalidFormatException( - "Multi-part rar file is incomplete. Entry expects a new volume: " - + fileName - ); - } - InitializeNextFilePart(); - } - else - { - break; - } - } - currentPartTotalReadBytes += totalRead; - currentEntryTotalReadBytes += totalRead; - streamListener.FireCompressedBytesRead( - currentPartTotalReadBytes, - currentEntryTotalReadBytes - ); - return totalRead; - } - - public override bool CanRead => true; - - public override bool CanSeek => false; - - public override bool CanWrite => false; - - public uint CurrentCrc { get; private set; } - - public override void Flush() - { - throw new NotSupportedException(); - } - - public override long Length => throw new NotSupportedException(); - - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); + currentStream = null; } } + + private void InitializeNextFilePart() + { + maxPosition = filePartEnumerator.Current.FileHeader.CompressedSize; + currentPosition = 0; + currentStream = filePartEnumerator.Current.GetCompressedStream(); + + currentPartTotalReadBytes = 0; + + CurrentCrc = filePartEnumerator.Current.FileHeader.FileCrc; + + streamListener.FireFilePartExtractionBegin( + filePartEnumerator.Current.FilePartName, + filePartEnumerator.Current.FileHeader.CompressedSize, + filePartEnumerator.Current.FileHeader.UncompressedSize + ); + } + + public override int Read(byte[] buffer, int offset, int count) + { + var totalRead = 0; + var currentOffset = offset; + var currentCount = count; + while (currentCount > 0) + { + var readSize = currentCount; + if (currentCount > maxPosition - currentPosition) + { + readSize = (int)(maxPosition - currentPosition); + } + + var read = currentStream.Read(buffer, currentOffset, readSize); + if (read < 0) + { + throw new EndOfStreamException(); + } + + currentPosition += read; + currentOffset += read; + currentCount -= read; + totalRead += read; + if ( + ((maxPosition - currentPosition) == 0) + && filePartEnumerator.Current.FileHeader.IsSplitAfter + ) + { + if (filePartEnumerator.Current.FileHeader.R4Salt != null) + { + throw new InvalidFormatException( + "Sharpcompress currently does not support multi-volume decryption." + ); + } + var fileName = filePartEnumerator.Current.FileHeader.FileName; + if (!filePartEnumerator.MoveNext()) + { + throw new InvalidFormatException( + "Multi-part rar file is incomplete. Entry expects a new volume: " + + fileName + ); + } + InitializeNextFilePart(); + } + else + { + break; + } + } + currentPartTotalReadBytes += totalRead; + currentEntryTotalReadBytes += totalRead; + streamListener.FireCompressedBytesRead( + currentPartTotalReadBytes, + currentEntryTotalReadBytes + ); + return totalRead; + } + + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public uint CurrentCrc { get; private set; } + + public override void Flush() + { + throw new NotSupportedException(); + } + + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } } diff --git a/src/SharpCompress/Compressors/Rar/RarCRC.cs b/src/SharpCompress/Compressors/Rar/RarCRC.cs index a7e565a4..75fd8187 100644 --- a/src/SharpCompress/Compressors/Rar/RarCRC.cs +++ b/src/SharpCompress/Compressors/Rar/RarCRC.cs @@ -1,48 +1,47 @@ using System; -namespace SharpCompress.Compressors.Rar +namespace SharpCompress.Compressors.Rar; + +internal static class RarCRC { - internal static class RarCRC + private static readonly uint[] crcTab; + + public static uint CheckCrc(uint startCrc, byte b) { - private static readonly uint[] crcTab; + return (crcTab[((int)startCrc ^ b) & 0xff] ^ (startCrc >> 8)); + } - public static uint CheckCrc(uint startCrc, byte b) + public static uint CheckCrc(uint startCrc, byte[] data, int offset, int count) + { + var size = Math.Min(data.Length - offset, count); + + for (var i = 0; i < size; i++) { - return (crcTab[((int)((int)startCrc ^ (int)b)) & 0xff] ^ (startCrc >> 8)); + startCrc = (crcTab[((int)startCrc ^ data[offset + i]) & 0xff] ^ (startCrc >> 8)); } + return (startCrc); + } - public static uint CheckCrc(uint startCrc, byte[] data, int offset, int count) + static RarCRC() + { { - int size = Math.Min(data.Length - offset, count); - - for (int i = 0; i < size; i++) + crcTab = new uint[256]; + for (uint i = 0; i < 256; i++) { - startCrc = (crcTab[((int)startCrc ^ data[offset + i]) & 0xff] ^ (startCrc >> 8)); - } - return (startCrc); - } - - static RarCRC() - { - { - crcTab = new uint[256]; - for (uint i = 0; i < 256; i++) + var c = i; + for (var j = 0; j < 8; j++) { - uint c = i; - for (int j = 0; j < 8; j++) + if ((c & 1) != 0) { - if ((c & 1) != 0) - { - c = c >> 1; - c ^= 0xEDB88320; - } - else - { - c = c >> 1; - } + c >>= 1; + c ^= 0xEDB88320; + } + else + { + c >>= 1; } - crcTab[i] = c; } + crcTab[i] = c; } } } diff --git a/src/SharpCompress/Compressors/Rar/RarCrcStream.cs b/src/SharpCompress/Compressors/Rar/RarCrcStream.cs index 120d8c3e..569799b8 100644 --- a/src/SharpCompress/Compressors/Rar/RarCrcStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarCrcStream.cs @@ -1,47 +1,46 @@ using SharpCompress.Common; using SharpCompress.Common.Rar.Headers; -namespace SharpCompress.Compressors.Rar +namespace SharpCompress.Compressors.Rar; + +internal class RarCrcStream : RarStream { - internal class RarCrcStream : RarStream + private readonly MultiVolumeReadOnlyStream readStream; + private uint currentCrc; + + public RarCrcStream( + IRarUnpack unpack, + FileHeader fileHeader, + MultiVolumeReadOnlyStream readStream + ) : base(unpack, fileHeader, readStream) { - private readonly MultiVolumeReadOnlyStream readStream; - private uint currentCrc; + this.readStream = readStream; + ResetCrc(); + } - public RarCrcStream( - IRarUnpack unpack, - FileHeader fileHeader, - MultiVolumeReadOnlyStream readStream - ) : base(unpack, fileHeader, readStream) + public uint GetCrc() + { + return ~currentCrc; + } + + public void ResetCrc() + { + currentCrc = 0xffffffff; + } + + public override int Read(byte[] buffer, int offset, int count) + { + var result = base.Read(buffer, offset, count); + if (result != 0) { - this.readStream = readStream; - ResetCrc(); + currentCrc = RarCRC.CheckCrc(currentCrc, buffer, offset, result); + } + else if (GetCrc() != readStream.CurrentCrc && count != 0) + { + // NOTE: we use the last FileHeader in a multipart volume to check CRC + throw new InvalidFormatException("file crc mismatch"); } - public uint GetCrc() - { - return ~currentCrc; - } - - public void ResetCrc() - { - currentCrc = 0xffffffff; - } - - public override int Read(byte[] buffer, int offset, int count) - { - var result = base.Read(buffer, offset, count); - if (result != 0) - { - currentCrc = RarCRC.CheckCrc(currentCrc, buffer, offset, result); - } - else if (GetCrc() != readStream.CurrentCrc && count != 0) - { - // NOTE: we use the last FileHeader in a multipart volume to check CRC - throw new InvalidFormatException("file crc mismatch"); - } - - return result; - } + return result; } } diff --git a/src/SharpCompress/Compressors/Rar/RarStream.cs b/src/SharpCompress/Compressors/Rar/RarStream.cs index 9c470fbd..7bbc1700 100644 --- a/src/SharpCompress/Compressors/Rar/RarStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarStream.cs @@ -4,138 +4,137 @@ using System; using System.IO; using SharpCompress.Common.Rar.Headers; -namespace SharpCompress.Compressors.Rar +namespace SharpCompress.Compressors.Rar; + +internal class RarStream : Stream { - internal class RarStream : Stream + private readonly IRarUnpack unpack; + private readonly FileHeader fileHeader; + private readonly Stream readStream; + + private bool fetch; + + private byte[] tmpBuffer = new byte[65536]; + private int tmpOffset; + private int tmpCount; + + private byte[] outBuffer; + private int outOffset; + private int outCount; + private int outTotal; + private bool isDisposed; + private long _position; + + public RarStream(IRarUnpack unpack, FileHeader fileHeader, Stream readStream) { - private readonly IRarUnpack unpack; - private readonly FileHeader fileHeader; - private readonly Stream readStream; + this.unpack = unpack; + this.fileHeader = fileHeader; + this.readStream = readStream; + fetch = true; + unpack.DoUnpack(fileHeader, readStream, this); + fetch = false; + _position = 0; + } - private bool fetch; - - private byte[] tmpBuffer = new byte[65536]; - private int tmpOffset; - private int tmpCount; - - private byte[] outBuffer; - private int outOffset; - private int outCount; - private int outTotal; - private bool isDisposed; - private long _position; - - public RarStream(IRarUnpack unpack, FileHeader fileHeader, Stream readStream) + protected override void Dispose(bool disposing) + { + if (!isDisposed) { - this.unpack = unpack; - this.fileHeader = fileHeader; - this.readStream = readStream; + isDisposed = true; + base.Dispose(disposing); + readStream.Dispose(); + } + } + + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override void Flush() { } + + public override long Length => fileHeader.UncompressedSize; + + //commented out code always returned the length of the file + public override long Position + { + get => _position; /* fileHeader.UncompressedSize - unpack.DestSize;*/ + set => throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + outTotal = 0; + if (tmpCount > 0) + { + var toCopy = tmpCount < count ? tmpCount : count; + Buffer.BlockCopy(tmpBuffer, tmpOffset, buffer, offset, toCopy); + tmpOffset += toCopy; + tmpCount -= toCopy; + offset += toCopy; + count -= toCopy; + outTotal += toCopy; + } + if (count > 0 && unpack.DestSize > 0) + { + outBuffer = buffer; + outOffset = offset; + outCount = count; fetch = true; - unpack.DoUnpack(fileHeader, readStream, this); + unpack.DoUnpack(); fetch = false; - _position = 0; } + _position += outTotal; + return outTotal; + } - protected override void Dispose(bool disposing) - { - if (!isDisposed) - { - isDisposed = true; - base.Dispose(disposing); - readStream.Dispose(); - } - } + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } - public override bool CanRead => true; + public override void SetLength(long value) + { + throw new NotSupportedException(); + } - public override bool CanSeek => false; - - public override bool CanWrite => false; - - public override void Flush() { } - - public override long Length => fileHeader.UncompressedSize; - - //commented out code always returned the length of the file - public override long Position - { - get => _position; /* fileHeader.UncompressedSize - unpack.DestSize;*/ - set => throw new NotSupportedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - outTotal = 0; - if (tmpCount > 0) - { - int toCopy = tmpCount < count ? tmpCount : count; - Buffer.BlockCopy(tmpBuffer, tmpOffset, buffer, offset, toCopy); - tmpOffset += toCopy; - tmpCount -= toCopy; - offset += toCopy; - count -= toCopy; - outTotal += toCopy; - } - if (count > 0 && unpack.DestSize > 0) - { - outBuffer = buffer; - outOffset = offset; - outCount = count; - fetch = true; - unpack.DoUnpack(); - fetch = false; - } - _position += (long)outTotal; - return outTotal; - } - - public override long Seek(long offset, SeekOrigin origin) + public override void Write(byte[] buffer, int offset, int count) + { + if (!fetch) { throw new NotSupportedException(); } - - public override void SetLength(long value) + if (outCount > 0) { - throw new NotSupportedException(); + var toCopy = outCount < count ? outCount : count; + Buffer.BlockCopy(buffer, offset, outBuffer, outOffset, toCopy); + outOffset += toCopy; + outCount -= toCopy; + offset += toCopy; + count -= toCopy; + outTotal += toCopy; } - - public override void Write(byte[] buffer, int offset, int count) + if (count > 0) { - if (!fetch) + if (tmpBuffer.Length < tmpCount + count) { - throw new NotSupportedException(); - } - if (outCount > 0) - { - int toCopy = outCount < count ? outCount : count; - Buffer.BlockCopy(buffer, offset, outBuffer, outOffset, toCopy); - outOffset += toCopy; - outCount -= toCopy; - offset += toCopy; - count -= toCopy; - outTotal += toCopy; - } - if (count > 0) - { - if (tmpBuffer.Length < tmpCount + count) - { - byte[] newBuffer = new byte[ - tmpBuffer.Length * 2 > tmpCount + count - ? tmpBuffer.Length * 2 - : tmpCount + count - ]; - Buffer.BlockCopy(tmpBuffer, 0, newBuffer, 0, tmpCount); - tmpBuffer = newBuffer; - } - Buffer.BlockCopy(buffer, offset, tmpBuffer, tmpCount, count); - tmpCount += count; - tmpOffset = 0; - unpack.Suspended = true; - } - else - { - unpack.Suspended = false; + var newBuffer = new byte[ + tmpBuffer.Length * 2 > tmpCount + count + ? tmpBuffer.Length * 2 + : tmpCount + count + ]; + Buffer.BlockCopy(tmpBuffer, 0, newBuffer, 0, tmpCount); + tmpBuffer = newBuffer; } + Buffer.BlockCopy(buffer, offset, tmpBuffer, tmpCount, count); + tmpCount += count; + tmpOffset = 0; + unpack.Suspended = true; + } + else + { + unpack.Suspended = false; } } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/AudioVariables.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/AudioVariables.cs index 3e7319f3..d53d777f 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/AudioVariables.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/AudioVariables.cs @@ -1,26 +1,25 @@ -namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode; + +internal class AudioVariables { - internal class AudioVariables + internal AudioVariables() { - internal AudioVariables() - { - Dif = new int[11]; - } - - internal int[] Dif { get; } - internal int ByteCount { get; set; } - internal int D1 { get; set; } - - internal int D2 { get; set; } - internal int D3 { get; set; } - internal int D4 { get; set; } - - internal int K1 { get; set; } - internal int K2 { get; set; } - internal int K3 { get; set; } - internal int K4 { get; set; } - internal int K5 { get; set; } - internal int LastChar { get; set; } - internal int LastDelta { get; set; } + Dif = new int[11]; } + + internal int[] Dif { get; } + internal int ByteCount { get; set; } + internal int D1 { get; set; } + + internal int D2 { get; set; } + internal int D3 { get; set; } + internal int D4 { get; set; } + + internal int K1 { get; set; } + internal int K2 { get; set; } + internal int K3 { get; set; } + internal int K4 { get; set; } + internal int K5 { get; set; } + internal int LastChar { get; set; } + internal int LastDelta { get; set; } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/BitDecode.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/BitDecode.cs index aa8ff4dd..d61ce17a 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/BitDecode.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/BitDecode.cs @@ -1,7 +1,6 @@ -namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode; + +internal class BitDecode : Decode { - internal class BitDecode : Decode - { - internal BitDecode() : base(new int[PackDef.BC]) { } - } + internal BitDecode() : base(new int[PackDef.BC]) { } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/CodeType.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/CodeType.cs index a0f31371..0ec7943d 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/CodeType.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/CodeType.cs @@ -1,15 +1,14 @@ -namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode; + +internal enum CodeType { - internal enum CodeType - { - CODE_HUFFMAN, - CODE_LZ, - CODE_LZ2, - CODE_REPEATLZ, - CODE_CACHELZ, - CODE_STARTFILE, - CODE_ENDFILE, - CODE_VM, - CODE_VMDATA - } + CODE_HUFFMAN, + CODE_LZ, + CODE_LZ2, + CODE_REPEATLZ, + CODE_CACHELZ, + CODE_STARTFILE, + CODE_ENDFILE, + CODE_VM, + CODE_VMDATA } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/Decode.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/Decode.cs index 7634dc7e..44ce0333 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/Decode.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/Decode.cs @@ -1,31 +1,30 @@ -namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode; + +internal class Decode { - internal class Decode + internal Decode() : this(new int[2]) { } + + protected Decode(int[] customDecodeNum) { - internal Decode() : this(new int[2]) { } - - protected Decode(int[] customDecodeNum) - { - DecodeLen = new int[16]; - DecodePos = new int[16]; - DecodeNum = customDecodeNum; - } - - /// returns the decode Length array - /// decodeLength - /// - internal int[] DecodeLen { get; } - - /// returns the decode num array - /// decodeNum - /// - internal int[] DecodeNum { get; } - - /// returns the decodePos array - /// decodePos - /// - internal int[] DecodePos { get; } - - internal int MaxNum { get; set; } + DecodeLen = new int[16]; + DecodePos = new int[16]; + DecodeNum = customDecodeNum; } + + /// returns the decode Length array + /// decodeLength + /// + internal int[] DecodeLen { get; } + + /// returns the decode num array + /// decodeNum + /// + internal int[] DecodeNum { get; } + + /// returns the decodePos array + /// decodePos + /// + internal int[] DecodePos { get; } + + internal int MaxNum { get; set; } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/DistDecode.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/DistDecode.cs index 9b49acc3..7b3c29b5 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/DistDecode.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/DistDecode.cs @@ -1,7 +1,6 @@ -namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode; + +internal class DistDecode : Decode { - internal class DistDecode : Decode - { - internal DistDecode() : base(new int[PackDef.DC]) { } - } + internal DistDecode() : base(new int[PackDef.DC]) { } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/FilterType.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/FilterType.cs index 1a6360dc..67e38c45 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/FilterType.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/FilterType.cs @@ -1,17 +1,16 @@ -namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode; + +internal enum FilterType : byte { - internal enum FilterType : byte - { - // These values must not be changed, because we use them directly - // in RAR5 compression and decompression code. - FILTER_DELTA = 0, - FILTER_E8, - FILTER_E8E9, - FILTER_ARM, - FILTER_AUDIO, - FILTER_RGB, - FILTER_ITANIUM, - FILTER_PPM, - FILTER_NONE - } + // These values must not be changed, because we use them directly + // in RAR5 compression and decompression code. + FILTER_DELTA = 0, + FILTER_E8, + FILTER_E8E9, + FILTER_ARM, + FILTER_AUDIO, + FILTER_RGB, + FILTER_ITANIUM, + FILTER_PPM, + FILTER_NONE } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/LitDecode.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/LitDecode.cs index 035e7ce4..b45eaf8d 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/LitDecode.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/LitDecode.cs @@ -1,7 +1,6 @@ -namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode; + +internal class LitDecode : Decode { - internal class LitDecode : Decode - { - internal LitDecode() : base(new int[PackDef.NC]) { } - } + internal LitDecode() : base(new int[PackDef.NC]) { } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/LowDistDecode.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/LowDistDecode.cs index d3f358e8..c438a3af 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/LowDistDecode.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/LowDistDecode.cs @@ -1,7 +1,6 @@ -namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode; + +internal class LowDistDecode : Decode { - internal class LowDistDecode : Decode - { - internal LowDistDecode() : base(new int[PackDef.LDC]) { } - } + internal LowDistDecode() : base(new int[PackDef.LDC]) { } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/MultDecode.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/MultDecode.cs index 6342ac1f..6ef48ceb 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/MultDecode.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/MultDecode.cs @@ -1,7 +1,6 @@ -namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode; + +internal class MultDecode : Decode { - internal class MultDecode : Decode - { - internal MultDecode() : base(new int[PackDef.MC20]) { } - } + internal MultDecode() : base(new int[PackDef.MC20]) { } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/PackDef.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/PackDef.cs index 00ee5bb6..bed8d3d0 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/PackDef.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/PackDef.cs @@ -1,49 +1,48 @@ -namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode; + +internal static class PackDef { - internal static class PackDef - { - // 20171217 NOTE: these contants are gone from unrar src code - // seems to be more dynamic - public const int MAXWINSIZE = 0x400000; - public const int MAXWINMASK = MAXWINSIZE - 1; + // 20171217 NOTE: these contants are gone from unrar src code + // seems to be more dynamic + public const int MAXWINSIZE = 0x400000; + public const int MAXWINMASK = MAXWINSIZE - 1; - public const uint MAX_LZ_MATCH = 0x1001; - public const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3. - public const int LOW_DIST_REP_COUNT = 16; + public const uint MAX_LZ_MATCH = 0x1001; + public const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3. + public const int LOW_DIST_REP_COUNT = 16; - public const int NC = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ - public const int DC = 60; - public const int LDC = 17; - public const int RC = 28; + public const int NC = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ + public const int DC = 60; + public const int LDC = 17; + public const int RC = 28; - // 20171217: NOTE: these constants seem to have been updated in the unrar src code - // at some unknown point. updating causes decompression failure, not sure why. - // public const int NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */ - // public const int DC = 64; - // public const int LDC = 16; - // public const int RC = 44; - public const int HUFF_TABLE_SIZE = NC + DC + RC + LDC; - public const int BC = 20; + // 20171217: NOTE: these constants seem to have been updated in the unrar src code + // at some unknown point. updating causes decompression failure, not sure why. + // public const int NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */ + // public const int DC = 64; + // public const int LDC = 16; + // public const int RC = 44; + public const int HUFF_TABLE_SIZE = NC + DC + RC + LDC; + public const int BC = 20; - public const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ - public const uint DC30 = 60; - public const uint LDC30 = 17; - public const uint RC30 = 28; - public const uint BC30 = 20; - public const uint HUFF_TABLE_SIZE30 = NC30 + DC30 + RC30 + LDC30; + public const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ + public const uint DC30 = 60; + public const uint LDC30 = 17; + public const uint RC30 = 28; + public const uint BC30 = 20; + public const uint HUFF_TABLE_SIZE30 = NC30 + DC30 + RC30 + LDC30; - public const int NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */ - public const int DC20 = 48; - public const int RC20 = 28; - public const int BC20 = 19; - public const int MC20 = 257; + public const int NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */ + public const int DC20 = 48; + public const int RC20 = 28; + public const int BC20 = 19; + public const int MC20 = 257; - // Largest alphabet size among all values listed above. - public const uint LARGEST_TABLE_SIZE = 306; + // Largest alphabet size among all values listed above. + public const uint LARGEST_TABLE_SIZE = 306; - //public enum { - // CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE, - // CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA - //} - } + //public enum { + // CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE, + // CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA + //} } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/RepDecode.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/RepDecode.cs index 648630e1..d13914b3 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/RepDecode.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/RepDecode.cs @@ -1,7 +1,6 @@ -namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode; + +internal class RepDecode : Decode { - internal class RepDecode : Decode - { - internal RepDecode() : base(new int[PackDef.RC]) { } - } + internal RepDecode() : base(new int[PackDef.RC]) { } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/PPM/BlockTypes.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/PPM/BlockTypes.cs index c25fae4e..3b4a9372 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/PPM/BlockTypes.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/PPM/BlockTypes.cs @@ -1,8 +1,7 @@ -namespace SharpCompress.Compressors.Rar.UnpackV1.PPM +namespace SharpCompress.Compressors.Rar.UnpackV1.PPM; + +internal enum BlockTypes { - internal enum BlockTypes - { - BLOCK_LZ = 0, - BLOCK_PPM = 1 - } + BLOCK_LZ = 0, + BLOCK_PPM = 1 } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs index 63dd4875..08ba47f2 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs @@ -10,1328 +10,1324 @@ using SharpCompress.Compressors.Rar.UnpackV1.Decode; using SharpCompress.Compressors.Rar.UnpackV1.PPM; using SharpCompress.Compressors.Rar.VM; -namespace SharpCompress.Compressors.Rar.UnpackV1 +namespace SharpCompress.Compressors.Rar.UnpackV1; + +internal sealed partial class Unpack : BitInput, IRarUnpack { - internal sealed partial class Unpack : BitInput, IRarUnpack + private readonly BitInput Inp; + + public Unpack() { - private readonly BitInput Inp; + // to ease in porting Unpack50.cs + Inp = this; + } - public Unpack() + public bool FileExtracted { get; private set; } + + public long DestSize + { + get => destUnpSize; + set { - // to ease in porting Unpack50.cs - Inp = this; + destUnpSize = value; + FileExtracted = false; } + } - public bool FileExtracted { get; private set; } + public bool Suspended + { + get => suspended; + set => suspended = value; + } - public long DestSize + public int Char + { + get { - get => destUnpSize; - set + if (inAddr > MAX_SIZE - 30) { - destUnpSize = value; - FileExtracted = false; + unpReadBuf(); } + return (InBuf[inAddr++] & 0xff); } + } - public bool Suspended + public int PpmEscChar { get; set; } + + private readonly ModelPpm ppm = new ModelPpm(); + + private readonly RarVM rarVM = new RarVM(); + + // Filters code, one entry per filter + private readonly List filters = new List(); + + // Filters stack, several entrances of same filter are possible + private readonly List prgStack = new List(); + + // lengths of preceding blocks, one length per filter. Used to reduce size + // required to write block length if lengths are repeating + private readonly List oldFilterLengths = new List(); + + private int lastFilter; + + private bool tablesRead; + + private readonly byte[] unpOldTable = new byte[PackDef.HUFF_TABLE_SIZE]; + + private BlockTypes unpBlockType; + + //private bool externalWindow; + + private long writtenFileSize; + + private bool ppmError; + + private int prevLowDist; + + private int lowDistRepCount; + + private static readonly int[] DBitLengthCounts = + { + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 14, + 0, + 12 + }; + + private FileHeader fileHeader; + + private void Init(byte[] window) + { + if (window is null) { - get => suspended; - set => suspended = value; + this.window = new byte[PackDef.MAXWINSIZE]; } - - public int Char + else { - get + this.window = window; + + //externalWindow = true; + } + inAddr = 0; + UnpInitData(false); + } + + public void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream) + { + destUnpSize = fileHeader.UncompressedSize; + this.fileHeader = fileHeader; + this.readStream = readStream; + this.writeStream = writeStream; + if (!fileHeader.IsSolid) + { + Init(null); + } + suspended = false; + DoUnpack(); + } + + public void DoUnpack() + { + if (fileHeader.CompressionMethod == 0) + { + UnstoreFile(); + return; + } + switch (fileHeader.CompressionAlgorithm) + { + case 15: // rar 1.5 compression + unpack15(fileHeader.IsSolid); + break; + + case 20: // rar 2.x compression + case 26: // files larger than 2GB + unpack20(fileHeader.IsSolid); + break; + + case 29: // rar 3.x compression + case 36: // alternative hash + Unpack29(fileHeader.IsSolid); + break; + + case 50: // rar 5.x compression + Unpack5(fileHeader.IsSolid); + break; + + default: + throw new InvalidFormatException( + "unknown rar compression version " + fileHeader.CompressionAlgorithm + ); + } + } + + private void UnstoreFile() + { + var buffer = new byte[0x10000]; + while (true) + { + var code = readStream.Read(buffer, 0, (int)Math.Min(buffer.Length, destUnpSize)); + if (code == 0 || code == -1) { - if (inAddr > MAX_SIZE - 30) - { - unpReadBuf(); - } - return (InBuf[inAddr++] & 0xff); + break; } - } - - public int PpmEscChar { get; set; } - - private readonly ModelPpm ppm = new ModelPpm(); - - private readonly RarVM rarVM = new RarVM(); - - // Filters code, one entry per filter - private readonly List filters = new List(); - - // Filters stack, several entrances of same filter are possible - private readonly List prgStack = new List(); - - // lengths of preceding blocks, one length per filter. Used to reduce size - // required to write block length if lengths are repeating - private readonly List oldFilterLengths = new List(); - - private int lastFilter; - - private bool tablesRead; - - private readonly byte[] unpOldTable = new byte[PackDef.HUFF_TABLE_SIZE]; - - private BlockTypes unpBlockType; - - //private bool externalWindow; - - private long writtenFileSize; - - private bool ppmError; - - private int prevLowDist; - - private int lowDistRepCount; - - private static readonly int[] DBitLengthCounts = - { - 4, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 14, - 0, - 12 - }; - - private FileHeader fileHeader; - - private void Init(byte[] window) - { - if (window is null) + code = code < destUnpSize ? code : (int)destUnpSize; + writeStream.Write(buffer, 0, code); + if (destUnpSize >= 0) { - this.window = new byte[PackDef.MAXWINSIZE]; + destUnpSize -= code; } - else + if (suspended) { - this.window = window; - - //externalWindow = true; - } - inAddr = 0; - UnpInitData(false); - } - - public void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream) - { - destUnpSize = fileHeader.UncompressedSize; - this.fileHeader = fileHeader; - this.readStream = readStream; - this.writeStream = writeStream; - if (!fileHeader.IsSolid) - { - Init(null); - } - suspended = false; - DoUnpack(); - } - - public void DoUnpack() - { - if (fileHeader.CompressionMethod == 0) - { - UnstoreFile(); return; } - switch (fileHeader.CompressionAlgorithm) + } + } + + private void Unpack29(bool solid) + { + var DDecode = new int[PackDef.DC]; + var DBits = new byte[PackDef.DC]; + + int Bits; + + if (DDecode[1] == 0) + { + int Dist = 0, + BitLength = 0, + Slot = 0; + for (var I = 0; I < DBitLengthCounts.Length; I++, BitLength++) { - case 15: // rar 1.5 compression - unpack15(fileHeader.IsSolid); - break; - - case 20: // rar 2.x compression - case 26: // files larger than 2GB - unpack20(fileHeader.IsSolid); - break; - - case 29: // rar 3.x compression - case 36: // alternative hash - Unpack29(fileHeader.IsSolid); - break; - - case 50: // rar 5.x compression - Unpack5(fileHeader.IsSolid); - break; - - default: - throw new InvalidFormatException( - "unknown rar compression version " + fileHeader.CompressionAlgorithm - ); + var count = DBitLengthCounts[I]; + for (var J = 0; J < count; J++, Slot++, Dist += (1 << BitLength)) + { + DDecode[Slot] = Dist; + DBits[Slot] = (byte)BitLength; + } } } - private void UnstoreFile() + FileExtracted = true; + + if (!suspended) { - byte[] buffer = new byte[0x10000]; - while (true) + UnpInitData(solid); + if (!unpReadBuf()) { - int code = readStream.Read(buffer, 0, (int)Math.Min(buffer.Length, destUnpSize)); - if (code == 0 || code == -1) + return; + } + if ((!solid || !tablesRead) && !ReadTables()) + { + return; + } + } + + if (ppmError) + { + return; + } + + while (true) + { + unpPtr &= PackDef.MAXWINMASK; + + if (inAddr > readBorder) + { + if (!unpReadBuf()) { break; } - code = code < destUnpSize ? code : (int)destUnpSize; - writeStream.Write(buffer, 0, code); - if (destUnpSize >= 0) + } + + // System.out.println(((wrPtr - unpPtr) & + // Compress.MAXWINMASK)+":"+wrPtr+":"+unpPtr); + if (((wrPtr - unpPtr) & PackDef.MAXWINMASK) < 260 && wrPtr != unpPtr) + { + UnpWriteBuf(); + if (destUnpSize <= 0) { - destUnpSize -= code; + return; } if (suspended) { + FileExtracted = false; return; } } - } - - private void Unpack29(bool solid) - { - int[] DDecode = new int[PackDef.DC]; - byte[] DBits = new byte[PackDef.DC]; - - int Bits; - - if (DDecode[1] == 0) + if (unpBlockType == BlockTypes.BLOCK_PPM) { - int Dist = 0, - BitLength = 0, - Slot = 0; - for (int I = 0; I < DBitLengthCounts.Length; I++, BitLength++) + var Ch = ppm.DecodeChar(); + if (Ch == -1) { - int count = DBitLengthCounts[I]; - for (int J = 0; J < count; J++, Slot++, Dist += (1 << BitLength)) - { - DDecode[Slot] = Dist; - DBits[Slot] = (byte)BitLength; - } + ppmError = true; + break; } - } - - FileExtracted = true; - - if (!suspended) - { - UnpInitData(solid); - if (!unpReadBuf()) + if (Ch == PpmEscChar) { - return; - } - if ((!solid || !tablesRead) && !ReadTables()) - { - return; - } - } - - if (ppmError) - { - return; - } - - while (true) - { - unpPtr &= PackDef.MAXWINMASK; - - if (inAddr > readBorder) - { - if (!unpReadBuf()) + var NextCh = ppm.DecodeChar(); + if (NextCh == 0) { - break; - } - } - - // System.out.println(((wrPtr - unpPtr) & - // Compress.MAXWINMASK)+":"+wrPtr+":"+unpPtr); - if (((wrPtr - unpPtr) & PackDef.MAXWINMASK) < 260 && wrPtr != unpPtr) - { - UnpWriteBuf(); - if (destUnpSize <= 0) - { - return; - } - if (suspended) - { - FileExtracted = false; - return; - } - } - if (unpBlockType == BlockTypes.BLOCK_PPM) - { - int Ch = ppm.DecodeChar(); - if (Ch == -1) - { - ppmError = true; - break; - } - if (Ch == PpmEscChar) - { - int NextCh = ppm.DecodeChar(); - if (NextCh == 0) - { - if (!ReadTables()) - { - break; - } - continue; - } - if (NextCh == 2 || NextCh == -1) + if (!ReadTables()) { break; } - if (NextCh == 3) + continue; + } + if (NextCh == 2 || NextCh == -1) + { + break; + } + if (NextCh == 3) + { + if (!ReadVMCodePPM()) { - if (!ReadVMCodePPM()) - { - break; - } - continue; + break; } - if (NextCh == 4) + continue; + } + if (NextCh == 4) + { + int Distance = 0, + Length = 0; + var failed = false; + for (var I = 0; I < 4 && !failed; I++) { - int Distance = 0, - Length = 0; - bool failed = false; - for (int I = 0; I < 4 && !failed; I++) + var ch = ppm.DecodeChar(); + if (ch == -1) { - int ch = ppm.DecodeChar(); - if (ch == -1) + failed = true; + } + else + { + if (I == 3) { - failed = true; + // Bug fixed + Length = ch & 0xff; } else { - if (I == 3) - { - // Bug fixed - Length = ch & 0xff; - } - else - { - // Bug fixed - Distance = (Distance << 8) + (ch & 0xff); - } + // Bug fixed + Distance = (Distance << 8) + (ch & 0xff); } } - if (failed) - { - break; - } - CopyString(Length + 32, Distance + 2); - continue; } - if (NextCh == 5) + if (failed) { - int Length = ppm.DecodeChar(); - if (Length == -1) - { - break; - } - CopyString(Length + 4, 1); - continue; + break; } + CopyString(Length + 32, Distance + 2); + continue; } - window[unpPtr++] = (byte)Ch; - continue; + if (NextCh == 5) + { + var Length = ppm.DecodeChar(); + if (Length == -1) + { + break; + } + CopyString(Length + 4, 1); + continue; + } + } + window[unpPtr++] = (byte)Ch; + continue; + } + + var Number = this.decodeNumber(LD); + if (Number < 256) + { + window[unpPtr++] = (byte)Number; + continue; + } + if (Number >= 271) + { + var Length = LDecode[Number -= 271] + 3; + if ((Bits = LBits[Number]) > 0) + { + Length += Utility.URShift(GetBits(), (16 - Bits)); + AddBits(Bits); } - int Number = this.decodeNumber(LD); - if (Number < 256) + var DistNumber = this.decodeNumber(DD); + var Distance = DDecode[DistNumber] + 1; + if ((Bits = DBits[DistNumber]) > 0) { - window[unpPtr++] = (byte)Number; - continue; - } - if (Number >= 271) - { - int Length = LDecode[Number -= 271] + 3; - if ((Bits = LBits[Number]) > 0) + if (DistNumber > 9) { - Length += Utility.URShift(GetBits(), (16 - Bits)); - AddBits(Bits); - } - - int DistNumber = this.decodeNumber(DD); - int Distance = DDecode[DistNumber] + 1; - if ((Bits = DBits[DistNumber]) > 0) - { - if (DistNumber > 9) + if (Bits > 4) { - if (Bits > 4) + Distance += ((Utility.URShift(GetBits(), (20 - Bits))) << 4); + AddBits(Bits - 4); + } + if (lowDistRepCount > 0) + { + lowDistRepCount--; + Distance += prevLowDist; + } + else + { + var LowDist = this.decodeNumber(LDD); + if (LowDist == 16) { - Distance += ((Utility.URShift(GetBits(), (20 - Bits))) << 4); - AddBits(Bits - 4); - } - if (lowDistRepCount > 0) - { - lowDistRepCount--; + lowDistRepCount = PackDef.LOW_DIST_REP_COUNT - 1; Distance += prevLowDist; } else { - int LowDist = this.decodeNumber(LDD); - if (LowDist == 16) - { - lowDistRepCount = PackDef.LOW_DIST_REP_COUNT - 1; - Distance += prevLowDist; - } - else - { - Distance += LowDist; - prevLowDist = LowDist; - } + Distance += LowDist; + prevLowDist = LowDist; } } - else - { - Distance += Utility.URShift(GetBits(), (16 - Bits)); - AddBits(Bits); - } } - - if (Distance >= 0x2000) - { - Length++; - if (Distance >= 0x40000L) - { - Length++; - } - } - - InsertOldDist(Distance); - InsertLastMatch(Length, Distance); - - CopyString(Length, Distance); - continue; - } - if (Number == 256) - { - if (!ReadEndOfBlock()) - { - break; - } - continue; - } - if (Number == 257) - { - if (!ReadVMCode()) - { - break; - } - continue; - } - if (Number == 258) - { - if (lastLength != 0) - { - CopyString(lastLength, lastDist); - } - continue; - } - if (Number < 263) - { - int DistNum = Number - 259; - int Distance = oldDist[DistNum]; - for (int I = DistNum; I > 0; I--) - { - oldDist[I] = oldDist[I - 1]; - } - oldDist[0] = Distance; - - int LengthNumber = this.decodeNumber(RD); - int Length = LDecode[LengthNumber] + 2; - if ((Bits = LBits[LengthNumber]) > 0) - { - Length += Utility.URShift(GetBits(), (16 - Bits)); - AddBits(Bits); - } - InsertLastMatch(Length, Distance); - CopyString(Length, Distance); - continue; - } - if (Number < 272) - { - int Distance = SDDecode[Number -= 263] + 1; - if ((Bits = SDBits[Number]) > 0) + else { Distance += Utility.URShift(GetBits(), (16 - Bits)); AddBits(Bits); } - InsertOldDist(Distance); - InsertLastMatch(2, Distance); - CopyString(2, Distance); } - } - UnpWriteBuf(); - } - private void UnpWriteBuf() - { - int WrittenBorder = wrPtr; - int WriteSize = (unpPtr - WrittenBorder) & PackDef.MAXWINMASK; - for (int I = 0; I < prgStack.Count; I++) - { - UnpackFilter flt = prgStack[I]; - if (flt is null) + if (Distance >= 0x2000) { - continue; - } - if (flt.NextWindow) - { - flt.NextWindow = false; // ->NextWindow=false; - continue; - } - int BlockStart = flt.BlockStart; // ->BlockStart; - int BlockLength = flt.BlockLength; // ->BlockLength; - if (((BlockStart - WrittenBorder) & PackDef.MAXWINMASK) < WriteSize) - { - if (WrittenBorder != BlockStart) + Length++; + if (Distance >= 0x40000L) { - UnpWriteArea(WrittenBorder, BlockStart); - WrittenBorder = BlockStart; - WriteSize = (unpPtr - WrittenBorder) & PackDef.MAXWINMASK; + Length++; } - if (BlockLength <= WriteSize) + } + + InsertOldDist(Distance); + InsertLastMatch(Length, Distance); + + CopyString(Length, Distance); + continue; + } + if (Number == 256) + { + if (!ReadEndOfBlock()) + { + break; + } + continue; + } + if (Number == 257) + { + if (!ReadVMCode()) + { + break; + } + continue; + } + if (Number == 258) + { + if (lastLength != 0) + { + CopyString(lastLength, lastDist); + } + continue; + } + if (Number < 263) + { + var DistNum = Number - 259; + var Distance = oldDist[DistNum]; + for (var I = DistNum; I > 0; I--) + { + oldDist[I] = oldDist[I - 1]; + } + oldDist[0] = Distance; + + var LengthNumber = this.decodeNumber(RD); + var Length = LDecode[LengthNumber] + 2; + if ((Bits = LBits[LengthNumber]) > 0) + { + Length += Utility.URShift(GetBits(), (16 - Bits)); + AddBits(Bits); + } + InsertLastMatch(Length, Distance); + CopyString(Length, Distance); + continue; + } + if (Number < 272) + { + var Distance = SDDecode[Number -= 263] + 1; + if ((Bits = SDBits[Number]) > 0) + { + Distance += Utility.URShift(GetBits(), (16 - Bits)); + AddBits(Bits); + } + InsertOldDist(Distance); + InsertLastMatch(2, Distance); + CopyString(2, Distance); + } + } + UnpWriteBuf(); + } + + private void UnpWriteBuf() + { + var WrittenBorder = wrPtr; + var WriteSize = (unpPtr - WrittenBorder) & PackDef.MAXWINMASK; + for (var I = 0; I < prgStack.Count; I++) + { + var flt = prgStack[I]; + if (flt is null) + { + continue; + } + if (flt.NextWindow) + { + flt.NextWindow = false; // ->NextWindow=false; + continue; + } + var BlockStart = flt.BlockStart; // ->BlockStart; + var BlockLength = flt.BlockLength; // ->BlockLength; + if (((BlockStart - WrittenBorder) & PackDef.MAXWINMASK) < WriteSize) + { + if (WrittenBorder != BlockStart) + { + UnpWriteArea(WrittenBorder, BlockStart); + WrittenBorder = BlockStart; + WriteSize = (unpPtr - WrittenBorder) & PackDef.MAXWINMASK; + } + if (BlockLength <= WriteSize) + { + var BlockEnd = (BlockStart + BlockLength) & PackDef.MAXWINMASK; + if (BlockStart < BlockEnd || BlockEnd == 0) { - int BlockEnd = (BlockStart + BlockLength) & PackDef.MAXWINMASK; - if (BlockStart < BlockEnd || BlockEnd == 0) - { - // VM.SetMemory(0,Window+BlockStart,BlockLength); - rarVM.setMemory(0, window, BlockStart, BlockLength); - } - else - { - int FirstPartLength = PackDef.MAXWINSIZE - BlockStart; - - // VM.SetMemory(0,Window+BlockStart,FirstPartLength); - rarVM.setMemory(0, window, BlockStart, FirstPartLength); - - // VM.SetMemory(FirstPartLength,Window,BlockEnd); - rarVM.setMemory(FirstPartLength, window, 0, BlockEnd); - } - - VMPreparedProgram ParentPrg = filters[flt.ParentFilter].Program; - VMPreparedProgram Prg = flt.Program; - - if (ParentPrg.GlobalData.Count > RarVM.VM_FIXEDGLOBALSIZE) - { - // copy global data from previous script execution if - // any - // Prg->GlobalData.Alloc(ParentPrg->GlobalData.Size()); - // memcpy(&Prg->GlobalData[VM_FIXEDGLOBALSIZE],&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],ParentPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); - Prg.GlobalData.Clear(); - for ( - int i = 0; - i < ParentPrg.GlobalData.Count - RarVM.VM_FIXEDGLOBALSIZE; - i++ - ) - { - Prg.GlobalData[RarVM.VM_FIXEDGLOBALSIZE + i] = ParentPrg.GlobalData[ - RarVM.VM_FIXEDGLOBALSIZE + i - ]; - } - } - - ExecuteCode(Prg); - - if (Prg.GlobalData.Count > RarVM.VM_FIXEDGLOBALSIZE) - { - // save global data for next script execution - if (ParentPrg.GlobalData.Count < Prg.GlobalData.Count) - { - //ParentPrg.GlobalData.Clear(); // ->GlobalData.Alloc(Prg->GlobalData.Size()); - ParentPrg.GlobalData.SetSize(Prg.GlobalData.Count); - } - - // memcpy(&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],&Prg->GlobalData[VM_FIXEDGLOBALSIZE],Prg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); - for ( - int i = 0; - i < Prg.GlobalData.Count - RarVM.VM_FIXEDGLOBALSIZE; - i++ - ) - { - ParentPrg.GlobalData[RarVM.VM_FIXEDGLOBALSIZE + i] = Prg.GlobalData[ - RarVM.VM_FIXEDGLOBALSIZE + i - ]; - } - } - else - { - ParentPrg.GlobalData.Clear(); - } - - int FilteredDataOffset = Prg.FilteredDataOffset; - int FilteredDataSize = Prg.FilteredDataSize; - byte[] FilteredData = new byte[FilteredDataSize]; - - for (int i = 0; i < FilteredDataSize; i++) - { - FilteredData[i] = rarVM.Mem[FilteredDataOffset + i]; - - // Prg.GlobalData.get(FilteredDataOffset - // + - // i); - } - - prgStack[I] = null; - while (I + 1 < prgStack.Count) - { - UnpackFilter NextFilter = prgStack[I + 1]; - if ( - NextFilter is null - || NextFilter.BlockStart != BlockStart - || NextFilter.BlockLength != FilteredDataSize - || NextFilter.NextWindow - ) - { - break; - } - - // apply several filters to same data block - - rarVM.setMemory(0, FilteredData, 0, FilteredDataSize); - - // .SetMemory(0,FilteredData,FilteredDataSize); - - VMPreparedProgram pPrg = filters[NextFilter.ParentFilter].Program; - VMPreparedProgram NextPrg = NextFilter.Program; - - if (pPrg.GlobalData.Count > RarVM.VM_FIXEDGLOBALSIZE) - { - // copy global data from previous script execution - // if any - // NextPrg->GlobalData.Alloc(ParentPrg->GlobalData.Size()); - NextPrg.GlobalData.SetSize(pPrg.GlobalData.Count); - - // memcpy(&NextPrg->GlobalData[VM_FIXEDGLOBALSIZE],&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],ParentPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); - for ( - int i = 0; - i < pPrg.GlobalData.Count - RarVM.VM_FIXEDGLOBALSIZE; - i++ - ) - { - NextPrg.GlobalData[RarVM.VM_FIXEDGLOBALSIZE + i] = - pPrg.GlobalData[RarVM.VM_FIXEDGLOBALSIZE + i]; - } - } - - ExecuteCode(NextPrg); - - if (NextPrg.GlobalData.Count > RarVM.VM_FIXEDGLOBALSIZE) - { - // save global data for next script execution - if (pPrg.GlobalData.Count < NextPrg.GlobalData.Count) - { - pPrg.GlobalData.SetSize(NextPrg.GlobalData.Count); - } - - // memcpy(&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],&NextPrg->GlobalData[VM_FIXEDGLOBALSIZE],NextPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); - for ( - int i = 0; - i < NextPrg.GlobalData.Count - RarVM.VM_FIXEDGLOBALSIZE; - i++ - ) - { - pPrg.GlobalData[RarVM.VM_FIXEDGLOBALSIZE + i] = - NextPrg.GlobalData[RarVM.VM_FIXEDGLOBALSIZE + i]; - } - } - else - { - pPrg.GlobalData.Clear(); - } - FilteredDataOffset = NextPrg.FilteredDataOffset; - FilteredDataSize = NextPrg.FilteredDataSize; - - FilteredData = new byte[FilteredDataSize]; - for (int i = 0; i < FilteredDataSize; i++) - { - FilteredData[i] = NextPrg.GlobalData[FilteredDataOffset + i]; - } - - I++; - prgStack[I] = null; - } - writeStream.Write(FilteredData, 0, FilteredDataSize); - unpSomeRead = true; - writtenFileSize += FilteredDataSize; - destUnpSize -= FilteredDataSize; - WrittenBorder = BlockEnd; - WriteSize = (unpPtr - WrittenBorder) & PackDef.MAXWINMASK; + // VM.SetMemory(0,Window+BlockStart,BlockLength); + rarVM.setMemory(0, window, BlockStart, BlockLength); } else { - for (int J = I; J < prgStack.Count; J++) + var FirstPartLength = PackDef.MAXWINSIZE - BlockStart; + + // VM.SetMemory(0,Window+BlockStart,FirstPartLength); + rarVM.setMemory(0, window, BlockStart, FirstPartLength); + + // VM.SetMemory(FirstPartLength,Window,BlockEnd); + rarVM.setMemory(FirstPartLength, window, 0, BlockEnd); + } + + var ParentPrg = filters[flt.ParentFilter].Program; + var Prg = flt.Program; + + if (ParentPrg.GlobalData.Count > RarVM.VM_FIXEDGLOBALSIZE) + { + // copy global data from previous script execution if + // any + // Prg->GlobalData.Alloc(ParentPrg->GlobalData.Size()); + // memcpy(&Prg->GlobalData[VM_FIXEDGLOBALSIZE],&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],ParentPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); + Prg.GlobalData.Clear(); + for ( + var i = 0; + i < ParentPrg.GlobalData.Count - RarVM.VM_FIXEDGLOBALSIZE; + i++ + ) { - UnpackFilter filt = prgStack[J]; - if (filt != null && filt.NextWindow) + Prg.GlobalData[RarVM.VM_FIXEDGLOBALSIZE + i] = ParentPrg.GlobalData[ + RarVM.VM_FIXEDGLOBALSIZE + i + ]; + } + } + + ExecuteCode(Prg); + + if (Prg.GlobalData.Count > RarVM.VM_FIXEDGLOBALSIZE) + { + // save global data for next script execution + if (ParentPrg.GlobalData.Count < Prg.GlobalData.Count) + { + //ParentPrg.GlobalData.Clear(); // ->GlobalData.Alloc(Prg->GlobalData.Size()); + ParentPrg.GlobalData.SetSize(Prg.GlobalData.Count); + } + + // memcpy(&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],&Prg->GlobalData[VM_FIXEDGLOBALSIZE],Prg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); + for ( + var i = 0; + i < Prg.GlobalData.Count - RarVM.VM_FIXEDGLOBALSIZE; + i++ + ) + { + ParentPrg.GlobalData[RarVM.VM_FIXEDGLOBALSIZE + i] = Prg.GlobalData[ + RarVM.VM_FIXEDGLOBALSIZE + i + ]; + } + } + else + { + ParentPrg.GlobalData.Clear(); + } + + var FilteredDataOffset = Prg.FilteredDataOffset; + var FilteredDataSize = Prg.FilteredDataSize; + var FilteredData = new byte[FilteredDataSize]; + + for (var i = 0; i < FilteredDataSize; i++) + { + FilteredData[i] = rarVM.Mem[FilteredDataOffset + i]; + + // Prg.GlobalData.get(FilteredDataOffset + // + + // i); + } + + prgStack[I] = null; + while (I + 1 < prgStack.Count) + { + var NextFilter = prgStack[I + 1]; + if ( + NextFilter is null + || NextFilter.BlockStart != BlockStart + || NextFilter.BlockLength != FilteredDataSize + || NextFilter.NextWindow + ) + { + break; + } + + // apply several filters to same data block + + rarVM.setMemory(0, FilteredData, 0, FilteredDataSize); + + // .SetMemory(0,FilteredData,FilteredDataSize); + + var pPrg = filters[NextFilter.ParentFilter].Program; + var NextPrg = NextFilter.Program; + + if (pPrg.GlobalData.Count > RarVM.VM_FIXEDGLOBALSIZE) + { + // copy global data from previous script execution + // if any + // NextPrg->GlobalData.Alloc(ParentPrg->GlobalData.Size()); + NextPrg.GlobalData.SetSize(pPrg.GlobalData.Count); + + // memcpy(&NextPrg->GlobalData[VM_FIXEDGLOBALSIZE],&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],ParentPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); + for ( + var i = 0; + i < pPrg.GlobalData.Count - RarVM.VM_FIXEDGLOBALSIZE; + i++ + ) { - filt.NextWindow = false; + NextPrg.GlobalData[RarVM.VM_FIXEDGLOBALSIZE + i] = + pPrg.GlobalData[RarVM.VM_FIXEDGLOBALSIZE + i]; } } - wrPtr = WrittenBorder; - return; + + ExecuteCode(NextPrg); + + if (NextPrg.GlobalData.Count > RarVM.VM_FIXEDGLOBALSIZE) + { + // save global data for next script execution + if (pPrg.GlobalData.Count < NextPrg.GlobalData.Count) + { + pPrg.GlobalData.SetSize(NextPrg.GlobalData.Count); + } + + // memcpy(&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],&NextPrg->GlobalData[VM_FIXEDGLOBALSIZE],NextPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); + for ( + var i = 0; + i < NextPrg.GlobalData.Count - RarVM.VM_FIXEDGLOBALSIZE; + i++ + ) + { + pPrg.GlobalData[RarVM.VM_FIXEDGLOBALSIZE + i] = + NextPrg.GlobalData[RarVM.VM_FIXEDGLOBALSIZE + i]; + } + } + else + { + pPrg.GlobalData.Clear(); + } + FilteredDataOffset = NextPrg.FilteredDataOffset; + FilteredDataSize = NextPrg.FilteredDataSize; + + FilteredData = new byte[FilteredDataSize]; + for (var i = 0; i < FilteredDataSize; i++) + { + FilteredData[i] = NextPrg.GlobalData[FilteredDataOffset + i]; + } + + I++; + prgStack[I] = null; } + writeStream.Write(FilteredData, 0, FilteredDataSize); + unpSomeRead = true; + writtenFileSize += FilteredDataSize; + destUnpSize -= FilteredDataSize; + WrittenBorder = BlockEnd; + WriteSize = (unpPtr - WrittenBorder) & PackDef.MAXWINMASK; + } + else + { + for (var J = I; J < prgStack.Count; J++) + { + var filt = prgStack[J]; + if (filt != null && filt.NextWindow) + { + filt.NextWindow = false; + } + } + wrPtr = WrittenBorder; + return; } } - - UnpWriteArea(WrittenBorder, unpPtr); - wrPtr = unpPtr; } - private void UnpWriteArea(int startPtr, int endPtr) + UnpWriteArea(WrittenBorder, unpPtr); + wrPtr = unpPtr; + } + + private void UnpWriteArea(int startPtr, int endPtr) + { + if (endPtr != startPtr) { - if (endPtr != startPtr) - { - unpSomeRead = true; - } - if (endPtr < startPtr) - { - UnpWriteData(window, startPtr, -startPtr & PackDef.MAXWINMASK); - UnpWriteData(window, 0, endPtr); - unpAllBuf = true; - } - else - { - UnpWriteData(window, startPtr, endPtr - startPtr); - } + unpSomeRead = true; } - - private void UnpWriteData(byte[] data, int offset, int size) + if (endPtr < startPtr) { - if (destUnpSize <= 0) - { - return; - } - int writeSize = size; - if (writeSize > destUnpSize) - { - writeSize = (int)destUnpSize; - } - writeStream.Write(data, offset, writeSize); - - writtenFileSize += size; - destUnpSize -= size; + UnpWriteData(window, startPtr, -startPtr & PackDef.MAXWINMASK); + UnpWriteData(window, 0, endPtr); + unpAllBuf = true; } - - private void InsertOldDist(uint distance) + else { - // TODO uint - InsertOldDist((int)distance); + UnpWriteData(window, startPtr, endPtr - startPtr); } + } - private void InsertOldDist(int distance) + private void UnpWriteData(byte[] data, int offset, int size) + { + if (destUnpSize <= 0) { - oldDist[3] = oldDist[2]; - oldDist[2] = oldDist[1]; - oldDist[1] = oldDist[0]; - oldDist[0] = distance; + return; } - - private void InsertLastMatch(int length, int distance) + var writeSize = size; + if (writeSize > destUnpSize) { - lastDist = distance; - lastLength = length; + writeSize = (int)destUnpSize; } + writeStream.Write(data, offset, writeSize); - private void CopyString(uint length, uint distance) + writtenFileSize += size; + destUnpSize -= size; + } + + private void InsertOldDist(uint distance) + { + // TODO uint + InsertOldDist((int)distance); + } + + private void InsertOldDist(int distance) + { + oldDist[3] = oldDist[2]; + oldDist[2] = oldDist[1]; + oldDist[1] = oldDist[0]; + oldDist[0] = distance; + } + + private void InsertLastMatch(int length, int distance) + { + lastDist = distance; + lastLength = length; + } + + private void CopyString(uint length, uint distance) + { + // TODO uint + CopyString((int)length, (int)distance); + } + + private void CopyString(int length, int distance) + { + // System.out.println("copyString(" + length + ", " + distance + ")"); + + var destPtr = unpPtr - distance; + + // System.out.println(unpPtr+":"+distance); + if ( + destPtr >= 0 + && destPtr < PackDef.MAXWINSIZE - 260 + && unpPtr < PackDef.MAXWINSIZE - 260 + ) { - // TODO uint - CopyString((int)length, (int)distance); - } + window[unpPtr++] = window[destPtr++]; - private void CopyString(int length, int distance) - { - // System.out.println("copyString(" + length + ", " + distance + ")"); - - int destPtr = unpPtr - distance; - - // System.out.println(unpPtr+":"+distance); - if ( - destPtr >= 0 - && destPtr < PackDef.MAXWINSIZE - 260 - && unpPtr < PackDef.MAXWINSIZE - 260 - ) + while (--length > 0) { window[unpPtr++] = window[destPtr++]; + } + } + else + { + while (length-- != 0) + { + window[unpPtr] = window[destPtr++ & PackDef.MAXWINMASK]; + unpPtr = (unpPtr + 1) & PackDef.MAXWINMASK; + } + } + } - while (--length > 0) + private void UnpInitData(bool solid) + { + if (!solid) + { + tablesRead = false; + new Span(oldDist).Clear(); // memset(oldDist,0,sizeof(OldDist)); + + oldDistPtr = 0; + lastDist = 0; + lastLength = 0; + + new Span(unpOldTable).Clear(); // memset(UnpOldTable,0,sizeof(UnpOldTable)); + + unpPtr = 0; + wrPtr = 0; + PpmEscChar = 2; + WriteBorder = Math.Min(MaxWinSize, UNPACK_MAX_WRITE) & MaxWinMask; + + InitFilters(); + } + InitBitInput(); + ppmError = false; + writtenFileSize = 0; + readTop = 0; + readBorder = 0; + unpInitData20(solid); + } + + //void Unpack::UnpInitData(bool Solid) + //{ + // if (!Solid) + // { + // memset(OldDist,0,sizeof(OldDist)); + // OldDistPtr=0; + // LastDist=LastLength=0; + //// memset(Window,0,MaxWinSize); + // memset(&BlockTables,0,sizeof(BlockTables)); + // UnpPtr=WrPtr=0; + // WriteBorder=Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; + // } + // // Filters never share several solid files, so we can safely reset them + // // even in solid archive. + // InitFilters(); + // + // Inp.InitBitInput(); + // WrittenFileSize=0; + // ReadTop=0; + // ReadBorder=0; + // + // memset(&BlockHeader,0,sizeof(BlockHeader)); + // BlockHeader.BlockSize=-1; // '-1' means not defined yet. + //#ifndef SFX_MODULE + // UnpInitData20(Solid); + //#endif + // UnpInitData30(Solid); + // UnpInitData50(Solid); + //} + + private void InitFilters() + { + oldFilterLengths.Clear(); + lastFilter = 0; + + filters.Clear(); + + prgStack.Clear(); + } + + private bool ReadEndOfBlock() + { + var BitField = GetBits(); + bool NewTable, + NewFile = false; + if ((BitField & 0x8000) != 0) + { + NewTable = true; + AddBits(1); + } + else + { + NewFile = true; + NewTable = (BitField & 0x4000) != 0; + AddBits(2); + } + tablesRead = !NewTable; + return !(NewFile || NewTable && !ReadTables()); + } + + private bool ReadTables() + { + var bitLength = new byte[PackDef.BC]; + + var table = new byte[PackDef.HUFF_TABLE_SIZE]; + if (inAddr > readTop - 25) + { + if (!unpReadBuf()) + { + return (false); + } + } + AddBits((8 - inBit) & 7); + long bitField = GetBits() & unchecked((int)0xffFFffFF); + if ((bitField & 0x8000) != 0) + { + unpBlockType = BlockTypes.BLOCK_PPM; + return (ppm.DecodeInit(this, PpmEscChar)); + } + unpBlockType = BlockTypes.BLOCK_LZ; + + prevLowDist = 0; + lowDistRepCount = 0; + + if ((bitField & 0x4000) == 0) + { + new Span(unpOldTable).Clear(); // memset(UnpOldTable,0,sizeof(UnpOldTable)); + } + AddBits(2); + + for (var i = 0; i < PackDef.BC; i++) + { + var length = (Utility.URShift(GetBits(), 12)) & 0xFF; + AddBits(4); + if (length == 15) + { + var zeroCount = (Utility.URShift(GetBits(), 12)) & 0xFF; + AddBits(4); + if (zeroCount == 0) { - window[unpPtr++] = window[destPtr++]; + bitLength[i] = 15; + } + else + { + zeroCount += 2; + while (zeroCount-- > 0 && i < bitLength.Length) + { + bitLength[i++] = 0; + } + i--; } } else { - while (length-- != 0) - { - window[unpPtr] = window[destPtr++ & PackDef.MAXWINMASK]; - unpPtr = (unpPtr + 1) & PackDef.MAXWINMASK; - } + bitLength[i] = (byte)length; } } - private void UnpInitData(bool solid) + UnpackUtility.makeDecodeTables(bitLength, 0, BD, PackDef.BC); + + var TableSize = PackDef.HUFF_TABLE_SIZE; + + for (var i = 0; i < TableSize;) { - if (!solid) - { - tablesRead = false; - new Span(oldDist).Clear(); // memset(oldDist,0,sizeof(OldDist)); - - oldDistPtr = 0; - lastDist = 0; - lastLength = 0; - - new Span(unpOldTable).Clear(); // memset(UnpOldTable,0,sizeof(UnpOldTable)); - - unpPtr = 0; - wrPtr = 0; - PpmEscChar = 2; - WriteBorder = Math.Min(MaxWinSize, UNPACK_MAX_WRITE) & MaxWinMask; - - InitFilters(); - } - InitBitInput(); - ppmError = false; - writtenFileSize = 0; - readTop = 0; - readBorder = 0; - unpInitData20(solid); - } - - //void Unpack::UnpInitData(bool Solid) - //{ - // if (!Solid) - // { - // memset(OldDist,0,sizeof(OldDist)); - // OldDistPtr=0; - // LastDist=LastLength=0; - //// memset(Window,0,MaxWinSize); - // memset(&BlockTables,0,sizeof(BlockTables)); - // UnpPtr=WrPtr=0; - // WriteBorder=Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; - // } - // // Filters never share several solid files, so we can safely reset them - // // even in solid archive. - // InitFilters(); - // - // Inp.InitBitInput(); - // WrittenFileSize=0; - // ReadTop=0; - // ReadBorder=0; - // - // memset(&BlockHeader,0,sizeof(BlockHeader)); - // BlockHeader.BlockSize=-1; // '-1' means not defined yet. - //#ifndef SFX_MODULE - // UnpInitData20(Solid); - //#endif - // UnpInitData30(Solid); - // UnpInitData50(Solid); - //} - - private void InitFilters() - { - oldFilterLengths.Clear(); - lastFilter = 0; - - filters.Clear(); - - prgStack.Clear(); - } - - private bool ReadEndOfBlock() - { - int BitField = GetBits(); - bool NewTable, - NewFile = false; - if ((BitField & 0x8000) != 0) - { - NewTable = true; - AddBits(1); - } - else - { - NewFile = true; - NewTable = (BitField & 0x4000) != 0; - AddBits(2); - } - tablesRead = !NewTable; - return !(NewFile || NewTable && !ReadTables()); - } - - private bool ReadTables() - { - byte[] bitLength = new byte[PackDef.BC]; - - byte[] table = new byte[PackDef.HUFF_TABLE_SIZE]; - if (inAddr > readTop - 25) + if (inAddr > readTop - 5) { if (!unpReadBuf()) { return (false); } } - AddBits((8 - inBit) & 7); - long bitField = GetBits() & unchecked((int)0xffFFffFF); - if ((bitField & 0x8000) != 0) + var Number = this.decodeNumber(BD); + if (Number < 16) { - unpBlockType = BlockTypes.BLOCK_PPM; - return (ppm.DecodeInit(this, PpmEscChar)); + table[i] = (byte)((Number + unpOldTable[i]) & 0xf); + i++; } - unpBlockType = BlockTypes.BLOCK_LZ; - - prevLowDist = 0; - lowDistRepCount = 0; - - if ((bitField & 0x4000) == 0) + else if (Number < 18) { - new Span(unpOldTable).Clear(); // memset(UnpOldTable,0,sizeof(UnpOldTable)); - } - AddBits(2); - - for (int i = 0; i < PackDef.BC; i++) - { - int length = (Utility.URShift(GetBits(), 12)) & 0xFF; - AddBits(4); - if (length == 15) + int N; + if (Number == 16) { - int zeroCount = (Utility.URShift(GetBits(), 12)) & 0xFF; - AddBits(4); - if (zeroCount == 0) - { - bitLength[i] = 15; - } - else - { - zeroCount += 2; - while (zeroCount-- > 0 && i < bitLength.Length) - { - bitLength[i++] = 0; - } - i--; - } + N = (Utility.URShift(GetBits(), 13)) + 3; + AddBits(3); } else { - bitLength[i] = (byte)length; + N = (Utility.URShift(GetBits(), 9)) + 11; + AddBits(7); } - } - - UnpackUtility.makeDecodeTables(bitLength, 0, BD, PackDef.BC); - - int TableSize = PackDef.HUFF_TABLE_SIZE; - - for (int i = 0; i < TableSize; ) - { - if (inAddr > readTop - 5) + while (N-- > 0 && i < TableSize) { - if (!unpReadBuf()) - { - return (false); - } - } - int Number = this.decodeNumber(BD); - if (Number < 16) - { - table[i] = (byte)((Number + unpOldTable[i]) & 0xf); + table[i] = table[i - 1]; i++; } - else if (Number < 18) + } + else + { + int N; + if (Number == 18) { - int N; - if (Number == 16) - { - N = (Utility.URShift(GetBits(), 13)) + 3; - AddBits(3); - } - else - { - N = (Utility.URShift(GetBits(), 9)) + 11; - AddBits(7); - } - while (N-- > 0 && i < TableSize) - { - table[i] = table[i - 1]; - i++; - } + N = (Utility.URShift(GetBits(), 13)) + 3; + AddBits(3); } else { - int N; - if (Number == 18) - { - 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; - } + N = (Utility.URShift(GetBits(), 9)) + 11; + AddBits(7); + } + while (N-- > 0 && i < TableSize) + { + table[i++] = 0; } } - tablesRead = true; - if (inAddr > readTop) - { - return (false); - } - UnpackUtility.makeDecodeTables(table, 0, LD, PackDef.NC); - UnpackUtility.makeDecodeTables(table, PackDef.NC, DD, PackDef.DC); - UnpackUtility.makeDecodeTables(table, PackDef.NC + PackDef.DC, LDD, PackDef.LDC); - UnpackUtility.makeDecodeTables( - table, - PackDef.NC + PackDef.DC + PackDef.LDC, - RD, - PackDef.RC - ); - - // memcpy(unpOldTable,table,sizeof(unpOldTable)); - - Buffer.BlockCopy(table, 0, unpOldTable, 0, unpOldTable.Length); - return (true); } - - private bool ReadVMCode() + tablesRead = true; + if (inAddr > readTop) { - int FirstByte = GetBits() >> 8; + return (false); + } + UnpackUtility.makeDecodeTables(table, 0, LD, PackDef.NC); + UnpackUtility.makeDecodeTables(table, PackDef.NC, DD, PackDef.DC); + UnpackUtility.makeDecodeTables(table, PackDef.NC + PackDef.DC, LDD, PackDef.LDC); + UnpackUtility.makeDecodeTables( + table, + PackDef.NC + PackDef.DC + PackDef.LDC, + RD, + PackDef.RC + ); + + // memcpy(unpOldTable,table,sizeof(unpOldTable)); + + Buffer.BlockCopy(table, 0, unpOldTable, 0, unpOldTable.Length); + return (true); + } + + private bool ReadVMCode() + { + var FirstByte = GetBits() >> 8; + AddBits(8); + var Length = (FirstByte & 7) + 1; + if (Length == 7) + { + Length = (GetBits() >> 8) + 7; AddBits(8); - int Length = (FirstByte & 7) + 1; - if (Length == 7) - { - Length = (GetBits() >> 8) + 7; - AddBits(8); - } - else if (Length == 8) - { - Length = GetBits(); - AddBits(16); - } - - List vmCode = new List(); - for (int I = 0; I < Length; I++) - { - if (inAddr >= readTop - 1 && !unpReadBuf() && I < Length - 1) - { - return (false); - } - vmCode.Add((byte)(GetBits() >> 8)); - AddBits(8); - } - return (AddVMCode(FirstByte, vmCode, Length)); + } + else if (Length == 8) + { + Length = GetBits(); + AddBits(16); } - private bool ReadVMCodePPM() + var vmCode = new List(); + for (var I = 0; I < Length; I++) { - int FirstByte = ppm.DecodeChar(); - if (FirstByte == -1) + if (inAddr >= readTop - 1 && !unpReadBuf() && I < Length - 1) { return (false); } - int Length = (FirstByte & 7) + 1; - if (Length == 7) - { - int B1 = ppm.DecodeChar(); - if (B1 == -1) - { - return (false); - } - Length = B1 + 7; - } - else if (Length == 8) - { - int B1 = ppm.DecodeChar(); - if (B1 == -1) - { - return (false); - } - int B2 = ppm.DecodeChar(); - if (B2 == -1) - { - return (false); - } - Length = B1 * 256 + B2; - } - - List vmCode = new List(); - for (int I = 0; I < Length; I++) - { - int Ch = ppm.DecodeChar(); - if (Ch == -1) - { - return (false); - } - vmCode.Add((byte)Ch); // VMCode[I]=Ch; - } - return (AddVMCode(FirstByte, vmCode, Length)); + vmCode.Add((byte)(GetBits() >> 8)); + AddBits(8); } + return (AddVMCode(FirstByte, vmCode, Length)); + } - private bool AddVMCode(int firstByte, List vmCode, int length) + private bool ReadVMCodePPM() + { + var FirstByte = ppm.DecodeChar(); + if (FirstByte == -1) { - BitInput Inp = new BitInput(); - Inp.InitBitInput(); - - // memcpy(Inp.InBuf,Code,Min(BitInput::MAX_SIZE,CodeSize)); - for (int i = 0; i < Math.Min(MAX_SIZE, vmCode.Count); i++) - { - Inp.InBuf[i] = vmCode[i]; - } - rarVM.init(); - - int FiltPos; - if ((firstByte & 0x80) != 0) - { - FiltPos = RarVM.ReadData(Inp); - if (FiltPos == 0) - { - InitFilters(); - } - else - { - FiltPos--; - } - } - else - { - FiltPos = lastFilter; // use the same filter as last time - } - - if (FiltPos > filters.Count || FiltPos > oldFilterLengths.Count) + return (false); + } + var Length = (FirstByte & 7) + 1; + if (Length == 7) + { + var B1 = ppm.DecodeChar(); + if (B1 == -1) { return (false); } - lastFilter = FiltPos; - bool NewFilter = (FiltPos == filters.Count); - - UnpackFilter StackFilter = new UnpackFilter(); // new filter for - - // PrgStack - - UnpackFilter Filter; - if (NewFilter) - // new filter code, never used before since VM reset + Length = B1 + 7; + } + else if (Length == 8) + { + var B1 = ppm.DecodeChar(); + if (B1 == -1) { - // too many different filters, corrupt archive - if (FiltPos > 1024) - { - return (false); - } - - // Filters[Filters.Size()-1]=Filter=new UnpackFilter; - Filter = new UnpackFilter(); - filters.Add(Filter); - StackFilter.ParentFilter = filters.Count - 1; - oldFilterLengths.Add(0); - Filter.ExecCount = 0; + return (false); } - // filter was used in the past - else + var B2 = ppm.DecodeChar(); + if (B2 == -1) { - Filter = filters[FiltPos]; - StackFilter.ParentFilter = FiltPos; - Filter.ExecCount = Filter.ExecCount + 1; // ->ExecCount++; + return (false); } + Length = (B1 * 256) + B2; + } - prgStack.Add(StackFilter); - StackFilter.ExecCount = Filter.ExecCount; // ->ExecCount; - - int BlockStart = RarVM.ReadData(Inp); - if ((firstByte & 0x40) != 0) + var vmCode = new List(); + for (var I = 0; I < Length; I++) + { + var Ch = ppm.DecodeChar(); + if (Ch == -1) { - BlockStart += 258; + return (false); } - StackFilter.BlockStart = ((BlockStart + unpPtr) & PackDef.MAXWINMASK); - if ((firstByte & 0x20) != 0) + vmCode.Add((byte)Ch); // VMCode[I]=Ch; + } + return (AddVMCode(FirstByte, vmCode, Length)); + } + + private bool AddVMCode(int firstByte, List vmCode, int length) + { + var Inp = new BitInput(); + Inp.InitBitInput(); + + // memcpy(Inp.InBuf,Code,Min(BitInput::MAX_SIZE,CodeSize)); + for (var i = 0; i < Math.Min(MAX_SIZE, vmCode.Count); i++) + { + Inp.InBuf[i] = vmCode[i]; + } + rarVM.init(); + + int FiltPos; + if ((firstByte & 0x80) != 0) + { + FiltPos = RarVM.ReadData(Inp); + if (FiltPos == 0) { - StackFilter.BlockLength = RarVM.ReadData(Inp); + InitFilters(); } else { - StackFilter.BlockLength = - FiltPos < oldFilterLengths.Count ? oldFilterLengths[FiltPos] : 0; + FiltPos--; } - StackFilter.NextWindow = - (wrPtr != unpPtr) && ((wrPtr - unpPtr) & PackDef.MAXWINMASK) <= BlockStart; + } + else + { + FiltPos = lastFilter; // use the same filter as last time + } - // DebugLog("\nNextWindow: UnpPtr=%08x WrPtr=%08x - // BlockStart=%08x",UnpPtr,WrPtr,BlockStart); + if (FiltPos > filters.Count || FiltPos > oldFilterLengths.Count) + { + return (false); + } + lastFilter = FiltPos; + var NewFilter = (FiltPos == filters.Count); - oldFilterLengths[FiltPos] = StackFilter.BlockLength; + var StackFilter = new UnpackFilter(); // new filter for - // memset(StackFilter->Prg.InitR,0,sizeof(StackFilter->Prg.InitR)); - new Span(StackFilter.Program.InitR).Clear(); - StackFilter.Program.InitR[3] = RarVM.VM_GLOBALMEMADDR; // StackFilter->Prg.InitR[3]=VM_GLOBALMEMADDR; - StackFilter.Program.InitR[4] = StackFilter.BlockLength; + // PrgStack - // StackFilter->Prg.InitR[4]=StackFilter->BlockLength; - StackFilter.Program.InitR[5] = StackFilter.ExecCount; // StackFilter->Prg.InitR[5]=StackFilter->ExecCount; - - if ((firstByte & 0x10) != 0) - // set registers to optional parameters - // if any + UnpackFilter Filter; + if (NewFilter) + // new filter code, never used before since VM reset + { + // too many different filters, corrupt archive + if (FiltPos > 1024) { - int InitMask = Utility.URShift(Inp.GetBits(), 9); - Inp.AddBits(7); - for (int I = 0; I < 7; I++) + return (false); + } + + // Filters[Filters.Size()-1]=Filter=new UnpackFilter; + Filter = new UnpackFilter(); + filters.Add(Filter); + StackFilter.ParentFilter = filters.Count - 1; + oldFilterLengths.Add(0); + Filter.ExecCount = 0; + } + // filter was used in the past + else + { + Filter = filters[FiltPos]; + StackFilter.ParentFilter = FiltPos; + Filter.ExecCount++; // ->ExecCount++; + } + + prgStack.Add(StackFilter); + StackFilter.ExecCount = Filter.ExecCount; // ->ExecCount; + + var BlockStart = RarVM.ReadData(Inp); + if ((firstByte & 0x40) != 0) + { + BlockStart += 258; + } + StackFilter.BlockStart = ((BlockStart + unpPtr) & PackDef.MAXWINMASK); + if ((firstByte & 0x20) != 0) + { + StackFilter.BlockLength = RarVM.ReadData(Inp); + } + else + { + StackFilter.BlockLength = + FiltPos < oldFilterLengths.Count ? oldFilterLengths[FiltPos] : 0; + } + StackFilter.NextWindow = + (wrPtr != unpPtr) && ((wrPtr - unpPtr) & PackDef.MAXWINMASK) <= BlockStart; + + // DebugLog("\nNextWindow: UnpPtr=%08x WrPtr=%08x + // BlockStart=%08x",UnpPtr,WrPtr,BlockStart); + + oldFilterLengths[FiltPos] = StackFilter.BlockLength; + + // memset(StackFilter->Prg.InitR,0,sizeof(StackFilter->Prg.InitR)); + new Span(StackFilter.Program.InitR).Clear(); + StackFilter.Program.InitR[3] = RarVM.VM_GLOBALMEMADDR; // StackFilter->Prg.InitR[3]=VM_GLOBALMEMADDR; + StackFilter.Program.InitR[4] = StackFilter.BlockLength; + + // StackFilter->Prg.InitR[4]=StackFilter->BlockLength; + StackFilter.Program.InitR[5] = StackFilter.ExecCount; // StackFilter->Prg.InitR[5]=StackFilter->ExecCount; + + if ((firstByte & 0x10) != 0) + // set registers to optional parameters + // if any + { + var InitMask = Utility.URShift(Inp.GetBits(), 9); + Inp.AddBits(7); + for (var I = 0; I < 7; I++) + { + if ((InitMask & (1 << I)) != 0) { - if ((InitMask & (1 << I)) != 0) - { - // StackFilter->Prg.InitR[I]=RarVM::ReadData(Inp); - StackFilter.Program.InitR[I] = RarVM.ReadData(Inp); - } + // StackFilter->Prg.InitR[I]=RarVM::ReadData(Inp); + StackFilter.Program.InitR[I] = RarVM.ReadData(Inp); } } + } - if (NewFilter) + if (NewFilter) + { + var VMCodeSize = RarVM.ReadData(Inp); + if (VMCodeSize >= 0x10000 || VMCodeSize == 0) { - int VMCodeSize = RarVM.ReadData(Inp); - if (VMCodeSize >= 0x10000 || VMCodeSize == 0) - { - return (false); - } - byte[] VMCode = new byte[VMCodeSize]; - for (int I = 0; I < VMCodeSize; I++) - { - if (Inp.Overflow(3)) - { - return (false); - } - VMCode[I] = (byte)(Inp.GetBits() >> 8); - Inp.AddBits(8); - } - - // VM.Prepare(&VMCode[0],VMCodeSize,&Filter->Prg); - rarVM.prepare(VMCode, VMCodeSize, Filter.Program); + return (false); } - StackFilter.Program.AltCommands = Filter.Program.Commands; // StackFilter->Prg.AltCmd=&Filter->Prg.Cmd[0]; - StackFilter.Program.CommandCount = Filter.Program.CommandCount; - - // StackFilter->Prg.CmdCount=Filter->Prg.CmdCount; - - int StaticDataSize = Filter.Program.StaticData.Count; - if (StaticDataSize > 0 && StaticDataSize < RarVM.VM_GLOBALMEMSIZE) - { - // read statically defined data contained in DB commands - // StackFilter->Prg.StaticData.Add(StaticDataSize); - StackFilter.Program.StaticData = Filter.Program.StaticData; - - // memcpy(&StackFilter->Prg.StaticData[0],&Filter->Prg.StaticData[0],StaticDataSize); - } - - if (StackFilter.Program.GlobalData.Count < RarVM.VM_FIXEDGLOBALSIZE) - { - // StackFilter->Prg.GlobalData.Reset(); - // StackFilter->Prg.GlobalData.Add(VM_FIXEDGLOBALSIZE); - StackFilter.Program.GlobalData.Clear(); - StackFilter.Program.GlobalData.SetSize(RarVM.VM_FIXEDGLOBALSIZE); - } - - // byte *GlobalData=&StackFilter->Prg.GlobalData[0]; - - List globalData = StackFilter.Program.GlobalData; - for (int I = 0; I < 7; I++) - { - rarVM.SetLowEndianValue(globalData, I * 4, StackFilter.Program.InitR[I]); - } - - // VM.SetLowEndianValue((uint - // *)&GlobalData[0x1c],StackFilter->BlockLength); - rarVM.SetLowEndianValue(globalData, 0x1c, StackFilter.BlockLength); - - // VM.SetLowEndianValue((uint *)&GlobalData[0x20],0); - rarVM.SetLowEndianValue(globalData, 0x20, 0); - rarVM.SetLowEndianValue(globalData, 0x24, 0); - rarVM.SetLowEndianValue(globalData, 0x28, 0); - - // VM.SetLowEndianValue((uint - // *)&GlobalData[0x2c],StackFilter->ExecCount); - rarVM.SetLowEndianValue(globalData, 0x2c, StackFilter.ExecCount); - - // memset(&GlobalData[0x30],0,16); - for (int i = 0; i < 16; i++) - { - globalData[0x30 + i] = 0x0; - } - if ((firstByte & 8) != 0) - // put data block passed as parameter if any + var VMCode = new byte[VMCodeSize]; + for (var I = 0; I < VMCodeSize; I++) { if (Inp.Overflow(3)) { return (false); } - int DataSize = RarVM.ReadData(Inp); - if (DataSize > RarVM.VM_GLOBALMEMSIZE - RarVM.VM_FIXEDGLOBALSIZE) + VMCode[I] = (byte)(Inp.GetBits() >> 8); + Inp.AddBits(8); + } + + // VM.Prepare(&VMCode[0],VMCodeSize,&Filter->Prg); + rarVM.prepare(VMCode, VMCodeSize, Filter.Program); + } + StackFilter.Program.AltCommands = Filter.Program.Commands; // StackFilter->Prg.AltCmd=&Filter->Prg.Cmd[0]; + StackFilter.Program.CommandCount = Filter.Program.CommandCount; + + // StackFilter->Prg.CmdCount=Filter->Prg.CmdCount; + + var StaticDataSize = Filter.Program.StaticData.Count; + if (StaticDataSize > 0 && StaticDataSize < RarVM.VM_GLOBALMEMSIZE) + { + // read statically defined data contained in DB commands + // StackFilter->Prg.StaticData.Add(StaticDataSize); + StackFilter.Program.StaticData = Filter.Program.StaticData; + + // memcpy(&StackFilter->Prg.StaticData[0],&Filter->Prg.StaticData[0],StaticDataSize); + } + + if (StackFilter.Program.GlobalData.Count < RarVM.VM_FIXEDGLOBALSIZE) + { + // StackFilter->Prg.GlobalData.Reset(); + // StackFilter->Prg.GlobalData.Add(VM_FIXEDGLOBALSIZE); + StackFilter.Program.GlobalData.Clear(); + StackFilter.Program.GlobalData.SetSize(RarVM.VM_FIXEDGLOBALSIZE); + } + + // byte *GlobalData=&StackFilter->Prg.GlobalData[0]; + + var globalData = StackFilter.Program.GlobalData; + for (var I = 0; I < 7; I++) + { + rarVM.SetLowEndianValue(globalData, I * 4, StackFilter.Program.InitR[I]); + } + + // VM.SetLowEndianValue((uint + // *)&GlobalData[0x1c],StackFilter->BlockLength); + rarVM.SetLowEndianValue(globalData, 0x1c, StackFilter.BlockLength); + + // VM.SetLowEndianValue((uint *)&GlobalData[0x20],0); + rarVM.SetLowEndianValue(globalData, 0x20, 0); + rarVM.SetLowEndianValue(globalData, 0x24, 0); + rarVM.SetLowEndianValue(globalData, 0x28, 0); + + // VM.SetLowEndianValue((uint + // *)&GlobalData[0x2c],StackFilter->ExecCount); + rarVM.SetLowEndianValue(globalData, 0x2c, StackFilter.ExecCount); + + // memset(&GlobalData[0x30],0,16); + for (var i = 0; i < 16; i++) + { + globalData[0x30 + i] = 0x0; + } + if ((firstByte & 8) != 0) + // put data block passed as parameter if any + { + if (Inp.Overflow(3)) + { + return (false); + } + var DataSize = RarVM.ReadData(Inp); + if (DataSize > RarVM.VM_GLOBALMEMSIZE - RarVM.VM_FIXEDGLOBALSIZE) + { + return (false); + } + var CurSize = StackFilter.Program.GlobalData.Count; + if (CurSize < DataSize + RarVM.VM_FIXEDGLOBALSIZE) + { + // StackFilter->Prg.GlobalData.Add(DataSize+VM_FIXEDGLOBALSIZE-CurSize); + StackFilter.Program.GlobalData.SetSize( + DataSize + RarVM.VM_FIXEDGLOBALSIZE - CurSize + ); + } + var offset = RarVM.VM_FIXEDGLOBALSIZE; + globalData = StackFilter.Program.GlobalData; + for (var I = 0; I < DataSize; I++) + { + if (Inp.Overflow(3)) { return (false); } - int CurSize = StackFilter.Program.GlobalData.Count; - if (CurSize < DataSize + RarVM.VM_FIXEDGLOBALSIZE) - { - // StackFilter->Prg.GlobalData.Add(DataSize+VM_FIXEDGLOBALSIZE-CurSize); - StackFilter.Program.GlobalData.SetSize( - DataSize + RarVM.VM_FIXEDGLOBALSIZE - CurSize - ); - } - int offset = RarVM.VM_FIXEDGLOBALSIZE; - globalData = StackFilter.Program.GlobalData; - for (int I = 0; I < DataSize; I++) - { - if (Inp.Overflow(3)) - { - return (false); - } - globalData[offset + I] = (byte)(Utility.URShift(Inp.GetBits(), 8)); - Inp.AddBits(8); - } - } - return (true); - } - - private void ExecuteCode(VMPreparedProgram Prg) - { - if (Prg.GlobalData.Count > 0) - { - // Prg->InitR[6]=int64to32(WrittenFileSize); - Prg.InitR[6] = (int)(writtenFileSize); - - // rarVM.SetLowEndianValue((uint - // *)&Prg->GlobalData[0x24],int64to32(WrittenFileSize)); - rarVM.SetLowEndianValue(Prg.GlobalData, 0x24, (int)writtenFileSize); - - // rarVM.SetLowEndianValue((uint - // *)&Prg->GlobalData[0x28],int64to32(WrittenFileSize>>32)); - rarVM.SetLowEndianValue( - Prg.GlobalData, - 0x28, - (int)(Utility.URShift(writtenFileSize, 32)) - ); - rarVM.execute(Prg); + globalData[offset + I] = (byte)(Utility.URShift(Inp.GetBits(), 8)); + Inp.AddBits(8); } } + return (true); + } - private void CleanUp() + private void ExecuteCode(VMPreparedProgram Prg) + { + if (Prg.GlobalData.Count > 0) { - if (ppm != null) - { - SubAllocator allocator = ppm.SubAlloc; - if (allocator != null) - { - allocator.StopSubAllocator(); - } - } + // Prg->InitR[6]=int64to32(WrittenFileSize); + Prg.InitR[6] = (int)(writtenFileSize); + + // rarVM.SetLowEndianValue((uint + // *)&Prg->GlobalData[0x24],int64to32(WrittenFileSize)); + rarVM.SetLowEndianValue(Prg.GlobalData, 0x24, (int)writtenFileSize); + + // rarVM.SetLowEndianValue((uint + // *)&Prg->GlobalData[0x28],int64to32(WrittenFileSize>>32)); + rarVM.SetLowEndianValue( + Prg.GlobalData, + 0x28, + (int)(Utility.URShift(writtenFileSize, 32)) + ); + rarVM.execute(Prg); + } + } + + private void CleanUp() + { + if (ppm != null) + { + var allocator = ppm.SubAlloc; + allocator?.StopSubAllocator(); } } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack15.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack15.cs index d463a020..c0257809 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack15.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack15.cs @@ -11,487 +11,461 @@ using System; using System.IO; using SharpCompress.Compressors.Rar.UnpackV1.Decode; -namespace SharpCompress.Compressors.Rar.UnpackV1 +namespace SharpCompress.Compressors.Rar.UnpackV1; + +internal partial class Unpack { - internal partial class Unpack + private int readBorder; + + private bool suspended; + + internal bool unpAllBuf; + + //private ComprDataIO unpIO; + private Stream readStream; + private Stream writeStream; + + internal bool unpSomeRead; + + private int readTop; + + private long destUnpSize; + + private byte[] window; + + private readonly int[] oldDist = new int[4]; + + private int unpPtr, + wrPtr; + + private int oldDistPtr; + + private readonly int[] ChSet = new int[256], + ChSetA = new int[256], + ChSetB = new int[256], + ChSetC = new int[256]; + + private readonly int[] Place = new int[256], + PlaceA = new int[256], + PlaceB = new int[256], + PlaceC = new int[256]; + + private readonly int[] NToPl = new int[256], + NToPlB = new int[256], + NToPlC = new int[256]; + + private int FlagBuf, + AvrPlc, + AvrPlcB, + AvrLn1, + AvrLn2, + AvrLn3; + + private int Buf60, + NumHuf, + StMode, + LCount, + FlagsCnt; + + private int Nhfb, + Nlzb, + MaxDist3; + + private int lastDist, + lastLength; + + private const int STARTL1 = 2; + + private static readonly int[] DecL1 = { - private int readBorder; + 0x8000, + 0xa000, + 0xc000, + 0xd000, + 0xe000, + 0xea00, + 0xee00, + 0xf000, + 0xf200, + 0xf200, + 0xffff + }; - private bool suspended; + private static readonly int[] PosL1 = { 0, 0, 0, 2, 3, 5, 7, 11, 16, 20, 24, 32, 32 }; - internal bool unpAllBuf; + private const int STARTL2 = 3; - //private ComprDataIO unpIO; - private Stream readStream; - private Stream writeStream; + private static readonly int[] DecL2 = + { + 0xa000, + 0xc000, + 0xd000, + 0xe000, + 0xea00, + 0xee00, + 0xf000, + 0xf200, + 0xf240, + 0xffff + }; - internal bool unpSomeRead; + private static readonly int[] PosL2 = { 0, 0, 0, 0, 5, 7, 9, 13, 18, 22, 26, 34, 36 }; - private int readTop; + private const int STARTHF0 = 4; - private long destUnpSize; + private static readonly int[] DecHf0 = + { + 0x8000, + 0xc000, + 0xe000, + 0xf200, + 0xf200, + 0xf200, + 0xf200, + 0xf200, + 0xffff + }; - private byte[] window; + private static readonly int[] PosHf0 = { 0, 0, 0, 0, 0, 8, 16, 24, 33, 33, 33, 33, 33 }; - private readonly int[] oldDist = new int[4]; + private const int STARTHF1 = 5; - private int unpPtr, - wrPtr; + private static readonly int[] DecHf1 = + { + 0x2000, + 0xc000, + 0xe000, + 0xf000, + 0xf200, + 0xf200, + 0xf7e0, + 0xffff + }; - private int oldDistPtr; + private static readonly int[] PosHf1 = { 0, 0, 0, 0, 0, 0, 4, 44, 60, 76, 80, 80, 127 }; - private readonly int[] ChSet = new int[256], - ChSetA = new int[256], - ChSetB = new int[256], - ChSetC = new int[256]; + private const int STARTHF2 = 5; - private readonly int[] Place = new int[256], - PlaceA = new int[256], - PlaceB = new int[256], - PlaceC = new int[256]; + private static readonly int[] DecHf2 = + { + 0x1000, + 0x2400, + 0x8000, + 0xc000, + 0xfa00, + 0xffff, + 0xffff, + 0xffff + }; - private readonly int[] NToPl = new int[256], - NToPlB = new int[256], - NToPlC = new int[256]; + private static readonly int[] PosHf2 = { 0, 0, 0, 0, 0, 0, 2, 7, 53, 117, 233, 0, 0 }; - private int FlagBuf, - AvrPlc, - AvrPlcB, - AvrLn1, - AvrLn2, - AvrLn3; + private const int STARTHF3 = 6; - private int Buf60, - NumHuf, - StMode, - LCount, - FlagsCnt; + private static readonly int[] DecHf3 = + { + 0x800, + 0x2400, + 0xee00, + 0xfe80, + 0xffff, + 0xffff, + 0xffff + }; - private int Nhfb, - Nlzb, - MaxDist3; + private static readonly int[] PosHf3 = { 0, 0, 0, 0, 0, 0, 0, 2, 16, 218, 251, 0, 0 }; - private int lastDist, - lastLength; + private const int STARTHF4 = 8; - private const int STARTL1 = 2; + private static readonly int[] DecHf4 = { 0xff00, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff }; - private static readonly int[] DecL1 = + private static readonly int[] PosHf4 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0 }; + + private static readonly int[] ShortLen1 = + { + 1, + 3, + 4, + 4, + 5, + 6, + 7, + 8, + 8, + 4, + 4, + 5, + 6, + 6, + 4, + 0 + }; + + private static readonly int[] ShortXor1 = + { + 0, + 0xa0, + 0xd0, + 0xe0, + 0xf0, + 0xf8, + 0xfc, + 0xfe, + 0xff, + 0xc0, + 0x80, + 0x90, + 0x98, + 0x9c, + 0xb0 + }; + + private static readonly int[] ShortLen2 = + { + 2, + 3, + 3, + 3, + 4, + 4, + 5, + 6, + 6, + 4, + 4, + 5, + 6, + 6, + 4, + 0 + }; + + private static readonly int[] ShortXor2 = + { + 0, + 0x40, + 0x60, + 0xa0, + 0xd0, + 0xe0, + 0xf0, + 0xf8, + 0xfc, + 0xc0, + 0x80, + 0x90, + 0x98, + 0x9c, + 0xb0 + }; + + private void unpack15(bool solid) + { + if (suspended) { - 0x8000, - 0xa000, - 0xc000, - 0xd000, - 0xe000, - 0xea00, - 0xee00, - 0xf000, - 0xf200, - 0xf200, - 0xffff - }; - - private static readonly int[] PosL1 = { 0, 0, 0, 2, 3, 5, 7, 11, 16, 20, 24, 32, 32 }; - - private const int STARTL2 = 3; - - private static readonly int[] DecL2 = + unpPtr = wrPtr; + } + else { - 0xa000, - 0xc000, - 0xd000, - 0xe000, - 0xea00, - 0xee00, - 0xf000, - 0xf200, - 0xf240, - 0xffff - }; - - private static readonly int[] PosL2 = { 0, 0, 0, 0, 5, 7, 9, 13, 18, 22, 26, 34, 36 }; - - private const int STARTHF0 = 4; - - private static readonly int[] DecHf0 = - { - 0x8000, - 0xc000, - 0xe000, - 0xf200, - 0xf200, - 0xf200, - 0xf200, - 0xf200, - 0xffff - }; - - private static readonly int[] PosHf0 = { 0, 0, 0, 0, 0, 8, 16, 24, 33, 33, 33, 33, 33 }; - - private const int STARTHF1 = 5; - - private static readonly int[] DecHf1 = - { - 0x2000, - 0xc000, - 0xe000, - 0xf000, - 0xf200, - 0xf200, - 0xf7e0, - 0xffff - }; - - private static readonly int[] PosHf1 = { 0, 0, 0, 0, 0, 0, 4, 44, 60, 76, 80, 80, 127 }; - - private const int STARTHF2 = 5; - - private static readonly int[] DecHf2 = - { - 0x1000, - 0x2400, - 0x8000, - 0xc000, - 0xfa00, - 0xffff, - 0xffff, - 0xffff - }; - - private static readonly int[] PosHf2 = { 0, 0, 0, 0, 0, 0, 2, 7, 53, 117, 233, 0, 0 }; - - private const int STARTHF3 = 6; - - private static readonly int[] DecHf3 = - { - 0x800, - 0x2400, - 0xee00, - 0xfe80, - 0xffff, - 0xffff, - 0xffff - }; - - private static readonly int[] PosHf3 = { 0, 0, 0, 0, 0, 0, 0, 2, 16, 218, 251, 0, 0 }; - - private const int STARTHF4 = 8; - - private static readonly int[] DecHf4 = { 0xff00, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff }; - - private static readonly int[] PosHf4 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0 }; - - private static readonly int[] ShortLen1 = - { - 1, - 3, - 4, - 4, - 5, - 6, - 7, - 8, - 8, - 4, - 4, - 5, - 6, - 6, - 4, - 0 - }; - - private static readonly int[] ShortXor1 = - { - 0, - 0xa0, - 0xd0, - 0xe0, - 0xf0, - 0xf8, - 0xfc, - 0xfe, - 0xff, - 0xc0, - 0x80, - 0x90, - 0x98, - 0x9c, - 0xb0 - }; - - private static readonly int[] ShortLen2 = - { - 2, - 3, - 3, - 3, - 4, - 4, - 5, - 6, - 6, - 4, - 4, - 5, - 6, - 6, - 4, - 0 - }; - - private static readonly int[] ShortXor2 = - { - 0, - 0x40, - 0x60, - 0xa0, - 0xd0, - 0xe0, - 0xf0, - 0xf8, - 0xfc, - 0xc0, - 0x80, - 0x90, - 0x98, - 0x9c, - 0xb0 - }; - - private void unpack15(bool solid) - { - if (suspended) + UnpInitData(solid); + oldUnpInitData(solid); + unpReadBuf(); + if (!solid) { - unpPtr = wrPtr; + initHuff(); + unpPtr = 0; } else { - UnpInitData(solid); - oldUnpInitData(solid); - unpReadBuf(); - if (!solid) + unpPtr = wrPtr; + } + --destUnpSize; + } + if (destUnpSize >= 0) + { + getFlagsBuf(); + FlagsCnt = 8; + } + + while (destUnpSize >= 0) + { + unpPtr &= PackDef.MAXWINMASK; + + if (inAddr > readTop - 30 && !unpReadBuf()) + { + break; + } + if (((wrPtr - unpPtr) & PackDef.MAXWINMASK) < 270 && wrPtr != unpPtr) + { + oldUnpWriteBuf(); + if (suspended) { - initHuff(); - unpPtr = 0; + return; + } + } + if (StMode != 0) + { + huffDecode(); + continue; + } + + if (--FlagsCnt < 0) + { + getFlagsBuf(); + FlagsCnt = 7; + } + + if ((FlagBuf & 0x80) != 0) + { + FlagBuf <<= 1; + if (Nlzb > Nhfb) + { + longLZ(); } else - { - unpPtr = wrPtr; - } - --destUnpSize; - } - if (destUnpSize >= 0) - { - getFlagsBuf(); - FlagsCnt = 8; - } - - while (destUnpSize >= 0) - { - unpPtr &= PackDef.MAXWINMASK; - - if (inAddr > readTop - 30 && !unpReadBuf()) - { - break; - } - if (((wrPtr - unpPtr) & PackDef.MAXWINMASK) < 270 && wrPtr != unpPtr) - { - oldUnpWriteBuf(); - if (suspended) - { - return; - } - } - if (StMode != 0) { huffDecode(); - continue; } - + } + else + { + FlagBuf <<= 1; if (--FlagsCnt < 0) { getFlagsBuf(); FlagsCnt = 7; } - if ((FlagBuf & 0x80) != 0) { FlagBuf <<= 1; if (Nlzb > Nhfb) { - longLZ(); + huffDecode(); } else { - huffDecode(); + longLZ(); } } 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(); - } + shortLZ(); } } - oldUnpWriteBuf(); + } + oldUnpWriteBuf(); + } + + private bool unpReadBuf() + { + var dataSize = readTop - inAddr; + if (dataSize < 0) + { + return (false); + } + if (inAddr > MAX_SIZE / 2) + { + if (dataSize > 0) + { + //memmove(InBuf,InBuf+InAddr,DataSize); + // for (int i = 0; i < dataSize; i++) { + // inBuf[i] = inBuf[inAddr + i]; + // } + Array.Copy(InBuf, inAddr, InBuf, 0, dataSize); + } + inAddr = 0; + readTop = dataSize; + } + else + { + dataSize = readTop; } - private bool unpReadBuf() + //int readCode=UnpIO->UnpRead(InBuf+DataSize,(BitInput::MAX_SIZE-DataSize)&~0xf); + var readCode = readStream.Read(InBuf, dataSize, (MAX_SIZE - dataSize) & ~0xf); + if (readCode > 0) { - int dataSize = readTop - inAddr; - if (dataSize < 0) + readTop += readCode; + } + readBorder = readTop - 30; + return (readCode != -1); + } + + private int getShortLen1(int pos) + { + return pos == 1 ? Buf60 + 3 : ShortLen1[pos]; + } + + private int getShortLen2(int pos) + { + return pos == 3 ? Buf60 + 3 : ShortLen2[pos]; + } + + private void shortLZ() + { + int Length, + SaveLength; + int LastDistance; + int Distance; + int DistancePlace; + NumHuf = 0; + + var BitField = GetBits(); + if (LCount == 2) + { + AddBits(1); + if (BitField >= 0x8000) { - return (false); + oldCopyString(lastDist, lastLength); + return; } - if (inAddr > MAX_SIZE / 2) + BitField <<= 1; + LCount = 0; + } + BitField = Utility.URShift(BitField, 8); + if (AvrLn1 < 37) + { + for (Length = 0; ; Length++) { - if (dataSize > 0) + if ( + ( + (BitField ^ ShortXor1[Length]) + & (~(Utility.URShift(0xff, getShortLen1(Length)))) + ) == 0 + ) { - //memmove(InBuf,InBuf+InAddr,DataSize); - // for (int i = 0; i < dataSize; i++) { - // inBuf[i] = inBuf[inAddr + i]; - // } - Array.Copy(InBuf, inAddr, InBuf, 0, dataSize); + break; } - inAddr = 0; - readTop = dataSize; } - else - { - dataSize = readTop; - } - - //int readCode=UnpIO->UnpRead(InBuf+DataSize,(BitInput::MAX_SIZE-DataSize)&~0xf); - int readCode = readStream.Read(InBuf, dataSize, (MAX_SIZE - dataSize) & ~0xf); - if (readCode > 0) - { - readTop += readCode; - } - readBorder = readTop - 30; - return (readCode != -1); + AddBits(getShortLen1(Length)); } - - private int getShortLen1(int pos) + else { - return pos == 1 ? Buf60 + 3 : ShortLen1[pos]; - } - - private int getShortLen2(int pos) - { - return pos == 3 ? Buf60 + 3 : ShortLen2[pos]; - } - - private void shortLZ() - { - int Length, - SaveLength; - int LastDistance; - int Distance; - int DistancePlace; - NumHuf = 0; - - int BitField = GetBits(); - if (LCount == 2) + for (Length = 0; ; Length++) { - AddBits(1); - if (BitField >= 0x8000) + if (((BitField ^ ShortXor2[Length]) & (~(0xff >> getShortLen2(Length)))) == 0) { - oldCopyString(lastDist, lastLength); - return; + break; } - BitField <<= 1; + } + AddBits(getShortLen2(Length)); + } + + if (Length >= 9) + { + if (Length == 9) + { + LCount++; + oldCopyString(lastDist, lastLength); + return; + } + if (Length == 14) + { LCount = 0; - } - BitField = Utility.URShift(BitField, 8); - if (AvrLn1 < 37) - { - for (Length = 0; ; Length++) - { - if ( - ( - (BitField ^ ShortXor1[Length]) - & (~(Utility.URShift(0xff, getShortLen1(Length)))) - ) == 0 - ) - { - break; - } - } - AddBits(getShortLen1(Length)); - } - else - { - for (Length = 0; ; Length++) - { - if (((BitField ^ ShortXor2[Length]) & (~(0xff >> getShortLen2(Length)))) == 0) - { - break; - } - } - AddBits(getShortLen2(Length)); - } - - if (Length >= 9) - { - if (Length == 9) - { - LCount++; - oldCopyString(lastDist, lastLength); - return; - } - if (Length == 14) - { - LCount = 0; - Length = decodeNum(GetBits(), STARTL2, DecL2, PosL2) + 5; - Distance = (GetBits() >> 1) | 0x8000; - AddBits(15); - lastLength = Length; - lastDist = Distance; - oldCopyString(Distance, Length); - return; - } - - LCount = 0; - SaveLength = Length; - Distance = oldDist[(oldDistPtr - (Length - 9)) & 3]; - Length = decodeNum(GetBits(), STARTL1, DecL1, PosL1) + 2; - if (Length == 0x101 && SaveLength == 10) - { - Buf60 ^= 1; - return; - } - if (Distance > 256) - { - Length++; - } - if (Distance >= MaxDist3) - { - Length++; - } - - oldDist[oldDistPtr++] = Distance; - oldDistPtr = oldDistPtr & 3; + Length = decodeNum(GetBits(), STARTL2, DecL2, PosL2) + 5; + Distance = (GetBits() >> 1) | 0x8000; + AddBits(15); lastLength = Length; lastDist = Distance; oldCopyString(Distance, Length); @@ -499,372 +473,397 @@ namespace SharpCompress.Compressors.Rar.UnpackV1 } LCount = 0; - AvrLn1 += Length; - AvrLn1 -= (AvrLn1 >> 4); - - DistancePlace = decodeNum(GetBits(), STARTHF2, DecHf2, PosHf2) & 0xff; - Distance = ChSetA[DistancePlace]; - if (--DistancePlace != -1) + SaveLength = Length; + Distance = oldDist[(oldDistPtr - (Length - 9)) & 3]; + Length = decodeNum(GetBits(), STARTL1, DecL1, PosL1) + 2; + if (Length == 0x101 && SaveLength == 10) { - PlaceA[Distance]--; - LastDistance = ChSetA[DistancePlace]; - PlaceA[LastDistance]++; - ChSetA[DistancePlace + 1] = LastDistance; - ChSetA[DistancePlace] = Distance; + Buf60 ^= 1; + return; } - Length += 2; - oldDist[oldDistPtr++] = ++Distance; - oldDistPtr = oldDistPtr & 3; - lastLength = Length; - lastDist = Distance; - oldCopyString(Distance, Length); - } - - private void longLZ() - { - int Length; - int Distance; - int DistancePlace, - NewDistancePlace; - int OldAvr2, - OldAvr3; - - NumHuf = 0; - Nlzb += 16; - if (Nlzb > 0xff) + if (Distance > 256) { - Nlzb = 0x90; - Nhfb = Utility.URShift(Nhfb, 1); + Length++; } - OldAvr2 = AvrLn2; - - int BitField = GetBits(); - if (AvrLn2 >= 122) - { - Length = decodeNum(BitField, STARTL2, DecL2, PosL2); - } - else - { - if (AvrLn2 >= 64) - { - Length = decodeNum(BitField, STARTL1, DecL1, PosL1); - } - else - { - if (BitField < 0x100) - { - Length = BitField; - AddBits(16); - } - else - { - for (Length = 0; ((BitField << Length) & 0x8000) == 0; Length++) - { - ; - } - AddBits(Length + 1); - } - } - } - AvrLn2 += Length; - AvrLn2 -= Utility.URShift(AvrLn2, 5); - - BitField = GetBits(); - if (AvrPlcB > 0x28ff) - { - DistancePlace = decodeNum(BitField, STARTHF2, DecHf2, PosHf2); - } - else - { - if (AvrPlcB > 0x6ff) - { - DistancePlace = decodeNum(BitField, STARTHF1, DecHf1, PosHf1); - } - else - { - DistancePlace = decodeNum(BitField, STARTHF0, DecHf0, PosHf0); - } - } - AvrPlcB += DistancePlace; - AvrPlcB -= (AvrPlcB >> 8); - while (true) - { - Distance = ChSetB[DistancePlace & 0xff]; - NewDistancePlace = NToPlB[Distance++ & 0xff]++; - if ((Distance & 0xff) == 0) - { - corrHuff(ChSetB, NToPlB); - } - else - { - break; - } - } - - ChSetB[DistancePlace] = ChSetB[NewDistancePlace]; - ChSetB[NewDistancePlace] = Distance; - - Distance = Utility.URShift(((Distance & 0xff00) | (Utility.URShift(GetBits(), 8))), 1); - AddBits(7); - - OldAvr3 = AvrLn3; - if (Length != 1 && Length != 4) - { - if (Length == 0 && Distance <= MaxDist3) - { - AvrLn3++; - AvrLn3 -= (AvrLn3 >> 8); - } - else - { - if (AvrLn3 > 0) - { - AvrLn3--; - } - } - } - Length += 3; if (Distance >= MaxDist3) { Length++; } - if (Distance <= 256) - { - Length += 8; - } - if (OldAvr3 > 0xb0 || AvrPlc >= 0x2a00 && OldAvr2 < 0x40) - { - MaxDist3 = 0x7f00; - } - else - { - MaxDist3 = 0x2001; - } + oldDist[oldDistPtr++] = Distance; - oldDistPtr = oldDistPtr & 3; + oldDistPtr &= 3; lastLength = Length; lastDist = Distance; oldCopyString(Distance, Length); + return; } - private void huffDecode() + LCount = 0; + AvrLn1 += Length; + AvrLn1 -= (AvrLn1 >> 4); + + DistancePlace = decodeNum(GetBits(), STARTHF2, DecHf2, PosHf2) & 0xff; + Distance = ChSetA[DistancePlace]; + if (--DistancePlace != -1) { - int CurByte, - NewBytePlace; - int Length; - int Distance; - int BytePlace; + PlaceA[Distance]--; + LastDistance = ChSetA[DistancePlace]; + PlaceA[LastDistance]++; + ChSetA[DistancePlace + 1] = LastDistance; + ChSetA[DistancePlace] = Distance; + } + Length += 2; + oldDist[oldDistPtr++] = ++Distance; + oldDistPtr &= 3; + lastLength = Length; + lastDist = Distance; + oldCopyString(Distance, Length); + } - int BitField = GetBits(); + private void longLZ() + { + int Length; + int Distance; + int DistancePlace, + NewDistancePlace; + int OldAvr2, + OldAvr3; - if (AvrPlc > 0x75ff) + NumHuf = 0; + Nlzb += 16; + if (Nlzb > 0xff) + { + Nlzb = 0x90; + Nhfb = Utility.URShift(Nhfb, 1); + } + OldAvr2 = AvrLn2; + + var BitField = GetBits(); + if (AvrLn2 >= 122) + { + Length = decodeNum(BitField, STARTL2, DecL2, PosL2); + } + else + { + if (AvrLn2 >= 64) { - BytePlace = decodeNum(BitField, STARTHF4, DecHf4, PosHf4); + Length = decodeNum(BitField, STARTL1, DecL1, PosL1); } else { - if (AvrPlc > 0x5dff) + if (BitField < 0x100) { - BytePlace = decodeNum(BitField, STARTHF3, DecHf3, PosHf3); + Length = BitField; + AddBits(16); } else { - if (AvrPlc > 0x35ff) + for (Length = 0; ((BitField << Length) & 0x8000) == 0; Length++) { - BytePlace = decodeNum(BitField, STARTHF2, DecHf2, PosHf2); + ; + } + AddBits(Length + 1); + } + } + } + AvrLn2 += Length; + AvrLn2 -= Utility.URShift(AvrLn2, 5); + + BitField = GetBits(); + if (AvrPlcB > 0x28ff) + { + DistancePlace = decodeNum(BitField, STARTHF2, DecHf2, PosHf2); + } + else + { + if (AvrPlcB > 0x6ff) + { + DistancePlace = decodeNum(BitField, STARTHF1, DecHf1, PosHf1); + } + else + { + DistancePlace = decodeNum(BitField, STARTHF0, DecHf0, PosHf0); + } + } + AvrPlcB += DistancePlace; + AvrPlcB -= (AvrPlcB >> 8); + while (true) + { + Distance = ChSetB[DistancePlace & 0xff]; + NewDistancePlace = NToPlB[Distance++ & 0xff]++; + if ((Distance & 0xff) == 0) + { + corrHuff(ChSetB, NToPlB); + } + else + { + break; + } + } + + ChSetB[DistancePlace] = ChSetB[NewDistancePlace]; + ChSetB[NewDistancePlace] = Distance; + + Distance = Utility.URShift(((Distance & 0xff00) | (Utility.URShift(GetBits(), 8))), 1); + AddBits(7); + + OldAvr3 = AvrLn3; + if (Length != 1 && Length != 4) + { + if (Length == 0 && Distance <= MaxDist3) + { + AvrLn3++; + AvrLn3 -= (AvrLn3 >> 8); + } + else + { + if (AvrLn3 > 0) + { + AvrLn3--; + } + } + } + Length += 3; + if (Distance >= MaxDist3) + { + Length++; + } + if (Distance <= 256) + { + Length += 8; + } + if (OldAvr3 > 0xb0 || AvrPlc >= 0x2a00 && OldAvr2 < 0x40) + { + MaxDist3 = 0x7f00; + } + else + { + MaxDist3 = 0x2001; + } + oldDist[oldDistPtr++] = Distance; + oldDistPtr &= 3; + lastLength = Length; + lastDist = Distance; + oldCopyString(Distance, Length); + } + + private void huffDecode() + { + int CurByte, + NewBytePlace; + int Length; + int Distance; + int BytePlace; + + var BitField = GetBits(); + + if (AvrPlc > 0x75ff) + { + BytePlace = decodeNum(BitField, STARTHF4, DecHf4, PosHf4); + } + else + { + if (AvrPlc > 0x5dff) + { + BytePlace = decodeNum(BitField, STARTHF3, DecHf3, PosHf3); + } + else + { + if (AvrPlc > 0x35ff) + { + BytePlace = decodeNum(BitField, STARTHF2, DecHf2, PosHf2); + } + else + { + if (AvrPlc > 0x0dff) + { + BytePlace = decodeNum(BitField, STARTHF1, DecHf1, PosHf1); } else { - if (AvrPlc > 0x0dff) - { - BytePlace = decodeNum(BitField, STARTHF1, DecHf1, PosHf1); - } - else - { - BytePlace = decodeNum(BitField, STARTHF0, DecHf0, PosHf0); - } + BytePlace = decodeNum(BitField, STARTHF0, DecHf0, PosHf0); } } } - BytePlace &= 0xff; - if (StMode != 0) + } + BytePlace &= 0xff; + if (StMode != 0) + { + if (BytePlace == 0 && BitField > 0xfff) { - if (BytePlace == 0 && BitField > 0xfff) + BytePlace = 0x100; + } + if (--BytePlace == -1) + { + BitField = GetBits(); + AddBits(1); + if ((BitField & 0x8000) != 0) { - BytePlace = 0x100; - } - if (--BytePlace == -1) - { - BitField = GetBits(); - AddBits(1); - if ((BitField & 0x8000) != 0) - { - NumHuf = StMode = 0; - return; - } - Length = (BitField & 0x4000) != 0 ? 4 : 3; - AddBits(1); - Distance = decodeNum(GetBits(), STARTHF2, DecHf2, PosHf2); - Distance = (Distance << 5) | (Utility.URShift(GetBits(), 11)); - AddBits(5); - oldCopyString(Distance, Length); + NumHuf = StMode = 0; return; } + Length = (BitField & 0x4000) != 0 ? 4 : 3; + AddBits(1); + Distance = decodeNum(GetBits(), STARTHF2, DecHf2, PosHf2); + Distance = (Distance << 5) | (Utility.URShift(GetBits(), 11)); + AddBits(5); + oldCopyString(Distance, Length); + return; + } + } + else + { + if (NumHuf++ >= 16 && FlagsCnt == 0) + { + StMode = 1; + } + } + AvrPlc += BytePlace; + AvrPlc -= Utility.URShift(AvrPlc, 8); + Nhfb += 16; + if (Nhfb > 0xff) + { + Nhfb = 0x90; + Nlzb = Utility.URShift(Nlzb, 1); + } + + window[unpPtr++] = (byte)(Utility.URShift(ChSet[BytePlace], 8)); + --destUnpSize; + + while (true) + { + CurByte = ChSet[BytePlace]; + NewBytePlace = NToPl[CurByte++ & 0xff]++; + if ((CurByte & 0xff) > 0xa1) + { + corrHuff(ChSet, NToPl); } else { - if (NumHuf++ >= 16 && FlagsCnt == 0) - { - StMode = 1; - } - } - AvrPlc += BytePlace; - AvrPlc -= Utility.URShift(AvrPlc, 8); - Nhfb += 16; - if (Nhfb > 0xff) - { - Nhfb = 0x90; - Nlzb = Utility.URShift(Nlzb, 1); - } - - window[unpPtr++] = (byte)(Utility.URShift(ChSet[BytePlace], 8)); - --destUnpSize; - - while (true) - { - CurByte = ChSet[BytePlace]; - NewBytePlace = NToPl[CurByte++ & 0xff]++; - if ((CurByte & 0xff) > 0xa1) - { - corrHuff(ChSet, NToPl); - } - else - { - break; - } - } - - ChSet[BytePlace] = ChSet[NewBytePlace]; - ChSet[NewBytePlace] = CurByte; - } - - private void getFlagsBuf() - { - int Flags, - NewFlagsPlace; - int FlagsPlace = decodeNum(GetBits(), STARTHF2, DecHf2, PosHf2); - - while (true) - { - Flags = ChSetC[FlagsPlace]; - FlagBuf = Utility.URShift(Flags, 8); - NewFlagsPlace = NToPlC[Flags++ & 0xff]++; - if ((Flags & 0xff) != 0) - { - break; - } - corrHuff(ChSetC, NToPlC); - } - - ChSetC[FlagsPlace] = ChSetC[NewFlagsPlace]; - ChSetC[NewFlagsPlace] = Flags; - } - - private void oldUnpInitData(bool Solid) - { - if (!Solid) - { - AvrPlcB = AvrLn1 = AvrLn2 = AvrLn3 = NumHuf = Buf60 = 0; - AvrPlc = 0x3500; - MaxDist3 = 0x2001; - Nhfb = Nlzb = 0x80; - } - FlagsCnt = 0; - FlagBuf = 0; - StMode = 0; - LCount = 0; - readTop = 0; - } - - private void initHuff() - { - for (int I = 0; I < 256; I++) - { - Place[I] = PlaceA[I] = PlaceB[I] = I; - PlaceC[I] = (~I + 1) & 0xff; - ChSet[I] = ChSetB[I] = I << 8; - ChSetA[I] = I; - ChSetC[I] = ((~I + 1) & 0xff) << 8; - } - - new Span(NToPl).Clear(); // memset(NToPl,0,sizeof(NToPl)); - new Span(NToPlB).Clear(); // memset(NToPlB,0,sizeof(NToPlB)); - new Span(NToPlC).Clear(); // memset(NToPlC,0,sizeof(NToPlC)); - corrHuff(ChSetB, NToPlB); - } - - private void corrHuff(int[] CharSet, int[] NumToPlace) - { - int I, - J, - pos = 0; - for (I = 7; I >= 0; I--) - { - for (J = 0; J < 32; J++, pos++) - { - CharSet[pos] = ((CharSet[pos] & ~0xff) | I); // *CharSet=(*CharSet - - // & ~0xff) | I; - } - } - new Span(NumToPlace).Clear(); // memset(NumToPlace,0,sizeof(NToPl)); - for (I = 6; I >= 0; I--) - { - NumToPlace[I] = (7 - I) * 32; + break; } } - private void oldCopyString(int Distance, int Length) + ChSet[BytePlace] = ChSet[NewBytePlace]; + ChSet[NewBytePlace] = CurByte; + } + + private void getFlagsBuf() + { + int Flags, + NewFlagsPlace; + var FlagsPlace = decodeNum(GetBits(), STARTHF2, DecHf2, PosHf2); + + while (true) { - destUnpSize -= Length; - while ((Length--) != 0) + Flags = ChSetC[FlagsPlace]; + FlagBuf = Utility.URShift(Flags, 8); + NewFlagsPlace = NToPlC[Flags++ & 0xff]++; + if ((Flags & 0xff) != 0) { - window[unpPtr] = window[(unpPtr - Distance) & PackDef.MAXWINMASK]; - unpPtr = (unpPtr + 1) & PackDef.MAXWINMASK; + break; } + corrHuff(ChSetC, NToPlC); } - private int decodeNum(int Num, int StartPos, int[] DecTab, int[] PosTab) + ChSetC[FlagsPlace] = ChSetC[NewFlagsPlace]; + ChSetC[NewFlagsPlace] = Flags; + } + + private void oldUnpInitData(bool Solid) + { + if (!Solid) { - int I; - for (Num &= 0xfff0, I = 0; DecTab[I] <= Num; I++) - { - StartPos++; - } - AddBits(StartPos); - return ( - (Utility.URShift((Num - (I != 0 ? DecTab[I - 1] : 0)), (16 - StartPos))) - + PosTab[StartPos] - ); + AvrPlcB = AvrLn1 = AvrLn2 = AvrLn3 = NumHuf = Buf60 = 0; + AvrPlc = 0x3500; + MaxDist3 = 0x2001; + Nhfb = Nlzb = 0x80; + } + FlagsCnt = 0; + FlagBuf = 0; + StMode = 0; + LCount = 0; + readTop = 0; + } + + private void initHuff() + { + for (var I = 0; I < 256; I++) + { + Place[I] = PlaceA[I] = PlaceB[I] = I; + PlaceC[I] = (~I + 1) & 0xff; + ChSet[I] = ChSetB[I] = I << 8; + ChSetA[I] = I; + ChSetC[I] = ((~I + 1) & 0xff) << 8; } - private void oldUnpWriteBuf() + new Span(NToPl).Clear(); // memset(NToPl,0,sizeof(NToPl)); + new Span(NToPlB).Clear(); // memset(NToPlB,0,sizeof(NToPlB)); + new Span(NToPlC).Clear(); // memset(NToPlC,0,sizeof(NToPlC)); + corrHuff(ChSetB, NToPlB); + } + + private void corrHuff(int[] CharSet, int[] NumToPlace) + { + int I, + J, + pos = 0; + for (I = 7; I >= 0; I--) { - if (unpPtr != wrPtr) + for (J = 0; J < 32; J++, pos++) { - unpSomeRead = true; + CharSet[pos] = ((CharSet[pos] & ~0xff) | I); // *CharSet=(*CharSet + + // & ~0xff) | I; } - if (unpPtr < wrPtr) - { - writeStream.Write(window, wrPtr, -wrPtr & PackDef.MAXWINMASK); - writeStream.Write(window, 0, unpPtr); - unpAllBuf = true; - } - else - { - writeStream.Write(window, wrPtr, unpPtr - wrPtr); - } - wrPtr = unpPtr; + } + new Span(NumToPlace).Clear(); // memset(NumToPlace,0,sizeof(NToPl)); + for (I = 6; I >= 0; I--) + { + NumToPlace[I] = (7 - I) * 32; } } + + private void oldCopyString(int Distance, int Length) + { + destUnpSize -= Length; + while ((Length--) != 0) + { + window[unpPtr] = window[(unpPtr - Distance) & PackDef.MAXWINMASK]; + unpPtr = (unpPtr + 1) & PackDef.MAXWINMASK; + } + } + + private int decodeNum(int Num, int StartPos, int[] DecTab, int[] PosTab) + { + int I; + for (Num &= 0xfff0, I = 0; DecTab[I] <= Num; I++) + { + StartPos++; + } + AddBits(StartPos); + return ( + (Utility.URShift((Num - (I != 0 ? DecTab[I - 1] : 0)), (16 - StartPos))) + + PosTab[StartPos] + ); + } + + private void oldUnpWriteBuf() + { + if (unpPtr != wrPtr) + { + unpSomeRead = true; + } + if (unpPtr < wrPtr) + { + writeStream.Write(window, wrPtr, -wrPtr & PackDef.MAXWINMASK); + writeStream.Write(window, 0, unpPtr); + unpAllBuf = true; + } + else + { + writeStream.Write(window, wrPtr, unpPtr - wrPtr); + } + wrPtr = unpPtr; + } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs index fa28fc0d..1b882545 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs @@ -10,667 +10,666 @@ using System; using SharpCompress.Compressors.Rar.UnpackV1.Decode; -namespace SharpCompress.Compressors.Rar.UnpackV1 +namespace SharpCompress.Compressors.Rar.UnpackV1; + +internal partial class Unpack { - internal partial class Unpack + private readonly MultDecode[] MD = new MultDecode[4]; + + private readonly byte[] UnpOldTable20 = new byte[PackDef.MC20 * 4]; + + private int UnpAudioBlock, + UnpChannels, + UnpCurChannel, + UnpChannelDelta; + + private readonly AudioVariables[] AudV = new AudioVariables[4]; + + private readonly LitDecode LD = new LitDecode(); + + private readonly DistDecode DD = new DistDecode(); + + private readonly LowDistDecode LDD = new LowDistDecode(); + + private readonly RepDecode RD = new RepDecode(); + + private readonly BitDecode BD = new BitDecode(); + + private static readonly int[] LDecode = { - private readonly MultDecode[] MD = new MultDecode[4]; + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 10, + 12, + 14, + 16, + 20, + 24, + 28, + 32, + 40, + 48, + 56, + 64, + 80, + 96, + 112, + 128, + 160, + 192, + 224 + }; - private readonly byte[] UnpOldTable20 = new byte[PackDef.MC20 * 4]; - - private int UnpAudioBlock, - UnpChannels, - UnpCurChannel, - UnpChannelDelta; - - private readonly AudioVariables[] AudV = new AudioVariables[4]; - - private readonly LitDecode LD = new LitDecode(); - - private readonly DistDecode DD = new DistDecode(); - - private readonly LowDistDecode LDD = new LowDistDecode(); - - private readonly RepDecode RD = new RepDecode(); - - private readonly BitDecode BD = new BitDecode(); - - private static readonly int[] LDecode = + private static ReadOnlySpan LBits => + new byte[] { 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, 1, 2, + 2, + 2, + 2, + 3, + 3, + 3, 3, 4, + 4, + 4, + 4, 5, - 6, - 7, - 8, - 10, - 12, - 14, - 16, - 20, - 24, - 28, - 32, - 40, - 48, - 56, - 64, - 80, - 96, - 112, - 128, - 160, - 192, - 224 + 5, + 5, + 5 }; - private static ReadOnlySpan LBits => - new byte[] + private static readonly int[] DDecode = + { + 0, + 1, + 2, + 3, + 4, + 6, + 8, + 12, + 16, + 24, + 32, + 48, + 64, + 96, + 128, + 192, + 256, + 384, + 512, + 768, + 1024, + 1536, + 2048, + 3072, + 4096, + 6144, + 8192, + 12288, + 16384, + 24576, + 32768, + 49152, + 65536, + 98304, + 131072, + 196608, + 262144, + 327680, + 393216, + 458752, + 524288, + 589824, + 655360, + 720896, + 786432, + 851968, + 917504, + 983040 + }; + + private static readonly int[] DBits = + { + 0, + 0, + 0, + 0, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 5, + 6, + 6, + 7, + 7, + 8, + 8, + 9, + 9, + 10, + 10, + 11, + 11, + 12, + 12, + 13, + 13, + 14, + 14, + 15, + 15, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16 + }; + + private static readonly int[] SDDecode = { 0, 4, 8, 16, 32, 64, 128, 192 }; + + private static readonly int[] SDBits = { 2, 2, 3, 4, 5, 6, 6, 6 }; + + private void unpack20(bool solid) + { + int Bits; + + if (suspended) + { + unpPtr = wrPtr; + } + else + { + UnpInitData(solid); + if (!unpReadBuf()) { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 4, - 4, - 4, - 4, - 5, - 5, - 5, - 5 - }; - - private static readonly int[] DDecode = - { - 0, - 1, - 2, - 3, - 4, - 6, - 8, - 12, - 16, - 24, - 32, - 48, - 64, - 96, - 128, - 192, - 256, - 384, - 512, - 768, - 1024, - 1536, - 2048, - 3072, - 4096, - 6144, - 8192, - 12288, - 16384, - 24576, - 32768, - 49152, - 65536, - 98304, - 131072, - 196608, - 262144, - 327680, - 393216, - 458752, - 524288, - 589824, - 655360, - 720896, - 786432, - 851968, - 917504, - 983040 - }; - - private static readonly int[] DBits = - { - 0, - 0, - 0, - 0, - 1, - 1, - 2, - 2, - 3, - 3, - 4, - 4, - 5, - 5, - 6, - 6, - 7, - 7, - 8, - 8, - 9, - 9, - 10, - 10, - 11, - 11, - 12, - 12, - 13, - 13, - 14, - 14, - 15, - 15, - 16, - 16, - 16, - 16, - 16, - 16, - 16, - 16, - 16, - 16, - 16, - 16, - 16, - 16 - }; - - private static readonly int[] SDDecode = { 0, 4, 8, 16, 32, 64, 128, 192 }; - - private static readonly int[] SDBits = { 2, 2, 3, 4, 5, 6, 6, 6 }; - - private void unpack20(bool solid) - { - int Bits; - - if (suspended) - { - unpPtr = wrPtr; + return; } - else + if (!solid) { - UnpInitData(solid); - if (!unpReadBuf()) + if (!ReadTables20()) { return; } - if (!solid) + } + --destUnpSize; + } + + while (destUnpSize >= 0) + { + unpPtr &= PackDef.MAXWINMASK; + + if (inAddr > readTop - 30) + { + if (!unpReadBuf()) + { + break; + } + } + if (((wrPtr - unpPtr) & PackDef.MAXWINMASK) < 270 && wrPtr != unpPtr) + { + oldUnpWriteBuf(); + if (suspended) + { + return; + } + } + if (UnpAudioBlock != 0) + { + var AudioNumber = this.decodeNumber(MD[UnpCurChannel]); + + if (AudioNumber == 256) { if (!ReadTables20()) - { - return; - } - } - --destUnpSize; - } - - while (destUnpSize >= 0) - { - unpPtr &= PackDef.MAXWINMASK; - - if (inAddr > readTop - 30) - { - if (!unpReadBuf()) { break; } - } - if (((wrPtr - unpPtr) & PackDef.MAXWINMASK) < 270 && wrPtr != unpPtr) - { - oldUnpWriteBuf(); - if (suspended) - { - return; - } - } - if (UnpAudioBlock != 0) - { - int AudioNumber = this.decodeNumber(MD[UnpCurChannel]); - - if (AudioNumber == 256) - { - if (!ReadTables20()) - { - break; - } - continue; - } - window[unpPtr++] = DecodeAudio(AudioNumber); - if (++UnpCurChannel == UnpChannels) - { - UnpCurChannel = 0; - } - --destUnpSize; continue; } - - int Number = this.decodeNumber(LD); - if (Number < 256) + window[unpPtr++] = DecodeAudio(AudioNumber); + if (++UnpCurChannel == UnpChannels) { - window[unpPtr++] = (byte)Number; - --destUnpSize; - continue; + UnpCurChannel = 0; } - if (Number > 269) + --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) { - int Length = LDecode[Number -= 270] + 3; - if ((Bits = LBits[Number]) > 0) - { - Length += Utility.URShift(GetBits(), (16 - Bits)); - AddBits(Bits); - } + Length += Utility.URShift(GetBits(), (16 - Bits)); + AddBits(Bits); + } - int DistNumber = this.decodeNumber(DD); - int Distance = DDecode[DistNumber] + 1; - if ((Bits = DBits[DistNumber]) > 0) - { - Distance += 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 (!ReadTables20()) + { + 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 >= 0x40000L) + if (Distance >= 0x40000) { Length++; } } - - CopyString20(Length, Distance); - continue; - } - if (Number == 269) - { - if (!ReadTables20()) - { - break; - } - continue; - } - if (Number == 256) - { - CopyString20(lastLength, lastDist); - continue; - } - if (Number < 261) - { - int Distance = oldDist[(oldDistPtr - (Number - 256)) & 3]; - int LengthNumber = this.decodeNumber(RD); - int 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) - { - int Distance = SDDecode[Number -= 261] + 1; - if ((Bits = SDBits[Number]) > 0) - { - Distance += Utility.URShift(GetBits(), (16 - Bits)); - AddBits(Bits); - } - CopyString20(2, Distance); } + CopyString20(Length, Distance); + continue; } - ReadLastTables(); - oldUnpWriteBuf(); - } - - private void CopyString20(int Length, int Distance) - { - lastDist = oldDist[oldDistPtr++ & 3] = Distance; - lastLength = Length; - destUnpSize -= Length; - - int DestPtr = unpPtr - Distance; - if (DestPtr < PackDef.MAXWINSIZE - 300 && unpPtr < PackDef.MAXWINSIZE - 300) + if (Number < 270) { - window[unpPtr++] = window[DestPtr++]; - window[unpPtr++] = window[DestPtr++]; - while (Length > 2) + var Distance = SDDecode[Number -= 261] + 1; + if ((Bits = SDBits[Number]) > 0) { - Length--; - window[unpPtr++] = window[DestPtr++]; - } - } - else - { - while ((Length--) != 0) - { - window[unpPtr] = window[DestPtr++ & PackDef.MAXWINMASK]; - unpPtr = (unpPtr + 1) & PackDef.MAXWINMASK; + Distance += Utility.URShift(GetBits(), (16 - Bits)); + AddBits(Bits); } + CopyString20(2, Distance); } } + ReadLastTables(); + oldUnpWriteBuf(); + } - private bool ReadTables20() + private void CopyString20(int Length, int Distance) + { + lastDist = oldDist[oldDistPtr++ & 3] = Distance; + lastLength = Length; + destUnpSize -= Length; + + var DestPtr = unpPtr - Distance; + if (DestPtr < PackDef.MAXWINSIZE - 300 && unpPtr < PackDef.MAXWINSIZE - 300) { - byte[] BitLength = new byte[PackDef.BC20]; - byte[] Table = new byte[PackDef.MC20 * 4]; - int TableSize, - N, - I; - if (inAddr > readTop - 25) + window[unpPtr++] = window[DestPtr++]; + window[unpPtr++] = window[DestPtr++]; + while (Length > 2) + { + Length--; + window[unpPtr++] = window[DestPtr++]; + } + } + else + { + while ((Length--) != 0) + { + window[unpPtr] = window[DestPtr++ & PackDef.MAXWINMASK]; + unpPtr = (unpPtr + 1) & PackDef.MAXWINMASK; + } + } + } + + private bool ReadTables20() + { + var BitLength = new byte[PackDef.BC20]; + var Table = new byte[PackDef.MC20 * 4]; + int TableSize, + N, + I; + if (inAddr > readTop - 25) + { + if (!unpReadBuf()) + { + return (false); + } + } + var BitField = GetBits(); + UnpAudioBlock = (BitField & 0x8000); + + if (0 == (BitField & 0x4000)) + { + // memset(UnpOldTable20,0,sizeof(UnpOldTable20)); + 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 (!unpReadBuf()) { return (false); } } - int BitField = GetBits(); - UnpAudioBlock = (BitField & 0x8000); - - if (0 == (BitField & 0x4000)) + var Number = this.decodeNumber(BD); + if (Number < 16) { - // memset(UnpOldTable20,0,sizeof(UnpOldTable20)); - new Span(UnpOldTable20).Clear(); + Table[I] = (byte)((Number + UnpOldTable20[I]) & 0xf); + I++; } - AddBits(2); - - if (UnpAudioBlock != 0) + else if (Number == 16) { - UnpChannels = ((Utility.URShift(BitField, 12)) & 3) + 1; - if (UnpCurChannel >= UnpChannels) - { - UnpCurChannel = 0; - } + N = (Utility.URShift(GetBits(), 14)) + 3; 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) + while (N-- > 0 && I < TableSize) { - if (!unpReadBuf()) - { - return (false); - } - } - int Number = this.decodeNumber(BD); - if (Number < 16) - { - Table[I] = (byte)((Number + UnpOldTable20[I]) & 0xf); + Table[I] = Table[I - 1]; I++; } - else if (Number == 16) + } + else + { + if (Number == 17) { - N = (Utility.URShift(GetBits(), 14)) + 3; - AddBits(2); - while (N-- > 0 && I < TableSize) - { - Table[I] = Table[I - 1]; - I++; - } + N = (Utility.URShift(GetBits(), 13)) + 3; + AddBits(3); } 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; - } + N = (Utility.URShift(GetBits(), 9)) + 11; + AddBits(7); + } + while (N-- > 0 && I < TableSize) + { + Table[I++] = 0; } } - if (inAddr > readTop) + } + if (inAddr > readTop) + { + return (true); + } + if (UnpAudioBlock != 0) + { + for (I = 0; I < UnpChannels; I++) { - return (true); + 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 + ); + } + + // memcpy(UnpOldTable20,Table,sizeof(UnpOldTable20)); + for (var i = 0; i < UnpOldTable20.Length; i++) + { + UnpOldTable20[i] = Table[i]; + } + return (true); + } + + private void unpInitData20(bool Solid) + { + if (!Solid) + { + UnpChannelDelta = UnpCurChannel = 0; + UnpChannels = 1; + + // memset(AudV,0,sizeof(AudV)); + AudV[0] = new AudioVariables(); + AudV[1] = new AudioVariables(); + AudV[2] = new AudioVariables(); + AudV[3] = new AudioVariables(); + + // memset(UnpOldTable20,0,sizeof(UnpOldTable20)); + new Span(UnpOldTable20).Clear(); + } + } + + private void ReadLastTables() + { + if (readTop >= inAddr + 5) + { if (UnpAudioBlock != 0) { - for (I = 0; I < UnpChannels; I++) + if (this.decodeNumber(MD[UnpCurChannel]) == 256) { - UnpackUtility.makeDecodeTables(Table, I * PackDef.MC20, MD[I], PackDef.MC20); + ReadTables20(); } } 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 - ); - } - - // memcpy(UnpOldTable20,Table,sizeof(UnpOldTable20)); - for (int i = 0; i < UnpOldTable20.Length; i++) - { - UnpOldTable20[i] = Table[i]; - } - return (true); - } - - private void unpInitData20(bool Solid) - { - if (!Solid) - { - UnpChannelDelta = UnpCurChannel = 0; - UnpChannels = 1; - - // memset(AudV,0,sizeof(AudV)); - AudV[0] = new AudioVariables(); - AudV[1] = new AudioVariables(); - AudV[2] = new AudioVariables(); - AudV[3] = new AudioVariables(); - - // memset(UnpOldTable20,0,sizeof(UnpOldTable20)); - new Span(UnpOldTable20).Clear(); - } - } - - private void ReadLastTables() - { - if (readTop >= inAddr + 5) - { - if (UnpAudioBlock != 0) + if (this.decodeNumber(LD) == 269) { - if (this.decodeNumber(MD[UnpCurChannel]) == 256) - { - ReadTables20(); - } - } - else - { - if (this.decodeNumber(LD) == 269) - { - ReadTables20(); - } + ReadTables20(); } } } - - private byte DecodeAudio(int Delta) - { - AudioVariables v = AudV[UnpCurChannel]; - v.ByteCount = v.ByteCount + 1; - v.D4 = v.D3; - v.D3 = v.D2; // ->D3=V->D2; - v.D2 = v.LastDelta - v.D1; // ->D2=V->LastDelta-V->D1; - v.D1 = v.LastDelta; // V->D1=V->LastDelta; - - // int PCh=8*V->LastChar+V->K1*V->D1 +V->K2*V->D2 +V->K3*V->D3 - // +V->K4*V->D4+ V->K5*UnpChannelDelta; - int PCh = 8 * v.LastChar + v.K1 * v.D1; - PCh += v.K2 * v.D2 + v.K3 * v.D3; - PCh += v.K4 * v.D4 + v.K5 * UnpChannelDelta; - PCh = (Utility.URShift(PCh, 3)) & 0xFF; - - int Ch = PCh - Delta; - - int D = ((byte)Delta) << 3; - - v.Dif[0] += Math.Abs(D); // V->Dif[0]+=abs(D); - v.Dif[1] += Math.Abs(D - v.D1); // V->Dif[1]+=abs(D-V->D1); - v.Dif[2] += Math.Abs(D + v.D1); // V->Dif[2]+=abs(D+V->D1); - v.Dif[3] += Math.Abs(D - v.D2); // V->Dif[3]+=abs(D-V->D2); - v.Dif[4] += Math.Abs(D + v.D2); // V->Dif[4]+=abs(D+V->D2); - v.Dif[5] += Math.Abs(D - v.D3); // V->Dif[5]+=abs(D-V->D3); - v.Dif[6] += Math.Abs(D + v.D3); // V->Dif[6]+=abs(D+V->D3); - v.Dif[7] += Math.Abs(D - v.D4); // V->Dif[7]+=abs(D-V->D4); - v.Dif[8] += Math.Abs(D + v.D4); // V->Dif[8]+=abs(D+V->D4); - v.Dif[9] += Math.Abs(D - UnpChannelDelta); // V->Dif[9]+=abs(D-UnpChannelDelta); - v.Dif[10] += Math.Abs(D + UnpChannelDelta); // V->Dif[10]+=abs(D+UnpChannelDelta); - - v.LastDelta = (byte)(Ch - v.LastChar); - UnpChannelDelta = v.LastDelta; - v.LastChar = Ch; // V->LastChar=Ch; - - if ((v.ByteCount & 0x1F) == 0) - { - int MinDif = v.Dif[0], - NumMinDif = 0; - v.Dif[0] = 0; // ->Dif[0]=0; - for (int I = 1; I < v.Dif.Length; I++) - { - if (v.Dif[I] < MinDif) - { - MinDif = v.Dif[I]; - NumMinDif = I; - } - v.Dif[I] = 0; - } - switch (NumMinDif) - { - case 1: - if (v.K1 >= -16) - { - v.K1 = v.K1 - 1; // V->K1--; - } - break; - - case 2: - if (v.K1 < 16) - { - v.K1 = v.K1 + 1; // V->K1++; - } - break; - - case 3: - if (v.K2 >= -16) - { - v.K2 = v.K2 - 1; // V->K2--; - } - break; - - case 4: - if (v.K2 < 16) - { - v.K2 = v.K2 + 1; // V->K2++; - } - break; - - case 5: - if (v.K3 >= -16) - { - v.K3 = v.K3 - 1; - } - break; - - case 6: - if (v.K3 < 16) - { - v.K3 = v.K3 + 1; - } - break; - - case 7: - if (v.K4 >= -16) - { - v.K4 = v.K4 - 1; - } - break; - - case 8: - if (v.K4 < 16) - { - v.K4 = v.K4 + 1; - } - break; - - case 9: - if (v.K5 >= -16) - { - v.K5 = v.K5 - 1; - } - break; - - case 10: - if (v.K5 < 16) - { - v.K5 = v.K5 + 1; - } - break; - } - } - return ((byte)Ch); - } + } + + private byte DecodeAudio(int Delta) + { + var v = AudV[UnpCurChannel]; + v.ByteCount++; + v.D4 = v.D3; + v.D3 = v.D2; // ->D3=V->D2; + v.D2 = v.LastDelta - v.D1; // ->D2=V->LastDelta-V->D1; + v.D1 = v.LastDelta; // V->D1=V->LastDelta; + + // int PCh=8*V->LastChar+V->K1*V->D1 +V->K2*V->D2 +V->K3*V->D3 + // +V->K4*V->D4+ V->K5*UnpChannelDelta; + var PCh = (8 * v.LastChar) + (v.K1 * v.D1); + PCh += (v.K2 * v.D2) + (v.K3 * v.D3); + PCh += (v.K4 * v.D4) + (v.K5 * UnpChannelDelta); + PCh = (Utility.URShift(PCh, 3)) & 0xFF; + + var Ch = PCh - Delta; + + var D = ((byte)Delta) << 3; + + v.Dif[0] += Math.Abs(D); // V->Dif[0]+=abs(D); + v.Dif[1] += Math.Abs(D - v.D1); // V->Dif[1]+=abs(D-V->D1); + v.Dif[2] += Math.Abs(D + v.D1); // V->Dif[2]+=abs(D+V->D1); + v.Dif[3] += Math.Abs(D - v.D2); // V->Dif[3]+=abs(D-V->D2); + v.Dif[4] += Math.Abs(D + v.D2); // V->Dif[4]+=abs(D+V->D2); + v.Dif[5] += Math.Abs(D - v.D3); // V->Dif[5]+=abs(D-V->D3); + v.Dif[6] += Math.Abs(D + v.D3); // V->Dif[6]+=abs(D+V->D3); + v.Dif[7] += Math.Abs(D - v.D4); // V->Dif[7]+=abs(D-V->D4); + v.Dif[8] += Math.Abs(D + v.D4); // V->Dif[8]+=abs(D+V->D4); + v.Dif[9] += Math.Abs(D - UnpChannelDelta); // V->Dif[9]+=abs(D-UnpChannelDelta); + v.Dif[10] += Math.Abs(D + UnpChannelDelta); // V->Dif[10]+=abs(D+UnpChannelDelta); + + v.LastDelta = (byte)(Ch - v.LastChar); + UnpChannelDelta = v.LastDelta; + v.LastChar = Ch; // V->LastChar=Ch; + + if ((v.ByteCount & 0x1F) == 0) + { + int MinDif = v.Dif[0], + NumMinDif = 0; + v.Dif[0] = 0; // ->Dif[0]=0; + for (var I = 1; I < v.Dif.Length; I++) + { + if (v.Dif[I] < MinDif) + { + MinDif = v.Dif[I]; + NumMinDif = I; + } + v.Dif[I] = 0; + } + switch (NumMinDif) + { + case 1: + if (v.K1 >= -16) + { + v.K1--; // V->K1--; + } + break; + + case 2: + if (v.K1 < 16) + { + v.K1++; // V->K1++; + } + break; + + case 3: + if (v.K2 >= -16) + { + v.K2--; // V->K2--; + } + break; + + case 4: + if (v.K2 < 16) + { + v.K2++; // V->K2++; + } + break; + + case 5: + if (v.K3 >= -16) + { + v.K3--; + } + break; + + case 6: + if (v.K3 < 16) + { + v.K3++; + } + break; + + case 7: + if (v.K4 >= -16) + { + v.K4--; + } + break; + + case 8: + if (v.K4 < 16) + { + v.K4++; + } + break; + + case 9: + if (v.K5 >= -16) + { + v.K5--; + } + break; + + case 10: + if (v.K5 < 16) + { + v.K5++; + } + break; + } + } + return ((byte)Ch); } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack50.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack50.cs index a7cc9408..553e1896 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack50.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack50.cs @@ -1,954 +1,951 @@ -#if true +#if true using System; using System.Collections.Generic; using SharpCompress.Compressors.Rar.UnpackV1.Decode; -using size_t = System.UInt32; -using UnpackBlockHeader = SharpCompress.Compressors.Rar.UnpackV1; -namespace SharpCompress.Compressors.Rar.UnpackV1 +namespace SharpCompress.Compressors.Rar.UnpackV1; + +internal partial class Unpack { - internal partial class Unpack + // Maximum allowed number of compressed bits processed in quick mode. + private const int MAX_QUICK_DECODE_BITS = 10; + + // Maximum number of filters per entire data block. Must be at least + // twice more than MAX_PACK_FILTERS to store filters from two data blocks. + private const int MAX_UNPACK_FILTERS = 8192; + + // Maximum number of filters per entire data block for RAR3 unpack. + // Must be at least twice more than v3_MAX_PACK_FILTERS to store filters + // from two data blocks. + private const int MAX3_UNPACK_FILTERS = 8192; + + // Limit maximum number of channels in RAR3 delta filter to some reasonable + // value to prevent too slow processing of corrupt archives with invalid + // channels number. Must be equal or larger than v3_MAX_FILTER_CHANNELS. + // No need to provide it for RAR5, which uses only 5 bits to store channels. + private const int MAX3_UNPACK_CHANNELS = 1024; + + // Maximum size of single filter block. We restrict it to limit memory + // allocation. Must be equal or larger than MAX_ANALYZE_SIZE. + private const int MAX_FILTER_BLOCK_SIZE = 0x400000; + + // Write data in 4 MB or smaller blocks. Must not exceed PACK_MAX_WRITE, + // so we keep number of buffered filter in unpacker reasonable. + private const int UNPACK_MAX_WRITE = 0x400000; + + // Decode compressed bit fields to alphabet numbers. + // struct DecodeTable + // { + // // Real size of DecodeNum table. + // public uint MaxNum; + // + // // Left aligned start and upper limit codes defining code space + // // ranges for bit lengths. DecodeLen[BitLength-1] defines the start of + // // range for bit length and DecodeLen[BitLength] defines next code + // // after the end of range or in other words the upper limit code + // // for specified bit length. + // //uint DecodeLen[16]; + // public uint [] DecodeLen = new uint[16]; + // + // // Every item of this array contains the sum of all preceding items. + // // So it contains the start position in code list for every bit length. + // public uint DecodePos[16]; + // + // // Number of compressed bits processed in quick mode. + // // Must not exceed MAX_QUICK_DECODE_BITS. + // public uint QuickBits; + // + // // Translates compressed bits (up to QuickBits length) + // // to bit length in quick mode. + // public byte QuickLen[1< Filters + { + get { return filters; } + } - // Maximum size of single filter block. We restrict it to limit memory - // allocation. Must be equal or larger than MAX_ANALYZE_SIZE. - private const int MAX_FILTER_BLOCK_SIZE = 0x400000; + // TODO: make sure these aren't already somewhere else + public int BlockSize; + public int BlockBitSize; + public int BlockStart; + public int HeaderSize; + public bool LastBlockInFile; + public bool TablePresent; - // Write data in 4 MB or smaller blocks. Must not exceed PACK_MAX_WRITE, - // so we keep number of buffered filter in unpacker reasonable. - private const int UNPACK_MAX_WRITE = 0x400000; + public void Unpack5(bool Solid) + { + FileExtracted = true; - // Decode compressed bit fields to alphabet numbers. - // struct DecodeTable - // { - // // Real size of DecodeNum table. - // public uint MaxNum; - // - // // Left aligned start and upper limit codes defining code space - // // ranges for bit lengths. DecodeLen[BitLength-1] defines the start of - // // range for bit length and DecodeLen[BitLength] defines next code - // // after the end of range or in other words the upper limit code - // // for specified bit length. - // //uint DecodeLen[16]; - // public uint [] DecodeLen = new uint[16]; - // - // // Every item of this array contains the sum of all preceding items. - // // So it contains the start position in code list for every bit length. - // public uint DecodePos[16]; - // - // // Number of compressed bits processed in quick mode. - // // Must not exceed MAX_QUICK_DECODE_BITS. - // public uint QuickBits; - // - // // Translates compressed bits (up to QuickBits length) - // // to bit length in quick mode. - // public byte QuickLen[1< Filters - { - get { return filters; } - } - - // TODO: make sure these aren't already somewhere else - public int BlockSize; - public int BlockBitSize; - public int BlockStart; - public int HeaderSize; - public bool LastBlockInFile; - public bool TablePresent; - - public void Unpack5(bool Solid) - { - FileExtracted = true; - - if (!Suspended) + UnpInitData(Solid); + if (!UnpReadBuf()) { - UnpInitData(Solid); - if (!UnpReadBuf()) + return; + } + + // Check TablesRead5 to be sure that we read tables at least once + // regardless of current block header TablePresent flag. + // So we can safefly use these tables below. + if (!ReadBlockHeader() || !ReadTables() || !TablesRead5) + { + return; + } + } + + while (true) + { + UnpPtr &= MaxWinMask; + + if (Inp.InAddr >= ReadBorder) + { + var FileDone = false; + + // We use 'while', because for empty block containing only Huffman table, + // we'll be on the block border once again just after reading the table. + while ( + Inp.InAddr > BlockHeader.BlockStart + BlockHeader.BlockSize - 1 + || Inp.InAddr == BlockHeader.BlockStart + BlockHeader.BlockSize - 1 + && Inp.InBit >= BlockHeader.BlockBitSize + ) + { + if (BlockHeader.LastBlockInFile) + { + FileDone = true; + break; + } + if (!ReadBlockHeader() || !ReadTables()) + { + return; + } + } + if (FileDone || !UnpReadBuf()) + { + break; + } + } + + if ( + ((WriteBorder - UnpPtr) & MaxWinMask) < PackDef.MAX_LZ_MATCH + 3 + && WriteBorder != UnpPtr + ) + { + UnpWriteBuf(); + if (WrittenFileSize > DestUnpSize) { return; } - // Check TablesRead5 to be sure that we read tables at least once - // regardless of current block header TablePresent flag. - // So we can safefly use these tables below. - if (!ReadBlockHeader() || !ReadTables() || !TablesRead5) + if (Suspended) { + FileExtracted = false; return; } } - while (true) + //uint MainSlot=DecodeNumber(Inp,LD); + var MainSlot = this.DecodeNumber(LD); + if (MainSlot < 256) { - UnpPtr &= MaxWinMask; + // if (Fragmented) + // FragWindow[UnpPtr++]=(byte)MainSlot; + // else + Window[UnpPtr++] = (byte)MainSlot; + continue; + } + if (MainSlot >= 262) + { + var Length = SlotToLength(MainSlot - 262); - if (Inp.InAddr >= ReadBorder) + //uint DBits,Distance=1,DistSlot=DecodeNumber(Inp,&BlockTables.DD); + int DBits; + uint Distance = 1, + DistSlot = this.DecodeNumber(DD); + if (DistSlot < 4) { - bool FileDone = false; + DBits = 0; + Distance += DistSlot; + } + else + { + //DBits=DistSlot/2 - 1; + DBits = (int)((DistSlot / 2) - 1); + Distance += (2 | (DistSlot & 1)) << DBits; + } - // We use 'while', because for empty block containing only Huffman table, - // we'll be on the block border once again just after reading the table. - while ( - Inp.InAddr > BlockHeader.BlockStart + BlockHeader.BlockSize - 1 - || Inp.InAddr == BlockHeader.BlockStart + BlockHeader.BlockSize - 1 - && Inp.InBit >= BlockHeader.BlockBitSize - ) + if (DBits > 0) + { + if (DBits >= 4) { - if (BlockHeader.LastBlockInFile) + if (DBits > 4) { - FileDone = true; - break; + Distance += ((Inp.getbits() >> (36 - DBits)) << 4); + Inp.AddBits(DBits - 4); } - if (!ReadBlockHeader() || !ReadTables()) - { - return; - } - } - if (FileDone || !UnpReadBuf()) - { - break; - } - } - - if ( - ((WriteBorder - UnpPtr) & MaxWinMask) < PackDef.MAX_LZ_MATCH + 3 - && WriteBorder != UnpPtr - ) - { - UnpWriteBuf(); - if (WrittenFileSize > DestUnpSize) - { - return; - } - - if (Suspended) - { - FileExtracted = false; - return; - } - } - - //uint MainSlot=DecodeNumber(Inp,LD); - uint MainSlot = this.DecodeNumber(LD); - if (MainSlot < 256) - { - // if (Fragmented) - // FragWindow[UnpPtr++]=(byte)MainSlot; - // else - Window[UnpPtr++] = (byte)MainSlot; - continue; - } - if (MainSlot >= 262) - { - uint Length = SlotToLength(MainSlot - 262); - - //uint DBits,Distance=1,DistSlot=DecodeNumber(Inp,&BlockTables.DD); - int DBits; - uint Distance = 1, - DistSlot = this.DecodeNumber(DD); - if (DistSlot < 4) - { - DBits = 0; - Distance += DistSlot; + //uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); + var LowDist = this.DecodeNumber(LDD); + Distance += LowDist; } else { - //DBits=DistSlot/2 - 1; - DBits = (int)(DistSlot / 2 - 1); - Distance += (2 | (DistSlot & 1)) << DBits; + Distance += Inp.getbits() >> (32 - DBits); + Inp.AddBits(DBits); } + } - if (DBits > 0) - { - if (DBits >= 4) - { - if (DBits > 4) - { - Distance += ((Inp.getbits() >> (36 - DBits)) << 4); - Inp.AddBits(DBits - 4); - } - //uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); - uint LowDist = this.DecodeNumber(LDD); - Distance += LowDist; - } - else - { - Distance += Inp.getbits() >> (32 - DBits); - Inp.AddBits(DBits); - } - } - - if (Distance > 0x100) + if (Distance > 0x100) + { + Length++; + if (Distance > 0x2000) { Length++; - if (Distance > 0x2000) + if (Distance > 0x40000) { Length++; - if (Distance > 0x40000) - { - Length++; - } } } - - InsertOldDist(Distance); - LastLength = Length; - // if (Fragmented) - // FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); - // else - CopyString(Length, Distance); - continue; } - if (MainSlot == 256) - { - UnpackFilter Filter = new UnpackFilter(); - if (!ReadFilter(Filter) || !AddFilter(Filter)) - { - break; - } - continue; - } - if (MainSlot == 257) - { - if (LastLength != 0) - // if (Fragmented) - // FragWindow.CopyString(LastLength,OldDist[0],UnpPtr,MaxWinMask); - // else - //CopyString(LastLength,OldDist[0]); - { - CopyString(LastLength, OldDistN(0)); - } - - continue; - } - if (MainSlot < 262) - { - //uint DistNum=MainSlot-258; - int DistNum = (int)(MainSlot - 258); - //uint Distance=OldDist[DistNum]; - uint Distance = OldDistN(DistNum); - //for (uint I=DistNum;I>0;I--) - for (int I = DistNum; I > 0; I--) - //OldDistN[I]=OldDistN(I-1); - { - SetOldDistN(I, OldDistN(I - 1)); - } - - //OldDistN[0]=Distance; - SetOldDistN(0, Distance); - - uint LengthSlot = this.DecodeNumber(RD); - uint Length = SlotToLength(LengthSlot); - LastLength = Length; - // if (Fragmented) - // FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); - // else - CopyString(Length, Distance); - continue; - } + InsertOldDist(Distance); + LastLength = Length; + // if (Fragmented) + // FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); + // else + CopyString(Length, Distance); + continue; + } + if (MainSlot == 256) + { + var Filter = new UnpackFilter(); + if (!ReadFilter(Filter) || !AddFilter(Filter)) + { + break; + } + + continue; + } + if (MainSlot == 257) + { + if (LastLength != 0) + // if (Fragmented) + // FragWindow.CopyString(LastLength,OldDist[0],UnpPtr,MaxWinMask); + // else + //CopyString(LastLength,OldDist[0]); + { + CopyString(LastLength, OldDistN(0)); + } + + continue; + } + if (MainSlot < 262) + { + //uint DistNum=MainSlot-258; + var DistNum = (int)(MainSlot - 258); + //uint Distance=OldDist[DistNum]; + var Distance = OldDistN(DistNum); + //for (uint I=DistNum;I>0;I--) + for (var I = DistNum; I > 0; I--) + //OldDistN[I]=OldDistN(I-1); + { + SetOldDistN(I, OldDistN(I - 1)); + } + + //OldDistN[0]=Distance; + SetOldDistN(0, Distance); + + var LengthSlot = this.DecodeNumber(RD); + var Length = SlotToLength(LengthSlot); + LastLength = Length; + // if (Fragmented) + // FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); + // else + CopyString(Length, Distance); + continue; + } + } + UnpWriteBuf(); + } + + private uint ReadFilterData() + { + var ByteCount = (Inp.fgetbits() >> 14) + 1; + Inp.AddBits(2); + + uint Data = 0; + //for (uint I=0;I> 8) << (I * 8); + Inp.AddBits(8); + } + return Data; + } + + private bool ReadFilter(UnpackFilter Filter) + { + if (!Inp.ExternalBuffer && Inp.InAddr > ReadTop - 16) + { + if (!UnpReadBuf()) + { + return false; } - UnpWriteBuf(); } - private uint ReadFilterData() + Filter.uBlockStart = ReadFilterData(); + Filter.uBlockLength = ReadFilterData(); + if (Filter.BlockLength > MAX_FILTER_BLOCK_SIZE) { - uint ByteCount = (Inp.fgetbits() >> 14) + 1; - Inp.AddBits(2); - - uint Data = 0; - //for (uint I=0;I> 8) << (I * 8); - Inp.AddBits(8); - } - return Data; + Filter.BlockLength = 0; } - private bool ReadFilter(UnpackFilter Filter) + //Filter.Type=Inp.fgetbits()>>13; + Filter.Type = (byte)(Inp.fgetbits() >> 13); + Inp.faddbits(3); + + if (Filter.Type == (byte)FilterType.FILTER_DELTA) { - if (!Inp.ExternalBuffer && Inp.InAddr > ReadTop - 16) - { - if (!UnpReadBuf()) - { - return false; - } - } - - Filter.uBlockStart = ReadFilterData(); - Filter.uBlockLength = ReadFilterData(); - if (Filter.BlockLength > MAX_FILTER_BLOCK_SIZE) - { - Filter.BlockLength = 0; - } - - //Filter.Type=Inp.fgetbits()>>13; - Filter.Type = (byte)(Inp.fgetbits() >> 13); - Inp.faddbits(3); - - if (Filter.Type == (byte)FilterType.FILTER_DELTA) - { - //Filter.Channels=(Inp.fgetbits()>>11)+1; - Filter.Channels = (byte)((Inp.fgetbits() >> 11) + 1); - Inp.faddbits(5); - } - - return true; + //Filter.Channels=(Inp.fgetbits()>>11)+1; + Filter.Channels = (byte)((Inp.fgetbits() >> 11) + 1); + Inp.faddbits(5); } - private bool AddFilter(UnpackFilter Filter) + return true; + } + + private bool AddFilter(UnpackFilter Filter) + { + if (Filters.Count >= MAX_UNPACK_FILTERS) { + UnpWriteBuf(); // Write data, apply and flush filters. if (Filters.Count >= MAX_UNPACK_FILTERS) { - UnpWriteBuf(); // Write data, apply and flush filters. - if (Filters.Count >= MAX_UNPACK_FILTERS) - { - InitFilters(); // Still too many filters, prevent excessive memory use. - } - } - - // If distance to filter start is that large that due to circular dictionary - // mode now it points to old not written yet data, then we set 'NextWindow' - // flag and process this filter only after processing that older data. - Filter.NextWindow = - WrPtr != UnpPtr && ((WrPtr - UnpPtr) & MaxWinMask) <= Filter.BlockStart; - - Filter.uBlockStart = (uint)((Filter.BlockStart + UnpPtr) & MaxWinMask); - Filters.Add(Filter); - return true; - } - - private bool UnpReadBuf() - { - int DataSize = ReadTop - Inp.InAddr; // Data left to process. - if (DataSize < 0) - { - return false; - } - - BlockHeader.BlockSize -= Inp.InAddr - BlockHeader.BlockStart; - if (Inp.InAddr > MAX_SIZE / 2) - { - // If we already processed more than half of buffer, let's move - // remaining data into beginning to free more space for new data - // and ensure that calling function does not cross the buffer border - // even if we did not read anything here. Also it ensures that read size - // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk - // to make it zero. - if (DataSize > 0) - //memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); - { - Array.Copy(InBuf, inAddr, InBuf, 0, DataSize); - } - - // TODO: perf - //Buffer.BlockCopy(InBuf, inAddr, InBuf, 0, DataSize); - - Inp.InAddr = 0; - ReadTop = DataSize; - } - else - { - DataSize = ReadTop; - } - - int ReadCode = 0; - if (MAX_SIZE != DataSize) - //ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput.MAX_SIZE-DataSize); - { - ReadCode = readStream.Read(InBuf, DataSize, MAX_SIZE - DataSize); - } - - if (ReadCode > 0) // Can be also -1. - { - ReadTop += ReadCode; - } - - ReadBorder = ReadTop - 30; - BlockHeader.BlockStart = Inp.InAddr; - if (BlockHeader.BlockSize != -1) // '-1' means not defined yet. - { - // We may need to quit from main extraction loop and read new block header - // and trees earlier than data in input buffer ends. - ReadBorder = Math.Min( - ReadBorder, - BlockHeader.BlockStart + BlockHeader.BlockSize - 1 - ); - } - return ReadCode != -1; - } - - //? - // void UnpWriteBuf() - // { - // size_t WrittenBorder=WrPtr; - // size_t FullWriteSize=(UnpPtr-WrittenBorder)&MaxWinMask; - // size_t WriteSizeLeft=FullWriteSize; - // bool NotAllFiltersProcessed=false; - // for (size_t I=0;IType==FilterType.FILTER_NONE) - // continue; - // if (flt->NextWindow) - // { - // // Here we skip filters which have block start in current data range - // // due to address wrap around in circular dictionary, but actually - // // belong to next dictionary block. If such filter start position - // // is included to current write range, then we reset 'NextWindow' flag. - // // In fact we can reset it even without such check, because current - // // implementation seems to guarantee 'NextWindow' flag reset after - // // buffer writing for all existing filters. But let's keep this check - // // just in case. Compressor guarantees that distance between - // // filter block start and filter storing position cannot exceed - // // the dictionary size. So if we covered the filter block start with - // // our write here, we can safely assume that filter is applicable - // // to next block on no further wrap arounds is possible. - // if (((flt->BlockStart-WrPtr)&MaxWinMask)<=FullWriteSize) - // flt->NextWindow=false; - // continue; - // } - // uint BlockStart=flt->BlockStart; - // uint BlockLength=flt->BlockLength; - // if (((BlockStart-WrittenBorder)&MaxWinMask)0) // We set it to 0 also for invalid filters. - // { - // uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask; - // - // FilterSrcMemory.Alloc(BlockLength); - // byte *Mem=&FilterSrcMemory[0]; - // if (BlockStartUnpWrite(OutMem,BlockLength); - // - // UnpSomeRead=true; - // WrittenFileSize+=BlockLength; - // WrittenBorder=BlockEnd; - // WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask; - // } - // } - // else - // { - // // Current filter intersects the window write border, so we adjust - // // the window border to process this filter next time, not now. - // WrPtr=WrittenBorder; - // - // // Since Filter start position can only increase, we quit processing - // // all following filters for this data block and reset 'NextWindow' - // // flag for them. - // for (size_t J=I;JType!=FilterType.FILTER_NONE) - // flt->NextWindow=false; - // } - // - // // Do not write data left after current filter now. - // NotAllFiltersProcessed=true; - // break; - // } - // } - // } - // - // // Remove processed filters from queue. - // size_t EmptyCount=0; - // for (size_t I=0;I0) - // Filters[I-EmptyCount]=Filters[I]; - // if (Filters[I].Type==FilterType.FILTER_NONE) - // EmptyCount++; - // } - // if (EmptyCount>0) - // Filters.Alloc(Filters.Size()-EmptyCount); - // - // if (!NotAllFiltersProcessed) // Only if all filters are processed. - // { - // // Write data left after last filter. - // UnpWriteArea(WrittenBorder,UnpPtr); - // WrPtr=UnpPtr; - // } - // - // // We prefer to write data in blocks not exceeding UNPACK_MAX_WRITE - // // instead of potentially huge MaxWinSize blocks. It also allows us - // // to keep the size of Filters array reasonable. - // WriteBorder=(UnpPtr+Min(MaxWinSize,UNPACK_MAX_WRITE))&MaxWinMask; - // - // // Choose the nearest among WriteBorder and WrPtr actual written border. - // // If border is equal to UnpPtr, it means that we have MaxWinSize data ahead. - // if (WriteBorder==UnpPtr || - // WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<((WriteBorder-UnpPtr)&MaxWinMask)) - // WriteBorder=WrPtr; - // } - - - // unused - //x byte* ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt) - // byte[] ApplyFilter(byte []Data, uint DataSize, UnpackFilter Flt) - // { - // //x byte *SrcData=Data; - // byte []SrcData=Data; - // switch(Flt.Type) - // { - // case (byte)FilterType.FILTER_E8: - // case (byte)FilterType.FILTER_E8E9: - // { - // uint FileOffset=(uint)WrittenFileSize; - // - // const uint FileSize=0x1000000; - // byte CmpByte2=Flt.Type==(byte)FilterType.FILTER_E8E9 ? (byte)0xe9 : (byte)0xe8; - // // DataSize is unsigned, so we use "CurPos+4" and not "DataSize-4" - // // to avoid overflow for DataSize<4. - // for (uint CurPos=0;CurPos+4=0 - // RawPut4(Addr+FileSize,Data); - // } - // else - // if (((Addr-FileSize) & 0x80000000)!=0) // Addr>8); - // D[2]=(byte)(Offset>>16); - // } - // } - // } - // return SrcData; - // case (byte)FilterType.FILTER_DELTA: - // { - // // Unlike RAR3, we do not need to reject excessive channel - // // values here, since RAR5 uses only 5 bits to store channel. - // uint Channels=Flt->Channels,SrcPos=0; - // - // FilterDstMemory.Alloc(DataSize); - // byte *DstData=&FilterDstMemory[0]; - // - // // Bytes from same channels are grouped to continual data blocks, - // // so we need to place them back to their interleaving positions. - // for (uint CurChannel=0;CurChannel0) - //// { - //// size_t BlockSize=FragWindow.GetBlockSize(StartPtr,SizeToWrite); - //// UnpWriteData(&FragWindow[StartPtr],BlockSize); - //// SizeToWrite-=BlockSize; - //// StartPtr=(StartPtr+BlockSize) & MaxWinMask; - //// } - //// } - //// else - // if (EndPtr=DestUnpSize) - // return; - // size_t WriteSize=Size; - // long LeftToWrite=DestUnpSize-WrittenFileSize; - // if ((long)WriteSize>LeftToWrite) - // WriteSize=(size_t)LeftToWrite; - // UnpIO->UnpWrite(Data,WriteSize); - // WrittenFileSize+=Size; - // } - - private void UnpInitData50(bool Solid) - { - if (!Solid) - { - TablesRead5 = false; + InitFilters(); // Still too many filters, prevent excessive memory use. } } - private bool ReadBlockHeader() - { - Header.HeaderSize = 0; + // If distance to filter start is that large that due to circular dictionary + // mode now it points to old not written yet data, then we set 'NextWindow' + // flag and process this filter only after processing that older data. + Filter.NextWindow = + WrPtr != UnpPtr && ((WrPtr - UnpPtr) & MaxWinMask) <= Filter.BlockStart; - if (!Inp.ExternalBuffer && Inp.InAddr > ReadTop - 7) - { - if (!UnpReadBuf()) - { - return false; - } - } - - //Inp.faddbits((8-Inp.InBit)&7); - Inp.faddbits((uint)((8 - Inp.InBit) & 7)); - - byte BlockFlags = (byte)(Inp.fgetbits() >> 8); - Inp.faddbits(8); - //uint ByteCount=((BlockFlags>>3)&3)+1; // Block size byte count. - uint ByteCount = (uint)(((BlockFlags >> 3) & 3) + 1); // Block size byte count. - - if (ByteCount == 4) - { - return false; - } - - //Header.HeaderSize=2+ByteCount; - Header.HeaderSize = (int)(2 + ByteCount); - - Header.BlockBitSize = (BlockFlags & 7) + 1; - - byte SavedCheckSum = (byte)(Inp.fgetbits() >> 8); - Inp.faddbits(8); - - int BlockSize = 0; - //for (uint I=0;I>8)<<(I*8); - BlockSize += (int)(Inp.fgetbits() >> 8) << (I * 8); - Inp.AddBits(8); - } - - Header.BlockSize = BlockSize; - byte CheckSum = (byte)( - 0x5a ^ BlockFlags ^ BlockSize ^ (BlockSize >> 8) ^ (BlockSize >> 16) - ); - if (CheckSum != SavedCheckSum) - { - return false; - } - - Header.BlockStart = Inp.InAddr; - ReadBorder = Math.Min(ReadBorder, Header.BlockStart + Header.BlockSize - 1); - - Header.LastBlockInFile = (BlockFlags & 0x40) != 0; - Header.TablePresent = (BlockFlags & 0x80) != 0; - return true; - } - - //? - // bool ReadTables(BitInput Inp, ref UnpackBlockHeader Header, ref UnpackBlockTables Tables) - // { - // if (!Header.TablePresent) - // return true; - // - // if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-25) - // if (!UnpReadBuf()) - // return false; - // - // byte BitLength[BC]; - // for (uint I=0;I> 12); - // Inp.faddbits(4); - // if (Length==15) - // { - // uint ZeroCount=(byte)(Inp.fgetbits() >> 12); - // Inp.faddbits(4); - // if (ZeroCount==0) - // BitLength[I]=15; - // else - // { - // ZeroCount+=2; - // while (ZeroCount-- > 0 && IReadTop-5) - // if (!UnpReadBuf()) - // return false; - // uint Number=DecodeNumber(Inp,&Tables.BD); - // if (Number<16) - // { - // Table[I]=Number; - // I++; - // } - // else - // if (Number<18) - // { - // uint N; - // if (Number==16) - // { - // N=(Inp.fgetbits() >> 13)+3; - // Inp.faddbits(3); - // } - // else - // { - // N=(Inp.fgetbits() >> 9)+11; - // Inp.faddbits(7); - // } - // if (I==0) - // { - // // We cannot have "repeat previous" code at the first position. - // // Multiple such codes would shift Inp position without changing I, - // // which can lead to reading beyond of Inp boundary in mutithreading - // // mode, where Inp.ExternalBuffer disables bounds check and we just - // // reserve a lot of buffer space to not need such check normally. - // return false; - // } - // else - // while (N-- > 0 && I> 13)+3; - // Inp.faddbits(3); - // } - // else - // { - // N=(Inp.fgetbits() >> 9)+11; - // Inp.faddbits(7); - // } - // while (N-- > 0 && IReadTop) - // return false; - // MakeDecodeTables(&Table[0],&Tables.LD,NC); - // MakeDecodeTables(&Table[NC],&Tables.DD,DC); - // MakeDecodeTables(&Table[NC+DC],&Tables.LDD,LDC); - // MakeDecodeTables(&Table[NC+DC+LDC],&Tables.RD,RC); - // return true; - // } - - //? - // void InitFilters() - // { - // Filters.SoftReset(); - // } + Filter.uBlockStart = (uint)((Filter.BlockStart + UnpPtr) & MaxWinMask); + Filters.Add(Filter); + return true; } + + private bool UnpReadBuf() + { + var DataSize = ReadTop - Inp.InAddr; // Data left to process. + if (DataSize < 0) + { + return false; + } + + BlockHeader.BlockSize -= Inp.InAddr - BlockHeader.BlockStart; + if (Inp.InAddr > MAX_SIZE / 2) + { + // If we already processed more than half of buffer, let's move + // remaining data into beginning to free more space for new data + // and ensure that calling function does not cross the buffer border + // even if we did not read anything here. Also it ensures that read size + // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk + // to make it zero. + if (DataSize > 0) + //memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); + { + Array.Copy(InBuf, inAddr, InBuf, 0, DataSize); + } + + // TODO: perf + //Buffer.BlockCopy(InBuf, inAddr, InBuf, 0, DataSize); + + Inp.InAddr = 0; + ReadTop = DataSize; + } + else + { + DataSize = ReadTop; + } + + var ReadCode = 0; + if (MAX_SIZE != DataSize) + //ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput.MAX_SIZE-DataSize); + { + ReadCode = readStream.Read(InBuf, DataSize, MAX_SIZE - DataSize); + } + + if (ReadCode > 0) // Can be also -1. + { + ReadTop += ReadCode; + } + + ReadBorder = ReadTop - 30; + BlockHeader.BlockStart = Inp.InAddr; + if (BlockHeader.BlockSize != -1) // '-1' means not defined yet. + { + // We may need to quit from main extraction loop and read new block header + // and trees earlier than data in input buffer ends. + ReadBorder = Math.Min( + ReadBorder, + BlockHeader.BlockStart + BlockHeader.BlockSize - 1 + ); + } + return ReadCode != -1; + } + + //? + // void UnpWriteBuf() + // { + // size_t WrittenBorder=WrPtr; + // size_t FullWriteSize=(UnpPtr-WrittenBorder)&MaxWinMask; + // size_t WriteSizeLeft=FullWriteSize; + // bool NotAllFiltersProcessed=false; + // for (size_t I=0;IType==FilterType.FILTER_NONE) + // continue; + // if (flt->NextWindow) + // { + // // Here we skip filters which have block start in current data range + // // due to address wrap around in circular dictionary, but actually + // // belong to next dictionary block. If such filter start position + // // is included to current write range, then we reset 'NextWindow' flag. + // // In fact we can reset it even without such check, because current + // // implementation seems to guarantee 'NextWindow' flag reset after + // // buffer writing for all existing filters. But let's keep this check + // // just in case. Compressor guarantees that distance between + // // filter block start and filter storing position cannot exceed + // // the dictionary size. So if we covered the filter block start with + // // our write here, we can safely assume that filter is applicable + // // to next block on no further wrap arounds is possible. + // if (((flt->BlockStart-WrPtr)&MaxWinMask)<=FullWriteSize) + // flt->NextWindow=false; + // continue; + // } + // uint BlockStart=flt->BlockStart; + // uint BlockLength=flt->BlockLength; + // if (((BlockStart-WrittenBorder)&MaxWinMask)0) // We set it to 0 also for invalid filters. + // { + // uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask; + // + // FilterSrcMemory.Alloc(BlockLength); + // byte *Mem=&FilterSrcMemory[0]; + // if (BlockStartUnpWrite(OutMem,BlockLength); + // + // UnpSomeRead=true; + // WrittenFileSize+=BlockLength; + // WrittenBorder=BlockEnd; + // WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask; + // } + // } + // else + // { + // // Current filter intersects the window write border, so we adjust + // // the window border to process this filter next time, not now. + // WrPtr=WrittenBorder; + // + // // Since Filter start position can only increase, we quit processing + // // all following filters for this data block and reset 'NextWindow' + // // flag for them. + // for (size_t J=I;JType!=FilterType.FILTER_NONE) + // flt->NextWindow=false; + // } + // + // // Do not write data left after current filter now. + // NotAllFiltersProcessed=true; + // break; + // } + // } + // } + // + // // Remove processed filters from queue. + // size_t EmptyCount=0; + // for (size_t I=0;I0) + // Filters[I-EmptyCount]=Filters[I]; + // if (Filters[I].Type==FilterType.FILTER_NONE) + // EmptyCount++; + // } + // if (EmptyCount>0) + // Filters.Alloc(Filters.Size()-EmptyCount); + // + // if (!NotAllFiltersProcessed) // Only if all filters are processed. + // { + // // Write data left after last filter. + // UnpWriteArea(WrittenBorder,UnpPtr); + // WrPtr=UnpPtr; + // } + // + // // We prefer to write data in blocks not exceeding UNPACK_MAX_WRITE + // // instead of potentially huge MaxWinSize blocks. It also allows us + // // to keep the size of Filters array reasonable. + // WriteBorder=(UnpPtr+Min(MaxWinSize,UNPACK_MAX_WRITE))&MaxWinMask; + // + // // Choose the nearest among WriteBorder and WrPtr actual written border. + // // If border is equal to UnpPtr, it means that we have MaxWinSize data ahead. + // if (WriteBorder==UnpPtr || + // WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<((WriteBorder-UnpPtr)&MaxWinMask)) + // WriteBorder=WrPtr; + // } + + + // unused + //x byte* ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt) + // byte[] ApplyFilter(byte []Data, uint DataSize, UnpackFilter Flt) + // { + // //x byte *SrcData=Data; + // byte []SrcData=Data; + // switch(Flt.Type) + // { + // case (byte)FilterType.FILTER_E8: + // case (byte)FilterType.FILTER_E8E9: + // { + // uint FileOffset=(uint)WrittenFileSize; + // + // const uint FileSize=0x1000000; + // byte CmpByte2=Flt.Type==(byte)FilterType.FILTER_E8E9 ? (byte)0xe9 : (byte)0xe8; + // // DataSize is unsigned, so we use "CurPos+4" and not "DataSize-4" + // // to avoid overflow for DataSize<4. + // for (uint CurPos=0;CurPos+4=0 + // RawPut4(Addr+FileSize,Data); + // } + // else + // if (((Addr-FileSize) & 0x80000000)!=0) // Addr>8); + // D[2]=(byte)(Offset>>16); + // } + // } + // } + // return SrcData; + // case (byte)FilterType.FILTER_DELTA: + // { + // // Unlike RAR3, we do not need to reject excessive channel + // // values here, since RAR5 uses only 5 bits to store channel. + // uint Channels=Flt->Channels,SrcPos=0; + // + // FilterDstMemory.Alloc(DataSize); + // byte *DstData=&FilterDstMemory[0]; + // + // // Bytes from same channels are grouped to continual data blocks, + // // so we need to place them back to their interleaving positions. + // for (uint CurChannel=0;CurChannel0) + //// { + //// size_t BlockSize=FragWindow.GetBlockSize(StartPtr,SizeToWrite); + //// UnpWriteData(&FragWindow[StartPtr],BlockSize); + //// SizeToWrite-=BlockSize; + //// StartPtr=(StartPtr+BlockSize) & MaxWinMask; + //// } + //// } + //// else + // if (EndPtr=DestUnpSize) + // return; + // size_t WriteSize=Size; + // long LeftToWrite=DestUnpSize-WrittenFileSize; + // if ((long)WriteSize>LeftToWrite) + // WriteSize=(size_t)LeftToWrite; + // UnpIO->UnpWrite(Data,WriteSize); + // WrittenFileSize+=Size; + // } + + private void UnpInitData50(bool Solid) + { + if (!Solid) + { + TablesRead5 = false; + } + } + + private bool ReadBlockHeader() + { + Header.HeaderSize = 0; + + if (!Inp.ExternalBuffer && Inp.InAddr > ReadTop - 7) + { + if (!UnpReadBuf()) + { + return false; + } + } + + //Inp.faddbits((8-Inp.InBit)&7); + Inp.faddbits((uint)((8 - Inp.InBit) & 7)); + + var BlockFlags = (byte)(Inp.fgetbits() >> 8); + Inp.faddbits(8); + //uint ByteCount=((BlockFlags>>3)&3)+1; // Block size byte count. + var ByteCount = (uint)(((BlockFlags >> 3) & 3) + 1); // Block size byte count. + + if (ByteCount == 4) + { + return false; + } + + //Header.HeaderSize=2+ByteCount; + Header.HeaderSize = (int)(2 + ByteCount); + + Header.BlockBitSize = (BlockFlags & 7) + 1; + + var SavedCheckSum = (byte)(Inp.fgetbits() >> 8); + Inp.faddbits(8); + + var BlockSize = 0; + //for (uint I=0;I>8)<<(I*8); + BlockSize += (int)(Inp.fgetbits() >> 8) << (I * 8); + Inp.AddBits(8); + } + + Header.BlockSize = BlockSize; + var CheckSum = (byte)( + 0x5a ^ BlockFlags ^ BlockSize ^ (BlockSize >> 8) ^ (BlockSize >> 16) + ); + if (CheckSum != SavedCheckSum) + { + return false; + } + + Header.BlockStart = Inp.InAddr; + ReadBorder = Math.Min(ReadBorder, Header.BlockStart + Header.BlockSize - 1); + + Header.LastBlockInFile = (BlockFlags & 0x40) != 0; + Header.TablePresent = (BlockFlags & 0x80) != 0; + return true; + } + + //? + // bool ReadTables(BitInput Inp, ref UnpackBlockHeader Header, ref UnpackBlockTables Tables) + // { + // if (!Header.TablePresent) + // return true; + // + // if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-25) + // if (!UnpReadBuf()) + // return false; + // + // byte BitLength[BC]; + // for (uint I=0;I> 12); + // Inp.faddbits(4); + // if (Length==15) + // { + // uint ZeroCount=(byte)(Inp.fgetbits() >> 12); + // Inp.faddbits(4); + // if (ZeroCount==0) + // BitLength[I]=15; + // else + // { + // ZeroCount+=2; + // while (ZeroCount-- > 0 && IReadTop-5) + // if (!UnpReadBuf()) + // return false; + // uint Number=DecodeNumber(Inp,&Tables.BD); + // if (Number<16) + // { + // Table[I]=Number; + // I++; + // } + // else + // if (Number<18) + // { + // uint N; + // if (Number==16) + // { + // N=(Inp.fgetbits() >> 13)+3; + // Inp.faddbits(3); + // } + // else + // { + // N=(Inp.fgetbits() >> 9)+11; + // Inp.faddbits(7); + // } + // if (I==0) + // { + // // We cannot have "repeat previous" code at the first position. + // // Multiple such codes would shift Inp position without changing I, + // // which can lead to reading beyond of Inp boundary in mutithreading + // // mode, where Inp.ExternalBuffer disables bounds check and we just + // // reserve a lot of buffer space to not need such check normally. + // return false; + // } + // else + // while (N-- > 0 && I> 13)+3; + // Inp.faddbits(3); + // } + // else + // { + // N=(Inp.fgetbits() >> 9)+11; + // Inp.faddbits(7); + // } + // while (N-- > 0 && IReadTop) + // return false; + // MakeDecodeTables(&Table[0],&Tables.LD,NC); + // MakeDecodeTables(&Table[NC],&Tables.DD,DC); + // MakeDecodeTables(&Table[NC+DC],&Tables.LDD,LDC); + // MakeDecodeTables(&Table[NC+DC+LDC],&Tables.RD,RC); + // return true; + // } + + //? + // void InitFilters() + // { + // Filters.SoftReset(); + // } } #endif diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackFilter.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackFilter.cs index 82fb4e5e..adfa6856 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackFilter.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackFilter.cs @@ -1,41 +1,40 @@ using SharpCompress.Compressors.Rar.VM; -namespace SharpCompress.Compressors.Rar.UnpackV1 +namespace SharpCompress.Compressors.Rar.UnpackV1; + +internal class UnpackFilter { - internal class UnpackFilter + public byte Type; + public byte Channels; + + internal UnpackFilter() { - public byte Type; - public byte Channels; - - internal UnpackFilter() - { - Program = new VMPreparedProgram(); - } - - // TODO uint - internal uint uBlockStart - { - get { return (uint)BlockStart; } - set { BlockStart = (int)value; } - } - internal uint uBlockLength - { - get { return (uint)BlockLength; } - set { BlockLength = (int)value; } - } - - internal int BlockStart { get; set; } - - internal int BlockLength { get; set; } - - internal int ExecCount { get; set; } - - internal bool NextWindow { get; set; } - - // position of parent filter in Filters array used as prototype for filter - // in PrgStack array. Not defined for filters in Filters array. - internal int ParentFilter { get; set; } - - internal VMPreparedProgram Program { get; set; } + Program = new VMPreparedProgram(); } + + // TODO uint + internal uint uBlockStart + { + get { return (uint)BlockStart; } + set { BlockStart = (int)value; } + } + internal uint uBlockLength + { + get { return (uint)BlockLength; } + set { BlockLength = (int)value; } + } + + internal int BlockStart { get; set; } + + internal int BlockLength { get; set; } + + internal int ExecCount { get; set; } + + internal bool NextWindow { get; set; } + + // position of parent filter in Filters array used as prototype for filter + // in PrgStack array. Not defined for filters in Filters array. + internal int ParentFilter { get; set; } + + internal VMPreparedProgram Program { get; set; } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackInline.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackInline.cs index c49007f7..8148b250 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackInline.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackInline.cs @@ -1,30 +1,29 @@ -namespace SharpCompress.Compressors.Rar.UnpackV1 -{ - internal partial class Unpack - { - private uint SlotToLength(uint Slot) - { - //uint LBits,Length=2; - int LBits; - uint Length = 2; - if (Slot < 8) - { - LBits = 0; - Length += Slot; - } - else - { - //LBits=Slot/4-1; - LBits = (int)(Slot / 4 - 1); - Length += (4 | (Slot & 3)) << LBits; - } +namespace SharpCompress.Compressors.Rar.UnpackV1; - if (LBits > 0) - { - Length += getbits() >> (16 - LBits); - AddBits(LBits); - } - return Length; +internal partial class Unpack +{ + private uint SlotToLength(uint Slot) + { + //uint LBits,Length=2; + int LBits; + uint Length = 2; + if (Slot < 8) + { + LBits = 0; + Length += Slot; } + else + { + //LBits=Slot/4-1; + LBits = (int)((Slot / 4) - 1); + Length += (4 | (Slot & 3)) << LBits; + } + + if (LBits > 0) + { + Length += getbits() >> (16 - LBits); + AddBits(LBits); + } + return Length; } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackUtility.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackUtility.cs index b00ffcfa..1db60c89 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackUtility.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackUtility.cs @@ -1,233 +1,232 @@ -using System; +using System; using SharpCompress.Compressors.Rar.VM; -namespace SharpCompress.Compressors.Rar.UnpackV1 +namespace SharpCompress.Compressors.Rar.UnpackV1; + +internal static class UnpackUtility { - internal static class UnpackUtility + //!!! TODO rename methods + internal static uint DecodeNumber(this BitInput input, Decode.Decode dec) { - //!!! TODO rename methods - internal static uint DecodeNumber(this BitInput input, Decode.Decode dec) - { - return (uint)input.decodeNumber(dec); - } + return (uint)input.decodeNumber(dec); + } - internal static int decodeNumber(this BitInput input, Decode.Decode dec) - { - int bits; - long bitField = input.GetBits() & 0xfffe; + internal static int decodeNumber(this BitInput input, Decode.Decode dec) + { + int bits; + long bitField = input.GetBits() & 0xfffe; - // if (bitField < dec.getDecodeLen()[8]) { - // if (bitField < dec.getDecodeLen()[4]) { - // if (bitField < dec.getDecodeLen()[2]) { - // if (bitField < dec.getDecodeLen()[1]) { - // bits = 1; - // } else { - // bits = 2; - // } - // } else { - // if (bitField < dec.getDecodeLen()[3]) { - // bits = 3; - // } else { - // bits = 4; - // } - // } - // } else { - // if (bitField < dec.getDecodeLen()[6]) { - // if (bitField < dec.getDecodeLen()[5]) - // bits = 5; - // else - // bits = 6; - // } else { - // if (bitField < dec.getDecodeLen()[7]) { - // bits = 7; - // } else { - // bits = 8; - // } - // } - // } - // } else { - // if (bitField < dec.getDecodeLen()[12]) { - // if (bitField < dec.getDecodeLen()[10]) - // if (bitField < dec.getDecodeLen()[9]) - // bits = 9; - // else - // bits = 10; - // else if (bitField < dec.getDecodeLen()[11]) - // bits = 11; - // else - // bits = 12; - // } else { - // if (bitField < dec.getDecodeLen()[14]) { - // if (bitField < dec.getDecodeLen()[13]) { - // bits = 13; - // } else { - // bits = 14; - // } - // } else { - // bits = 15; - // } - // } - // } - // addbits(bits); - // int N = dec.getDecodePos()[bits] - // + (((int) bitField - dec.getDecodeLen()[bits - 1]) >>> (16 - bits)); - // if (N >= dec.getMaxNum()) { - // N = 0; - // } - // return (dec.getDecodeNum()[N]); - int[] decodeLen = dec.DecodeLen; - if (bitField < decodeLen[8]) + // if (bitField < dec.getDecodeLen()[8]) { + // if (bitField < dec.getDecodeLen()[4]) { + // if (bitField < dec.getDecodeLen()[2]) { + // if (bitField < dec.getDecodeLen()[1]) { + // bits = 1; + // } else { + // bits = 2; + // } + // } else { + // if (bitField < dec.getDecodeLen()[3]) { + // bits = 3; + // } else { + // bits = 4; + // } + // } + // } else { + // if (bitField < dec.getDecodeLen()[6]) { + // if (bitField < dec.getDecodeLen()[5]) + // bits = 5; + // else + // bits = 6; + // } else { + // if (bitField < dec.getDecodeLen()[7]) { + // bits = 7; + // } else { + // bits = 8; + // } + // } + // } + // } else { + // if (bitField < dec.getDecodeLen()[12]) { + // if (bitField < dec.getDecodeLen()[10]) + // if (bitField < dec.getDecodeLen()[9]) + // bits = 9; + // else + // bits = 10; + // else if (bitField < dec.getDecodeLen()[11]) + // bits = 11; + // else + // bits = 12; + // } else { + // if (bitField < dec.getDecodeLen()[14]) { + // if (bitField < dec.getDecodeLen()[13]) { + // bits = 13; + // } else { + // bits = 14; + // } + // } else { + // bits = 15; + // } + // } + // } + // addbits(bits); + // int N = dec.getDecodePos()[bits] + // + (((int) bitField - dec.getDecodeLen()[bits - 1]) >>> (16 - bits)); + // if (N >= dec.getMaxNum()) { + // N = 0; + // } + // return (dec.getDecodeNum()[N]); + var decodeLen = dec.DecodeLen; + if (bitField < decodeLen[8]) + { + if (bitField < decodeLen[4]) { - if (bitField < decodeLen[4]) + if (bitField < decodeLen[2]) { - if (bitField < decodeLen[2]) + if (bitField < decodeLen[1]) { - if (bitField < decodeLen[1]) - { - bits = 1; - } - else - { - bits = 2; - } + bits = 1; } else { - if (bitField < decodeLen[3]) - { - bits = 3; - } - else - { - bits = 4; - } + bits = 2; } } else { - if (bitField < decodeLen[6]) + if (bitField < decodeLen[3]) { - if (bitField < decodeLen[5]) - { - bits = 5; - } - else - { - bits = 6; - } + bits = 3; } else { - if (bitField < decodeLen[7]) - { - bits = 7; - } - else - { - bits = 8; - } + bits = 4; } } } else { - if (bitField < decodeLen[12]) + if (bitField < decodeLen[6]) { - if (bitField < decodeLen[10]) + if (bitField < decodeLen[5]) { - if (bitField < decodeLen[9]) - { - bits = 9; - } - else - { - bits = 10; - } - } - else if (bitField < decodeLen[11]) - { - bits = 11; + bits = 5; } else { - bits = 12; + bits = 6; } } else { - if (bitField < decodeLen[14]) + if (bitField < decodeLen[7]) { - if (bitField < decodeLen[13]) - { - bits = 13; - } - else - { - bits = 14; - } + bits = 7; } else { - bits = 15; + bits = 8; } } } - input.AddBits(bits); - int N = - dec.DecodePos[bits] - + (Utility.URShift(((int)bitField - decodeLen[bits - 1]), (16 - bits))); - if (N >= dec.MaxNum) - { - N = 0; - } - return (dec.DecodeNum[N]); } + else + { + if (bitField < decodeLen[12]) + { + if (bitField < decodeLen[10]) + { + if (bitField < decodeLen[9]) + { + bits = 9; + } + else + { + bits = 10; + } + } + else if (bitField < decodeLen[11]) + { + bits = 11; + } + else + { + bits = 12; + } + } + else + { + if (bitField < decodeLen[14]) + { + if (bitField < decodeLen[13]) + { + bits = 13; + } + else + { + bits = 14; + } + } + else + { + bits = 15; + } + } + } + input.AddBits(bits); + var N = + dec.DecodePos[bits] + + (Utility.URShift(((int)bitField - decodeLen[bits - 1]), (16 - bits))); + if (N >= dec.MaxNum) + { + N = 0; + } + return (dec.DecodeNum[N]); + } - internal static void makeDecodeTables( - byte[] lenTab, - int offset, - Decode.Decode dec, - int size + internal static void makeDecodeTables( + byte[] lenTab, + int offset, + Decode.Decode dec, + int size + ) + { + Span lenCount = stackalloc int[16]; + Span tmpPos = stackalloc int[16]; + int i; + long M, + N; + + new Span(dec.DecodeNum).Clear(); // memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum)); + + for (i = 0; i < size; i++) + { + lenCount[lenTab[offset + i] & 0xF]++; + } + lenCount[0] = 0; + for ( + tmpPos[0] = 0, dec.DecodePos[0] = 0, dec.DecodeLen[0] = 0, N = 0, i = 1; + i < 16; + i++ ) { - Span lenCount = stackalloc int[16]; - Span tmpPos = stackalloc int[16]; - int i; - long M, - N; - - new Span(dec.DecodeNum).Clear(); // memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum)); - - for (i = 0; i < size; i++) + N = 2 * (N + lenCount[i]); + M = N << (15 - i); + if (M > 0xFFFF) { - lenCount[lenTab[offset + i] & 0xF]++; + M = 0xFFFF; } - lenCount[0] = 0; - for ( - tmpPos[0] = 0, dec.DecodePos[0] = 0, dec.DecodeLen[0] = 0, N = 0, i = 1; - i < 16; - i++ - ) - { - N = 2 * (N + lenCount[i]); - M = N << (15 - i); - if (M > 0xFFFF) - { - M = 0xFFFF; - } - dec.DecodeLen[i] = (int)M; - tmpPos[i] = dec.DecodePos[i] = dec.DecodePos[i - 1] + lenCount[i - 1]; - } - - for (i = 0; i < size; i++) - { - if (lenTab[offset + i] != 0) - { - dec.DecodeNum[tmpPos[lenTab[offset + i] & 0xF]++] = i; - } - } - dec.MaxNum = size; + dec.DecodeLen[i] = (int)M; + tmpPos[i] = dec.DecodePos[i] = dec.DecodePos[i - 1] + lenCount[i - 1]; } + + for (i = 0; i < size; i++) + { + if (lenTab[offset + i] != 0) + { + dec.DecodeNum[tmpPos[lenTab[offset + i] & 0xF]++] = i; + } + } + dec.MaxNum = size; } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_cpp.cs index b06d56a2..58af921b 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_cpp.cs @@ -1,6 +1,4 @@ -#if !Rar2017_64bit -using nint = System.Int32; -using nuint = System.UInt32; +#if !Rar2017_64bit using size_t = System.UInt32; #else using nint = System.Int64; @@ -10,57 +8,48 @@ using size_t = System.UInt64; #nullable disable -namespace SharpCompress.Compressors.Rar.UnpackV2017 +namespace SharpCompress.Compressors.Rar.UnpackV2017; + +internal partial class BitInput { - internal partial class BitInput + public BitInput(bool AllocBuffer) { - public BitInput(bool AllocBuffer) + ExternalBuffer = false; + if (AllocBuffer) { - ExternalBuffer = false; - if (AllocBuffer) - { - // getbits32 attempts to read data from InAddr, ... InAddr+3 positions. - // So let's allocate 3 additional bytes for situation, when we need to - // read only 1 byte from the last position of buffer and avoid a crash - // from access to next 3 bytes, which contents we do not need. - size_t BufSize = MAX_SIZE + 3; - InBuf = new byte[BufSize]; + // getbits32 attempts to read data from InAddr, ... InAddr+3 positions. + // So let's allocate 3 additional bytes for situation, when we need to + // read only 1 byte from the last position of buffer and avoid a crash + // from access to next 3 bytes, which contents we do not need. + size_t BufSize = MAX_SIZE + 3; + InBuf = new byte[BufSize]; - // Ensure that we get predictable results when accessing bytes in area - // not filled with read data. - //memset(InBuf,0,BufSize); - } - else - { - InBuf = null; - } + // Ensure that we get predictable results when accessing bytes in area + // not filled with read data. + //memset(InBuf,0,BufSize); } - - //BitInput::~BitInput() - //{ - // if (!ExternalBuffer) - // delete[] InBuf; - //} - // - - public void faddbits(uint Bits) + else { - // Function wrapped version of inline addbits to save code size. - addbits(Bits); - } - - public uint fgetbits() - { - // Function wrapped version of inline getbits to save code size. - return getbits(); - } - - private void SetExternalBuffer(byte[] Buf) - { - //if (InBuf!=NULL && !ExternalBuffer) - // delete[] InBuf; - InBuf = Buf; - ExternalBuffer = true; + InBuf = null; } } + + //BitInput::~BitInput() + //{ + // if (!ExternalBuffer) + // delete[] InBuf; + //} + // + + public void faddbits(uint Bits) + { + // Function wrapped version of inline addbits to save code size. + addbits(Bits); + } + + public uint fgetbits() + { + // Function wrapped version of inline getbits to save code size. + return getbits(); + } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_hpp.cs index 3acb8f28..47409b0e 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_hpp.cs @@ -1,67 +1,56 @@ -namespace SharpCompress.Compressors.Rar.UnpackV2017 +namespace SharpCompress.Compressors.Rar.UnpackV2017; + +internal partial class BitInput { - internal partial class BitInput + public const int MAX_SIZE = 0x8000; // Size of input buffer. + + public int InAddr; // Curent byte position in the buffer. + public int InBit; // Current bit position in the current byte. + + public bool ExternalBuffer; + + //BitInput(bool AllocBuffer); + //~BitInput(); + + public byte[] InBuf; // Dynamically allocated input buffer. + + public void InitBitInput() { - public const int MAX_SIZE = 0x8000; // Size of input buffer. - - public int InAddr; // Curent byte position in the buffer. - public int InBit; // Current bit position in the current byte. - - public bool ExternalBuffer; - - //BitInput(bool AllocBuffer); - //~BitInput(); - - public byte[] InBuf; // Dynamically allocated input buffer. - - public void InitBitInput() - { - InAddr = InBit = 0; - } - - // Move forward by 'Bits' bits. - public void addbits(uint _Bits) - { - var Bits = checked((int)_Bits); - Bits += InBit; - InAddr += Bits >> 3; - InBit = Bits & 7; - } - - // Return 16 bits from current position in the buffer. - // Bit at (InAddr,InBit) has the highest position in returning data. - public uint getbits() - { - uint BitField = (uint)InBuf[InAddr] << 16; - BitField |= (uint)InBuf[InAddr + 1] << 8; - BitField |= (uint)InBuf[InAddr + 2]; - BitField >>= (8 - InBit); - return BitField & 0xffff; - } - - // Return 32 bits from current position in the buffer. - // Bit at (InAddr,InBit) has the highest position in returning data. - public uint getbits32() - { - uint BitField = (uint)InBuf[InAddr] << 24; - BitField |= (uint)InBuf[InAddr + 1] << 16; - BitField |= (uint)InBuf[InAddr + 2] << 8; - BitField |= (uint)InBuf[InAddr + 3]; - BitField <<= InBit; - BitField |= (uint)InBuf[InAddr + 4] >> (8 - InBit); - return BitField & 0xffffffff; - } - - //void faddbits(uint Bits); - //uint fgetbits(); - - // Check if buffer has enough space for IncPtr bytes. Returns 'true' - // if buffer will be overflown. - private bool Overflow(uint IncPtr) - { - return InAddr + IncPtr >= MAX_SIZE; - } - - //void SetExternalBuffer(byte *Buf); + InAddr = InBit = 0; } + + // Move forward by 'Bits' bits. + public void addbits(uint _Bits) + { + var Bits = checked((int)_Bits); + Bits += InBit; + InAddr += Bits >> 3; + InBit = Bits & 7; + } + + // Return 16 bits from current position in the buffer. + // Bit at (InAddr,InBit) has the highest position in returning data. + public uint getbits() + { + var BitField = (uint)InBuf[InAddr] << 16; + BitField |= (uint)InBuf[InAddr + 1] << 8; + BitField |= InBuf[InAddr + 2]; + BitField >>= (8 - InBit); + return BitField & 0xffff; + } + + // Return 32 bits from current position in the buffer. + // Bit at (InAddr,InBit) has the highest position in returning data. + public uint getbits32() + { + var BitField = (uint)InBuf[InAddr] << 24; + BitField |= (uint)InBuf[InAddr + 1] << 16; + BitField |= (uint)InBuf[InAddr + 2] << 8; + BitField |= InBuf[InAddr + 3]; + BitField <<= InBit; + BitField |= (uint)InBuf[InAddr + 4] >> (8 - InBit); + return BitField & 0xffffffff; + } + + //void SetExternalBuffer(byte *Buf); } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs index 7b2a378d..bfa69829 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs @@ -1,191 +1,181 @@ -#nullable disable +#nullable disable #if !Rar2017_64bit -using nint = System.Int32; -using nuint = System.UInt32; using size_t = System.UInt32; #else using nint = System.Int64; using nuint = System.UInt64; using size_t = System.UInt64; #endif -using int64 = System.Int64; - using System; -namespace SharpCompress.Compressors.Rar.UnpackV2017 +namespace SharpCompress.Compressors.Rar.UnpackV2017; + +internal partial class FragmentedWindow { - internal partial class FragmentedWindow + public FragmentedWindow() { - public FragmentedWindow() - { - //memset(Mem,0,sizeof(Mem)); - //memset(MemSize,0,sizeof(MemSize)); - } + //memset(Mem,0,sizeof(Mem)); + //memset(MemSize,0,sizeof(MemSize)); + } - //FragmentedWindow::~FragmentedWindow() - //{ - // Reset(); - //} + //FragmentedWindow::~FragmentedWindow() + //{ + // Reset(); + //} - private void Reset() + private void Reset() + { + for (uint I = 0; I < Mem.Length; I++) { - for (uint I = 0; I < Mem.Length; I++) + if (Mem[I] != null) { - if (Mem[I] != null) - { - //free(Mem[I]); - Mem[I] = null; - } + //free(Mem[I]); + Mem[I] = null; } } + } - public void Init(size_t WinSize) + public void Init(size_t WinSize) + { + Reset(); + + uint BlockNum = 0; + size_t TotalSize = 0; // Already allocated. + while (TotalSize < WinSize && BlockNum < Mem.Length) { - Reset(); + var Size = WinSize - TotalSize; // Size needed to allocate. - uint BlockNum = 0; - size_t TotalSize = 0; // Already allocated. - while (TotalSize < WinSize && BlockNum < Mem.Length) + // Minimum still acceptable block size. Next allocations cannot be larger + // than current, so we do not need blocks if they are smaller than + // "size left / attempts left". Also we do not waste time to blocks + // smaller than some arbitrary constant. + var MinSize = Math.Max(Size / (size_t)(Mem.Length - BlockNum), 0x400000); + + byte[] NewMem = null; + while (Size >= MinSize) { - size_t Size = WinSize - TotalSize; // Size needed to allocate. - - // Minimum still acceptable block size. Next allocations cannot be larger - // than current, so we do not need blocks if they are smaller than - // "size left / attempts left". Also we do not waste time to blocks - // smaller than some arbitrary constant. - size_t MinSize = Math.Max(Size / (size_t)(Mem.Length - BlockNum), 0x400000); - - byte[] NewMem = null; - while (Size >= MinSize) + NewMem = new byte[Size]; + if (NewMem != null) { - NewMem = new byte[Size]; - if (NewMem != null) - { - break; - } - - Size -= Size / 32; - } - if (NewMem == null) - //throw std::bad_alloc(); - { - throw new InvalidOperationException(); + break; } - // Clean the window to generate the same output when unpacking corrupt - // RAR files, which may access to unused areas of sliding dictionary. - // sharpcompress: don't need this, freshly allocated above - //Utility.Memset(NewMem,0,Size); + Size -= Size / 32; + } - Mem[BlockNum] = NewMem; - TotalSize += Size; - MemSize[BlockNum] = TotalSize; - BlockNum++; - } - if (TotalSize < WinSize) // Not found enough free blocks. - //throw std::bad_alloc(); - { - throw new InvalidOperationException(); - } + // Clean the window to generate the same output when unpacking corrupt + // RAR files, which may access to unused areas of sliding dictionary. + // sharpcompress: don't need this, freshly allocated above + //Utility.Memset(NewMem,0,Size); + + Mem[BlockNum] = NewMem ?? throw new InvalidOperationException(); + TotalSize += Size; + MemSize[BlockNum] = TotalSize; + BlockNum++; } - - public byte this[size_t Item] + if (TotalSize < WinSize) // Not found enough free blocks. + //throw std::bad_alloc(); { - get - { - if (Item < MemSize[0]) - { - return Mem[0][Item]; - } - - for (uint I = 1; I < MemSize.Length; I++) - { - if (Item < MemSize[I]) - { - return Mem[I][Item - MemSize[I - 1]]; - } - } - - return Mem[0][0]; // Must never happen; - } - set - { - if (Item < MemSize[0]) - { - Mem[0][Item] = value; - return; - } - for (uint I = 1; I < MemSize.Length; I++) - { - if (Item < MemSize[I]) - { - Mem[I][Item - MemSize[I - 1]] = value; - return; - } - } - - Mem[0][0] = value; // Must never happen; - } + throw new InvalidOperationException(); } + } - // sharpcompress: added the following code - public void GetBuffer(size_t Item, out byte[] buf, out uint offset) + public byte this[size_t Item] + { + get { if (Item < MemSize[0]) { - //return Mem[0][Item]; - buf = Mem[0]; - offset = Item; + return Mem[0][Item]; + } + + for (uint I = 1; I < MemSize.Length; I++) + { + if (Item < MemSize[I]) + { + return Mem[I][Item - MemSize[I - 1]]; + } + } + + return Mem[0][0]; // Must never happen; + } + set + { + if (Item < MemSize[0]) + { + Mem[0][Item] = value; return; } for (uint I = 1; I < MemSize.Length; I++) { if (Item < MemSize[I]) { - //return Mem[I][Item-MemSize[I-1]]; - buf = Mem[I]; - offset = Item - MemSize[I - 1]; + Mem[I][Item - MemSize[I - 1]] = value; return; } } - //return Mem[0][0]; // Must never happen; - buf = Mem[0]; - offset = 0; - return; // Must never happen; - } - public void CopyString(uint Length, uint Distance, ref size_t UnpPtr, size_t MaxWinMask) - { - size_t SrcPtr = UnpPtr - Distance; - while (Length-- > 0) - { - this[UnpPtr] = this[SrcPtr++ & MaxWinMask]; - // We need to have masked UnpPtr after quit from loop, so it must not - // be replaced with '(*this)[UnpPtr++ & MaxWinMask]' - UnpPtr = (UnpPtr + 1) & MaxWinMask; - } - } - - public void CopyData(byte[] Dest, size_t destOffset, size_t WinPos, size_t Size) - { - for (size_t I = 0; I < Size; I++) - { - Dest[destOffset + I] = this[WinPos + I]; - } - } - - public size_t GetBlockSize(size_t StartPos, size_t RequiredSize) - { - for (uint I = 0; I < MemSize.Length; I++) - { - if (StartPos < MemSize[I]) - { - return Math.Min(MemSize[I] - StartPos, RequiredSize); - } - } - - return 0; // Must never be here. + Mem[0][0] = value; // Must never happen; } } + + // sharpcompress: added the following code + public void GetBuffer(size_t Item, out byte[] buf, out uint offset) + { + if (Item < MemSize[0]) + { + //return Mem[0][Item]; + buf = Mem[0]; + offset = Item; + return; + } + for (uint I = 1; I < MemSize.Length; I++) + { + if (Item < MemSize[I]) + { + //return Mem[I][Item-MemSize[I-1]]; + buf = Mem[I]; + offset = Item - MemSize[I - 1]; + return; + } + } + //return Mem[0][0]; // Must never happen; + buf = Mem[0]; + offset = 0; + return; // Must never happen; + } + + public void CopyString(uint Length, uint Distance, ref size_t UnpPtr, size_t MaxWinMask) + { + var SrcPtr = UnpPtr - Distance; + while (Length-- > 0) + { + this[UnpPtr] = this[SrcPtr++ & MaxWinMask]; + // We need to have masked UnpPtr after quit from loop, so it must not + // be replaced with '(*this)[UnpPtr++ & MaxWinMask]' + UnpPtr = (UnpPtr + 1) & MaxWinMask; + } + } + + public void CopyData(byte[] Dest, size_t destOffset, size_t WinPos, size_t Size) + { + for (size_t I = 0; I < Size; I++) + { + Dest[destOffset + I] = this[WinPos + I]; + } + } + + public size_t GetBlockSize(size_t StartPos, size_t RequiredSize) + { + for (uint I = 0; I < MemSize.Length; I++) + { + if (StartPos < MemSize[I]) + { + return Math.Min(MemSize[I] - StartPos, RequiredSize); + } + } + + return 0; // Must never be here. + } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/PackDef.compress_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/PackDef.compress_hpp.cs index 9863446d..ed4e74ea 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/PackDef.compress_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/PackDef.compress_hpp.cs @@ -1,54 +1,53 @@ -namespace SharpCompress.Compressors.Rar.UnpackV2017 +namespace SharpCompress.Compressors.Rar.UnpackV2017; + +internal static class PackDef { - internal static class PackDef - { - // Combine pack and unpack constants to class to avoid polluting global - // namespace with numerous short names. - public const uint MAX_LZ_MATCH = 0x1001; - public const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3. - public const uint LOW_DIST_REP_COUNT = 16; + // Combine pack and unpack constants to class to avoid polluting global + // namespace with numerous short names. + public const uint MAX_LZ_MATCH = 0x1001; + public const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3. + public const uint LOW_DIST_REP_COUNT = 16; - public const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */ - public const uint DC = 64; - public const uint LDC = 16; - public const uint RC = 44; - public const uint HUFF_TABLE_SIZE = NC + DC + RC + LDC; - public const uint BC = 20; + public const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */ + public const uint DC = 64; + public const uint LDC = 16; + public const uint RC = 44; + public const uint HUFF_TABLE_SIZE = NC + DC + RC + LDC; + public const uint BC = 20; - public const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ - public const uint DC30 = 60; - public const uint LDC30 = 17; - public const uint RC30 = 28; - public const uint BC30 = 20; - public const uint HUFF_TABLE_SIZE30 = NC30 + DC30 + RC30 + LDC30; + public const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ + public const uint DC30 = 60; + public const uint LDC30 = 17; + public const uint RC30 = 28; + public const uint BC30 = 20; + public const uint HUFF_TABLE_SIZE30 = NC30 + DC30 + RC30 + LDC30; - public const uint NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */ - public const uint DC20 = 48; - public const uint RC20 = 28; - public const uint BC20 = 19; - public const uint MC20 = 257; + public const uint NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */ + public const uint DC20 = 48; + public const uint RC20 = 28; + public const uint BC20 = 19; + public const uint MC20 = 257; - // Largest alphabet size among all values listed above. - public const uint LARGEST_TABLE_SIZE = 306; + // Largest alphabet size among all values listed above. + public const uint LARGEST_TABLE_SIZE = 306; - // enum { - // CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE, - // CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA - // }; + // enum { + // CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE, + // CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA + // }; - //enum FilterType { - // These values must not be changed, because we use them directly - // in RAR5 compression and decompression code. - public const int FILTER_DELTA = 0; - public const int FILTER_E8 = 1; - public const int FILTER_E8E9 = 2; - public const int FILTER_ARM = 3; - public const int FILTER_AUDIO = 4; - public const int FILTER_RGB = 5; - public const int FILTER_ITANIUM = 6; - public const int FILTER_PPM = 7; - public const int FILTER_NONE = 8; - //} - } + //enum FilterType { + // These values must not be changed, because we use them directly + // in RAR5 compression and decompression code. + public const int FILTER_DELTA = 0; + public const int FILTER_E8 = 1; + public const int FILTER_E8E9 = 2; + public const int FILTER_ARM = 3; + public const int FILTER_AUDIO = 4; + public const int FILTER_RGB = 5; + public const int FILTER_ITANIUM = 6; + public const int FILTER_PPM = 7; + public const int FILTER_NONE = 8; + //} } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs index f840edf7..93ed3e64 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs @@ -1,122 +1,117 @@ -#if !Rar2017_64bit -using nint = System.Int32; -using nuint = System.UInt32; +#if !Rar2017_64bit using size_t = System.UInt32; #else using nint = System.Int64; using nuint = System.UInt64; using size_t = System.UInt64; #endif -using int64 = System.Int64; - using System; using System.IO; using SharpCompress.Common.Rar.Headers; -namespace SharpCompress.Compressors.Rar.UnpackV2017 +namespace SharpCompress.Compressors.Rar.UnpackV2017; + +internal partial class Unpack : IRarUnpack { - internal partial class Unpack : IRarUnpack + private FileHeader fileHeader; + private Stream readStream; + private Stream writeStream; + + private void _UnpackCtor() { - private FileHeader fileHeader; - private Stream readStream; - private Stream writeStream; - - private void _UnpackCtor() + for (var i = 0; i < AudV.Length; i++) { - for (int i = 0; i < AudV.Length; i++) - { - AudV[i] = new AudioVariables(); - } - } - - private int UnpIO_UnpRead(byte[] buf, int offset, int count) - { - // NOTE: caller has logic to check for -1 for error we throw instead. - return readStream.Read(buf, offset, count); - } - - private void UnpIO_UnpWrite(byte[] buf, size_t offset, uint count) - { - writeStream.Write(buf, checked((int)offset), checked((int)count)); - } - - public void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream) - { - // as of 12/2017 .NET limits array indexing to using a signed integer - // MaxWinSize causes unpack to use a fragmented window when the file - // window size exceeds MaxWinSize - // uggh, that's not how this variable is used, it's the size of the currently allocated window buffer - //x MaxWinSize = ((uint)int.MaxValue) + 1; - - // may be long.MaxValue which could indicate unknown size (not present in header) - DestUnpSize = fileHeader.UncompressedSize; - this.fileHeader = fileHeader; - this.readStream = readStream; - this.writeStream = writeStream; - if (!fileHeader.IsStored) - { - Init(fileHeader.WindowSize, fileHeader.IsSolid); - } - Suspended = false; - DoUnpack(); - } - - public void DoUnpack() - { - if (fileHeader.IsStored) - { - UnstoreFile(); - } - else - { - DoUnpack(fileHeader.CompressionAlgorithm, fileHeader.IsSolid); - } - } - - private void UnstoreFile() - { - var b = new byte[0x10000]; - do - { - int n = readStream.Read(b, 0, (int)Math.Min(b.Length, DestUnpSize)); - if (n == 0) - { - break; - } - writeStream.Write(b, 0, n); - DestUnpSize -= n; - } while (!Suspended); - } - - public bool Suspended { get; set; } - - public long DestSize - { - get => DestUnpSize; - } - - public int Char - { - get - { - // TODO: coderb: not sure where the "MAXSIZE-30" comes from, ported from V1 code - if (InAddr > MAX_SIZE - 30) - { - UnpReadBuf(); - } - return InBuf[InAddr++]; - } - } - - public int PpmEscChar - { - get => PPMEscChar; - set => PPMEscChar = value; - } - - public static byte[] EnsureCapacity(byte[] array, int length) - { - return array.Length < length ? new byte[length] : array; + AudV[i] = new AudioVariables(); } } + + private int UnpIO_UnpRead(byte[] buf, int offset, int count) + { + // NOTE: caller has logic to check for -1 for error we throw instead. + return readStream.Read(buf, offset, count); + } + + private void UnpIO_UnpWrite(byte[] buf, size_t offset, uint count) + { + writeStream.Write(buf, checked((int)offset), checked((int)count)); + } + + public void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream) + { + // as of 12/2017 .NET limits array indexing to using a signed integer + // MaxWinSize causes unpack to use a fragmented window when the file + // window size exceeds MaxWinSize + // uggh, that's not how this variable is used, it's the size of the currently allocated window buffer + //x MaxWinSize = ((uint)int.MaxValue) + 1; + + // may be long.MaxValue which could indicate unknown size (not present in header) + DestUnpSize = fileHeader.UncompressedSize; + this.fileHeader = fileHeader; + this.readStream = readStream; + this.writeStream = writeStream; + if (!fileHeader.IsStored) + { + Init(fileHeader.WindowSize, fileHeader.IsSolid); + } + Suspended = false; + DoUnpack(); + } + + public void DoUnpack() + { + if (fileHeader.IsStored) + { + UnstoreFile(); + } + else + { + DoUnpack(fileHeader.CompressionAlgorithm, fileHeader.IsSolid); + } + } + + private void UnstoreFile() + { + var b = new byte[0x10000]; + do + { + var n = readStream.Read(b, 0, (int)Math.Min(b.Length, DestUnpSize)); + if (n == 0) + { + break; + } + writeStream.Write(b, 0, n); + DestUnpSize -= n; + } while (!Suspended); + } + + public bool Suspended { get; set; } + + public long DestSize + { + get => DestUnpSize; + } + + public int Char + { + get + { + // TODO: coderb: not sure where the "MAXSIZE-30" comes from, ported from V1 code + if (InAddr > MAX_SIZE - 30) + { + UnpReadBuf(); + } + return InBuf[InAddr++]; + } + } + + public int PpmEscChar + { + get => PPMEscChar; + set => PPMEscChar = value; + } + + public static byte[] EnsureCapacity(byte[] array, int length) + { + return array.Length < length ? new byte[length] : array; + } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.rawint_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.rawint_hpp.cs index 69281b36..cb572e9d 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.rawint_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.rawint_hpp.cs @@ -1,123 +1,118 @@ -#if !Rar2017_64bit -using nint = System.Int32; -using nuint = System.UInt32; -using size_t = System.UInt32; +#if !Rar2017_64bit #else using nint = System.Int64; using nuint = System.UInt64; using size_t = System.UInt64; #endif -using int64 = System.Int64; using uint32 = System.UInt32; -namespace SharpCompress.Compressors.Rar.UnpackV2017 +namespace SharpCompress.Compressors.Rar.UnpackV2017; + +internal partial class Unpack { - internal partial class Unpack + //#define rotls(x,n,xsize) (((x)<<(n)) | ((x)>>(xsize-(n)))) + //#define rotrs(x,n,xsize) (((x)>>(n)) | ((x)<<(xsize-(n)))) + //#define rotl32(x,n) rotls(x,n,32) + //#define rotr32(x,n) rotrs(x,n,32) + // + //inline uint RawGet2(const void *Data) + //{ + // byte *D=(byte *)Data; + // return D[0]+(D[1]<<8); + //} + + private uint32 RawGet4(byte[] D, int offset) { - //#define rotls(x,n,xsize) (((x)<<(n)) | ((x)>>(xsize-(n)))) - //#define rotrs(x,n,xsize) (((x)>>(n)) | ((x)<<(xsize-(n)))) - //#define rotl32(x,n) rotls(x,n,32) - //#define rotr32(x,n) rotrs(x,n,32) - // - //inline uint RawGet2(const void *Data) - //{ - // byte *D=(byte *)Data; - // return D[0]+(D[1]<<8); - //} - - private uint32 RawGet4(byte[] D, int offset) - { - return (uint)( - D[offset] + (D[offset + 1] << 8) + (D[offset + 2] << 16) + (D[offset + 3] << 24) - ); - } - - //inline uint64 RawGet8(const void *Data) - //{ - //#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) - // byte *D=(byte *)Data; - // return INT32TO64(RawGet4(D+4),RawGet4(D)); - //#else - // return *(uint64 *)Data; - //#endif - //} - // - // - //inline void RawPut2(uint Field,void *Data) - //{ - // byte *D=(byte *)Data; - // D[0]=(byte)(Field); - // D[1]=(byte)(Field>>8); - //} - - private void RawPut4(uint32 Field, byte[] D, int offset) - { - D[offset] = (byte)(Field); - D[offset + 1] = (byte)(Field >> 8); - D[offset + 2] = (byte)(Field >> 16); - D[offset + 3] = (byte)(Field >> 24); - } - - //inline void RawPut8(uint64 Field,void *Data) - //{ - //#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) - // byte *D=(byte *)Data; - // D[0]=(byte)(Field); - // D[1]=(byte)(Field>>8); - // D[2]=(byte)(Field>>16); - // D[3]=(byte)(Field>>24); - // D[4]=(byte)(Field>>32); - // D[5]=(byte)(Field>>40); - // D[6]=(byte)(Field>>48); - // D[7]=(byte)(Field>>56); - //#else - // *(uint64 *)Data=Field; - //#endif - //} - - - //#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) - //#define USE_MEM_BYTESWAP - //#endif - - // Load 4 big endian bytes from memory and return uint32. - //inline uint32 RawGetBE4(const byte *m) - //{ - //#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) - // return _byteswap_ulong(*(uint32 *)m); - //#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) - // return __builtin_bswap32(*(uint32 *)m); - //#else - // return uint32(m[0]<<24) | uint32(m[1]<<16) | uint32(m[2]<<8) | m[3]; - //#endif - //} - - - // Save integer to memory as big endian. - //inline void RawPutBE4(uint32 i,byte *mem) - //{ - //#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) - // *(uint32*)mem = _byteswap_ulong(i); - //#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) - // *(uint32*)mem = __builtin_bswap32(i); - //#else - // mem[0]=byte(i>>24); - // mem[1]=byte(i>>16); - // mem[2]=byte(i>>8); - // mem[3]=byte(i); - //#endif - //} - - - //inline uint32 ByteSwap32(uint32 i) - //{ - //#ifdef _MSC_VER - // return _byteswap_ulong(i); - //#elif (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) - // return __builtin_bswap32(i); - //#else - // return (rotl32(i,24)&0xFF00FF00)|(rotl32(i,8)&0x00FF00FF); - //#endif - //} + return (uint)( + D[offset] + (D[offset + 1] << 8) + (D[offset + 2] << 16) + (D[offset + 3] << 24) + ); } + + //inline uint64 RawGet8(const void *Data) + //{ + //#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) + // byte *D=(byte *)Data; + // return INT32TO64(RawGet4(D+4),RawGet4(D)); + //#else + // return *(uint64 *)Data; + //#endif + //} + // + // + //inline void RawPut2(uint Field,void *Data) + //{ + // byte *D=(byte *)Data; + // D[0]=(byte)(Field); + // D[1]=(byte)(Field>>8); + //} + + private void RawPut4(uint32 Field, byte[] D, int offset) + { + D[offset] = (byte)(Field); + D[offset + 1] = (byte)(Field >> 8); + D[offset + 2] = (byte)(Field >> 16); + D[offset + 3] = (byte)(Field >> 24); + } + + //inline void RawPut8(uint64 Field,void *Data) + //{ + //#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) + // byte *D=(byte *)Data; + // D[0]=(byte)(Field); + // D[1]=(byte)(Field>>8); + // D[2]=(byte)(Field>>16); + // D[3]=(byte)(Field>>24); + // D[4]=(byte)(Field>>32); + // D[5]=(byte)(Field>>40); + // D[6]=(byte)(Field>>48); + // D[7]=(byte)(Field>>56); + //#else + // *(uint64 *)Data=Field; + //#endif + //} + + + //#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) + //#define USE_MEM_BYTESWAP + //#endif + + // Load 4 big endian bytes from memory and return uint32. + //inline uint32 RawGetBE4(const byte *m) + //{ + //#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) + // return _byteswap_ulong(*(uint32 *)m); + //#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) + // return __builtin_bswap32(*(uint32 *)m); + //#else + // return uint32(m[0]<<24) | uint32(m[1]<<16) | uint32(m[2]<<8) | m[3]; + //#endif + //} + + + // Save integer to memory as big endian. + //inline void RawPutBE4(uint32 i,byte *mem) + //{ + //#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) + // *(uint32*)mem = _byteswap_ulong(i); + //#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) + // *(uint32*)mem = __builtin_bswap32(i); + //#else + // mem[0]=byte(i>>24); + // mem[1]=byte(i>>16); + // mem[2]=byte(i>>8); + // mem[3]=byte(i); + //#endif + //} + + + //inline uint32 ByteSwap32(uint32 i) + //{ + //#ifdef _MSC_VER + // return _byteswap_ulong(i); + //#elif (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) + // return __builtin_bswap32(i); + //#else + // return (rotl32(i,24)&0xFF00FF00)|(rotl32(i,8)&0x00FF00FF); + //#endif + //} } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs index b661725d..80c2d03c 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs @@ -1,394 +1,367 @@ -using System; +using System; using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack15Local; -namespace SharpCompress.Compressors.Rar.UnpackV2017 +namespace SharpCompress.Compressors.Rar.UnpackV2017; + +internal partial class Unpack { - internal partial class Unpack + private const int STARTL1 = 2; + + private static readonly uint[] DecL1 = { - private const int STARTL1 = 2; + 0x8000, + 0xa000, + 0xc000, + 0xd000, + 0xe000, + 0xea00, + 0xee00, + 0xf000, + 0xf200, + 0xf200, + 0xffff + }; - private static readonly uint[] DecL1 = + private static readonly uint[] PosL1 = { 0, 0, 0, 2, 3, 5, 7, 11, 16, 20, 24, 32, 32 }; + + private const int STARTL2 = 3; + + private static readonly uint[] DecL2 = + { + 0xa000, + 0xc000, + 0xd000, + 0xe000, + 0xea00, + 0xee00, + 0xf000, + 0xf200, + 0xf240, + 0xffff + }; + + private static readonly uint[] PosL2 = { 0, 0, 0, 0, 5, 7, 9, 13, 18, 22, 26, 34, 36 }; + + private const int STARTHF0 = 4; + + private static readonly uint[] DecHf0 = + { + 0x8000, + 0xc000, + 0xe000, + 0xf200, + 0xf200, + 0xf200, + 0xf200, + 0xf200, + 0xffff + }; + + private static readonly uint[] PosHf0 = { 0, 0, 0, 0, 0, 8, 16, 24, 33, 33, 33, 33, 33 }; + + private const int STARTHF1 = 5; + + private static readonly uint[] DecHf1 = + { + 0x2000, + 0xc000, + 0xe000, + 0xf000, + 0xf200, + 0xf200, + 0xf7e0, + 0xffff + }; + + private static readonly uint[] PosHf1 = { 0, 0, 0, 0, 0, 0, 4, 44, 60, 76, 80, 80, 127 }; + + private const int STARTHF2 = 5; + + private static readonly uint[] DecHf2 = + { + 0x1000, + 0x2400, + 0x8000, + 0xc000, + 0xfa00, + 0xffff, + 0xffff, + 0xffff + }; + + private static readonly uint[] PosHf2 = { 0, 0, 0, 0, 0, 0, 2, 7, 53, 117, 233, 0, 0 }; + + private const int STARTHF3 = 6; + + private static readonly uint[] DecHf3 = + { + 0x800, + 0x2400, + 0xee00, + 0xfe80, + 0xffff, + 0xffff, + 0xffff + }; + + private static readonly uint[] PosHf3 = { 0, 0, 0, 0, 0, 0, 0, 2, 16, 218, 251, 0, 0 }; + + private const int STARTHF4 = 8; + private static readonly uint[] DecHf4 = { 0xff00, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff }; + private static readonly uint[] PosHf4 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0 }; + + private void Unpack15(bool Solid) + { + UnpInitData(Solid); + UnpInitData15(Solid); + UnpReadBuf(); + if (!Solid) { - 0x8000, - 0xa000, - 0xc000, - 0xd000, - 0xe000, - 0xea00, - 0xee00, - 0xf000, - 0xf200, - 0xf200, - 0xffff - }; - - private static readonly uint[] PosL1 = { 0, 0, 0, 2, 3, 5, 7, 11, 16, 20, 24, 32, 32 }; - - private const int STARTL2 = 3; - - private static readonly uint[] DecL2 = + InitHuff(); + UnpPtr = 0; + } + else { - 0xa000, - 0xc000, - 0xd000, - 0xe000, - 0xea00, - 0xee00, - 0xf000, - 0xf200, - 0xf240, - 0xffff - }; + UnpPtr = WrPtr; + } - private static readonly uint[] PosL2 = { 0, 0, 0, 0, 5, 7, 9, 13, 18, 22, 26, 34, 36 }; - - private const int STARTHF0 = 4; - - private static readonly uint[] DecHf0 = + --DestUnpSize; + if (DestUnpSize >= 0) { - 0x8000, - 0xc000, - 0xe000, - 0xf200, - 0xf200, - 0xf200, - 0xf200, - 0xf200, - 0xffff - }; + GetFlagsBuf(); + FlagsCnt = 8; + } - private static readonly uint[] PosHf0 = { 0, 0, 0, 0, 0, 8, 16, 24, 33, 33, 33, 33, 33 }; - - private const int STARTHF1 = 5; - - private static readonly uint[] DecHf1 = + while (DestUnpSize >= 0) { - 0x2000, - 0xc000, - 0xe000, - 0xf000, - 0xf200, - 0xf200, - 0xf7e0, - 0xffff - }; + UnpPtr &= MaxWinMask; - private static readonly uint[] PosHf1 = { 0, 0, 0, 0, 0, 0, 4, 44, 60, 76, 80, 80, 127 }; - - private const int STARTHF2 = 5; - - private static readonly uint[] DecHf2 = - { - 0x1000, - 0x2400, - 0x8000, - 0xc000, - 0xfa00, - 0xffff, - 0xffff, - 0xffff - }; - - private static readonly uint[] PosHf2 = { 0, 0, 0, 0, 0, 0, 2, 7, 53, 117, 233, 0, 0 }; - - private const int STARTHF3 = 6; - - private static readonly uint[] DecHf3 = - { - 0x800, - 0x2400, - 0xee00, - 0xfe80, - 0xffff, - 0xffff, - 0xffff - }; - - private static readonly uint[] PosHf3 = { 0, 0, 0, 0, 0, 0, 0, 2, 16, 218, 251, 0, 0 }; - - private const int STARTHF4 = 8; - private static readonly uint[] DecHf4 = { 0xff00, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff }; - private static readonly uint[] PosHf4 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0 }; - - private void Unpack15(bool Solid) - { - UnpInitData(Solid); - UnpInitData15(Solid); - UnpReadBuf(); - if (!Solid) + if (Inp.InAddr > ReadTop - 30 && !UnpReadBuf()) { - InitHuff(); - UnpPtr = 0; + break; + } + + if (((WrPtr - UnpPtr) & MaxWinMask) < 270 && WrPtr != UnpPtr) + { + UnpWriteBuf20(); + } + + if (StMode != 0) + { + HuffDecode(); + continue; + } + + if (--FlagsCnt < 0) + { + GetFlagsBuf(); + FlagsCnt = 7; + } + + if ((FlagBuf & 0x80) != 0) + { + FlagBuf <<= 1; + if (Nlzb > Nhfb) + { + LongLZ(); + } + else + { + HuffDecode(); + } } else { - UnpPtr = WrPtr; - } - - --DestUnpSize; - if (DestUnpSize >= 0) - { - GetFlagsBuf(); - FlagsCnt = 8; - } - - while (DestUnpSize >= 0) - { - UnpPtr &= MaxWinMask; - - if (Inp.InAddr > ReadTop - 30 && !UnpReadBuf()) - { - break; - } - - if (((WrPtr - UnpPtr) & MaxWinMask) < 270 && WrPtr != UnpPtr) - { - UnpWriteBuf20(); - } - - if (StMode != 0) - { - HuffDecode(); - continue; - } - + FlagBuf <<= 1; if (--FlagsCnt < 0) { GetFlagsBuf(); FlagsCnt = 7; } - if ((FlagBuf & 0x80) != 0) { FlagBuf <<= 1; if (Nlzb > Nhfb) { - LongLZ(); + HuffDecode(); } else { - HuffDecode(); + LongLZ(); } } 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(); - } + ShortLZ(); } } - UnpWriteBuf20(); + } + UnpWriteBuf20(); + } + + //#define GetShortLen1(pos) ((pos)==1 ? Buf60+3:ShortLen1[pos]) + private uint GetShortLen1(uint pos) + { + return ((pos) == 1 ? (uint)(Buf60 + 3) : ShortLen1[pos]); + } + + //#define GetShortLen2(pos) ((pos)==3 ? Buf60+3:ShortLen2[pos]) + private uint GetShortLen2(uint pos) + { + return ((pos) == 3 ? (uint)(Buf60 + 3) : ShortLen2[pos]); + } + + internal static class Unpack15Local + { + public static readonly uint[] ShortLen1 = + { + 1, + 3, + 4, + 4, + 5, + 6, + 7, + 8, + 8, + 4, + 4, + 5, + 6, + 6, + 4, + 0 + }; + public static readonly uint[] ShortXor1 = + { + 0, + 0xa0, + 0xd0, + 0xe0, + 0xf0, + 0xf8, + 0xfc, + 0xfe, + 0xff, + 0xc0, + 0x80, + 0x90, + 0x98, + 0x9c, + 0xb0 + }; + public static readonly uint[] ShortLen2 = + { + 2, + 3, + 3, + 3, + 4, + 4, + 5, + 6, + 6, + 4, + 4, + 5, + 6, + 6, + 4, + 0 + }; + public static readonly uint[] ShortXor2 = + { + 0, + 0x40, + 0x60, + 0xa0, + 0xd0, + 0xe0, + 0xf0, + 0xf8, + 0xfc, + 0xc0, + 0x80, + 0x90, + 0x98, + 0x9c, + 0xb0 + }; + } + + private void ShortLZ() + { + uint Length, + SaveLength; + uint LastDistance; + uint Distance; + int DistancePlace; + NumHuf = 0; + + var BitField = Inp.fgetbits(); + if (LCount == 2) + { + Inp.faddbits(1); + if (BitField >= 0x8000) + { + CopyString15(LastDist, LastLength); + return; + } + BitField <<= 1; + LCount = 0; } - //#define GetShortLen1(pos) ((pos)==1 ? Buf60+3:ShortLen1[pos]) - private uint GetShortLen1(uint pos) - { - return ((pos) == 1 ? (uint)(Buf60 + 3) : ShortLen1[pos]); - } + BitField >>= 8; - //#define GetShortLen2(pos) ((pos)==3 ? Buf60+3:ShortLen2[pos]) - private uint GetShortLen2(uint pos) - { - return ((pos) == 3 ? (uint)(Buf60 + 3) : ShortLen2[pos]); - } + // not thread safe, replaced by GetShortLen1 and GetShortLen2 macro + // ShortLen1[1]=ShortLen2[3]=Buf60+3; - internal static class Unpack15Local + if (AvrLn1 < 37) { - public static readonly uint[] ShortLen1 = + for (Length = 0; ; Length++) { - 1, - 3, - 4, - 4, - 5, - 6, - 7, - 8, - 8, - 4, - 4, - 5, - 6, - 6, - 4, - 0 - }; - public static readonly uint[] ShortXor1 = - { - 0, - 0xa0, - 0xd0, - 0xe0, - 0xf0, - 0xf8, - 0xfc, - 0xfe, - 0xff, - 0xc0, - 0x80, - 0x90, - 0x98, - 0x9c, - 0xb0 - }; - public static readonly uint[] ShortLen2 = - { - 2, - 3, - 3, - 3, - 4, - 4, - 5, - 6, - 6, - 4, - 4, - 5, - 6, - 6, - 4, - 0 - }; - public static readonly uint[] ShortXor2 = - { - 0, - 0x40, - 0x60, - 0xa0, - 0xd0, - 0xe0, - 0xf0, - 0xf8, - 0xfc, - 0xc0, - 0x80, - 0x90, - 0x98, - 0x9c, - 0xb0 - }; - } - - private void ShortLZ() - { - uint Length, - SaveLength; - uint LastDistance; - uint Distance; - int DistancePlace; - NumHuf = 0; - - uint BitField = Inp.fgetbits(); - if (LCount == 2) - { - Inp.faddbits(1); - if (BitField >= 0x8000) + if ( + ((BitField ^ ShortXor1[Length]) & (~(0xff >> (int)GetShortLen1(Length)))) + == 0 + ) { - CopyString15((uint)LastDist, LastLength); - return; + break; } - BitField <<= 1; + } + + Inp.faddbits(GetShortLen1(Length)); + } + else + { + for (Length = 0; ; Length++) + { + if ( + ((BitField ^ ShortXor2[Length]) & (~(0xff >> (int)GetShortLen2(Length)))) + == 0 + ) + { + break; + } + } + + Inp.faddbits(GetShortLen2(Length)); + } + + if (Length >= 9) + { + if (Length == 9) + { + LCount++; + CopyString15(LastDist, LastLength); + return; + } + if (Length == 14) + { LCount = 0; - } - - BitField >>= 8; - - // not thread safe, replaced by GetShortLen1 and GetShortLen2 macro - // ShortLen1[1]=ShortLen2[3]=Buf60+3; - - if (AvrLn1 < 37) - { - for (Length = 0; ; Length++) - { - if ( - ((BitField ^ ShortXor1[Length]) & (~(0xff >> (int)GetShortLen1(Length)))) - == 0 - ) - { - break; - } - } - - Inp.faddbits(GetShortLen1(Length)); - } - else - { - for (Length = 0; ; Length++) - { - if ( - ((BitField ^ ShortXor2[Length]) & (~(0xff >> (int)GetShortLen2(Length)))) - == 0 - ) - { - break; - } - } - - Inp.faddbits(GetShortLen2(Length)); - } - - if (Length >= 9) - { - if (Length == 9) - { - LCount++; - CopyString15((uint)LastDist, LastLength); - return; - } - if (Length == 14) - { - LCount = 0; - Length = DecodeNum(Inp.fgetbits(), STARTL2, DecL2, PosL2) + 5; - Distance = (Inp.fgetbits() >> 1) | 0x8000; - Inp.faddbits(15); - LastLength = Length; - LastDist = Distance; - CopyString15(Distance, Length); - return; - } - - LCount = 0; - SaveLength = Length; - Distance = OldDist[(OldDistPtr - (Length - 9)) & 3]; - Length = DecodeNum(Inp.fgetbits(), STARTL1, DecL1, PosL1) + 2; - if (Length == 0x101 && SaveLength == 10) - { - Buf60 ^= 1; - return; - } - if (Distance > 256) - { - Length++; - } - - if (Distance >= MaxDist3) - { - Length++; - } - - OldDist[OldDistPtr++] = Distance; - OldDistPtr = OldDistPtr & 3; + Length = DecodeNum(Inp.fgetbits(), STARTL2, DecL2, PosL2) + 5; + Distance = (Inp.fgetbits() >> 1) | 0x8000; + Inp.faddbits(15); LastLength = Length; LastDist = Distance; CopyString15(Distance, Length); @@ -396,345 +369,373 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 } LCount = 0; - AvrLn1 += Length; - AvrLn1 -= AvrLn1 >> 4; - - DistancePlace = (int)(DecodeNum(Inp.fgetbits(), STARTHF2, DecHf2, PosHf2) & 0xff); - Distance = ChSetA[DistancePlace]; - if (--DistancePlace != -1) + SaveLength = Length; + Distance = OldDist[(OldDistPtr - (Length - 9)) & 3]; + Length = DecodeNum(Inp.fgetbits(), STARTL1, DecL1, PosL1) + 2; + if (Length == 0x101 && SaveLength == 10) { - LastDistance = ChSetA[DistancePlace]; - ChSetA[DistancePlace + 1] = (ushort)LastDistance; - ChSetA[DistancePlace] = (ushort)Distance; + Buf60 ^= 1; + return; } - Length += 2; - OldDist[OldDistPtr++] = ++Distance; - OldDistPtr = OldDistPtr & 3; - LastLength = Length; - LastDist = Distance; - CopyString15(Distance, Length); - } - - private void LongLZ() - { - uint Length; - uint Distance; - uint DistancePlace, - NewDistancePlace; - uint OldAvr2, - OldAvr3; - - NumHuf = 0; - Nlzb += 16; - if (Nlzb > 0xff) + if (Distance > 256) { - Nlzb = 0x90; - Nhfb >>= 1; - } - OldAvr2 = AvrLn2; - - uint BitField = Inp.fgetbits(); - if (AvrLn2 >= 122) - { - Length = DecodeNum(BitField, STARTL2, DecL2, PosL2); - } - else if (AvrLn2 >= 64) - { - Length = DecodeNum(BitField, STARTL1, DecL1, PosL1); - } - else if (BitField < 0x100) - { - Length = BitField; - Inp.faddbits(16); - } - else - { - for (Length = 0; ((BitField << (int)Length) & 0x8000) == 0; Length++) - { - ; - } - - Inp.faddbits(Length + 1); + Length++; } - AvrLn2 += Length; - AvrLn2 -= AvrLn2 >> 5; - - BitField = Inp.fgetbits(); - if (AvrPlcB > 0x28ff) - { - DistancePlace = DecodeNum(BitField, STARTHF2, DecHf2, PosHf2); - } - else if (AvrPlcB > 0x6ff) - { - DistancePlace = DecodeNum(BitField, STARTHF1, DecHf1, PosHf1); - } - else - { - DistancePlace = DecodeNum(BitField, STARTHF0, DecHf0, PosHf0); - } - - AvrPlcB += DistancePlace; - AvrPlcB -= AvrPlcB >> 8; - while (true) - { - Distance = ChSetB[DistancePlace & 0xff]; - NewDistancePlace = NToPlB[Distance++ & 0xff]++; - if ((Distance & 0xff) != 0) - { - CorrHuff(ChSetB, NToPlB); - } - else - { - break; - } - } - - ChSetB[DistancePlace & 0xff] = ChSetB[NewDistancePlace]; - ChSetB[NewDistancePlace] = (ushort)Distance; - - Distance = ((Distance & 0xff00) | (Inp.fgetbits() >> 8)) >> 1; - Inp.faddbits(7); - - OldAvr3 = AvrLn3; - if (Length != 1 && Length != 4) - { - if (Length == 0 && Distance <= MaxDist3) - { - AvrLn3++; - AvrLn3 -= AvrLn3 >> 8; - } - else if (AvrLn3 > 0) - { - AvrLn3--; - } - } - - Length += 3; if (Distance >= MaxDist3) { Length++; } - if (Distance <= 256) - { - Length += 8; - } - - if (OldAvr3 > 0xb0 || AvrPlc >= 0x2a00 && OldAvr2 < 0x40) - { - MaxDist3 = 0x7f00; - } - else - { - MaxDist3 = 0x2001; - } - OldDist[OldDistPtr++] = Distance; - OldDistPtr = OldDistPtr & 3; + OldDistPtr &= 3; LastLength = Length; LastDist = Distance; CopyString15(Distance, Length); + return; } - private void HuffDecode() + LCount = 0; + AvrLn1 += Length; + AvrLn1 -= AvrLn1 >> 4; + + DistancePlace = (int)(DecodeNum(Inp.fgetbits(), STARTHF2, DecHf2, PosHf2) & 0xff); + Distance = ChSetA[DistancePlace]; + if (--DistancePlace != -1) { - uint CurByte, - NewBytePlace; - uint Length; - uint Distance; - int BytePlace; + LastDistance = ChSetA[DistancePlace]; + ChSetA[DistancePlace + 1] = (ushort)LastDistance; + ChSetA[DistancePlace] = (ushort)Distance; + } + Length += 2; + OldDist[OldDistPtr++] = ++Distance; + OldDistPtr &= 3; + LastLength = Length; + LastDist = Distance; + CopyString15(Distance, Length); + } - uint BitField = Inp.fgetbits(); + private void LongLZ() + { + uint Length; + uint Distance; + uint DistancePlace, + NewDistancePlace; + uint OldAvr2, + OldAvr3; - if (AvrPlc > 0x75ff) + NumHuf = 0; + Nlzb += 16; + if (Nlzb > 0xff) + { + Nlzb = 0x90; + Nhfb >>= 1; + } + OldAvr2 = AvrLn2; + + var BitField = Inp.fgetbits(); + if (AvrLn2 >= 122) + { + Length = DecodeNum(BitField, STARTL2, DecL2, PosL2); + } + else if (AvrLn2 >= 64) + { + Length = DecodeNum(BitField, STARTL1, DecL1, PosL1); + } + else if (BitField < 0x100) + { + Length = BitField; + Inp.faddbits(16); + } + else + { + for (Length = 0; ((BitField << (int)Length) & 0x8000) == 0; Length++) { - BytePlace = (int)DecodeNum(BitField, STARTHF4, DecHf4, PosHf4); + ; } - else if (AvrPlc > 0x5dff) + + Inp.faddbits(Length + 1); + } + + AvrLn2 += Length; + AvrLn2 -= AvrLn2 >> 5; + + BitField = Inp.fgetbits(); + if (AvrPlcB > 0x28ff) + { + DistancePlace = DecodeNum(BitField, STARTHF2, DecHf2, PosHf2); + } + else if (AvrPlcB > 0x6ff) + { + DistancePlace = DecodeNum(BitField, STARTHF1, DecHf1, PosHf1); + } + else + { + DistancePlace = DecodeNum(BitField, STARTHF0, DecHf0, PosHf0); + } + + AvrPlcB += DistancePlace; + AvrPlcB -= AvrPlcB >> 8; + while (true) + { + Distance = ChSetB[DistancePlace & 0xff]; + NewDistancePlace = NToPlB[Distance++ & 0xff]++; + if ((Distance & 0xff) != 0) { - BytePlace = (int)DecodeNum(BitField, STARTHF3, DecHf3, PosHf3); - } - else if (AvrPlc > 0x35ff) - { - BytePlace = (int)DecodeNum(BitField, STARTHF2, DecHf2, PosHf2); - } - else if (AvrPlc > 0x0dff) - { - BytePlace = (int)DecodeNum(BitField, STARTHF1, DecHf1, PosHf1); + CorrHuff(ChSetB, NToPlB); } else { - BytePlace = (int)DecodeNum(BitField, STARTHF0, DecHf0, PosHf0); + break; + } + } + + ChSetB[DistancePlace & 0xff] = ChSetB[NewDistancePlace]; + ChSetB[NewDistancePlace] = (ushort)Distance; + + Distance = ((Distance & 0xff00) | (Inp.fgetbits() >> 8)) >> 1; + Inp.faddbits(7); + + OldAvr3 = AvrLn3; + if (Length != 1 && Length != 4) + { + if (Length == 0 && Distance <= MaxDist3) + { + AvrLn3++; + AvrLn3 -= AvrLn3 >> 8; + } + else if (AvrLn3 > 0) + { + AvrLn3--; + } + } + + Length += 3; + if (Distance >= MaxDist3) + { + Length++; + } + + if (Distance <= 256) + { + Length += 8; + } + + if (OldAvr3 > 0xb0 || AvrPlc >= 0x2a00 && OldAvr2 < 0x40) + { + MaxDist3 = 0x7f00; + } + else + { + MaxDist3 = 0x2001; + } + + OldDist[OldDistPtr++] = Distance; + OldDistPtr &= 3; + LastLength = Length; + LastDist = Distance; + CopyString15(Distance, Length); + } + + private void HuffDecode() + { + uint CurByte, + NewBytePlace; + uint Length; + uint Distance; + int BytePlace; + + var BitField = Inp.fgetbits(); + + if (AvrPlc > 0x75ff) + { + BytePlace = (int)DecodeNum(BitField, STARTHF4, DecHf4, PosHf4); + } + else if (AvrPlc > 0x5dff) + { + BytePlace = (int)DecodeNum(BitField, STARTHF3, DecHf3, PosHf3); + } + else if (AvrPlc > 0x35ff) + { + BytePlace = (int)DecodeNum(BitField, STARTHF2, DecHf2, PosHf2); + } + else if (AvrPlc > 0x0dff) + { + BytePlace = (int)DecodeNum(BitField, STARTHF1, DecHf1, PosHf1); + } + else + { + BytePlace = (int)DecodeNum(BitField, STARTHF0, DecHf0, PosHf0); + } + + BytePlace &= 0xff; + if (StMode != 0) + { + if (BytePlace == 0 && BitField > 0xfff) + { + BytePlace = 0x100; } - BytePlace &= 0xff; - if (StMode != 0) + if (--BytePlace == -1) { - if (BytePlace == 0 && BitField > 0xfff) + BitField = Inp.fgetbits(); + Inp.faddbits(1); + if ((BitField & 0x8000) != 0) { - BytePlace = 0x100; - } - - if (--BytePlace == -1) - { - BitField = Inp.fgetbits(); - Inp.faddbits(1); - if ((BitField & 0x8000) != 0) - { - NumHuf = StMode = 0; - return; - } - else - { - Length = (BitField & 0x4000) != 0 ? 4U : 3; - Inp.faddbits(1); - Distance = DecodeNum(Inp.fgetbits(), STARTHF2, DecHf2, PosHf2); - Distance = (Distance << 5) | (Inp.fgetbits() >> 11); - Inp.faddbits(5); - CopyString15(Distance, Length); - return; - } - } - } - else if (NumHuf++ >= 16 && FlagsCnt == 0) - { - StMode = 1; - } - - AvrPlc += (uint)BytePlace; - AvrPlc -= AvrPlc >> 8; - Nhfb += 16; - if (Nhfb > 0xff) - { - Nhfb = 0x90; - Nlzb >>= 1; - } - - Window[UnpPtr++] = (byte)(ChSet[BytePlace] >> 8); - --DestUnpSize; - - while (true) - { - CurByte = ChSet[BytePlace]; - NewBytePlace = NToPl[CurByte++ & 0xff]++; - if ((CurByte & 0xff) > 0xa1) - { - CorrHuff(ChSet, NToPl); + NumHuf = StMode = 0; + return; } else { - break; + Length = (BitField & 0x4000) != 0 ? 4U : 3; + Inp.faddbits(1); + Distance = DecodeNum(Inp.fgetbits(), STARTHF2, DecHf2, PosHf2); + Distance = (Distance << 5) | (Inp.fgetbits() >> 11); + Inp.faddbits(5); + CopyString15(Distance, Length); + return; } } - - ChSet[BytePlace] = ChSet[NewBytePlace]; - ChSet[NewBytePlace] = (ushort)CurByte; + } + else if (NumHuf++ >= 16 && FlagsCnt == 0) + { + StMode = 1; } - private void GetFlagsBuf() + AvrPlc += (uint)BytePlace; + AvrPlc -= AvrPlc >> 8; + Nhfb += 16; + if (Nhfb > 0xff) { - uint Flags, - NewFlagsPlace; - uint FlagsPlace = DecodeNum(Inp.fgetbits(), STARTHF2, DecHf2, PosHf2); - - // Our Huffman table stores 257 items and needs all them in other parts - // of code such as when StMode is on, so the first item is control item. - // While normally we do not use the last item to code the flags byte here, - // we need to check for value 256 when unpacking in case we unpack - // a corrupt archive. - if (FlagsPlace >= ChSetC.Length) - { - return; - } - - while (true) - { - Flags = ChSetC[FlagsPlace]; - FlagBuf = Flags >> 8; - NewFlagsPlace = NToPlC[Flags++ & 0xff]++; - if ((Flags & 0xff) != 0) - { - break; - } - - CorrHuff(ChSetC, NToPlC); - } - - ChSetC[FlagsPlace] = ChSetC[NewFlagsPlace]; - ChSetC[NewFlagsPlace] = (ushort)Flags; + Nhfb = 0x90; + Nlzb >>= 1; } - private void UnpInitData15(bool Solid) + Window[UnpPtr++] = (byte)(ChSet[BytePlace] >> 8); + --DestUnpSize; + + while (true) { - if (!Solid) + CurByte = ChSet[BytePlace]; + NewBytePlace = NToPl[CurByte++ & 0xff]++; + if ((CurByte & 0xff) > 0xa1) { - AvrPlcB = AvrLn1 = AvrLn2 = AvrLn3 = 0; - NumHuf = Buf60 = 0; - AvrPlc = 0x3500; - MaxDist3 = 0x2001; - Nhfb = Nlzb = 0x80; + CorrHuff(ChSet, NToPl); } - FlagsCnt = 0; - FlagBuf = 0; - StMode = 0; - LCount = 0; - ReadTop = 0; - } - - private void InitHuff() - { - for (uint I = 0; I < 256; I++) + else { - ChSet[I] = ChSetB[I] = (ushort)(I << 8); - ChSetA[I] = (ushort)I; - ChSetC[I] = (ushort)(((~I + 1) & 0xff) << 8); - } - new Span(NToPl).Clear(); - new Span(NToPlB).Clear(); - new Span(NToPlC).Clear(); - CorrHuff(ChSetB, NToPlB); - } - - private void CorrHuff(ushort[] CharSet, byte[] NumToPlace) - { - int I, - J; - for (I = 7; I >= 0; I--) - for (J = 0; J < 32; J++) - { - CharSet[J] = (ushort)((CharSet[J] & ~0xff) | I); - } - - new Span(NumToPlace, 0, NToPl.Length).Clear(); - for (I = 6; I >= 0; I--) - { - NumToPlace[I] = (byte)((7 - I) * 32); + break; } } - private void CopyString15(uint Distance, uint Length) + ChSet[BytePlace] = ChSet[NewBytePlace]; + ChSet[NewBytePlace] = (ushort)CurByte; + } + + private void GetFlagsBuf() + { + uint Flags, + NewFlagsPlace; + var FlagsPlace = DecodeNum(Inp.fgetbits(), STARTHF2, DecHf2, PosHf2); + + // Our Huffman table stores 257 items and needs all them in other parts + // of code such as when StMode is on, so the first item is control item. + // While normally we do not use the last item to code the flags byte here, + // we need to check for value 256 when unpacking in case we unpack + // a corrupt archive. + if (FlagsPlace >= ChSetC.Length) { - DestUnpSize -= Length; - while (Length-- != 0) + return; + } + + while (true) + { + Flags = ChSetC[FlagsPlace]; + FlagBuf = Flags >> 8; + NewFlagsPlace = NToPlC[Flags++ & 0xff]++; + if ((Flags & 0xff) != 0) { - Window[UnpPtr] = Window[(UnpPtr - Distance) & MaxWinMask]; - UnpPtr = (UnpPtr + 1) & MaxWinMask; + break; + } + + CorrHuff(ChSetC, NToPlC); + } + + ChSetC[FlagsPlace] = ChSetC[NewFlagsPlace]; + ChSetC[NewFlagsPlace] = (ushort)Flags; + } + + private void UnpInitData15(bool Solid) + { + if (!Solid) + { + AvrPlcB = AvrLn1 = AvrLn2 = AvrLn3 = 0; + NumHuf = Buf60 = 0; + AvrPlc = 0x3500; + MaxDist3 = 0x2001; + Nhfb = Nlzb = 0x80; + } + FlagsCnt = 0; + FlagBuf = 0; + StMode = 0; + LCount = 0; + ReadTop = 0; + } + + private void InitHuff() + { + for (uint I = 0; I < 256; I++) + { + ChSet[I] = ChSetB[I] = (ushort)(I << 8); + ChSetA[I] = (ushort)I; + ChSetC[I] = (ushort)(((~I + 1) & 0xff) << 8); + } + new Span(NToPl).Clear(); + new Span(NToPlB).Clear(); + new Span(NToPlC).Clear(); + CorrHuff(ChSetB, NToPlB); + } + + private void CorrHuff(ushort[] CharSet, byte[] NumToPlace) + { + int I, + J; + for (I = 7; I >= 0; I--) + { + for (J = 0; J < 32; J++) + { + CharSet[J] = (ushort)((CharSet[J] & ~0xff) | I); } } - private uint DecodeNum(uint Num, uint StartPos, uint[] DecTab, uint[] PosTab) + new Span(NumToPlace, 0, NToPl.Length).Clear(); + for (I = 6; I >= 0; I--) { - int I; - for (Num &= 0xfff0, I = 0; DecTab[I] <= Num; I++) - { - StartPos++; - } - - Inp.faddbits(StartPos); - return ( - ((Num - (I != 0 ? DecTab[I - 1] : 0)) >> (int)(16 - StartPos)) + PosTab[StartPos] - ); + NumToPlace[I] = (byte)((7 - I) * 32); } } + + private void CopyString15(uint Distance, uint Length) + { + DestUnpSize -= Length; + while (Length-- != 0) + { + Window[UnpPtr] = Window[(UnpPtr - Distance) & MaxWinMask]; + UnpPtr = (UnpPtr + 1) & MaxWinMask; + } + } + + private uint DecodeNum(uint Num, uint StartPos, uint[] DecTab, uint[] PosTab) + { + int I; + for (Num &= 0xfff0, I = 0; DecTab[I] <= Num; I++) + { + StartPos++; + } + + Inp.faddbits(StartPos); + return ( + ((Num - (I != 0 ? DecTab[I - 1] : 0)) >> (int)(16 - StartPos)) + PosTab[StartPos] + ); + } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs index 52133cf9..d43a4ff2 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs @@ -1,304 +1,244 @@ -#if !Rar2017_64bit -using nint = System.Int32; -using nuint = System.UInt32; -using size_t = System.UInt32; +#if !Rar2017_64bit #else using nint = System.Int64; using nuint = System.UInt64; using size_t = System.UInt64; #endif -using int64 = System.Int64; - using System; using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack20Local; -namespace SharpCompress.Compressors.Rar.UnpackV2017 +namespace SharpCompress.Compressors.Rar.UnpackV2017; + +internal partial class Unpack { - internal partial class Unpack + private void CopyString20(uint Length, uint Distance) { - private void CopyString20(uint Length, uint Distance) + LastDist = OldDist[OldDistPtr++ & 3] = Distance; + LastLength = Length; + DestUnpSize -= Length; + CopyString(Length, Distance); + } + + internal static class Unpack20Local + { + public static readonly byte[] LDecode = { - LastDist = OldDist[OldDistPtr++ & 3] = Distance; - LastLength = Length; - DestUnpSize -= Length; - CopyString(Length, Distance); + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 10, + 12, + 14, + 16, + 20, + 24, + 28, + 32, + 40, + 48, + 56, + 64, + 80, + 96, + 112, + 128, + 160, + 192, + 224 + }; + public static readonly byte[] LBits = + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5 + }; + public static readonly uint[] DDecode = + { + 0, + 1, + 2, + 3, + 4, + 6, + 8, + 12, + 16, + 24, + 32, + 48, + 64, + 96, + 128, + 192, + 256, + 384, + 512, + 768, + 1024, + 1536, + 2048, + 3072, + 4096, + 6144, + 8192, + 12288, + 16384, + 24576, + 32768U, + 49152U, + 65536, + 98304, + 131072, + 196608, + 262144, + 327680, + 393216, + 458752, + 524288, + 589824, + 655360, + 720896, + 786432, + 851968, + 917504, + 983040 + }; + public static readonly byte[] DBits = + { + 0, + 0, + 0, + 0, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 5, + 6, + 6, + 7, + 7, + 8, + 8, + 9, + 9, + 10, + 10, + 11, + 11, + 12, + 12, + 13, + 13, + 14, + 14, + 15, + 15, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16 + }; + public static readonly byte[] SDDecode = { 0, 4, 8, 16, 32, 64, 128, 192 }; + public static readonly byte[] SDBits = { 2, 2, 3, 4, 5, 6, 6, 6 }; + } + + private void Unpack20(bool Solid) + { + uint Bits; + + if (Suspended) + { + UnpPtr = WrPtr; } - - internal static class Unpack20Local + else { - public static readonly byte[] LDecode = + UnpInitData(Solid); + if (!UnpReadBuf()) { - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 10, - 12, - 14, - 16, - 20, - 24, - 28, - 32, - 40, - 48, - 56, - 64, - 80, - 96, - 112, - 128, - 160, - 192, - 224 - }; - public static readonly byte[] LBits = - { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 1, - 1, - 1, - 2, - 2, - 2, - 2, - 3, - 3, - 3, - 3, - 4, - 4, - 4, - 4, - 5, - 5, - 5, - 5 - }; - public static readonly uint[] DDecode = - { - 0, - 1, - 2, - 3, - 4, - 6, - 8, - 12, - 16, - 24, - 32, - 48, - 64, - 96, - 128, - 192, - 256, - 384, - 512, - 768, - 1024, - 1536, - 2048, - 3072, - 4096, - 6144, - 8192, - 12288, - 16384, - 24576, - 32768U, - 49152U, - 65536, - 98304, - 131072, - 196608, - 262144, - 327680, - 393216, - 458752, - 524288, - 589824, - 655360, - 720896, - 786432, - 851968, - 917504, - 983040 - }; - public static readonly byte[] DBits = - { - 0, - 0, - 0, - 0, - 1, - 1, - 2, - 2, - 3, - 3, - 4, - 4, - 5, - 5, - 6, - 6, - 7, - 7, - 8, - 8, - 9, - 9, - 10, - 10, - 11, - 11, - 12, - 12, - 13, - 13, - 14, - 14, - 15, - 15, - 16, - 16, - 16, - 16, - 16, - 16, - 16, - 16, - 16, - 16, - 16, - 16, - 16, - 16 - }; - public static readonly byte[] SDDecode = { 0, 4, 8, 16, 32, 64, 128, 192 }; - public static readonly byte[] SDBits = { 2, 2, 3, 4, 5, 6, 6, 6 }; - } - - private void Unpack20(bool Solid) - { - uint Bits; - - if (Suspended) - { - UnpPtr = WrPtr; + return; } - else + + if ((!Solid || !TablesRead2) && !ReadTables20()) + { + return; + } + + --DestUnpSize; + } + + while (DestUnpSize >= 0) + { + UnpPtr &= MaxWinMask; + + if (Inp.InAddr > ReadTop - 30) { - UnpInitData(Solid); if (!UnpReadBuf()) { - return; + break; } - - if ((!Solid || !TablesRead2) && !ReadTables20()) - { - return; - } - - --DestUnpSize; } - while (DestUnpSize >= 0) + if (((WrPtr - UnpPtr) & MaxWinMask) < 270 && WrPtr != UnpPtr) { - UnpPtr &= MaxWinMask; - - if (Inp.InAddr > ReadTop - 30) + UnpWriteBuf20(); + if (Suspended) { - if (!UnpReadBuf()) - { - break; - } + return; } + } + if (UnpAudioBlock) + { + var AudioNumber = DecodeNumber(Inp, MD[UnpCurChannel]); - if (((WrPtr - UnpPtr) & MaxWinMask) < 270 && WrPtr != UnpPtr) - { - UnpWriteBuf20(); - if (Suspended) - { - return; - } - } - if (UnpAudioBlock) - { - uint AudioNumber = DecodeNumber(Inp, MD[UnpCurChannel]); - - if (AudioNumber == 256) - { - if (!ReadTables20()) - { - break; - } - - continue; - } - Window[UnpPtr++] = DecodeAudio((int)AudioNumber); - if (++UnpCurChannel == UnpChannels) - { - UnpCurChannel = 0; - } - - --DestUnpSize; - continue; - } - - uint Number = DecodeNumber(Inp, BlockTables.LD); - if (Number < 256) - { - Window[UnpPtr++] = (byte)Number; - --DestUnpSize; - continue; - } - if (Number > 269) - { - uint Length = (uint)(LDecode[Number -= 270] + 3); - if ((Bits = LBits[Number]) > 0) - { - Length += Inp.getbits() >> (int)(16 - Bits); - Inp.addbits(Bits); - } - - uint DistNumber = DecodeNumber(Inp, BlockTables.DD); - uint Distance = DDecode[DistNumber] + 1; - if ((Bits = DBits[DistNumber]) > 0) - { - Distance += Inp.getbits() >> (int)(16 - Bits); - Inp.addbits(Bits); - } - - if (Distance >= 0x2000) - { - Length++; - if (Distance >= 0x40000L) - { - Length++; - } - } - - CopyString20(Length, Distance); - continue; - } - if (Number == 269) + if (AudioNumber == 256) { if (!ReadTables20()) { @@ -307,78 +247,176 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 continue; } - if (Number == 256) + Window[UnpPtr++] = DecodeAudio((int)AudioNumber); + if (++UnpCurChannel == UnpChannels) { - CopyString20(LastLength, LastDist); - continue; + UnpCurChannel = 0; } - if (Number < 261) + + --DestUnpSize; + continue; + } + + var Number = DecodeNumber(Inp, BlockTables.LD); + if (Number < 256) + { + Window[UnpPtr++] = (byte)Number; + --DestUnpSize; + continue; + } + if (Number > 269) + { + var Length = (uint)(LDecode[Number -= 270] + 3); + if ((Bits = LBits[Number]) > 0) { - uint Distance = OldDist[(OldDistPtr - (Number - 256)) & 3]; - uint LengthNumber = DecodeNumber(Inp, BlockTables.RD); - uint Length = (uint)(LDecode[LengthNumber] + 2); - if ((Bits = LBits[LengthNumber]) > 0) - { - Length += Inp.getbits() >> (int)(16 - Bits); - Inp.addbits(Bits); - } - if (Distance >= 0x101) + Length += Inp.getbits() >> (int)(16 - Bits); + Inp.addbits(Bits); + } + + var DistNumber = DecodeNumber(Inp, BlockTables.DD); + var Distance = DDecode[DistNumber] + 1; + if ((Bits = DBits[DistNumber]) > 0) + { + Distance += Inp.getbits() >> (int)(16 - Bits); + Inp.addbits(Bits); + } + + if (Distance >= 0x2000) + { + Length++; + if (Distance >= 0x40000L) { Length++; - if (Distance >= 0x2000) + } + } + + CopyString20(Length, Distance); + continue; + } + if (Number == 269) + { + if (!ReadTables20()) + { + break; + } + + continue; + } + if (Number == 256) + { + CopyString20(LastLength, LastDist); + continue; + } + if (Number < 261) + { + var Distance = OldDist[(OldDistPtr - (Number - 256)) & 3]; + var LengthNumber = DecodeNumber(Inp, BlockTables.RD); + var Length = (uint)(LDecode[LengthNumber] + 2); + if ((Bits = LBits[LengthNumber]) > 0) + { + Length += Inp.getbits() >> (int)(16 - Bits); + Inp.addbits(Bits); + } + if (Distance >= 0x101) + { + Length++; + if (Distance >= 0x2000) + { + Length++; + if (Distance >= 0x40000) { Length++; - if (Distance >= 0x40000) - { - Length++; - } } } - CopyString20(Length, Distance); - continue; } - if (Number < 270) + CopyString20(Length, Distance); + continue; + } + if (Number < 270) + { + var Distance = (uint)(SDDecode[Number -= 261] + 1); + if ((Bits = SDBits[Number]) > 0) { - uint Distance = (uint)(SDDecode[Number -= 261] + 1); - if ((Bits = SDBits[Number]) > 0) - { - Distance += Inp.getbits() >> (int)(16 - Bits); - Inp.addbits(Bits); - } - CopyString20(2, Distance); - continue; + Distance += Inp.getbits() >> (int)(16 - Bits); + Inp.addbits(Bits); } + CopyString20(2, Distance); + continue; } - ReadLastTables(); - UnpWriteBuf20(); + } + ReadLastTables(); + UnpWriteBuf20(); + } + + private void UnpWriteBuf20() + { + if (UnpPtr != WrPtr) + { + UnpSomeRead = true; } - private void UnpWriteBuf20() + if (UnpPtr < WrPtr) { - if (UnpPtr != WrPtr) - { - UnpSomeRead = true; - } - - if (UnpPtr < WrPtr) - { - UnpIO_UnpWrite(Window, WrPtr, (uint)(-(int)WrPtr & MaxWinMask)); - UnpIO_UnpWrite(Window, 0, UnpPtr); - UnpAllBuf = true; - } - else - { - UnpIO_UnpWrite(Window, WrPtr, UnpPtr - WrPtr); - } - - WrPtr = UnpPtr; + UnpIO_UnpWrite(Window, WrPtr, (uint)(-(int)WrPtr & MaxWinMask)); + UnpIO_UnpWrite(Window, 0, UnpPtr); + UnpAllBuf = true; + } + else + { + UnpIO_UnpWrite(Window, WrPtr, UnpPtr - WrPtr); } - private bool ReadTables20() + WrPtr = UnpPtr; + } + + private bool ReadTables20() + { + var BitLength = new byte[BC20]; + var Table = new byte[MC20 * 4]; + if (Inp.InAddr > ReadTop - 25) { - byte[] BitLength = new byte[BC20]; - byte[] Table = new byte[MC20 * 4]; - if (Inp.InAddr > ReadTop - 25) + if (!UnpReadBuf()) + { + return false; + } + } + + var BitField = Inp.getbits(); + UnpAudioBlock = (BitField & 0x8000) != 0; + + if ((BitField & 0x4000) != 0) + { + new Span(UnpOldTable20).Clear(); + } + + Inp.addbits(2); + + uint TableSize; + if (UnpAudioBlock) + { + UnpChannels = ((BitField >> 12) & 3) + 1; + if (UnpCurChannel >= UnpChannels) + { + UnpCurChannel = 0; + } + + Inp.addbits(2); + TableSize = MC20 * UnpChannels; + } + else + { + TableSize = NC20 + DC20 + RC20; + } + + for (uint I = 0; I < BC20; I++) + { + BitLength[I] = (byte)(Inp.getbits() >> 12); + Inp.addbits(4); + } + MakeDecodeTables(BitLength, 0, BlockTables.BD, BC20); + for (uint I = 0; I < TableSize;) + { + if (Inp.InAddr > ReadTop - 5) { if (!UnpReadBuf()) { @@ -386,279 +424,235 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 } } - uint BitField = Inp.getbits(); - UnpAudioBlock = (BitField & 0x8000) != 0; - - if ((BitField & 0x4000) != 0) + var Number = DecodeNumber(Inp, BlockTables.BD); + if (Number < 16) { - new Span(UnpOldTable20).Clear(); + Table[I] = (byte)((Number + UnpOldTable20[I]) & 0xf); + I++; } - - Inp.addbits(2); - - uint TableSize; - if (UnpAudioBlock) + else if (Number == 16) { - UnpChannels = ((BitField >> 12) & 3) + 1; - if (UnpCurChannel >= UnpChannels) - { - UnpCurChannel = 0; - } - + var N = (Inp.getbits() >> 14) + 3; Inp.addbits(2); - TableSize = MC20 * UnpChannels; - } - else - { - TableSize = NC20 + DC20 + RC20; - } - - for (uint I = 0; I < BC20; I++) - { - BitLength[I] = (byte)(Inp.getbits() >> 12); - Inp.addbits(4); - } - MakeDecodeTables(BitLength, 0, BlockTables.BD, BC20); - for (uint I = 0; I < TableSize; ) - { - if (Inp.InAddr > ReadTop - 5) + if (I == 0) { - if (!UnpReadBuf()) - { - return false; - } - } - - uint Number = DecodeNumber(Inp, BlockTables.BD); - if (Number < 16) - { - Table[I] = (byte)((Number + UnpOldTable20[I]) & 0xf); - I++; - } - else if (Number == 16) - { - uint N = (Inp.getbits() >> 14) + 3; - Inp.addbits(2); - if (I == 0) - { - return false; // We cannot have "repeat previous" code at the first position. - } - else - { - while (N-- > 0 && I < TableSize) - { - Table[I] = Table[I - 1]; - I++; - } - } + return false; // We cannot have "repeat previous" code at the first position. } else { - uint N; - if (Number == 17) - { - N = (Inp.getbits() >> 13) + 3; - Inp.addbits(3); - } - else - { - N = (Inp.getbits() >> 9) + 11; - Inp.addbits(7); - } while (N-- > 0 && I < TableSize) { - Table[I++] = 0; + Table[I] = Table[I - 1]; + I++; } } } - TablesRead2 = true; - if (Inp.InAddr > ReadTop) - { - return true; - } - - if (UnpAudioBlock) - { - for (uint I = 0; I < UnpChannels; I++) - { - MakeDecodeTables(Table, (int)(I * MC20), MD[I], MC20); - } - } else { - MakeDecodeTables(Table, 0, BlockTables.LD, NC20); - MakeDecodeTables(Table, (int)NC20, BlockTables.DD, DC20); - MakeDecodeTables(Table, (int)(NC20 + DC20), BlockTables.RD, RC20); + uint N; + if (Number == 17) + { + N = (Inp.getbits() >> 13) + 3; + Inp.addbits(3); + } + else + { + N = (Inp.getbits() >> 9) + 11; + Inp.addbits(7); + } + while (N-- > 0 && I < TableSize) + { + Table[I++] = 0; + } } - //x memcpy(UnpOldTable20,Table,sizeof(UnpOldTable20)); - Array.Copy(Table, UnpOldTable20, UnpOldTable20.Length); + } + TablesRead2 = true; + if (Inp.InAddr > ReadTop) + { return true; } - private void ReadLastTables() + if (UnpAudioBlock) { - if (ReadTop >= Inp.InAddr + 5) + for (uint I = 0; I < UnpChannels; I++) { - if (UnpAudioBlock) - { - if (DecodeNumber(Inp, MD[UnpCurChannel]) == 256) - { - ReadTables20(); - } - } - else if (DecodeNumber(Inp, BlockTables.LD) == 269) + MakeDecodeTables(Table, (int)(I * MC20), MD[I], MC20); + } + } + else + { + MakeDecodeTables(Table, 0, BlockTables.LD, NC20); + MakeDecodeTables(Table, (int)NC20, BlockTables.DD, DC20); + MakeDecodeTables(Table, (int)(NC20 + DC20), BlockTables.RD, RC20); + } + //x memcpy(UnpOldTable20,Table,sizeof(UnpOldTable20)); + Array.Copy(Table, UnpOldTable20, UnpOldTable20.Length); + return true; + } + + private void ReadLastTables() + { + if (ReadTop >= Inp.InAddr + 5) + { + if (UnpAudioBlock) + { + if (DecodeNumber(Inp, MD[UnpCurChannel]) == 256) { ReadTables20(); } } - } - - private void UnpInitData20(bool Solid) - { - if (!Solid) + else if (DecodeNumber(Inp, BlockTables.LD) == 269) { - TablesRead2 = false; - UnpAudioBlock = false; - UnpChannelDelta = 0; - UnpCurChannel = 0; - UnpChannels = 1; - - //memset(AudV,0,sizeof(AudV)); - AudV = new AudioVariables[4]; - new Span(UnpOldTable20).Clear(); - //memset(MD,0,sizeof(MD)); - MD = new DecodeTable[4]; + ReadTables20(); } } - - private byte DecodeAudio(int Delta) - { - AudioVariables V = AudV[UnpCurChannel]; - V.ByteCount++; - V.D4 = V.D3; - V.D3 = V.D2; - V.D2 = V.LastDelta - V.D1; - V.D1 = V.LastDelta; - int PCh = - 8 * V.LastChar - + V.K1 * V.D1 - + V.K2 * V.D2 - + V.K3 * V.D3 - + V.K4 * V.D4 - + V.K5 * UnpChannelDelta; - PCh = (PCh >> 3) & 0xFF; - - uint Ch = (uint)(PCh - Delta); - - int D = (sbyte)Delta; - // Left shift of negative value is undefined behavior in C++, - // so we cast it to unsigned to follow the standard. - D = (int)((uint)D << 3); - - V.Dif[0] += (uint)Math.Abs(D); - V.Dif[1] += (uint)Math.Abs(D - V.D1); - V.Dif[2] += (uint)Math.Abs(D + V.D1); - V.Dif[3] += (uint)Math.Abs(D - V.D2); - V.Dif[4] += (uint)Math.Abs(D + V.D2); - V.Dif[5] += (uint)Math.Abs(D - V.D3); - V.Dif[6] += (uint)Math.Abs(D + V.D3); - V.Dif[7] += (uint)Math.Abs(D - V.D4); - V.Dif[8] += (uint)Math.Abs(D + V.D4); - V.Dif[9] += (uint)Math.Abs(D - UnpChannelDelta); - V.Dif[10] += (uint)Math.Abs(D + UnpChannelDelta); - - UnpChannelDelta = V.LastDelta = (sbyte)(Ch - V.LastChar); - V.LastChar = (int)Ch; - - if ((V.ByteCount & 0x1F) == 0) - { - uint MinDif = V.Dif[0], - NumMinDif = 0; - V.Dif[0] = 0; - for (uint I = 1; I < V.Dif.Length; I++) - { - if (V.Dif[I] < MinDif) - { - MinDif = V.Dif[I]; - NumMinDif = I; - } - V.Dif[I] = 0; - } - switch (NumMinDif) - { - case 1: - if (V.K1 >= -16) - { - V.K1--; - } - - break; - case 2: - if (V.K1 < 16) - { - V.K1++; - } - - break; - case 3: - if (V.K2 >= -16) - { - V.K2--; - } - - break; - case 4: - if (V.K2 < 16) - { - V.K2++; - } - - break; - case 5: - if (V.K3 >= -16) - { - V.K3--; - } - - break; - case 6: - if (V.K3 < 16) - { - V.K3++; - } - - break; - case 7: - if (V.K4 >= -16) - { - V.K4--; - } - - break; - case 8: - if (V.K4 < 16) - { - V.K4++; - } - - break; - case 9: - if (V.K5 >= -16) - { - V.K5--; - } - - break; - case 10: - if (V.K5 < 16) - { - V.K5++; - } - - break; - } - } - return (byte)Ch; - } + } + + private void UnpInitData20(bool Solid) + { + if (!Solid) + { + TablesRead2 = false; + UnpAudioBlock = false; + UnpChannelDelta = 0; + UnpCurChannel = 0; + UnpChannels = 1; + + //memset(AudV,0,sizeof(AudV)); + AudV = new AudioVariables[4]; + new Span(UnpOldTable20).Clear(); + //memset(MD,0,sizeof(MD)); + MD = new DecodeTable[4]; + } + } + + private byte DecodeAudio(int Delta) + { + var V = AudV[UnpCurChannel]; + V.ByteCount++; + V.D4 = V.D3; + V.D3 = V.D2; + V.D2 = V.LastDelta - V.D1; + V.D1 = V.LastDelta; + var PCh = + (8 * V.LastChar) + + (V.K1 * V.D1) + + (V.K2 * V.D2) + + (V.K3 * V.D3) + + (V.K4 * V.D4) + + (V.K5 * UnpChannelDelta); + PCh = (PCh >> 3) & 0xFF; + + var Ch = (uint)(PCh - Delta); + + int D = (sbyte)Delta; + // Left shift of negative value is undefined behavior in C++, + // so we cast it to unsigned to follow the standard. + D = (int)((uint)D << 3); + + V.Dif[0] += (uint)Math.Abs(D); + V.Dif[1] += (uint)Math.Abs(D - V.D1); + V.Dif[2] += (uint)Math.Abs(D + V.D1); + V.Dif[3] += (uint)Math.Abs(D - V.D2); + V.Dif[4] += (uint)Math.Abs(D + V.D2); + V.Dif[5] += (uint)Math.Abs(D - V.D3); + V.Dif[6] += (uint)Math.Abs(D + V.D3); + V.Dif[7] += (uint)Math.Abs(D - V.D4); + V.Dif[8] += (uint)Math.Abs(D + V.D4); + V.Dif[9] += (uint)Math.Abs(D - UnpChannelDelta); + V.Dif[10] += (uint)Math.Abs(D + UnpChannelDelta); + + UnpChannelDelta = V.LastDelta = (sbyte)(Ch - V.LastChar); + V.LastChar = (int)Ch; + + if ((V.ByteCount & 0x1F) == 0) + { + uint MinDif = V.Dif[0], + NumMinDif = 0; + V.Dif[0] = 0; + for (uint I = 1; I < V.Dif.Length; I++) + { + if (V.Dif[I] < MinDif) + { + MinDif = V.Dif[I]; + NumMinDif = I; + } + V.Dif[I] = 0; + } + switch (NumMinDif) + { + case 1: + if (V.K1 >= -16) + { + V.K1--; + } + + break; + case 2: + if (V.K1 < 16) + { + V.K1++; + } + + break; + case 3: + if (V.K2 >= -16) + { + V.K2--; + } + + break; + case 4: + if (V.K2 < 16) + { + V.K2++; + } + + break; + case 5: + if (V.K3 >= -16) + { + V.K3--; + } + + break; + case 6: + if (V.K3 < 16) + { + V.K3++; + } + + break; + case 7: + if (V.K4 >= -16) + { + V.K4--; + } + + break; + case 8: + if (V.K4 < 16) + { + V.K4++; + } + + break; + case 9: + if (V.K5 >= -16) + { + V.K5--; + } + + break; + case 10: + if (V.K5 < 16) + { + V.K5++; + } + + break; + } + } + return (byte)Ch; } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs index 8491f7af..7a5caee0 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs @@ -1,13 +1,9 @@ -#if !Rar2017_64bit -using nint = System.Int32; -using nuint = System.UInt32; -using size_t = System.UInt32; +#if !Rar2017_64bit #else using nint = System.Int64; using nuint = System.UInt64; using size_t = System.UInt64; #endif -using int64 = System.Int64; //using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack30Local; /* diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs index 32897c44..08f58601 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs @@ -1,8 +1,6 @@ -#nullable disable +#nullable disable #if !Rar2017_64bit -using nint = System.Int32; -using nuint = System.UInt32; using size_t = System.UInt32; #else using nint = System.Int64; @@ -15,748 +13,792 @@ using System; using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; -namespace SharpCompress.Compressors.Rar.UnpackV2017 +namespace SharpCompress.Compressors.Rar.UnpackV2017; + +internal partial class Unpack { - internal partial class Unpack + private void Unpack5(bool Solid) { - private void Unpack5(bool Solid) + FileExtracted = true; + + if (!Suspended) { - FileExtracted = true; - - if (!Suspended) - { - UnpInitData(Solid); - if (!UnpReadBuf()) - { - return; - } - - // Check TablesRead5 to be sure that we read tables at least once - // regardless of current block header TablePresent flag. - // So we can safefly use these tables below. - if ( - !ReadBlockHeader(Inp, ref BlockHeader) - || !ReadTables(Inp, ref BlockHeader, ref BlockTables) - || !TablesRead5 - ) - { - return; - } - } - - while (true) - { - UnpPtr &= MaxWinMask; - - if (Inp.InAddr >= ReadBorder) - { - bool FileDone = false; - - // We use 'while', because for empty block containing only Huffman table, - // we'll be on the block border once again just after reading the table. - while ( - Inp.InAddr > BlockHeader.BlockStart + BlockHeader.BlockSize - 1 - || Inp.InAddr == BlockHeader.BlockStart + BlockHeader.BlockSize - 1 - && Inp.InBit >= BlockHeader.BlockBitSize - ) - { - if (BlockHeader.LastBlockInFile) - { - FileDone = true; - break; - } - if ( - !ReadBlockHeader(Inp, ref BlockHeader) - || !ReadTables(Inp, ref BlockHeader, ref BlockTables) - ) - { - return; - } - } - if (FileDone || !UnpReadBuf()) - { - break; - } - } - - if ( - ((WriteBorder - UnpPtr) & MaxWinMask) < MAX_LZ_MATCH + 3 - && WriteBorder != UnpPtr - ) - { - UnpWriteBuf(); - if (WrittenFileSize > DestUnpSize) - { - return; - } - - if (Suspended) - { - FileExtracted = false; - return; - } - } - - uint MainSlot = DecodeNumber(Inp, BlockTables.LD); - if (MainSlot < 256) - { - if (Fragmented) - { - FragWindow[UnpPtr++] = (byte)MainSlot; - } - else - { - Window[UnpPtr++] = (byte)MainSlot; - } - - continue; - } - if (MainSlot >= 262) - { - uint Length = SlotToLength(Inp, MainSlot - 262); - - uint DBits, - Distance = 1, - DistSlot = DecodeNumber(Inp, BlockTables.DD); - if (DistSlot < 4) - { - DBits = 0; - Distance += DistSlot; - } - else - { - DBits = DistSlot / 2 - 1; - Distance += (2 | (DistSlot & 1)) << (int)DBits; - } - - if (DBits > 0) - { - if (DBits >= 4) - { - if (DBits > 4) - { - Distance += ((Inp.getbits32() >> (int)(36 - DBits)) << 4); - Inp.addbits(DBits - 4); - } - uint LowDist = DecodeNumber(Inp, BlockTables.LDD); - Distance += LowDist; - } - else - { - Distance += Inp.getbits32() >> (int)(32 - DBits); - Inp.addbits(DBits); - } - } - - if (Distance > 0x100) - { - Length++; - if (Distance > 0x2000) - { - Length++; - if (Distance > 0x40000) - { - Length++; - } - } - } - - InsertOldDist(Distance); - LastLength = Length; - if (Fragmented) - { - FragWindow.CopyString(Length, Distance, ref UnpPtr, MaxWinMask); - } - else - { - CopyString(Length, Distance); - } - - continue; - } - if (MainSlot == 256) - { - UnpackFilter Filter = new UnpackFilter(); - if (!ReadFilter(Inp, Filter) || !AddFilter(Filter)) - { - break; - } - - continue; - } - if (MainSlot == 257) - { - if (LastLength != 0) - { - if (Fragmented) - { - FragWindow.CopyString(LastLength, OldDist[0], ref UnpPtr, MaxWinMask); - } - else - { - CopyString(LastLength, OldDist[0]); - } - } - - continue; - } - if (MainSlot < 262) - { - uint DistNum = MainSlot - 258; - uint Distance = OldDist[DistNum]; - for (uint I = DistNum; I > 0; I--) - { - OldDist[I] = OldDist[I - 1]; - } - - OldDist[0] = Distance; - - uint LengthSlot = DecodeNumber(Inp, BlockTables.RD); - uint Length = SlotToLength(Inp, LengthSlot); - LastLength = Length; - if (Fragmented) - { - FragWindow.CopyString(Length, Distance, ref UnpPtr, MaxWinMask); - } - else - { - CopyString(Length, Distance); - } - - continue; - } - } - UnpWriteBuf(); - } - - private uint ReadFilterData(BitInput Inp) - { - uint ByteCount = (Inp.fgetbits() >> 14) + 1; - Inp.addbits(2); - - uint Data = 0; - for (uint I = 0; I < ByteCount; I++) - { - Data += (Inp.fgetbits() >> 8) << (int)(I * 8); - Inp.addbits(8); - } - return Data; - } - - private bool ReadFilter(BitInput Inp, UnpackFilter Filter) - { - if (!Inp.ExternalBuffer && Inp.InAddr > ReadTop - 16) - { - if (!UnpReadBuf()) - { - return false; - } - } - - Filter.BlockStart = ReadFilterData(Inp); - Filter.BlockLength = ReadFilterData(Inp); - if (Filter.BlockLength > MAX_FILTER_BLOCK_SIZE) - { - Filter.BlockLength = 0; - } - - Filter.Type = (byte)(Inp.fgetbits() >> 13); - Inp.faddbits(3); - - if (Filter.Type == FILTER_DELTA) - { - Filter.Channels = (byte)((Inp.fgetbits() >> 11) + 1); - Inp.faddbits(5); - } - - return true; - } - - private bool AddFilter(UnpackFilter Filter) - { - if (Filters.Count >= MAX_UNPACK_FILTERS) - { - UnpWriteBuf(); // Write data, apply and flush filters. - if (Filters.Count >= MAX_UNPACK_FILTERS) - { - InitFilters(); // Still too many filters, prevent excessive memory use. - } - } - - // If distance to filter start is that large that due to circular dictionary - // mode now it points to old not written yet data, then we set 'NextWindow' - // flag and process this filter only after processing that older data. - Filter.NextWindow = - WrPtr != UnpPtr && ((WrPtr - UnpPtr) & MaxWinMask) <= Filter.BlockStart; - - Filter.BlockStart = (uint)((Filter.BlockStart + UnpPtr) & MaxWinMask); - Filters.Add(Filter); - return true; - } - - private bool UnpReadBuf() - { - int DataSize = ReadTop - Inp.InAddr; // Data left to process. - if (DataSize < 0) - { - return false; - } - - BlockHeader.BlockSize -= Inp.InAddr - BlockHeader.BlockStart; - if (Inp.InAddr > MAX_SIZE / 2) - { - // If we already processed more than half of buffer, let's move - // remaining data into beginning to free more space for new data - // and ensure that calling function does not cross the buffer border - // even if we did not read anything here. Also it ensures that read size - // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk - // to make it zero. - if (DataSize > 0) - //x memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); - { - Buffer.BlockCopy(Inp.InBuf, Inp.InAddr, Inp.InBuf, 0, DataSize); - } - - Inp.InAddr = 0; - ReadTop = DataSize; - } - else - { - DataSize = ReadTop; - } - - int ReadCode = 0; - if (MAX_SIZE != DataSize) - { - ReadCode = UnpIO_UnpRead(Inp.InBuf, DataSize, MAX_SIZE - DataSize); - } - - if (ReadCode > 0) // Can be also -1. - { - ReadTop += ReadCode; - } - - ReadBorder = ReadTop - 30; - BlockHeader.BlockStart = Inp.InAddr; - if (BlockHeader.BlockSize != -1) // '-1' means not defined yet. - { - // We may need to quit from main extraction loop and read new block header - // and trees earlier than data in input buffer ends. - ReadBorder = Math.Min( - ReadBorder, - BlockHeader.BlockStart + BlockHeader.BlockSize - 1 - ); - } - return ReadCode != -1; - } - - private void UnpWriteBuf() - { - size_t WrittenBorder = WrPtr; - size_t FullWriteSize = (UnpPtr - WrittenBorder) & MaxWinMask; - size_t WriteSizeLeft = FullWriteSize; - bool NotAllFiltersProcessed = false; - //for (size_t I=0;I int - for (int I = 0; I < Filters.Count; I++) - { - // Here we apply filters to data which we need to write. - // We always copy data to another memory block before processing. - // We cannot process them just in place in Window buffer, because - // these data can be used for future string matches, so we must - // preserve them in original form. - - UnpackFilter flt = Filters[I]; - if (flt.Type == FILTER_NONE) - { - continue; - } - - if (flt.NextWindow) - { - // Here we skip filters which have block start in current data range - // due to address wrap around in circular dictionary, but actually - // belong to next dictionary block. If such filter start position - // is included to current write range, then we reset 'NextWindow' flag. - // In fact we can reset it even without such check, because current - // implementation seems to guarantee 'NextWindow' flag reset after - // buffer writing for all existing filters. But let's keep this check - // just in case. Compressor guarantees that distance between - // filter block start and filter storing position cannot exceed - // the dictionary size. So if we covered the filter block start with - // our write here, we can safely assume that filter is applicable - // to next block on no further wrap arounds is possible. - if (((flt.BlockStart - WrPtr) & MaxWinMask) <= FullWriteSize) - { - flt.NextWindow = false; - } - - continue; - } - uint BlockStart = flt.BlockStart; - uint BlockLength = flt.BlockLength; - if (((BlockStart - WrittenBorder) & MaxWinMask) < WriteSizeLeft) - { - if (WrittenBorder != BlockStart) - { - UnpWriteArea(WrittenBorder, BlockStart); - WrittenBorder = BlockStart; - WriteSizeLeft = (UnpPtr - WrittenBorder) & MaxWinMask; - } - if (BlockLength <= WriteSizeLeft) - { - if (BlockLength > 0) // We set it to 0 also for invalid filters. - { - uint BlockEnd = (BlockStart + BlockLength) & MaxWinMask; - - //x FilterSrcMemory.Alloc(BlockLength); - FilterSrcMemory = EnsureCapacity( - FilterSrcMemory, - checked((int)BlockLength) - ); - byte[] Mem = FilterSrcMemory; - if (BlockStart < BlockEnd || BlockEnd == 0) - { - if (Fragmented) - { - FragWindow.CopyData(Mem, 0, BlockStart, BlockLength); - } - else - //x memcpy(Mem,Window+BlockStart,BlockLength); - { - Utility.Copy(Window, BlockStart, Mem, 0, BlockLength); - } - } - else - { - size_t FirstPartLength = (size_t)(MaxWinSize - BlockStart); - if (Fragmented) - { - FragWindow.CopyData(Mem, 0, BlockStart, FirstPartLength); - FragWindow.CopyData(Mem, FirstPartLength, 0, BlockEnd); - } - else - { - //x memcpy(Mem,Window+BlockStart,FirstPartLength); - Utility.Copy(Window, BlockStart, Mem, 0, FirstPartLength); - //x memcpy(Mem+FirstPartLength,Window,BlockEnd); - Utility.Copy(Window, 0, Mem, FirstPartLength, BlockEnd); - } - } - - byte[] OutMem = ApplyFilter(Mem, BlockLength, flt); - - Filters[I].Type = FILTER_NONE; - - if (OutMem != null) - { - UnpIO_UnpWrite(OutMem, 0, BlockLength); - } - - UnpSomeRead = true; - WrittenFileSize += BlockLength; - WrittenBorder = BlockEnd; - WriteSizeLeft = (UnpPtr - WrittenBorder) & MaxWinMask; - } - } - else - { - // Current filter intersects the window write border, so we adjust - // the window border to process this filter next time, not now. - WrPtr = WrittenBorder; - - // Since Filter start position can only increase, we quit processing - // all following filters for this data block and reset 'NextWindow' - // flag for them. - //for (size_t J=I;J int - for (int J = I; J < Filters.Count; J++) - { - UnpackFilter _flt = Filters[J]; - if (_flt.Type != FILTER_NONE) - { - _flt.NextWindow = false; - } - } - - // Do not write data left after current filter now. - NotAllFiltersProcessed = true; - break; - } - } - } - - // Remove processed filters from queue. - // sharpcompress: size_t -> int - int EmptyCount = 0; - // sharpcompress: size_t -> int - for (int I = 0; I < Filters.Count; I++) - { - if (EmptyCount > 0) - { - Filters[I - EmptyCount] = Filters[I]; - } - - if (Filters[I].Type == FILTER_NONE) - { - EmptyCount++; - } - } - if (EmptyCount > 0) - //Filters.Alloc(Filters.Count-EmptyCount); - { - Filters.RemoveRange(Filters.Count - EmptyCount, EmptyCount); - } - - if (!NotAllFiltersProcessed) // Only if all filters are processed. - { - // Write data left after last filter. - UnpWriteArea(WrittenBorder, UnpPtr); - WrPtr = UnpPtr; - } - - // We prefer to write data in blocks not exceeding UNPACK_MAX_WRITE - // instead of potentially huge MaxWinSize blocks. It also allows us - // to keep the size of Filters array reasonable. - WriteBorder = (UnpPtr + Math.Min(MaxWinSize, UNPACK_MAX_WRITE)) & MaxWinMask; - - // Choose the nearest among WriteBorder and WrPtr actual written border. - // If border is equal to UnpPtr, it means that we have MaxWinSize data ahead. - if ( - WriteBorder == UnpPtr - || WrPtr != UnpPtr - && ((WrPtr - UnpPtr) & MaxWinMask) < ((WriteBorder - UnpPtr) & MaxWinMask) - ) - { - WriteBorder = WrPtr; - } - } - - private byte[] ApplyFilter(byte[] __d, uint DataSize, UnpackFilter Flt) - { - int Data = 0; - byte[] SrcData = __d; - switch (Flt.Type) - { - case FILTER_E8: - case FILTER_E8E9: - - { - uint FileOffset = (uint)WrittenFileSize; - - const uint FileSize = 0x1000000; - byte CmpByte2 = Flt.Type == FILTER_E8E9 ? (byte)0xe9 : (byte)0xe8; - // DataSize is unsigned, so we use "CurPos+4" and not "DataSize-4" - // to avoid overflow for DataSize<4. - for (uint CurPos = 0; CurPos + 4 < DataSize; ) - { - //x byte CurByte=*(Data++); - byte CurByte = __d[Data++]; - CurPos++; - if (CurByte == 0xe8 || CurByte == CmpByte2) - { - uint Offset = (CurPos + FileOffset) % FileSize; - uint Addr = RawGet4(__d, Data); - - // We check 0x80000000 bit instead of '< 0' comparison - // not assuming int32 presence or uint size and endianness. - if ((Addr & 0x80000000) != 0) // Addr<0 - { - if (((Addr + Offset) & 0x80000000) == 0) // Addr+Offset>=0 - { - RawPut4(Addr + FileSize, __d, Data); - } - } - else if (((Addr - FileSize) & 0x80000000) != 0) // Addr> 8); - __d[D + 2] = (byte)(Offset >> 16); - } - } - } - return SrcData; - case FILTER_DELTA: - { - // Unlike RAR3, we do not need to reject excessive channel - // values here, since RAR5 uses only 5 bits to store channel. - uint Channels = Flt.Channels, - SrcPos = 0; - - //x FilterDstMemory.Alloc(DataSize); - FilterDstMemory = EnsureCapacity(FilterDstMemory, checked((int)DataSize)); - - byte[] DstData = FilterDstMemory; - - // Bytes from same channels are grouped to continual data blocks, - // so we need to place them back to their interleaving positions. - for (uint CurChannel = 0; CurChannel < Channels; CurChannel++) - { - byte PrevByte = 0; - for (uint DestPos = CurChannel; DestPos < DataSize; DestPos += Channels) - { - DstData[DestPos] = (PrevByte -= __d[Data + SrcPos++]); - } - } - return DstData; - } - } - return null; - } - - private void UnpWriteArea(size_t StartPtr, size_t EndPtr) - { - if (EndPtr != StartPtr) - { - UnpSomeRead = true; - } - - if (EndPtr < StartPtr) - { - UnpAllBuf = true; - } - - if (Fragmented) - { - size_t SizeToWrite = (EndPtr - StartPtr) & MaxWinMask; - while (SizeToWrite > 0) - { - size_t BlockSize = FragWindow.GetBlockSize(StartPtr, SizeToWrite); - //UnpWriteData(&FragWindow[StartPtr],BlockSize); - FragWindow.GetBuffer(StartPtr, out var __buffer, out var __offset); - UnpWriteData(__buffer, __offset, BlockSize); - SizeToWrite -= BlockSize; - StartPtr = (StartPtr + BlockSize) & MaxWinMask; - } - } - else if (EndPtr < StartPtr) - { - UnpWriteData(Window, StartPtr, MaxWinSize - StartPtr); - UnpWriteData(Window, 0, EndPtr); - } - else - { - UnpWriteData(Window, StartPtr, EndPtr - StartPtr); - } - } - - private void UnpWriteData(byte[] Data, size_t offset, size_t Size) - { - if (WrittenFileSize >= DestUnpSize) + UnpInitData(Solid); + if (!UnpReadBuf()) { return; } - size_t WriteSize = Size; - int64 LeftToWrite = DestUnpSize - WrittenFileSize; - if ((int64)WriteSize > LeftToWrite) + // Check TablesRead5 to be sure that we read tables at least once + // regardless of current block header TablePresent flag. + // So we can safefly use these tables below. + if ( + !ReadBlockHeader(Inp, ref BlockHeader) + || !ReadTables(Inp, ref BlockHeader, ref BlockTables) + || !TablesRead5 + ) { - WriteSize = (size_t)LeftToWrite; - } - - UnpIO_UnpWrite(Data, offset, WriteSize); - WrittenFileSize += Size; - } - - private void UnpInitData50(bool Solid) - { - if (!Solid) - { - TablesRead5 = false; + return; } } - private bool ReadBlockHeader(BitInput Inp, ref UnpackBlockHeader Header) + while (true) { - Header.HeaderSize = 0; + UnpPtr &= MaxWinMask; - if (!Inp.ExternalBuffer && Inp.InAddr > ReadTop - 7) + if (Inp.InAddr >= ReadBorder) { - if (!UnpReadBuf()) + var FileDone = false; + + // We use 'while', because for empty block containing only Huffman table, + // we'll be on the block border once again just after reading the table. + while ( + Inp.InAddr > BlockHeader.BlockStart + BlockHeader.BlockSize - 1 + || Inp.InAddr == BlockHeader.BlockStart + BlockHeader.BlockSize - 1 + && Inp.InBit >= BlockHeader.BlockBitSize + ) { - return false; + if (BlockHeader.LastBlockInFile) + { + FileDone = true; + break; + } + if ( + !ReadBlockHeader(Inp, ref BlockHeader) + || !ReadTables(Inp, ref BlockHeader, ref BlockTables) + ) + { + return; + } + } + if (FileDone || !UnpReadBuf()) + { + break; } } - Inp.faddbits((uint)((8 - Inp.InBit) & 7)); + if ( + ((WriteBorder - UnpPtr) & MaxWinMask) < MAX_LZ_MATCH + 3 + && WriteBorder != UnpPtr + ) + { + UnpWriteBuf(); + if (WrittenFileSize > DestUnpSize) + { + return; + } - byte BlockFlags = (byte)(Inp.fgetbits() >> 8); - Inp.faddbits(8); - uint ByteCount = (uint)(((BlockFlags >> 3) & 3) + 1); // Block size byte count. + if (Suspended) + { + FileExtracted = false; + return; + } + } - if (ByteCount == 4) + var MainSlot = DecodeNumber(Inp, BlockTables.LD); + if (MainSlot < 256) + { + if (Fragmented) + { + FragWindow[UnpPtr++] = (byte)MainSlot; + } + else + { + Window[UnpPtr++] = (byte)MainSlot; + } + + continue; + } + if (MainSlot >= 262) + { + var Length = SlotToLength(Inp, MainSlot - 262); + + uint DBits, + Distance = 1, + DistSlot = DecodeNumber(Inp, BlockTables.DD); + if (DistSlot < 4) + { + DBits = 0; + Distance += DistSlot; + } + else + { + DBits = (DistSlot / 2) - 1; + Distance += (2 | (DistSlot & 1)) << (int)DBits; + } + + if (DBits > 0) + { + if (DBits >= 4) + { + if (DBits > 4) + { + Distance += ((Inp.getbits32() >> (int)(36 - DBits)) << 4); + Inp.addbits(DBits - 4); + } + var LowDist = DecodeNumber(Inp, BlockTables.LDD); + Distance += LowDist; + } + else + { + Distance += Inp.getbits32() >> (int)(32 - DBits); + Inp.addbits(DBits); + } + } + + if (Distance > 0x100) + { + Length++; + if (Distance > 0x2000) + { + Length++; + if (Distance > 0x40000) + { + Length++; + } + } + } + + InsertOldDist(Distance); + LastLength = Length; + if (Fragmented) + { + FragWindow.CopyString(Length, Distance, ref UnpPtr, MaxWinMask); + } + else + { + CopyString(Length, Distance); + } + + continue; + } + if (MainSlot == 256) + { + var Filter = new UnpackFilter(); + if (!ReadFilter(Inp, Filter) || !AddFilter(Filter)) + { + break; + } + + continue; + } + if (MainSlot == 257) + { + if (LastLength != 0) + { + if (Fragmented) + { + FragWindow.CopyString(LastLength, OldDist[0], ref UnpPtr, MaxWinMask); + } + else + { + CopyString(LastLength, OldDist[0]); + } + } + + continue; + } + if (MainSlot < 262) + { + var DistNum = MainSlot - 258; + var Distance = OldDist[DistNum]; + for (var I = DistNum; I > 0; I--) + { + OldDist[I] = OldDist[I - 1]; + } + + OldDist[0] = Distance; + + var LengthSlot = DecodeNumber(Inp, BlockTables.RD); + var Length = SlotToLength(Inp, LengthSlot); + LastLength = Length; + if (Fragmented) + { + FragWindow.CopyString(Length, Distance, ref UnpPtr, MaxWinMask); + } + else + { + CopyString(Length, Distance); + } + + continue; + } + } + UnpWriteBuf(); + } + + private uint ReadFilterData(BitInput Inp) + { + var ByteCount = (Inp.fgetbits() >> 14) + 1; + Inp.addbits(2); + + uint Data = 0; + for (uint I = 0; I < ByteCount; I++) + { + Data += (Inp.fgetbits() >> 8) << (int)(I * 8); + Inp.addbits(8); + } + return Data; + } + + private bool ReadFilter(BitInput Inp, UnpackFilter Filter) + { + if (!Inp.ExternalBuffer && Inp.InAddr > ReadTop - 16) + { + if (!UnpReadBuf()) { return false; } - - Header.HeaderSize = (int)(2 + ByteCount); - - Header.BlockBitSize = (BlockFlags & 7) + 1; - - byte SavedCheckSum = (byte)(Inp.fgetbits() >> 8); - Inp.faddbits(8); - - int BlockSize = 0; - for (uint I = 0; I < ByteCount; I++) - { - BlockSize += (int)((Inp.fgetbits() >> 8) << (int)(I * 8)); - Inp.addbits(8); - } - - Header.BlockSize = BlockSize; - byte CheckSum = (byte)( - 0x5a ^ BlockFlags ^ BlockSize ^ (BlockSize >> 8) ^ (BlockSize >> 16) - ); - if (CheckSum != SavedCheckSum) - { - return false; - } - - Header.BlockStart = Inp.InAddr; - ReadBorder = Math.Min(ReadBorder, Header.BlockStart + Header.BlockSize - 1); - - Header.LastBlockInFile = (BlockFlags & 0x40) != 0; - Header.TablePresent = (BlockFlags & 0x80) != 0; - return true; } - private bool ReadTables( - BitInput Inp, - ref UnpackBlockHeader Header, - ref UnpackBlockTables Tables + Filter.BlockStart = ReadFilterData(Inp); + Filter.BlockLength = ReadFilterData(Inp); + if (Filter.BlockLength > MAX_FILTER_BLOCK_SIZE) + { + Filter.BlockLength = 0; + } + + Filter.Type = (byte)(Inp.fgetbits() >> 13); + Inp.faddbits(3); + + if (Filter.Type == FILTER_DELTA) + { + Filter.Channels = (byte)((Inp.fgetbits() >> 11) + 1); + Inp.faddbits(5); + } + + return true; + } + + private bool AddFilter(UnpackFilter Filter) + { + if (Filters.Count >= MAX_UNPACK_FILTERS) + { + UnpWriteBuf(); // Write data, apply and flush filters. + if (Filters.Count >= MAX_UNPACK_FILTERS) + { + InitFilters(); // Still too many filters, prevent excessive memory use. + } + } + + // If distance to filter start is that large that due to circular dictionary + // mode now it points to old not written yet data, then we set 'NextWindow' + // flag and process this filter only after processing that older data. + Filter.NextWindow = + WrPtr != UnpPtr && ((WrPtr - UnpPtr) & MaxWinMask) <= Filter.BlockStart; + + Filter.BlockStart = (Filter.BlockStart + UnpPtr) & MaxWinMask; + Filters.Add(Filter); + return true; + } + + private bool UnpReadBuf() + { + var DataSize = ReadTop - Inp.InAddr; // Data left to process. + if (DataSize < 0) + { + return false; + } + + BlockHeader.BlockSize -= Inp.InAddr - BlockHeader.BlockStart; + if (Inp.InAddr > MAX_SIZE / 2) + { + // If we already processed more than half of buffer, let's move + // remaining data into beginning to free more space for new data + // and ensure that calling function does not cross the buffer border + // even if we did not read anything here. Also it ensures that read size + // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk + // to make it zero. + if (DataSize > 0) + //x memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); + { + Buffer.BlockCopy(Inp.InBuf, Inp.InAddr, Inp.InBuf, 0, DataSize); + } + + Inp.InAddr = 0; + ReadTop = DataSize; + } + else + { + DataSize = ReadTop; + } + + var ReadCode = 0; + if (MAX_SIZE != DataSize) + { + ReadCode = UnpIO_UnpRead(Inp.InBuf, DataSize, MAX_SIZE - DataSize); + } + + if (ReadCode > 0) // Can be also -1. + { + ReadTop += ReadCode; + } + + ReadBorder = ReadTop - 30; + BlockHeader.BlockStart = Inp.InAddr; + if (BlockHeader.BlockSize != -1) // '-1' means not defined yet. + { + // We may need to quit from main extraction loop and read new block header + // and trees earlier than data in input buffer ends. + ReadBorder = Math.Min( + ReadBorder, + BlockHeader.BlockStart + BlockHeader.BlockSize - 1 + ); + } + return ReadCode != -1; + } + + private void UnpWriteBuf() + { + var WrittenBorder = WrPtr; + var FullWriteSize = (UnpPtr - WrittenBorder) & MaxWinMask; + var WriteSizeLeft = FullWriteSize; + var NotAllFiltersProcessed = false; + //for (size_t I=0;I int + for (var I = 0; I < Filters.Count; I++) + { + // Here we apply filters to data which we need to write. + // We always copy data to another memory block before processing. + // We cannot process them just in place in Window buffer, because + // these data can be used for future string matches, so we must + // preserve them in original form. + + var flt = Filters[I]; + if (flt.Type == FILTER_NONE) + { + continue; + } + + if (flt.NextWindow) + { + // Here we skip filters which have block start in current data range + // due to address wrap around in circular dictionary, but actually + // belong to next dictionary block. If such filter start position + // is included to current write range, then we reset 'NextWindow' flag. + // In fact we can reset it even without such check, because current + // implementation seems to guarantee 'NextWindow' flag reset after + // buffer writing for all existing filters. But let's keep this check + // just in case. Compressor guarantees that distance between + // filter block start and filter storing position cannot exceed + // the dictionary size. So if we covered the filter block start with + // our write here, we can safely assume that filter is applicable + // to next block on no further wrap arounds is possible. + if (((flt.BlockStart - WrPtr) & MaxWinMask) <= FullWriteSize) + { + flt.NextWindow = false; + } + + continue; + } + var BlockStart = flt.BlockStart; + var BlockLength = flt.BlockLength; + if (((BlockStart - WrittenBorder) & MaxWinMask) < WriteSizeLeft) + { + if (WrittenBorder != BlockStart) + { + UnpWriteArea(WrittenBorder, BlockStart); + WrittenBorder = BlockStart; + WriteSizeLeft = (UnpPtr - WrittenBorder) & MaxWinMask; + } + if (BlockLength <= WriteSizeLeft) + { + if (BlockLength > 0) // We set it to 0 also for invalid filters. + { + var BlockEnd = (BlockStart + BlockLength) & MaxWinMask; + + //x FilterSrcMemory.Alloc(BlockLength); + FilterSrcMemory = EnsureCapacity( + FilterSrcMemory, + checked((int)BlockLength) + ); + var Mem = FilterSrcMemory; + if (BlockStart < BlockEnd || BlockEnd == 0) + { + if (Fragmented) + { + FragWindow.CopyData(Mem, 0, BlockStart, BlockLength); + } + else + //x memcpy(Mem,Window+BlockStart,BlockLength); + { + Utility.Copy(Window, BlockStart, Mem, 0, BlockLength); + } + } + else + { + var FirstPartLength = MaxWinSize - BlockStart; + if (Fragmented) + { + FragWindow.CopyData(Mem, 0, BlockStart, FirstPartLength); + FragWindow.CopyData(Mem, FirstPartLength, 0, BlockEnd); + } + else + { + //x memcpy(Mem,Window+BlockStart,FirstPartLength); + Utility.Copy(Window, BlockStart, Mem, 0, FirstPartLength); + //x memcpy(Mem+FirstPartLength,Window,BlockEnd); + Utility.Copy(Window, 0, Mem, FirstPartLength, BlockEnd); + } + } + + var OutMem = ApplyFilter(Mem, BlockLength, flt); + + Filters[I].Type = FILTER_NONE; + + if (OutMem != null) + { + UnpIO_UnpWrite(OutMem, 0, BlockLength); + } + + UnpSomeRead = true; + WrittenFileSize += BlockLength; + WrittenBorder = BlockEnd; + WriteSizeLeft = (UnpPtr - WrittenBorder) & MaxWinMask; + } + } + else + { + // Current filter intersects the window write border, so we adjust + // the window border to process this filter next time, not now. + WrPtr = WrittenBorder; + + // Since Filter start position can only increase, we quit processing + // all following filters for this data block and reset 'NextWindow' + // flag for them. + //for (size_t J=I;J int + for (var J = I; J < Filters.Count; J++) + { + var _flt = Filters[J]; + if (_flt.Type != FILTER_NONE) + { + _flt.NextWindow = false; + } + } + + // Do not write data left after current filter now. + NotAllFiltersProcessed = true; + break; + } + } + } + + // Remove processed filters from queue. + // sharpcompress: size_t -> int + var EmptyCount = 0; + // sharpcompress: size_t -> int + for (var I = 0; I < Filters.Count; I++) + { + if (EmptyCount > 0) + { + Filters[I - EmptyCount] = Filters[I]; + } + + if (Filters[I].Type == FILTER_NONE) + { + EmptyCount++; + } + } + if (EmptyCount > 0) + //Filters.Alloc(Filters.Count-EmptyCount); + { + Filters.RemoveRange(Filters.Count - EmptyCount, EmptyCount); + } + + if (!NotAllFiltersProcessed) // Only if all filters are processed. + { + // Write data left after last filter. + UnpWriteArea(WrittenBorder, UnpPtr); + WrPtr = UnpPtr; + } + + // We prefer to write data in blocks not exceeding UNPACK_MAX_WRITE + // instead of potentially huge MaxWinSize blocks. It also allows us + // to keep the size of Filters array reasonable. + WriteBorder = (UnpPtr + Math.Min(MaxWinSize, UNPACK_MAX_WRITE)) & MaxWinMask; + + // Choose the nearest among WriteBorder and WrPtr actual written border. + // If border is equal to UnpPtr, it means that we have MaxWinSize data ahead. + if ( + WriteBorder == UnpPtr + || WrPtr != UnpPtr + && ((WrPtr - UnpPtr) & MaxWinMask) < ((WriteBorder - UnpPtr) & MaxWinMask) ) { - if (!Header.TablePresent) - { - return true; - } + WriteBorder = WrPtr; + } + } - if (!Inp.ExternalBuffer && Inp.InAddr > ReadTop - 25) + private byte[] ApplyFilter(byte[] __d, uint DataSize, UnpackFilter Flt) + { + var Data = 0; + var SrcData = __d; + switch (Flt.Type) + { + case FILTER_E8: + case FILTER_E8E9: + + { + var FileOffset = (uint)WrittenFileSize; + + const uint FileSize = 0x1000000; + var CmpByte2 = Flt.Type == FILTER_E8E9 ? (byte)0xe9 : (byte)0xe8; + // DataSize is unsigned, so we use "CurPos+4" and not "DataSize-4" + // to avoid overflow for DataSize<4. + for (uint CurPos = 0; CurPos + 4 < DataSize;) + { + //x byte CurByte=*(Data++); + var CurByte = __d[Data++]; + CurPos++; + if (CurByte == 0xe8 || CurByte == CmpByte2) + { + var Offset = (CurPos + FileOffset) % FileSize; + var Addr = RawGet4(__d, Data); + + // We check 0x80000000 bit instead of '< 0' comparison + // not assuming int32 presence or uint size and endianness. + if ((Addr & 0x80000000) != 0) // Addr<0 + { + if (((Addr + Offset) & 0x80000000) == 0) // Addr+Offset>=0 + { + RawPut4(Addr + FileSize, __d, Data); + } + } + else if (((Addr - FileSize) & 0x80000000) != 0) // Addr> 8); + __d[D + 2] = (byte)(Offset >> 16); + } + } + } + return SrcData; + case FILTER_DELTA: + { + // Unlike RAR3, we do not need to reject excessive channel + // values here, since RAR5 uses only 5 bits to store channel. + uint Channels = Flt.Channels, + SrcPos = 0; + + //x FilterDstMemory.Alloc(DataSize); + FilterDstMemory = EnsureCapacity(FilterDstMemory, checked((int)DataSize)); + + var DstData = FilterDstMemory; + + // Bytes from same channels are grouped to continual data blocks, + // so we need to place them back to their interleaving positions. + for (uint CurChannel = 0; CurChannel < Channels; CurChannel++) + { + byte PrevByte = 0; + for (var DestPos = CurChannel; DestPos < DataSize; DestPos += Channels) + { + DstData[DestPos] = (PrevByte -= __d[Data + SrcPos++]); + } + } + return DstData; + } + } + return null; + } + + private void UnpWriteArea(size_t StartPtr, size_t EndPtr) + { + if (EndPtr != StartPtr) + { + UnpSomeRead = true; + } + + if (EndPtr < StartPtr) + { + UnpAllBuf = true; + } + + if (Fragmented) + { + var SizeToWrite = (EndPtr - StartPtr) & MaxWinMask; + while (SizeToWrite > 0) + { + var BlockSize = FragWindow.GetBlockSize(StartPtr, SizeToWrite); + //UnpWriteData(&FragWindow[StartPtr],BlockSize); + FragWindow.GetBuffer(StartPtr, out var __buffer, out var __offset); + UnpWriteData(__buffer, __offset, BlockSize); + SizeToWrite -= BlockSize; + StartPtr = (StartPtr + BlockSize) & MaxWinMask; + } + } + else if (EndPtr < StartPtr) + { + UnpWriteData(Window, StartPtr, MaxWinSize - StartPtr); + UnpWriteData(Window, 0, EndPtr); + } + else + { + UnpWriteData(Window, StartPtr, EndPtr - StartPtr); + } + } + + private void UnpWriteData(byte[] Data, size_t offset, size_t Size) + { + if (WrittenFileSize >= DestUnpSize) + { + return; + } + + var WriteSize = Size; + var LeftToWrite = DestUnpSize - WrittenFileSize; + if (WriteSize > LeftToWrite) + { + WriteSize = (size_t)LeftToWrite; + } + + UnpIO_UnpWrite(Data, offset, WriteSize); + WrittenFileSize += Size; + } + + private void UnpInitData50(bool Solid) + { + if (!Solid) + { + TablesRead5 = false; + } + } + + private bool ReadBlockHeader(BitInput Inp, ref UnpackBlockHeader Header) + { + Header.HeaderSize = 0; + + if (!Inp.ExternalBuffer && Inp.InAddr > ReadTop - 7) + { + if (!UnpReadBuf()) + { + return false; + } + } + + Inp.faddbits((uint)((8 - Inp.InBit) & 7)); + + var BlockFlags = (byte)(Inp.fgetbits() >> 8); + Inp.faddbits(8); + var ByteCount = (uint)(((BlockFlags >> 3) & 3) + 1); // Block size byte count. + + if (ByteCount == 4) + { + return false; + } + + Header.HeaderSize = (int)(2 + ByteCount); + + Header.BlockBitSize = (BlockFlags & 7) + 1; + + var SavedCheckSum = (byte)(Inp.fgetbits() >> 8); + Inp.faddbits(8); + + var BlockSize = 0; + for (uint I = 0; I < ByteCount; I++) + { + BlockSize += (int)((Inp.fgetbits() >> 8) << (int)(I * 8)); + Inp.addbits(8); + } + + Header.BlockSize = BlockSize; + var CheckSum = (byte)( + 0x5a ^ BlockFlags ^ BlockSize ^ (BlockSize >> 8) ^ (BlockSize >> 16) + ); + if (CheckSum != SavedCheckSum) + { + return false; + } + + Header.BlockStart = Inp.InAddr; + ReadBorder = Math.Min(ReadBorder, Header.BlockStart + Header.BlockSize - 1); + + Header.LastBlockInFile = (BlockFlags & 0x40) != 0; + Header.TablePresent = (BlockFlags & 0x80) != 0; + return true; + } + + private bool ReadTables( + BitInput Inp, + ref UnpackBlockHeader Header, + ref UnpackBlockTables Tables + ) + { + if (!Header.TablePresent) + { + return true; + } + + if (!Inp.ExternalBuffer && Inp.InAddr > ReadTop - 25) + { + if (!UnpReadBuf()) + { + return false; + } + } + + var BitLength = new byte[BC]; + for (uint I = 0; I < BC; I++) + { + uint Length = (byte)(Inp.fgetbits() >> 12); + Inp.faddbits(4); + if (Length == 15) + { + uint ZeroCount = (byte)(Inp.fgetbits() >> 12); + Inp.faddbits(4); + if (ZeroCount == 0) + { + BitLength[I] = 15; + } + else + { + ZeroCount += 2; + while (ZeroCount-- > 0 && I < BitLength.Length) + { + BitLength[I++] = 0; + } + + I--; + } + } + else + { + BitLength[I] = (byte)Length; + } + } + + MakeDecodeTables(BitLength, 0, Tables.BD, BC); + + var Table = new byte[HUFF_TABLE_SIZE]; + const uint TableSize = HUFF_TABLE_SIZE; + for (uint I = 0; I < TableSize;) + { + if (!Inp.ExternalBuffer && Inp.InAddr > ReadTop - 5) { if (!UnpReadBuf()) { @@ -764,123 +806,78 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 } } - byte[] BitLength = new byte[BC]; - for (uint I = 0; I < BC; I++) + var Number = DecodeNumber(Inp, Tables.BD); + if (Number < 16) { - uint Length = (byte)(Inp.fgetbits() >> 12); - Inp.faddbits(4); - if (Length == 15) - { - uint ZeroCount = (byte)(Inp.fgetbits() >> 12); - Inp.faddbits(4); - if (ZeroCount == 0) - { - BitLength[I] = 15; - } - else - { - ZeroCount += 2; - while (ZeroCount-- > 0 && I < BitLength.Length) - { - BitLength[I++] = 0; - } - - I--; - } - } - else - { - BitLength[I] = (byte)Length; - } + Table[I] = (byte)Number; + I++; } - - MakeDecodeTables(BitLength, 0, Tables.BD, BC); - - byte[] Table = new byte[HUFF_TABLE_SIZE]; - const uint TableSize = HUFF_TABLE_SIZE; - for (uint I = 0; I < TableSize; ) + else if (Number < 18) { - if (!Inp.ExternalBuffer && Inp.InAddr > ReadTop - 5) + uint N; + if (Number == 16) { - if (!UnpReadBuf()) - { - return false; - } - } - - uint Number = DecodeNumber(Inp, Tables.BD); - if (Number < 16) - { - Table[I] = (byte)Number; - I++; - } - else if (Number < 18) - { - uint N; - if (Number == 16) - { - N = (Inp.fgetbits() >> 13) + 3; - Inp.faddbits(3); - } - else - { - N = (Inp.fgetbits() >> 9) + 11; - Inp.faddbits(7); - } - if (I == 0) - { - // We cannot have "repeat previous" code at the first position. - // Multiple such codes would shift Inp position without changing I, - // which can lead to reading beyond of Inp boundary in mutithreading - // mode, where Inp.ExternalBuffer disables bounds check and we just - // reserve a lot of buffer space to not need such check normally. - return false; - } - else - { - while (N-- > 0 && I < TableSize) - { - Table[I] = Table[I - 1]; - I++; - } - } + N = (Inp.fgetbits() >> 13) + 3; + Inp.faddbits(3); + } + else + { + N = (Inp.fgetbits() >> 9) + 11; + Inp.faddbits(7); + } + if (I == 0) + { + // We cannot have "repeat previous" code at the first position. + // Multiple such codes would shift Inp position without changing I, + // which can lead to reading beyond of Inp boundary in mutithreading + // mode, where Inp.ExternalBuffer disables bounds check and we just + // reserve a lot of buffer space to not need such check normally. + return false; } else { - uint N; - if (Number == 18) - { - N = (Inp.fgetbits() >> 13) + 3; - Inp.faddbits(3); - } - else - { - N = (Inp.fgetbits() >> 9) + 11; - Inp.faddbits(7); - } while (N-- > 0 && I < TableSize) { - Table[I++] = 0; + Table[I] = Table[I - 1]; + I++; } } } - TablesRead5 = true; - if (!Inp.ExternalBuffer && Inp.InAddr > ReadTop) + else { - return false; + uint N; + if (Number == 18) + { + N = (Inp.fgetbits() >> 13) + 3; + Inp.faddbits(3); + } + else + { + N = (Inp.fgetbits() >> 9) + 11; + Inp.faddbits(7); + } + while (N-- > 0 && I < TableSize) + { + Table[I++] = 0; + } } - - MakeDecodeTables(Table, 0, Tables.LD, NC); - MakeDecodeTables(Table, (int)NC, Tables.DD, DC); - MakeDecodeTables(Table, (int)(NC + DC), Tables.LDD, LDC); - MakeDecodeTables(Table, (int)(NC + DC + LDC), Tables.RD, RC); - return true; } - - private void InitFilters() + TablesRead5 = true; + if (!Inp.ExternalBuffer && Inp.InAddr > ReadTop) { - //Filters.SoftReset(); - Filters.Clear(); + return false; } + + MakeDecodeTables(Table, 0, Tables.LD, NC); + MakeDecodeTables(Table, (int)NC, Tables.DD, DC); + MakeDecodeTables(Table, (int)(NC + DC), Tables.LDD, LDC); + MakeDecodeTables(Table, (int)(NC + DC + LDC), Tables.RD, RC); + return true; + } + + private void InitFilters() + { + //Filters.SoftReset(); + Filters.Clear(); } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs index 17eecba5..69b35200 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs @@ -1,8 +1,6 @@ -#nullable disable +#nullable disable #if !Rar2017_64bit -using nint = System.Int32; -using nuint = System.UInt32; using size_t = System.UInt32; #else using nint = System.Int64; @@ -15,401 +13,400 @@ using SharpCompress.Common; using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; -namespace SharpCompress.Compressors.Rar.UnpackV2017 +namespace SharpCompress.Compressors.Rar.UnpackV2017; + +internal sealed partial class Unpack : BitInput { - internal sealed partial class Unpack : BitInput + public Unpack( /* ComprDataIO *DataIO */ + ) + //:Inp(true),VMCodeInp(true) + : base(true) { - public Unpack( /* ComprDataIO *DataIO */ - ) - //:Inp(true),VMCodeInp(true) - : base(true) - { - _UnpackCtor(); + _UnpackCtor(); - //UnpIO=DataIO; - Window = null; - Fragmented = false; - Suspended = false; - UnpAllBuf = false; - UnpSomeRead = false; + //UnpIO=DataIO; + Window = null; + Fragmented = false; + Suspended = false; + UnpAllBuf = false; + UnpSomeRead = false; #if RarV2017_RAR_SMP - MaxUserThreads = 1; - UnpThreadPool = CreateThreadPool(); - ReadBufMT = null; - UnpThreadData = null; + MaxUserThreads = 1; + UnpThreadPool = CreateThreadPool(); + ReadBufMT = null; + UnpThreadData = null; #endif - MaxWinSize = 0; - MaxWinMask = 0; + MaxWinSize = 0; + MaxWinMask = 0; - // Perform initialization, which should be done only once for all files. - // It prevents crash if first DoUnpack call is later made with wrong - // (true) 'Solid' value. - UnpInitData(false); + // Perform initialization, which should be done only once for all files. + // It prevents crash if first DoUnpack call is later made with wrong + // (true) 'Solid' value. + UnpInitData(false); #if !RarV2017_SFX_MODULE - // RAR 1.5 decompression initialization - UnpInitData15(false); - InitHuff(); + // RAR 1.5 decompression initialization + UnpInitData15(false); + InitHuff(); #endif + } + + // later: may need Dispose() if we support thread pool + //Unpack::~Unpack() + //{ + // InitFilters30(false); + // + // if (Window!=null) + // free(Window); + //#if RarV2017_RAR_SMP + // DestroyThreadPool(UnpThreadPool); + // delete[] ReadBufMT; + // delete[] UnpThreadData; + //#endif + //} + + private void Init(size_t WinSize, bool Solid) + { + // If 32-bit RAR unpacks an archive with 4 GB dictionary, the window size + // will be 0 because of size_t overflow. Let's issue the memory error. + if (WinSize == 0) + //ErrHandler.MemoryError(); + { + throw new InvalidFormatException( + "invalid window size (possibly due to a rar file with a 4GB being unpacked on a 32-bit platform)" + ); } - // later: may need Dispose() if we support thread pool - //Unpack::~Unpack() - //{ - // InitFilters30(false); - // - // if (Window!=null) - // free(Window); - //#if RarV2017_RAR_SMP - // DestroyThreadPool(UnpThreadPool); - // delete[] ReadBufMT; - // delete[] UnpThreadData; - //#endif - //} - - private void Init(size_t WinSize, bool Solid) + // Minimum window size must be at least twice more than maximum possible + // size of filter block, which is 0x10000 in RAR now. If window size is + // smaller, we can have a block with never cleared flt->NextWindow flag + // in UnpWriteBuf(). Minimum window size 0x20000 would be enough, but let's + // use 0x40000 for extra safety and possible filter area size expansion. + const size_t MinAllocSize = 0x40000; + if (WinSize < MinAllocSize) { - // If 32-bit RAR unpacks an archive with 4 GB dictionary, the window size - // will be 0 because of size_t overflow. Let's issue the memory error. - if (WinSize == 0) - //ErrHandler.MemoryError(); - { - throw new InvalidFormatException( - "invalid window size (possibly due to a rar file with a 4GB being unpacked on a 32-bit platform)" - ); - } - - // Minimum window size must be at least twice more than maximum possible - // size of filter block, which is 0x10000 in RAR now. If window size is - // smaller, we can have a block with never cleared flt->NextWindow flag - // in UnpWriteBuf(). Minimum window size 0x20000 would be enough, but let's - // use 0x40000 for extra safety and possible filter area size expansion. - const size_t MinAllocSize = 0x40000; - if (WinSize < MinAllocSize) - { - WinSize = MinAllocSize; - } - - if (WinSize <= MaxWinSize) // Use the already allocated window. - { - return; - } - - if ((WinSize >> 16) > 0x10000) // Window size must not exceed 4 GB. - { - return; - } - - // Archiving code guarantees that window size does not grow in the same - // solid stream. So if we are here, we are either creating a new window - // or increasing the size of non-solid window. So we could safely reject - // current window data without copying them to a new window, though being - // extra cautious, we still handle the solid window grow case below. - bool Grow = Solid && (Window != null || Fragmented); - - // We do not handle growth for existing fragmented window. - if (Grow && Fragmented) - //throw std::bad_alloc(); - { - throw new InvalidFormatException("Grow && Fragmented"); - } - - byte[] NewWindow = Fragmented ? null : new byte[WinSize]; - - if (NewWindow == null) - { - if (Grow || WinSize < 0x1000000) - { - // We do not support growth for new fragmented window. - // Also exclude RAR4 and small dictionaries. - //throw std::bad_alloc(); - throw new InvalidFormatException("Grow || WinSize<0x1000000"); - } - else - { - if (Window != null) // If allocated by preceding files. - { - //free(Window); - Window = null; - } - FragWindow.Init(WinSize); - Fragmented = true; - } - } - - if (!Fragmented) - { - // Clean the window to generate the same output when unpacking corrupt - // RAR files, which may access unused areas of sliding dictionary. - // sharpcompress: don't need this, freshly allocated above - //memset(NewWindow,0,WinSize); - - - // If Window is not NULL, it means that window size has grown. - // In solid streams we need to copy data to a new window in such case. - // RAR archiving code does not allow it in solid streams now, - // but let's implement it anyway just in case we'll change it sometimes. - if (Grow) - { - for (size_t I = 1; I <= MaxWinSize; I++) - { - NewWindow[(UnpPtr - I) & (WinSize - 1)] = Window[ - (UnpPtr - I) & (MaxWinSize - 1) - ]; - } - } - - //if (Window!=null) - // free(Window); - Window = NewWindow; - } - - MaxWinSize = WinSize; - MaxWinMask = MaxWinSize - 1; + WinSize = MinAllocSize; } - private void DoUnpack(uint Method, bool Solid) + if (WinSize <= MaxWinSize) // Use the already allocated window. { - // Methods <50 will crash in Fragmented mode when accessing NULL Window. - // They cannot be called in such mode now, but we check it below anyway - // just for extra safety. - switch (Method) + return; + } + + if ((WinSize >> 16) > 0x10000) // Window size must not exceed 4 GB. + { + return; + } + + // Archiving code guarantees that window size does not grow in the same + // solid stream. So if we are here, we are either creating a new window + // or increasing the size of non-solid window. So we could safely reject + // current window data without copying them to a new window, though being + // extra cautious, we still handle the solid window grow case below. + var Grow = Solid && (Window != null || Fragmented); + + // We do not handle growth for existing fragmented window. + if (Grow && Fragmented) + //throw std::bad_alloc(); + { + throw new InvalidFormatException("Grow && Fragmented"); + } + + var NewWindow = Fragmented ? null : new byte[WinSize]; + + if (NewWindow == null) + { + if (Grow || WinSize < 0x1000000) { + // We do not support growth for new fragmented window. + // Also exclude RAR4 and small dictionaries. + //throw std::bad_alloc(); + throw new InvalidFormatException("Grow || WinSize<0x1000000"); + } + else + { + if (Window != null) // If allocated by preceding files. + { + //free(Window); + Window = null; + } + FragWindow.Init(WinSize); + Fragmented = true; + } + } + + if (!Fragmented) + { + // Clean the window to generate the same output when unpacking corrupt + // RAR files, which may access unused areas of sliding dictionary. + // sharpcompress: don't need this, freshly allocated above + //memset(NewWindow,0,WinSize); + + + // If Window is not NULL, it means that window size has grown. + // In solid streams we need to copy data to a new window in such case. + // RAR archiving code does not allow it in solid streams now, + // but let's implement it anyway just in case we'll change it sometimes. + if (Grow) + { + for (size_t I = 1; I <= MaxWinSize; I++) + { + NewWindow[(UnpPtr - I) & (WinSize - 1)] = Window[ + (UnpPtr - I) & (MaxWinSize - 1) + ]; + } + } + + //if (Window!=null) + // free(Window); + Window = NewWindow; + } + + MaxWinSize = WinSize; + MaxWinMask = MaxWinSize - 1; + } + + private void DoUnpack(uint Method, bool Solid) + { + // Methods <50 will crash in Fragmented mode when accessing NULL Window. + // They cannot be called in such mode now, but we check it below anyway + // just for extra safety. + switch (Method) + { #if !RarV2017_SFX_MODULE - case 15: // rar 1.5 compression - if (!Fragmented) - { - Unpack15(Solid); - } + case 15: // rar 1.5 compression + if (!Fragmented) + { + Unpack15(Solid); + } - break; - case 20: // rar 2.x compression - case 26: // files larger than 2GB - if (!Fragmented) - { - Unpack20(Solid); - } + break; + case 20: // rar 2.x compression + case 26: // files larger than 2GB + if (!Fragmented) + { + Unpack20(Solid); + } - break; + break; #endif #if !RarV2017_RAR5ONLY - case 29: // rar 3.x compression + case 29: // rar 3.x compression + if (!Fragmented) + { + throw new NotImplementedException(); + } + + break; +#endif + case 50: // RAR 5.0 compression algorithm. +#if RarV2017_RAR_SMP + if (MaxUserThreads > 1) + { + // We do not use the multithreaded unpack routine to repack RAR archives + // in 'suspended' mode, because unlike the single threaded code it can + // write more than one dictionary for same loop pass. So we would need + // larger buffers of unknown size. Also we do not support multithreading + // in fragmented window mode. if (!Fragmented) { - throw new NotImplementedException(); + Unpack5MT(Solid); + break; } - - break; + } #endif - case 50: // RAR 5.0 compression algorithm. -#if RarV2017_RAR_SMP - if (MaxUserThreads > 1) - { - // We do not use the multithreaded unpack routine to repack RAR archives - // in 'suspended' mode, because unlike the single threaded code it can - // write more than one dictionary for same loop pass. So we would need - // larger buffers of unknown size. Also we do not support multithreading - // in fragmented window mode. - if (!Fragmented) - { - Unpack5MT(Solid); - break; - } - } -#endif - Unpack5(Solid); - break; + Unpack5(Solid); + break; #if !Rar2017_NOSTRICT - default: - throw new InvalidFormatException("unknown compression method " + Method); + default: + throw new InvalidFormatException("unknown compression method " + Method); #endif - } } + } - private void UnpInitData(bool Solid) + private void UnpInitData(bool Solid) + { + if (!Solid) { - if (!Solid) - { - new Span(OldDist).Clear(); - OldDistPtr = 0; - LastDist = LastLength = 0; - // memset(Window,0,MaxWinSize); - //memset(&BlockTables,0,sizeof(BlockTables)); - BlockTables = new UnpackBlockTables(); - // sharpcompress: no default ctor for struct - BlockTables.Init(); - UnpPtr = WrPtr = 0; - WriteBorder = Math.Min(MaxWinSize, UNPACK_MAX_WRITE) & MaxWinMask; - } - // Filters never share several solid files, so we can safely reset them - // even in solid archive. - InitFilters(); + new Span(OldDist).Clear(); + OldDistPtr = 0; + LastDist = LastLength = 0; + // memset(Window,0,MaxWinSize); + //memset(&BlockTables,0,sizeof(BlockTables)); + BlockTables = new UnpackBlockTables(); + // sharpcompress: no default ctor for struct + BlockTables.Init(); + UnpPtr = WrPtr = 0; + WriteBorder = Math.Min(MaxWinSize, UNPACK_MAX_WRITE) & MaxWinMask; + } + // Filters never share several solid files, so we can safely reset them + // even in solid archive. + InitFilters(); - Inp.InitBitInput(); - WrittenFileSize = 0; - ReadTop = 0; - ReadBorder = 0; + Inp.InitBitInput(); + WrittenFileSize = 0; + ReadTop = 0; + ReadBorder = 0; - //memset(&BlockHeader,0,sizeof(BlockHeader)); - BlockHeader = new UnpackBlockHeader(); - BlockHeader.BlockSize = -1; // '-1' means not defined yet. + //memset(&BlockHeader,0,sizeof(BlockHeader)); + BlockHeader = new UnpackBlockHeader(); + BlockHeader.BlockSize = -1; // '-1' means not defined yet. #if !RarV2017_SFX_MODULE - UnpInitData20(Solid); + UnpInitData20(Solid); #endif - //UnpInitData30(Solid); - UnpInitData50(Solid); + //UnpInitData30(Solid); + UnpInitData50(Solid); + } + + // LengthTable contains the length in bits for every element of alphabet. + // Dec is the structure to decode Huffman code/ + // Size is size of length table and DecodeNum field in Dec structure, + private void MakeDecodeTables(byte[] LengthTable, int offset, DecodeTable Dec, uint Size) + { + // Size of alphabet and DecodePos array. + Dec.MaxNum = Size; + + // Calculate how many entries for every bit length in LengthTable we have. + var LengthCount = new uint[16]; + //memset(LengthCount,0,sizeof(LengthCount)); + for (size_t I = 0; I < Size; I++) + { + LengthCount[LengthTable[offset + I] & 0xf]++; } - // LengthTable contains the length in bits for every element of alphabet. - // Dec is the structure to decode Huffman code/ - // Size is size of length table and DecodeNum field in Dec structure, - private void MakeDecodeTables(byte[] LengthTable, int offset, DecodeTable Dec, uint Size) + // We must not calculate the number of zero length codes. + LengthCount[0] = 0; + + // Set the entire DecodeNum to zero. + //memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum)); + new Span(Dec.DecodeNum).Clear(); + + // Initialize not really used entry for zero length code. + Dec.DecodePos[0] = 0; + + // Start code for bit length 1 is 0. + Dec.DecodeLen[0] = 0; + + // Right aligned upper limit code for current bit length. + uint UpperLimit = 0; + + for (var I = 1; I < 16; I++) { - // Size of alphabet and DecodePos array. - Dec.MaxNum = Size; + // Adjust the upper limit code. + UpperLimit += LengthCount[I]; - // Calculate how many entries for every bit length in LengthTable we have. - uint[] LengthCount = new uint[16]; - //memset(LengthCount,0,sizeof(LengthCount)); - for (size_t I = 0; I < Size; I++) + // Left aligned upper limit code. + var LeftAligned = UpperLimit << (16 - I); + + // Prepare the upper limit code for next bit length. + UpperLimit *= 2; + + // Store the left aligned upper limit code. + Dec.DecodeLen[I] = LeftAligned; + + // Every item of this array contains the sum of all preceding items. + // So it contains the start position in code list for every bit length. + Dec.DecodePos[I] = Dec.DecodePos[I - 1] + LengthCount[I - 1]; + } + + // Prepare the copy of DecodePos. We'll modify this copy below, + // so we cannot use the original DecodePos. + var CopyDecodePos = new uint[Dec.DecodePos.Length]; + //memcpy(CopyDecodePos,Dec->DecodePos,sizeof(CopyDecodePos)); + Array.Copy(Dec.DecodePos, CopyDecodePos, CopyDecodePos.Length); + + // For every bit length in the bit length table and so for every item + // of alphabet. + for (uint I = 0; I < Size; I++) + { + // Get the current bit length. + var _CurBitLength = (byte)(LengthTable[offset + I] & 0xf); + + if (_CurBitLength != 0) { - LengthCount[LengthTable[offset + I] & 0xf]++; + // Last position in code list for current bit length. + var LastPos = CopyDecodePos[_CurBitLength]; + + // Prepare the decode table, so this position in code list will be + // decoded to current alphabet item number. + Dec.DecodeNum[LastPos] = (ushort)I; + + // We'll use next position number for this bit length next time. + // So we pass through the entire range of positions available + // for every bit length. + CopyDecodePos[_CurBitLength]++; + } + } + + // Define the number of bits to process in quick mode. We use more bits + // for larger alphabets. More bits means that more codes will be processed + // in quick mode, but also that more time will be spent to preparation + // of tables for quick decode. + switch (Size) + { + case NC: + case NC20: + case NC30: + Dec.QuickBits = MAX_QUICK_DECODE_BITS; + break; + default: + Dec.QuickBits = MAX_QUICK_DECODE_BITS - 3; + break; + } + + // Size of tables for quick mode. + var QuickDataSize = 1U << (int)Dec.QuickBits; + + // Bit length for current code, start from 1 bit codes. It is important + // to use 1 bit instead of 0 for minimum code length, so we are moving + // forward even when processing a corrupt archive. + //uint CurBitLength=1; + byte CurBitLength = 1; + + // For every right aligned bit string which supports the quick decoding. + for (uint Code = 0; Code < QuickDataSize; Code++) + { + // Left align the current code, so it will be in usual bit field format. + var BitField = Code << (int)(16 - Dec.QuickBits); + + // Prepare the table for quick decoding of bit lengths. + + // Find the upper limit for current bit field and adjust the bit length + // accordingly if necessary. + while ( + CurBitLength < Dec.DecodeLen.Length && BitField >= Dec.DecodeLen[CurBitLength] + ) + { + CurBitLength++; } - // We must not calculate the number of zero length codes. - LengthCount[0] = 0; + // Translation of right aligned bit string to bit length. + Dec.QuickLen[Code] = CurBitLength; - // Set the entire DecodeNum to zero. - //memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum)); - new Span(Dec.DecodeNum).Clear(); + // Prepare the table for quick translation of position in code list + // to position in alphabet. - // Initialize not really used entry for zero length code. - Dec.DecodePos[0] = 0; + // Calculate the distance from the start code for current bit length. + var Dist = BitField - Dec.DecodeLen[CurBitLength - 1]; - // Start code for bit length 1 is 0. - Dec.DecodeLen[0] = 0; + // Right align the distance. + Dist >>= (16 - CurBitLength); - // Right aligned upper limit code for current bit length. - uint UpperLimit = 0; - - for (int I = 1; I < 16; I++) + // Now we can calculate the position in the code list. It is the sum + // of first position for current bit length and right aligned distance + // between our bit field and start code for current bit length. + uint Pos; + if ( + CurBitLength < Dec.DecodePos.Length + && (Pos = Dec.DecodePos[CurBitLength] + Dist) < Size + ) { - // Adjust the upper limit code. - UpperLimit += LengthCount[I]; - - // Left aligned upper limit code. - uint LeftAligned = UpperLimit << (16 - I); - - // Prepare the upper limit code for next bit length. - UpperLimit *= 2; - - // Store the left aligned upper limit code. - Dec.DecodeLen[I] = (uint)LeftAligned; - - // Every item of this array contains the sum of all preceding items. - // So it contains the start position in code list for every bit length. - Dec.DecodePos[I] = Dec.DecodePos[I - 1] + LengthCount[I - 1]; + // Define the code to alphabet number translation. + Dec.QuickNum[Code] = Dec.DecodeNum[Pos]; } - - // Prepare the copy of DecodePos. We'll modify this copy below, - // so we cannot use the original DecodePos. - uint[] CopyDecodePos = new uint[Dec.DecodePos.Length]; - //memcpy(CopyDecodePos,Dec->DecodePos,sizeof(CopyDecodePos)); - Array.Copy(Dec.DecodePos, CopyDecodePos, CopyDecodePos.Length); - - // For every bit length in the bit length table and so for every item - // of alphabet. - for (uint I = 0; I < Size; I++) + else { - // Get the current bit length. - byte _CurBitLength = (byte)(LengthTable[offset + I] & 0xf); - - if (_CurBitLength != 0) - { - // Last position in code list for current bit length. - uint LastPos = CopyDecodePos[_CurBitLength]; - - // Prepare the decode table, so this position in code list will be - // decoded to current alphabet item number. - Dec.DecodeNum[LastPos] = (ushort)I; - - // We'll use next position number for this bit length next time. - // So we pass through the entire range of positions available - // for every bit length. - CopyDecodePos[_CurBitLength]++; - } - } - - // Define the number of bits to process in quick mode. We use more bits - // for larger alphabets. More bits means that more codes will be processed - // in quick mode, but also that more time will be spent to preparation - // of tables for quick decode. - switch (Size) - { - case NC: - case NC20: - case NC30: - Dec.QuickBits = MAX_QUICK_DECODE_BITS; - break; - default: - Dec.QuickBits = MAX_QUICK_DECODE_BITS - 3; - break; - } - - // Size of tables for quick mode. - uint QuickDataSize = 1U << (int)Dec.QuickBits; - - // Bit length for current code, start from 1 bit codes. It is important - // to use 1 bit instead of 0 for minimum code length, so we are moving - // forward even when processing a corrupt archive. - //uint CurBitLength=1; - byte CurBitLength = 1; - - // For every right aligned bit string which supports the quick decoding. - for (uint Code = 0; Code < QuickDataSize; Code++) - { - // Left align the current code, so it will be in usual bit field format. - uint BitField = Code << (int)(16 - Dec.QuickBits); - - // Prepare the table for quick decoding of bit lengths. - - // Find the upper limit for current bit field and adjust the bit length - // accordingly if necessary. - while ( - CurBitLength < Dec.DecodeLen.Length && BitField >= Dec.DecodeLen[CurBitLength] - ) - { - CurBitLength++; - } - - // Translation of right aligned bit string to bit length. - Dec.QuickLen[Code] = CurBitLength; - - // Prepare the table for quick translation of position in code list - // to position in alphabet. - - // Calculate the distance from the start code for current bit length. - uint Dist = BitField - Dec.DecodeLen[CurBitLength - 1]; - - // Right align the distance. - Dist >>= (16 - CurBitLength); - - // Now we can calculate the position in the code list. It is the sum - // of first position for current bit length and right aligned distance - // between our bit field and start code for current bit length. - uint Pos; - if ( - CurBitLength < Dec.DecodePos.Length - && (Pos = Dec.DecodePos[CurBitLength] + Dist) < Size - ) - { - // Define the code to alphabet number translation. - Dec.QuickNum[Code] = Dec.DecodeNum[Pos]; - } - else - { - // Can be here for length table filled with zeroes only (empty). - Dec.QuickNum[Code] = 0; - } + // Can be here for length table filled with zeroes only (empty). + Dec.QuickNum[Code] = 0; } } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs index 8c585af2..584d0b51 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs @@ -1,179 +1,173 @@ -#if !Rar2017_64bit -using nint = System.Int32; -using nuint = System.UInt32; -using size_t = System.UInt32; +#if !Rar2017_64bit #else using nint = System.Int64; using nuint = System.UInt64; using size_t = System.UInt64; #endif -using int64 = System.Int64; - using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; -namespace SharpCompress.Compressors.Rar.UnpackV2017 +namespace SharpCompress.Compressors.Rar.UnpackV2017; + +internal partial class Unpack { - internal partial class Unpack + private void InsertOldDist(uint Distance) { - private void InsertOldDist(uint Distance) + OldDist[3] = OldDist[2]; + OldDist[2] = OldDist[1]; + OldDist[1] = OldDist[0]; + OldDist[0] = Distance; + } + + //#ifdef _MSC_VER + //#define FAST_MEMCPY + //#endif + + private void CopyString(uint Length, uint Distance) + { + var SrcPtr = UnpPtr - Distance; + + if (SrcPtr < MaxWinSize - MAX_LZ_MATCH && UnpPtr < MaxWinSize - MAX_LZ_MATCH) { - OldDist[3] = OldDist[2]; - OldDist[2] = OldDist[1]; - OldDist[1] = OldDist[0]; - OldDist[0] = Distance; + // If we are not close to end of window, we do not need to waste time + // to "& MaxWinMask" pointer protection. + + // TODO: sharpcompress: non-optimized loop, we may be able to unroll and speed up + var Window = this.Window; + while (Length-- > 0) + { + Window[UnpPtr++] = Window[SrcPtr++]; + } + + // byte *Src=Window+SrcPtr; + // byte *Dest=Window+UnpPtr; + // UnpPtr+=Length; + // + //#if FAST_MEMCPY + // if (Distance=8) + // { + // Dest[0]=Src[0]; + // Dest[1]=Src[1]; + // Dest[2]=Src[2]; + // Dest[3]=Src[3]; + // Dest[4]=Src[4]; + // Dest[5]=Src[5]; + // Dest[6]=Src[6]; + // Dest[7]=Src[7]; + // + // Src+=8; + // Dest+=8; + // Length-=8; + // } + //#if FAST_MEMCPY + // else + // while (Length>=8) + // { + // // In theory we still could overlap here. + // // Supposing Distance == MaxWinSize - 1 we have memcpy(Src, Src + 1, 8). + // // But for real RAR archives Distance <= MaxWinSize - MAX_LZ_MATCH + // // always, so overlap here is impossible. + // + // // This memcpy expanded inline by MSVC. We could also use uint64 + // // assignment, which seems to provide about the same speed. + // memcpy(Dest,Src,8); + // + // Src+=8; + // Dest+=8; + // Length-=8; + // } + //#endif + // + // // Unroll the loop for 0 - 7 bytes left. Note that we use nested "if"s. + // if (Length>0) { Dest[0]=Src[0]; + // if (Length>1) { Dest[1]=Src[1]; + // if (Length>2) { Dest[2]=Src[2]; + // if (Length>3) { Dest[3]=Src[3]; + // if (Length>4) { Dest[4]=Src[4]; + // if (Length>5) { Dest[5]=Src[5]; + // if (Length>6) { Dest[6]=Src[6]; } } } } } } } // Close all nested "if"s. } - - //#ifdef _MSC_VER - //#define FAST_MEMCPY - //#endif - - private void CopyString(uint Length, uint Distance) + else { - size_t SrcPtr = UnpPtr - Distance; - - if (SrcPtr < MaxWinSize - MAX_LZ_MATCH && UnpPtr < MaxWinSize - MAX_LZ_MATCH) + while (Length-- > 0) // Slow copying with all possible precautions. { - // If we are not close to end of window, we do not need to waste time - // to "& MaxWinMask" pointer protection. - - // TODO: sharpcompress: non-optimized loop, we may be able to unroll and speed up - var Window = this.Window; - while (Length-- > 0) - { - Window[UnpPtr++] = Window[SrcPtr++]; - } - - // byte *Src=Window+SrcPtr; - // byte *Dest=Window+UnpPtr; - // UnpPtr+=Length; - // - //#if FAST_MEMCPY - // if (Distance=8) - // { - // Dest[0]=Src[0]; - // Dest[1]=Src[1]; - // Dest[2]=Src[2]; - // Dest[3]=Src[3]; - // Dest[4]=Src[4]; - // Dest[5]=Src[5]; - // Dest[6]=Src[6]; - // Dest[7]=Src[7]; - // - // Src+=8; - // Dest+=8; - // Length-=8; - // } - //#if FAST_MEMCPY - // else - // while (Length>=8) - // { - // // In theory we still could overlap here. - // // Supposing Distance == MaxWinSize - 1 we have memcpy(Src, Src + 1, 8). - // // But for real RAR archives Distance <= MaxWinSize - MAX_LZ_MATCH - // // always, so overlap here is impossible. - // - // // This memcpy expanded inline by MSVC. We could also use uint64 - // // assignment, which seems to provide about the same speed. - // memcpy(Dest,Src,8); - // - // Src+=8; - // Dest+=8; - // Length-=8; - // } - //#endif - // - // // Unroll the loop for 0 - 7 bytes left. Note that we use nested "if"s. - // if (Length>0) { Dest[0]=Src[0]; - // if (Length>1) { Dest[1]=Src[1]; - // if (Length>2) { Dest[2]=Src[2]; - // if (Length>3) { Dest[3]=Src[3]; - // if (Length>4) { Dest[4]=Src[4]; - // if (Length>5) { Dest[5]=Src[5]; - // if (Length>6) { Dest[6]=Src[6]; } } } } } } } // Close all nested "if"s. + Window[UnpPtr] = Window[SrcPtr++ & MaxWinMask]; + // We need to have masked UnpPtr after quit from loop, so it must not + // be replaced with 'Window[UnpPtr++ & MaxWinMask]' + UnpPtr = (UnpPtr + 1) & MaxWinMask; } - else - { - while (Length-- > 0) // Slow copying with all possible precautions. - { - Window[UnpPtr] = Window[SrcPtr++ & MaxWinMask]; - // We need to have masked UnpPtr after quit from loop, so it must not - // be replaced with 'Window[UnpPtr++ & MaxWinMask]' - UnpPtr = (UnpPtr + 1) & MaxWinMask; - } - } - } - - private uint DecodeNumber(BitInput Inp, DecodeTable Dec) - { - // Left aligned 15 bit length raw bit field. - uint BitField = Inp.getbits() & 0xfffe; - - if (BitField < Dec.DecodeLen[Dec.QuickBits]) - { - uint Code = BitField >> (int)(16 - Dec.QuickBits); - Inp.addbits(Dec.QuickLen[Code]); - return Dec.QuickNum[Code]; - } - - // Detect the real bit length for current code. - uint Bits = 15; - for (uint I = Dec.QuickBits + 1; I < 15; I++) - { - if (BitField < Dec.DecodeLen[I]) - { - Bits = I; - break; - } - } - - Inp.addbits(Bits); - - // Calculate the distance from the start code for current bit length. - uint Dist = BitField - Dec.DecodeLen[Bits - 1]; - - // Start codes are left aligned, but we need the normal right aligned - // number. So we shift the distance to the right. - Dist >>= (int)(16 - Bits); - - // Now we can calculate the position in the code list. It is the sum - // of first position for current bit length and right aligned distance - // between our bit field and start code for current bit length. - uint Pos = Dec.DecodePos[Bits] + Dist; - - // Out of bounds safety check required for damaged archives. - if (Pos >= Dec.MaxNum) - { - Pos = 0; - } - - // Convert the position in the code list to position in alphabet - // and return it. - return Dec.DecodeNum[Pos]; - } - - private uint SlotToLength(BitInput Inp, uint Slot) - { - uint LBits, - Length = 2; - if (Slot < 8) - { - LBits = 0; - Length += Slot; - } - else - { - LBits = Slot / 4 - 1; - Length += (4 | (Slot & 3)) << (int)LBits; - } - - if (LBits > 0) - { - Length += Inp.getbits() >> (int)(16 - LBits); - Inp.addbits(LBits); - } - return Length; } } + + private uint DecodeNumber(BitInput Inp, DecodeTable Dec) + { + // Left aligned 15 bit length raw bit field. + var BitField = Inp.getbits() & 0xfffe; + + if (BitField < Dec.DecodeLen[Dec.QuickBits]) + { + var Code = BitField >> (int)(16 - Dec.QuickBits); + Inp.addbits(Dec.QuickLen[Code]); + return Dec.QuickNum[Code]; + } + + // Detect the real bit length for current code. + uint Bits = 15; + for (var I = Dec.QuickBits + 1; I < 15; I++) + { + if (BitField < Dec.DecodeLen[I]) + { + Bits = I; + break; + } + } + + Inp.addbits(Bits); + + // Calculate the distance from the start code for current bit length. + var Dist = BitField - Dec.DecodeLen[Bits - 1]; + + // Start codes are left aligned, but we need the normal right aligned + // number. So we shift the distance to the right. + Dist >>= (int)(16 - Bits); + + // Now we can calculate the position in the code list. It is the sum + // of first position for current bit length and right aligned distance + // between our bit field and start code for current bit length. + var Pos = Dec.DecodePos[Bits] + Dist; + + // Out of bounds safety check required for damaged archives. + if (Pos >= Dec.MaxNum) + { + Pos = 0; + } + + // Convert the position in the code list to position in alphabet + // and return it. + return Dec.DecodeNum[Pos]; + } + + private uint SlotToLength(BitInput Inp, uint Slot) + { + uint LBits, + Length = 2; + if (Slot < 8) + { + LBits = 0; + Length += Slot; + } + else + { + LBits = (Slot / 4) - 1; + Length += (4 | (Slot & 3)) << (int)LBits; + } + + if (LBits > 0) + { + Length += Inp.getbits() >> (int)(16 - LBits); + Inp.addbits(LBits); + } + return Length; + } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs index a0905ff4..ddeefa86 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs @@ -1,6 +1,4 @@ -#if !Rar2017_64bit -using nint = System.Int32; -using nuint = System.UInt32; +#if !Rar2017_64bit using size_t = System.UInt32; #else using nint = System.Int64; @@ -18,429 +16,383 @@ using System; #pragma warning disable 169 #pragma warning disable 414 -namespace SharpCompress.Compressors.Rar.UnpackV2017 +namespace SharpCompress.Compressors.Rar.UnpackV2017; + +internal static class UnpackGlobal { - internal static class UnpackGlobal + + + // Maximum allowed number of compressed bits processed in quick mode. + public const int MAX_QUICK_DECODE_BITS = 10; + + // Maximum number of filters per entire data block. Must be at least + // twice more than MAX_PACK_FILTERS to store filters from two data blocks. + public const int MAX_UNPACK_FILTERS = 8192; + + // Maximum number of filters per entire data block for RAR3 unpack. + // Must be at least twice more than v3_MAX_PACK_FILTERS to store filters + // from two data blocks. + public const int MAX3_UNPACK_FILTERS = 8192; + + // Maximum size of single filter block. We restrict it to limit memory + // allocation. Must be equal or larger than MAX_ANALYZE_SIZE. + public const int MAX_FILTER_BLOCK_SIZE = 0x400000; + + // Write data in 4 MB or smaller blocks. Must not exceed PACK_MAX_WRITE, + // so we keep number of buffered filter in unpacker reasonable. + public const int UNPACK_MAX_WRITE = 0x400000; +} + +// Decode compressed bit fields to alphabet numbers. +internal sealed class DecodeTable +{ + // Real size of DecodeNum table. + public uint MaxNum; + + // Left aligned start and upper limit codes defining code space + // ranges for bit lengths. DecodeLen[BitLength-1] defines the start of + // range for bit length and DecodeLen[BitLength] defines next code + // after the end of range or in other words the upper limit code + // for specified bit length. + public readonly uint[] DecodeLen = new uint[16]; + + // Every item of this array contains the sum of all preceding items. + // So it contains the start position in code list for every bit length. + public readonly uint[] DecodePos = new uint[16]; + + // Number of compressed bits processed in quick mode. + // Must not exceed MAX_QUICK_DECODE_BITS. + public uint QuickBits; + + // Translates compressed bits (up to QuickBits length) + // to bit length in quick mode. + public readonly byte[] QuickLen = new byte[1 << MAX_QUICK_DECODE_BITS]; + + // Translates compressed bits (up to QuickBits length) + // to position in alphabet in quick mode. + // 'ushort' saves some memory and even provides a little speed gain + // comparting to 'uint' here. + public readonly ushort[] QuickNum = new ushort[1 << MAX_QUICK_DECODE_BITS]; + + // Translate the position in code list to position in alphabet. + // We do not allocate it dynamically to avoid performance overhead + // introduced by pointer, so we use the largest possible table size + // as array dimension. Real size of this array is defined in MaxNum. + // We use this array if compressed bit field is too lengthy + // for QuickLen based translation. + // 'ushort' saves some memory and even provides a little speed gain + // comparting to 'uint' here. + public readonly ushort[] DecodeNum = new ushort[LARGEST_TABLE_SIZE]; +}; + +internal struct UnpackBlockHeader +{ + public int BlockSize; + public int BlockBitSize; + public int BlockStart; + public int HeaderSize; + public bool LastBlockInFile; + public bool TablePresent; +}; + +internal struct UnpackBlockTables +{ + public DecodeTable LD; // Decode literals. + public DecodeTable DD; // Decode distances. + public DecodeTable LDD; // Decode lower bits of distances. + public DecodeTable RD; // Decode repeating distances. + public DecodeTable BD; // Decode bit lengths in Huffman table. + + public void Init() { - - - // Maximum allowed number of compressed bits processed in quick mode. - public const int MAX_QUICK_DECODE_BITS = 10; - - // Maximum number of filters per entire data block. Must be at least - // twice more than MAX_PACK_FILTERS to store filters from two data blocks. - public const int MAX_UNPACK_FILTERS = 8192; - - // Maximum number of filters per entire data block for RAR3 unpack. - // Must be at least twice more than v3_MAX_PACK_FILTERS to store filters - // from two data blocks. - public const int MAX3_UNPACK_FILTERS = 8192; - - // Limit maximum number of channels in RAR3 delta filter to some reasonable - // value to prevent too slow processing of corrupt archives with invalid - // channels number. Must be equal or larger than v3_MAX_FILTER_CHANNELS. - // No need to provide it for RAR5, which uses only 5 bits to store channels. - private const int MAX3_UNPACK_CHANNELS = 1024; - - // Maximum size of single filter block. We restrict it to limit memory - // allocation. Must be equal or larger than MAX_ANALYZE_SIZE. - public const int MAX_FILTER_BLOCK_SIZE = 0x400000; - - // Write data in 4 MB or smaller blocks. Must not exceed PACK_MAX_WRITE, - // so we keep number of buffered filter in unpacker reasonable. - public const int UNPACK_MAX_WRITE = 0x400000; + LD = new DecodeTable(); + DD = new DecodeTable(); + LDD = new DecodeTable(); + RD = new DecodeTable(); + BD = new DecodeTable(); } - - // Decode compressed bit fields to alphabet numbers. - internal sealed class DecodeTable - { - // Real size of DecodeNum table. - public uint MaxNum; - - // Left aligned start and upper limit codes defining code space - // ranges for bit lengths. DecodeLen[BitLength-1] defines the start of - // range for bit length and DecodeLen[BitLength] defines next code - // after the end of range or in other words the upper limit code - // for specified bit length. - public readonly uint[] DecodeLen = new uint[16]; - - // Every item of this array contains the sum of all preceding items. - // So it contains the start position in code list for every bit length. - public readonly uint[] DecodePos = new uint[16]; - - // Number of compressed bits processed in quick mode. - // Must not exceed MAX_QUICK_DECODE_BITS. - public uint QuickBits; - - // Translates compressed bits (up to QuickBits length) - // to bit length in quick mode. - public readonly byte[] QuickLen = new byte[1 << MAX_QUICK_DECODE_BITS]; - - // Translates compressed bits (up to QuickBits length) - // to position in alphabet in quick mode. - // 'ushort' saves some memory and even provides a little speed gain - // comparting to 'uint' here. - public readonly ushort[] QuickNum = new ushort[1 << MAX_QUICK_DECODE_BITS]; - - // Translate the position in code list to position in alphabet. - // We do not allocate it dynamically to avoid performance overhead - // introduced by pointer, so we use the largest possible table size - // as array dimension. Real size of this array is defined in MaxNum. - // We use this array if compressed bit field is too lengthy - // for QuickLen based translation. - // 'ushort' saves some memory and even provides a little speed gain - // comparting to 'uint' here. - public readonly ushort[] DecodeNum = new ushort[LARGEST_TABLE_SIZE]; - }; - - internal struct UnpackBlockHeader - { - public int BlockSize; - public int BlockBitSize; - public int BlockStart; - public int HeaderSize; - public bool LastBlockInFile; - public bool TablePresent; - }; - - internal struct UnpackBlockTables - { - public DecodeTable LD; // Decode literals. - public DecodeTable DD; // Decode distances. - public DecodeTable LDD; // Decode lower bits of distances. - public DecodeTable RD; // Decode repeating distances. - public DecodeTable BD; // Decode bit lengths in Huffman table. - - public void Init() - { - LD = new DecodeTable(); - DD = new DecodeTable(); - LDD = new DecodeTable(); - RD = new DecodeTable(); - BD = new DecodeTable(); - } - }; +}; #if RarV2017_RAR_SMP enum UNP_DEC_TYPE { - UNPDT_LITERAL,UNPDT_MATCH,UNPDT_FULLREP,UNPDT_REP,UNPDT_FILTER +UNPDT_LITERAL,UNPDT_MATCH,UNPDT_FULLREP,UNPDT_REP,UNPDT_FILTER }; struct UnpackDecodedItem { - UNP_DEC_TYPE Type; - ushort Length; - union - { - uint Distance; - byte Literal[4]; - }; +UNP_DEC_TYPE Type; +ushort Length; +union +{ +uint Distance; +byte Literal[4]; +}; }; struct UnpackThreadData { - Unpack *UnpackPtr; - BitInput Inp; - bool HeaderRead; - UnpackBlockHeader BlockHeader; - bool TableRead; - UnpackBlockTables BlockTables; - int DataSize; // Data left in buffer. Can be less than block size. - bool DamagedData; - bool LargeBlock; - bool NoDataLeft; // 'true' if file is read completely. - bool Incomplete; // Not entire block was processed, need to read more data. +Unpack *UnpackPtr; +BitInput Inp; +bool HeaderRead; +UnpackBlockHeader BlockHeader; +bool TableRead; +UnpackBlockTables BlockTables; +int DataSize; // Data left in buffer. Can be less than block size. +bool DamagedData; +bool LargeBlock; +bool NoDataLeft; // 'true' if file is read completely. +bool Incomplete; // Not entire block was processed, need to read more data. - UnpackDecodedItem *Decoded; - uint DecodedSize; - uint DecodedAllocated; - uint ThreadNumber; // For debugging. +UnpackDecodedItem *Decoded; +uint DecodedSize; +uint DecodedAllocated; +uint ThreadNumber; // For debugging. - UnpackThreadData() - :Inp(false) - { - Decoded=NULL; - } - ~UnpackThreadData() - { - if (Decoded!=NULL) - free(Decoded); - } +UnpackThreadData() +:Inp(false) +{ +Decoded=NULL; +} +~UnpackThreadData() +{ +if (Decoded!=NULL) + free(Decoded); +} }; #endif - //struct UnpackFilter - internal class UnpackFilter - { - public byte Type; - public uint BlockStart; - public uint BlockLength; - public byte Channels; - // uint Width; - // byte PosR; - public bool NextWindow; - }; +//struct UnpackFilter +internal class UnpackFilter +{ + public byte Type; + public uint BlockStart; + public uint BlockLength; + public byte Channels; + // uint Width; + // byte PosR; + public bool NextWindow; +}; - //struct UnpackFilter30 - internal class UnpackFilter30 - { - public uint BlockStart; - public uint BlockLength; - public bool NextWindow; +//struct UnpackFilter30 +internal class UnpackFilter30 +{ + public uint BlockStart; + public uint BlockLength; + public bool NextWindow; - // Position of parent filter in Filters array used as prototype for filter - // in PrgStack array. Not defined for filters in Filters array. - public uint ParentFilter; + // Position of parent filter in Filters array used as prototype for filter + // in PrgStack array. Not defined for filters in Filters array. + public uint ParentFilter; - /*#if !RarV2017_RAR5ONLY - public VM_PreparedProgram Prg; - #endif*/ - }; + /*#if !RarV2017_RAR5ONLY + public VM_PreparedProgram Prg; + #endif*/ +}; - internal class AudioVariables // For RAR 2.0 archives only. - { - public int K1, K2, K3, K4, K5; - public int D1, D2, D3, D4; - public int LastDelta; - public readonly uint[] Dif = new uint[11]; - public uint ByteCount; - public int LastChar; - }; +internal class AudioVariables // For RAR 2.0 archives only. +{ + public int K1, K2, K3, K4, K5; + public int D1, D2, D3, D4; + public int LastDelta; + public readonly uint[] Dif = new uint[11]; + public uint ByteCount; + public int LastChar; +}; - // We can use the fragmented dictionary in case heap does not have the single - // large enough memory block. It is slower than normal dictionary. - internal partial class FragmentedWindow - { - private const int MAX_MEM_BLOCKS = 32; +// We can use the fragmented dictionary in case heap does not have the single +// large enough memory block. It is slower than normal dictionary. +internal partial class FragmentedWindow +{ + private const int MAX_MEM_BLOCKS = 32; - //void Reset(); - private readonly byte[][] Mem = new byte[MAX_MEM_BLOCKS][]; - private readonly size_t[] MemSize = new size_t[MAX_MEM_BLOCKS]; + //void Reset(); + private readonly byte[][] Mem = new byte[MAX_MEM_BLOCKS][]; + private readonly size_t[] MemSize = new size_t[MAX_MEM_BLOCKS]; - //FragmentedWindow(); - //~FragmentedWindow(); - //void Init(size_t WinSize); - //byte& operator [](size_t Item); - //void CopyString(uint Length,uint Distance,size_t &UnpPtr,size_t MaxWinMask); - //void CopyData(byte *Dest,size_t WinPos,size_t Size); - //size_t GetBlockSize(size_t StartPos,size_t RequiredSize); - }; + //FragmentedWindow(); + //~FragmentedWindow(); + //void Init(size_t WinSize); + //byte& operator [](size_t Item); + //void CopyString(uint Length,uint Distance,size_t &UnpPtr,size_t MaxWinMask); + //void CopyData(byte *Dest,size_t WinPos,size_t Size); + //size_t GetBlockSize(size_t StartPos,size_t RequiredSize); +}; - internal partial class Unpack - { +internal partial class Unpack +{ - //void Unpack5(bool Solid); - //void Unpack5MT(bool Solid); - //bool UnpReadBuf(); - //void UnpWriteBuf(); - //byte* ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt); - //void UnpWriteArea(size_t StartPtr,size_t EndPtr); - //void UnpWriteData(byte *Data,size_t Size); - //_forceinline uint SlotToLength(BitInput &Inp,uint Slot); - //void UnpInitData50(bool Solid); - //bool ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header); - //bool ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables); - //void MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size); - //_forceinline uint DecodeNumber(BitInput &Inp,DecodeTable *Dec); - //void CopyString(); - //inline void InsertOldDist(uint Distance); - //void UnpInitData(bool Solid); - //_forceinline void CopyString(uint Length,uint Distance); - //uint ReadFilterData(BitInput &Inp); - //bool ReadFilter(BitInput &Inp,UnpackFilter &Filter); - //bool AddFilter(UnpackFilter &Filter); - //bool AddFilter(); - //void InitFilters(); + //void Unpack5(bool Solid); + //void Unpack5MT(bool Solid); + //bool UnpReadBuf(); + //void UnpWriteBuf(); + //byte* ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt); + //void UnpWriteArea(size_t StartPtr,size_t EndPtr); + //void UnpWriteData(byte *Data,size_t Size); + //_forceinline uint SlotToLength(BitInput &Inp,uint Slot); + //void UnpInitData50(bool Solid); + //bool ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header); + //bool ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables); + //void MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size); + //_forceinline uint DecodeNumber(BitInput &Inp,DecodeTable *Dec); + //void CopyString(); + //inline void InsertOldDist(uint Distance); + //void UnpInitData(bool Solid); + //_forceinline void CopyString(uint Length,uint Distance); + //uint ReadFilterData(BitInput &Inp); + //bool ReadFilter(BitInput &Inp,UnpackFilter &Filter); + //bool AddFilter(UnpackFilter &Filter); + //bool AddFilter(); + //void InitFilters(); - //ComprDataIO *UnpIO; - //BitInput Inp; - private BitInput Inp { get { return this; } } // hopefully this gets inlined + //ComprDataIO *UnpIO; + //BitInput Inp; + private BitInput Inp { get { return this; } } // hopefully this gets inlined #if RarV2017_RAR_SMP - void InitMT(); - bool UnpackLargeBlock(UnpackThreadData &D); - bool ProcessDecoded(UnpackThreadData &D); +void InitMT(); +bool UnpackLargeBlock(UnpackThreadData &D); +bool ProcessDecoded(UnpackThreadData &D); - ThreadPool *UnpThreadPool; - UnpackThreadData *UnpThreadData; - uint MaxUserThreads; - byte *ReadBufMT; +ThreadPool *UnpThreadPool; +UnpackThreadData *UnpThreadData; +uint MaxUserThreads; +byte *ReadBufMT; #endif - private byte[] FilterSrcMemory = Array.Empty(); - private byte[] FilterDstMemory = Array.Empty(); + private byte[] FilterSrcMemory = Array.Empty(); + private byte[] FilterDstMemory = Array.Empty(); - // Filters code, one entry per filter. - private readonly List Filters = new List(); + // Filters code, one entry per filter. + private readonly List Filters = new List(); - private readonly uint[] OldDist = new uint[4]; - private uint OldDistPtr; - private uint LastLength; + private readonly uint[] OldDist = new uint[4]; + private uint OldDistPtr; + private uint LastLength; - // LastDist is necessary only for RAR2 and older with circular OldDist - // array. In RAR3 last distance is always stored in OldDist[0]. - private uint LastDist; + // LastDist is necessary only for RAR2 and older with circular OldDist + // array. In RAR3 last distance is always stored in OldDist[0]. + private uint LastDist; - private size_t UnpPtr, WrPtr; + private size_t UnpPtr, WrPtr; - // Top border of read packed data. - private int ReadTop; + // Top border of read packed data. + private int ReadTop; - // Border to call UnpReadBuf. We use it instead of (ReadTop-C) - // for optimization reasons. Ensures that we have C bytes in buffer - // unless we are at the end of file. - private int ReadBorder; + // Border to call UnpReadBuf. We use it instead of (ReadTop-C) + // for optimization reasons. Ensures that we have C bytes in buffer + // unless we are at the end of file. + private int ReadBorder; - private UnpackBlockHeader BlockHeader; - private UnpackBlockTables BlockTables; + private UnpackBlockHeader BlockHeader; + private UnpackBlockTables BlockTables; - private size_t WriteBorder; + private size_t WriteBorder; - private byte[] Window; + private byte[] Window; - private readonly FragmentedWindow FragWindow = new FragmentedWindow(); - private bool Fragmented; + private readonly FragmentedWindow FragWindow = new FragmentedWindow(); + private bool Fragmented; - private int64 DestUnpSize; + private int64 DestUnpSize; - //bool Suspended; - private bool UnpAllBuf; - private bool UnpSomeRead; - private int64 WrittenFileSize; - private bool FileExtracted; + //bool Suspended; + private bool UnpAllBuf; + private bool UnpSomeRead; + private int64 WrittenFileSize; + private bool FileExtracted; - /***************************** Unpack v 1.5 *********************************/ - //void Unpack15(bool Solid); - //void ShortLZ(); - //void LongLZ(); - //void HuffDecode(); - //void GetFlagsBuf(); - //void UnpInitData15(int Solid); - //void InitHuff(); - //void CorrHuff(ushort *CharSet,byte *NumToPlace); - //void CopyString15(uint Distance,uint Length); - //uint DecodeNum(uint Num,uint StartPos,uint *DecTab,uint *PosTab); + /***************************** Unpack v 1.5 *********************************/ + //void Unpack15(bool Solid); + //void ShortLZ(); + //void LongLZ(); + //void HuffDecode(); + //void GetFlagsBuf(); + //void UnpInitData15(int Solid); + //void InitHuff(); + //void CorrHuff(ushort *CharSet,byte *NumToPlace); + //void CopyString15(uint Distance,uint Length); + //uint DecodeNum(uint Num,uint StartPos,uint *DecTab,uint *PosTab); - private readonly ushort[] ChSet = new ushort[256], ChSetA = new ushort[256], ChSetB = new ushort[256], ChSetC = new ushort[256]; - private readonly byte[] NToPl = new byte[256], NToPlB = new byte[256], NToPlC = new byte[256]; - private uint FlagBuf, AvrPlc, AvrPlcB, AvrLn1, AvrLn2, AvrLn3; - private int Buf60, NumHuf, StMode, LCount, FlagsCnt; + private readonly ushort[] ChSet = new ushort[256], ChSetA = new ushort[256], ChSetB = new ushort[256], ChSetC = new ushort[256]; + private readonly byte[] NToPl = new byte[256], NToPlB = new byte[256], NToPlC = new byte[256]; + private uint FlagBuf, AvrPlc, AvrPlcB, AvrLn1, AvrLn2, AvrLn3; + private int Buf60, NumHuf, StMode, LCount, FlagsCnt; - private uint Nhfb, Nlzb, MaxDist3; - /***************************** Unpack v 1.5 *********************************/ + private uint Nhfb, Nlzb, MaxDist3; + /***************************** Unpack v 1.5 *********************************/ - /***************************** Unpack v 2.0 *********************************/ - //void Unpack20(bool Solid); + /***************************** Unpack v 2.0 *********************************/ + //void Unpack20(bool Solid); - private DecodeTable[] MD = new DecodeTable[4]; // Decode multimedia data, up to 4 channels. + private DecodeTable[] MD = new DecodeTable[4]; // Decode multimedia data, up to 4 channels. - private readonly byte[] UnpOldTable20 = new byte[MC20 * 4]; - private bool UnpAudioBlock; - private uint UnpChannels, UnpCurChannel; + private readonly byte[] UnpOldTable20 = new byte[MC20 * 4]; + private bool UnpAudioBlock; + private uint UnpChannels, UnpCurChannel; - private int UnpChannelDelta; - //void CopyString20(uint Length,uint Distance); - //bool ReadTables20(); - //void UnpWriteBuf20(); - //void UnpInitData20(int Solid); - //void ReadLastTables(); - //byte DecodeAudio(int Delta); - private AudioVariables[] AudV = new AudioVariables[4]; - /***************************** Unpack v 2.0 *********************************/ + private int UnpChannelDelta; + //void CopyString20(uint Length,uint Distance); + //bool ReadTables20(); + //void UnpWriteBuf20(); + //void UnpInitData20(int Solid); + //void ReadLastTables(); + //byte DecodeAudio(int Delta); + private AudioVariables[] AudV = new AudioVariables[4]; + /***************************** Unpack v 2.0 *********************************/ - /***************************** Unpack v 3.0 *********************************/ - public const int BLOCK_LZ = 0; - public const int BLOCK_PPM = 1; + /***************************** Unpack v 3.0 *********************************/ + public const int BLOCK_LZ = 0; + public const int BLOCK_PPM = 1; - //void UnpInitData30(bool Solid); - //void Unpack29(bool Solid); - //void InitFilters30(bool Solid); - //bool ReadEndOfBlock(); - //bool ReadVMCode(); - //bool ReadVMCodePPM(); - //bool AddVMCode(uint FirstByte,byte *Code,int CodeSize); - //int SafePPMDecodeChar(); - //bool ReadTables30(); - //bool UnpReadBuf30(); - //void UnpWriteBuf30(); - //void ExecuteCode(VM_PreparedProgram *Prg); + /*#if !RarV2017_RAR5ONLY + ModelPPM PPM; + #endif*/ + private int PPMEscChar; - private int PrevLowDist, LowDistRepCount; + private readonly byte[] UnpOldTable = new byte[HUFF_TABLE_SIZE30]; - /*#if !RarV2017_RAR5ONLY - ModelPPM PPM; - #endif*/ - private int PPMEscChar; + // If we already read decoding tables for Unpack v2,v3,v5. + // We should not use a single variable for all algorithm versions, + // because we can have a corrupt archive with one algorithm file + // followed by another algorithm file with "solid" flag and we do not + // want to reuse tables from one algorithm in another. + private bool TablesRead2, TablesRead5; - private readonly byte[] UnpOldTable = new byte[HUFF_TABLE_SIZE30]; - private int UnpBlockType; + // Virtual machine to execute filters code. + /*#if !RarV2017_RAR5ONLY + RarVM VM; + #endif*/ - // If we already read decoding tables for Unpack v2,v3,v5. - // We should not use a single variable for all algorithm versions, - // because we can have a corrupt archive with one algorithm file - // followed by another algorithm file with "solid" flag and we do not - // want to reuse tables from one algorithm in another. - private bool TablesRead2, TablesRead3, TablesRead5; + // Buffer to read VM filters code. We moved it here from AddVMCode + // function to reduce time spent in BitInput constructor. + private readonly BitInput VMCodeInp = new BitInput(true); - // Virtual machine to execute filters code. - /*#if !RarV2017_RAR5ONLY - RarVM VM; - #endif*/ + // Filters code, one entry per filter. + private readonly List Filters30 = new List(); - // Buffer to read VM filters code. We moved it here from AddVMCode - // function to reduce time spent in BitInput constructor. - private readonly BitInput VMCodeInp = new BitInput(true); + // Filters stack, several entrances of same filter are possible. + private readonly List PrgStack = new List(); - // Filters code, one entry per filter. - private readonly List Filters30 = new List(); - - // Filters stack, several entrances of same filter are possible. - private readonly List PrgStack = new List(); - - // Lengths of preceding data blocks, one length of one last block - // for every filter. Used to reduce the size required to write - // the data block length if lengths are repeating. - private readonly List OldFilterLengths = new List(); - - private int LastFilter; - /***************************** Unpack v 3.0 *********************************/ - - //Unpack(ComprDataIO *DataIO); - //~Unpack(); - //void Init(size_t WinSize,bool Solid); - //void DoUnpack(uint Method,bool Solid); - private bool IsFileExtracted() { return (FileExtracted); } - private void SetDestSize(int64 DestSize) { DestUnpSize = DestSize; FileExtracted = false; } - private void SetSuspended(bool Suspended) { this.Suspended = Suspended; } + // Lengths of preceding data blocks, one length of one last block + // for every filter. Used to reduce the size required to write + // the data block length if lengths are repeating. + private readonly List OldFilterLengths = new List(); #if RarV2017_RAR_SMP - // More than 8 threads are unlikely to provide a noticeable gain - // for unpacking, but would use the additional memory. - void SetThreads(uint Threads) {MaxUserThreads=Min(Threads,8);} +// More than 8 threads are unlikely to provide a noticeable gain +// for unpacking, but would use the additional memory. +void SetThreads(uint Threads) {MaxUserThreads=Min(Threads,8);} - void UnpackDecode(UnpackThreadData &D); +void UnpackDecode(UnpackThreadData &D); #endif - private size_t MaxWinSize; - private size_t MaxWinMask; - - private uint GetChar() - { - if (Inp.InAddr > MAX_SIZE - 30) - { - UnpReadBuf(); - } - - return (Inp.InBuf[Inp.InAddr++]); - } - - - } + private size_t MaxWinSize; + private size_t MaxWinMask; } diff --git a/src/SharpCompress/Compressors/Rar/VM/BitInput.cs b/src/SharpCompress/Compressors/Rar/VM/BitInput.cs index b90d40d8..39bbcc3f 100644 --- a/src/SharpCompress/Compressors/Rar/VM/BitInput.cs +++ b/src/SharpCompress/Compressors/Rar/VM/BitInput.cs @@ -1,105 +1,104 @@ -namespace SharpCompress.Compressors.Rar.VM +namespace SharpCompress.Compressors.Rar.VM; + +internal class BitInput { - internal class BitInput + /// the max size of the input + internal const int MAX_SIZE = 0x8000; + + public int inAddr; + public int inBit; + + // TODO: rename var + public int InAddr { - /// the max size of the input - internal const int MAX_SIZE = 0x8000; + get { return inAddr; } + set { inAddr = value; } + } + public int InBit + { + get { return inBit; } + set { inBit = value; } + } + public bool ExternalBuffer; - public int inAddr; - public int inBit; + /// + internal BitInput() + { + InBuf = new byte[MAX_SIZE]; + } - // TODO: rename var - public int InAddr - { - get { return inAddr; } - set { inAddr = value; } - } - public int InBit - { - get { return inBit; } - set { inBit = value; } - } - public bool ExternalBuffer; + internal byte[] InBuf { get; } - /// - internal BitInput() - { - InBuf = new byte[MAX_SIZE]; - } + internal void InitBitInput() + { + inAddr = 0; + inBit = 0; + } - internal byte[] InBuf { get; } + internal void faddbits(uint bits) + { + // TODO uint + AddBits((int)bits); + } - internal void InitBitInput() - { - inAddr = 0; - inBit = 0; - } + /// + /// also named faddbits + /// + /// + internal void AddBits(int bits) + { + bits += inBit; + inAddr += (bits >> 3); + inBit = bits & 7; + } - internal void faddbits(uint bits) - { - // TODO uint - AddBits((int)bits); - } + internal uint fgetbits() + { + // TODO uint + return (uint)GetBits(); + } - /// - /// also named faddbits - /// - /// - internal void AddBits(int bits) - { - bits += inBit; - inAddr += (bits >> 3); - inBit = bits & 7; - } + internal uint getbits() + { + // TODO uint + return (uint)GetBits(); + } - internal uint fgetbits() - { - // TODO uint - return (uint)GetBits(); - } + /// + /// (also named fgetbits) + /// + /// + /// the bits (unsigned short) + /// + internal int GetBits() + { + // int BitField=0; + // BitField|=(int)(inBuf[inAddr] << 16)&0xFF0000; + // BitField|=(int)(inBuf[inAddr+1] << 8)&0xff00; + // BitField|=(int)(inBuf[inAddr+2])&0xFF; + // BitField >>>= (8-inBit); + // return (BitField & 0xffff); + return ( + ( + Utility.URShift( + ( + ((InBuf[inAddr] & 0xff) << 16) + + ((InBuf[inAddr + 1] & 0xff) << 8) + + ((InBuf[inAddr + 2] & 0xff)) + ), + (8 - inBit) + ) + ) & 0xffff + ); + } - internal uint getbits() - { - // TODO uint - return (uint)GetBits(); - } - - /// - /// (also named fgetbits) - /// - /// - /// the bits (unsigned short) - /// - internal int GetBits() - { - // int BitField=0; - // BitField|=(int)(inBuf[inAddr] << 16)&0xFF0000; - // BitField|=(int)(inBuf[inAddr+1] << 8)&0xff00; - // BitField|=(int)(inBuf[inAddr+2])&0xFF; - // BitField >>>= (8-inBit); - // return (BitField & 0xffff); - return ( - ( - Utility.URShift( - ( - ((InBuf[inAddr] & 0xff) << 16) - + ((InBuf[inAddr + 1] & 0xff) << 8) - + ((InBuf[inAddr + 2] & 0xff)) - ), - (8 - inBit) - ) - ) & 0xffff - ); - } - - /// Indicates an Overfow - /// how many bytes to inc - /// - /// true if an Oververflow would occur - /// - internal bool Overflow(int IncPtr) - { - return (inAddr + IncPtr >= MAX_SIZE); - } + /// Indicates an Overfow + /// how many bytes to inc + /// + /// true if an Oververflow would occur + /// + internal bool Overflow(int IncPtr) + { + return (inAddr + IncPtr >= MAX_SIZE); } } diff --git a/src/SharpCompress/Compressors/Rar/VM/RarVM.cs b/src/SharpCompress/Compressors/Rar/VM/RarVM.cs index 3ab8e2ca..612f56ff 100644 --- a/src/SharpCompress/Compressors/Rar/VM/RarVM.cs +++ b/src/SharpCompress/Compressors/Rar/VM/RarVM.cs @@ -4,1612 +4,1608 @@ using System; using System.Buffers.Binary; using System.Collections.Generic; -namespace SharpCompress.Compressors.Rar.VM +namespace SharpCompress.Compressors.Rar.VM; + +internal sealed class RarVM : BitInput { - internal sealed class RarVM : BitInput + //private void InitBlock() + //{ + // Mem.set_Renamed(offset + 0, Byte.valueOf((sbyte) (value_Renamed & 0xff))); + // Mem.set_Renamed(offset + 1, Byte.valueOf((sbyte) ((Utility.URShift(value_Renamed, 8)) & 0xff))); + // Mem.set_Renamed(offset + 2, Byte.valueOf((sbyte) ((Utility.URShift(value_Renamed, 16)) & 0xff))); + // Mem.set_Renamed(offset + 3, Byte.valueOf((sbyte) ((Utility.URShift(value_Renamed, 24)) & 0xff))); + + //} + internal byte[] Mem { get; private set; } + + public const int VM_MEMSIZE = 0x40000; + + //UPGRADE_NOTE: Final was removed from the declaration of 'VM_MEMMASK '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + public static readonly int VM_MEMMASK = (VM_MEMSIZE - 1); + + public const int VM_GLOBALMEMADDR = 0x3C000; + + public const int VM_GLOBALMEMSIZE = 0x2000; + + public const int VM_FIXEDGLOBALSIZE = 64; + + private const int regCount = 8; + private readonly int[] R = new int[regCount]; + + private VMFlags flags; + + private int maxOpCount = 25000000; + + private int codeSize; + + private int IP; + + internal RarVM() { - //private void InitBlock() - //{ - // Mem.set_Renamed(offset + 0, Byte.valueOf((sbyte) (value_Renamed & 0xff))); - // Mem.set_Renamed(offset + 1, Byte.valueOf((sbyte) ((Utility.URShift(value_Renamed, 8)) & 0xff))); - // Mem.set_Renamed(offset + 2, Byte.valueOf((sbyte) ((Utility.URShift(value_Renamed, 16)) & 0xff))); - // Mem.set_Renamed(offset + 3, Byte.valueOf((sbyte) ((Utility.URShift(value_Renamed, 24)) & 0xff))); + //InitBlock(); + Mem = null; + } - //} - internal byte[] Mem { get; private set; } + internal void init() + { + Mem ??= new byte[VM_MEMSIZE + 4]; + } - public const int VM_MEMSIZE = 0x40000; + private bool IsVMMem(byte[] mem) + { + return Mem == mem; + } - //UPGRADE_NOTE: Final was removed from the declaration of 'VM_MEMMASK '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - public static readonly int VM_MEMMASK = (VM_MEMSIZE - 1); - - public const int VM_GLOBALMEMADDR = 0x3C000; - - public const int VM_GLOBALMEMSIZE = 0x2000; - - public const int VM_FIXEDGLOBALSIZE = 64; - - private const int regCount = 8; - - private const long UINT_MASK = 0xffffFFFF; //((long)2*(long)Integer.MAX_VALUE); - - private readonly int[] R = new int[regCount]; - - private VMFlags flags; - - private int maxOpCount = 25000000; - - private int codeSize; - - private int IP; - - internal RarVM() + private int GetValue(bool byteMode, byte[] mem, int offset) + { + if (byteMode) { - //InitBlock(); - Mem = null; - } - - internal void init() - { - Mem ??= new byte[VM_MEMSIZE + 4]; - } - - private bool IsVMMem(byte[] mem) - { - return Mem == mem; - } - - private int GetValue(bool byteMode, byte[] mem, int offset) - { - if (byteMode) - { - if (IsVMMem(mem)) - { - return (mem[offset]); - } - return (mem[offset] & 0xff); - } if (IsVMMem(mem)) { - return BinaryPrimitives.ReadInt32LittleEndian(mem.AsSpan(offset)); + return (mem[offset]); } - return BinaryPrimitives.ReadInt32BigEndian(mem.AsSpan(offset)); + return (mem[offset] & 0xff); } - - private void SetValue(bool byteMode, byte[] mem, int offset, int value) + if (IsVMMem(mem)) { - if (byteMode) + return BinaryPrimitives.ReadInt32LittleEndian(mem.AsSpan(offset)); + } + return BinaryPrimitives.ReadInt32BigEndian(mem.AsSpan(offset)); + } + + private void SetValue(bool byteMode, byte[] mem, int offset, int value) + { + if (byteMode) + { + if (IsVMMem(mem)) { - if (IsVMMem(mem)) - { - mem[offset] = (byte)value; - } - else - { - mem[offset] = (byte)((mem[offset] & 0x00) | (byte)(value & 0xff)); - } + mem[offset] = (byte)value; } else { - if (IsVMMem(mem)) - { - BinaryPrimitives.WriteInt32LittleEndian(mem.AsSpan(offset), value); - } - else - { - BinaryPrimitives.WriteInt32BigEndian(mem.AsSpan(offset), value); - } + mem[offset] = (byte)((mem[offset] & 0x00) | (byte)(value & 0xff)); } - - // #define SET_VALUE(ByteMode,Addr,Value) SetValue(ByteMode,(uint - // *)Addr,Value) } - - internal void SetLowEndianValue(List mem, int offset, int value) + else { - mem[offset + 0] = (byte)(value & 0xff); - mem[offset + 1] = (byte)(Utility.URShift(value, 8) & 0xff); - mem[offset + 2] = (byte)(Utility.URShift(value, 16) & 0xff); - mem[offset + 3] = (byte)(Utility.URShift(value, 24) & 0xff); - } - - private int GetOperand(VMPreparedOperand cmdOp) - { - int ret = 0; - if (cmdOp.Type == VMOpType.VM_OPREGMEM) + if (IsVMMem(mem)) { - int pos = (cmdOp.Offset + cmdOp.Base) & VM_MEMMASK; - ret = BinaryPrimitives.ReadInt32LittleEndian(Mem.AsSpan(pos)); + BinaryPrimitives.WriteInt32LittleEndian(mem.AsSpan(offset), value); } else { - int pos = cmdOp.Offset; - ret = BinaryPrimitives.ReadInt32LittleEndian(Mem.AsSpan(pos)); - } - return ret; - } - - public void execute(VMPreparedProgram prg) - { - for (int i = 0; i < prg.InitR.Length; i++) - // memcpy(R,Prg->InitR,sizeof(Prg->InitR)); - { - R[i] = prg.InitR[i]; - } - - long globalSize = Math.Min(prg.GlobalData.Count, VM_GLOBALMEMSIZE) & 0xffFFffFF; - if (globalSize != 0) - { - for (int i = 0; i < globalSize; i++) - // memcpy(Mem+VM_GLOBALMEMADDR,&Prg->GlobalData[0],GlobalSize); - { - Mem[VM_GLOBALMEMADDR + i] = prg.GlobalData[i]; - } - } - long staticSize = - Math.Min(prg.StaticData.Count, VM_GLOBALMEMSIZE - globalSize) & 0xffFFffFF; - if (staticSize != 0) - { - for (int i = 0; i < staticSize; i++) - // memcpy(Mem+VM_GLOBALMEMADDR+GlobalSize,&Prg->StaticData[0],StaticSize); - { - Mem[VM_GLOBALMEMADDR + (int)globalSize + i] = prg.StaticData[i]; - } - } - R[7] = VM_MEMSIZE; - flags = 0; - - //UPGRADE_NOTE: There is an untranslated Statement. Please refer to original code. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1153'" - List preparedCode = - prg.AltCommands.Count != 0 ? prg.AltCommands : prg.Commands; - - if (!ExecuteCode(preparedCode, prg.CommandCount)) - { - preparedCode[0].OpCode = VMCommands.VM_RET; - } - int newBlockPos = GetValue(false, Mem, VM_GLOBALMEMADDR + 0x20) & VM_MEMMASK; - int newBlockSize = GetValue(false, Mem, VM_GLOBALMEMADDR + 0x1c) & VM_MEMMASK; - if ((newBlockPos + newBlockSize) >= VM_MEMSIZE) - { - newBlockPos = 0; - newBlockSize = 0; - } - - prg.FilteredDataOffset = newBlockPos; - prg.FilteredDataSize = newBlockSize; - - prg.GlobalData.Clear(); - - int dataSize = Math.Min( - GetValue(false, Mem, VM_GLOBALMEMADDR + 0x30), - VM_GLOBALMEMSIZE - VM_FIXEDGLOBALSIZE - ); - if (dataSize != 0) - { - //prg.GlobalData.Clear(); - // ->GlobalData.Add(dataSize+VM_FIXEDGLOBALSIZE); - prg.GlobalData.Capacity = dataSize + VM_FIXEDGLOBALSIZE; - for (int i = 0; i < dataSize + VM_FIXEDGLOBALSIZE; i++) - // memcpy(&Prg->GlobalData[0],&Mem[VM_GLOBALMEMADDR],DataSize+VM_FIXEDGLOBALSIZE); - { - prg.GlobalData.Add(Mem[VM_GLOBALMEMADDR + i]); - } + BinaryPrimitives.WriteInt32BigEndian(mem.AsSpan(offset), value); } } - private bool setIP(int ip) + // #define SET_VALUE(ByteMode,Addr,Value) SetValue(ByteMode,(uint + // *)Addr,Value) + } + + internal void SetLowEndianValue(List mem, int offset, int value) + { + mem[offset + 0] = (byte)(value & 0xff); + mem[offset + 1] = (byte)(Utility.URShift(value, 8) & 0xff); + mem[offset + 2] = (byte)(Utility.URShift(value, 16) & 0xff); + mem[offset + 3] = (byte)(Utility.URShift(value, 24) & 0xff); + } + + private int GetOperand(VMPreparedOperand cmdOp) + { + var ret = 0; + if (cmdOp.Type == VMOpType.VM_OPREGMEM) { - if ((ip) >= codeSize) - { - return (true); - } + var pos = (cmdOp.Offset + cmdOp.Base) & VM_MEMMASK; + ret = BinaryPrimitives.ReadInt32LittleEndian(Mem.AsSpan(pos)); + } + else + { + var pos = cmdOp.Offset; + ret = BinaryPrimitives.ReadInt32LittleEndian(Mem.AsSpan(pos)); + } + return ret; + } - if (--maxOpCount <= 0) - { - return (false); - } - - IP = ip; - return true; + public void execute(VMPreparedProgram prg) + { + for (var i = 0; i < prg.InitR.Length; i++) + // memcpy(R,Prg->InitR,sizeof(Prg->InitR)); + { + R[i] = prg.InitR[i]; } - private bool ExecuteCode(List preparedCode, int cmdCount) + var globalSize = Math.Min(prg.GlobalData.Count, VM_GLOBALMEMSIZE) & 0xffFFffFF; + if (globalSize != 0) { - maxOpCount = 25000000; - codeSize = cmdCount; - IP = 0; - - while (true) + for (var i = 0; i < globalSize; i++) + // memcpy(Mem+VM_GLOBALMEMADDR,&Prg->GlobalData[0],GlobalSize); { - VMPreparedCommand cmd = preparedCode[IP]; - int op1 = GetOperand(cmd.Op1); - int op2 = GetOperand(cmd.Op2); - switch (cmd.OpCode) - { - case VMCommands.VM_MOV: - SetValue(cmd.IsByteMode, Mem, op1, GetValue(cmd.IsByteMode, Mem, op2)); - - // SET_VALUE(Cmd->ByteMode,Op1,GET_VALUE(Cmd->ByteMode,Op2)); - break; - - case VMCommands.VM_MOVB: - SetValue(true, Mem, op1, GetValue(true, Mem, op2)); - break; - - case VMCommands.VM_MOVD: - SetValue(false, Mem, op1, GetValue(false, Mem, op2)); - break; - - case VMCommands.VM_CMP: - - { - VMFlags value1 = (VMFlags)GetValue(cmd.IsByteMode, Mem, op1); - VMFlags result = value1 - GetValue(cmd.IsByteMode, Mem, op2); - - if (result == 0) - { - flags = VMFlags.VM_FZ; - } - else - { - flags = (VMFlags)( - (result > value1) ? 1 : 0 | (int)(result & VMFlags.VM_FS) - ); - } - } - break; - - case VMCommands.VM_CMPB: - - { - VMFlags value1 = (VMFlags)GetValue(true, Mem, op1); - VMFlags result = value1 - GetValue(true, Mem, op2); - if (result == 0) - { - flags = VMFlags.VM_FZ; - } - else - { - flags = (VMFlags)( - (result > value1) ? 1 : 0 | (int)(result & VMFlags.VM_FS) - ); - } - } - break; - - case VMCommands.VM_CMPD: - - { - VMFlags value1 = (VMFlags)GetValue(false, Mem, op1); - VMFlags result = value1 - GetValue(false, Mem, op2); - if (result == 0) - { - flags = VMFlags.VM_FZ; - } - else - { - flags = (VMFlags)( - (result > value1) ? 1 : 0 | (int)(result & VMFlags.VM_FS) - ); - } - } - break; - - case VMCommands.VM_ADD: - - { - int value1 = GetValue(cmd.IsByteMode, Mem, op1); - int result = (int)( - ((value1 + (long)GetValue(cmd.IsByteMode, Mem, op2))) - & unchecked((int)0xffffffff) - ); - if (cmd.IsByteMode) - { - result &= 0xff; - flags = (VMFlags)( - (result < value1) - ? 1 - : 0 - | ( - result == 0 - ? (int)VMFlags.VM_FZ - : ( - ((result & 0x80) != 0) - ? (int)VMFlags.VM_FS - : 0 - ) - ) - ); - - // Flags=(Result value1) ? 1 : 0 | (result & (int)VMFlags.VM_FS)) - ); - SetValue(cmd.IsByteMode, Mem, op1, result); // (Cmd->ByteMode,Op1,Result); - } - break; - - case VMCommands.VM_SUBB: - SetValue( - true, - Mem, - op1, - (int)( - GetValue(true, Mem, op1) - & 0xFFffFFff - GetValue(true, Mem, op2) - & unchecked((int)0xFFffFFff) - ) - ); - break; - - case VMCommands.VM_SUBD: - SetValue( - false, - Mem, - op1, - (int)( - GetValue(false, Mem, op1) - & 0xFFffFFff - GetValue(false, Mem, op2) - & unchecked((int)0xFFffFFff) - ) - ); - break; - - case VMCommands.VM_JZ: - if ((flags & VMFlags.VM_FZ) != 0) - { - setIP(GetValue(false, Mem, op1)); - continue; - } - break; - - case VMCommands.VM_JNZ: - if ((flags & VMFlags.VM_FZ) == 0) - { - setIP(GetValue(false, Mem, op1)); - continue; - } - break; - - case VMCommands.VM_INC: - - { - int result = (int)( - GetValue(cmd.IsByteMode, Mem, op1) & 0xFFffFFffL + 1L - ); - if (cmd.IsByteMode) - { - result &= 0xff; - } - - SetValue(cmd.IsByteMode, Mem, op1, result); - flags = (VMFlags)( - result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS - ); - } - break; - - case VMCommands.VM_INCB: - SetValue( - true, - Mem, - op1, - (int)(GetValue(true, Mem, op1) & 0xFFffFFffL + 1L) - ); - break; - - case VMCommands.VM_INCD: - SetValue( - false, - Mem, - op1, - (int)(GetValue(false, Mem, op1) & 0xFFffFFffL + 1L) - ); - break; - - case VMCommands.VM_DEC: - - { - int result = (int)(GetValue(cmd.IsByteMode, Mem, op1) & 0xFFffFFff - 1); - SetValue(cmd.IsByteMode, Mem, op1, result); - flags = (VMFlags)( - result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS - ); - } - break; - - case VMCommands.VM_DECB: - SetValue(true, Mem, op1, (int)(GetValue(true, Mem, op1) & 0xFFffFFff - 1)); - break; - - case VMCommands.VM_DECD: - SetValue( - false, - Mem, - op1, - (int)(GetValue(false, Mem, op1) & 0xFFffFFff - 1) - ); - break; - - case VMCommands.VM_JMP: - setIP(GetValue(false, Mem, op1)); - continue; - - case VMCommands.VM_XOR: - - { - int result = - GetValue(cmd.IsByteMode, Mem, op1) - ^ GetValue(cmd.IsByteMode, Mem, op2); - flags = (VMFlags)( - result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS - ); - SetValue(cmd.IsByteMode, Mem, op1, result); - } - break; - - case VMCommands.VM_AND: - - { - int result = - GetValue(cmd.IsByteMode, Mem, op1) - & GetValue(cmd.IsByteMode, Mem, op2); - flags = (VMFlags)( - result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS - ); - SetValue(cmd.IsByteMode, Mem, op1, result); - } - break; - - case VMCommands.VM_OR: - - { - int result = - GetValue(cmd.IsByteMode, Mem, op1) - | GetValue(cmd.IsByteMode, Mem, op2); - flags = (VMFlags)( - result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS - ); - SetValue(cmd.IsByteMode, Mem, op1, result); - } - break; - - case VMCommands.VM_TEST: - - { - int result = - GetValue(cmd.IsByteMode, Mem, op1) - & GetValue(cmd.IsByteMode, Mem, op2); - flags = (VMFlags)( - result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS - ); - } - break; - - case VMCommands.VM_JS: - if ((flags & VMFlags.VM_FS) != 0) - { - setIP(GetValue(false, Mem, op1)); - continue; - } - break; - - case VMCommands.VM_JNS: - if ((flags & VMFlags.VM_FS) == 0) - { - setIP(GetValue(false, Mem, op1)); - continue; - } - break; - - case VMCommands.VM_JB: - if ((flags & VMFlags.VM_FC) != 0) - { - setIP(GetValue(false, Mem, op1)); - continue; - } - break; - - case VMCommands.VM_JBE: - if ((flags & (VMFlags.VM_FC | VMFlags.VM_FZ)) != 0) - { - setIP(GetValue(false, Mem, op1)); - continue; - } - break; - - case VMCommands.VM_JA: - if ((flags & (VMFlags.VM_FC | VMFlags.VM_FZ)) == 0) - { - setIP(GetValue(false, Mem, op1)); - continue; - } - break; - - case VMCommands.VM_JAE: - if ((flags & VMFlags.VM_FC) == 0) - { - setIP(GetValue(false, Mem, op1)); - continue; - } - break; - - case VMCommands.VM_PUSH: - R[7] -= 4; - SetValue(false, Mem, R[7] & VM_MEMMASK, GetValue(false, Mem, op1)); - break; - - case VMCommands.VM_POP: - SetValue(false, Mem, op1, GetValue(false, Mem, R[7] & VM_MEMMASK)); - R[7] += 4; - break; - - case VMCommands.VM_CALL: - R[7] -= 4; - SetValue(false, Mem, R[7] & VM_MEMMASK, IP + 1); - setIP(GetValue(false, Mem, op1)); - continue; - - case VMCommands.VM_NOT: - SetValue(cmd.IsByteMode, Mem, op1, ~GetValue(cmd.IsByteMode, Mem, op1)); - break; - - case VMCommands.VM_SHL: - - { - int value1 = GetValue(cmd.IsByteMode, Mem, op1); - int value2 = GetValue(cmd.IsByteMode, Mem, op2); - int result = value1 << value2; - flags = (VMFlags)( - (result == 0 ? (int)VMFlags.VM_FZ : (result & (int)VMFlags.VM_FS)) - | ( - ((value1 << (value2 - 1)) & unchecked((int)0x80000000)) != 0 - ? (int)VMFlags.VM_FC - : 0 - ) - ); - SetValue(cmd.IsByteMode, Mem, op1, result); - } - break; - - case VMCommands.VM_SHR: - - { - int value1 = GetValue(cmd.IsByteMode, Mem, op1); - int value2 = GetValue(cmd.IsByteMode, Mem, op2); - int result = Utility.URShift(value1, value2); - flags = (VMFlags)( - (result == 0 ? (int)VMFlags.VM_FZ : (result & (int)VMFlags.VM_FS)) - | ((Utility.URShift(value1, (value2 - 1))) & (int)VMFlags.VM_FC) - ); - SetValue(cmd.IsByteMode, Mem, op1, result); - } - break; - - case VMCommands.VM_SAR: - - { - int value1 = GetValue(cmd.IsByteMode, Mem, op1); - int value2 = GetValue(cmd.IsByteMode, Mem, op2); - int result = value1 >> value2; - flags = (VMFlags)( - (result == 0 ? (int)VMFlags.VM_FZ : (result & (int)VMFlags.VM_FS)) - | ((value1 >> (value2 - 1)) & (int)VMFlags.VM_FC) - ); - SetValue(cmd.IsByteMode, Mem, op1, result); - } - break; - - case VMCommands.VM_NEG: - - { - int result = -GetValue(cmd.IsByteMode, Mem, op1); - flags = (VMFlags)( - result == 0 - ? (int)VMFlags.VM_FZ - : (int)VMFlags.VM_FC | (result & (int)VMFlags.VM_FS) - ); - SetValue(cmd.IsByteMode, Mem, op1, result); - } - break; - - case VMCommands.VM_NEGB: - SetValue(true, Mem, op1, -GetValue(true, Mem, op1)); - break; - - case VMCommands.VM_NEGD: - SetValue(false, Mem, op1, -GetValue(false, Mem, op1)); - break; - - case VMCommands.VM_PUSHA: - - { - for (int i = 0, SP = R[7] - 4; i < regCount; i++, SP -= 4) - { - SetValue(false, Mem, SP & VM_MEMMASK, R[i]); - } - R[7] -= regCount * 4; - } - break; - - case VMCommands.VM_POPA: - - { - for (int i = 0, SP = R[7]; i < regCount; i++, SP += 4) - { - R[7 - i] = GetValue(false, Mem, SP & VM_MEMMASK); - } - } - break; - - case VMCommands.VM_PUSHF: - R[7] -= 4; - SetValue(false, Mem, R[7] & VM_MEMMASK, (int)flags); - break; - - case VMCommands.VM_POPF: - flags = (VMFlags)GetValue(false, Mem, R[7] & VM_MEMMASK); - R[7] += 4; - break; - - case VMCommands.VM_MOVZX: - SetValue(false, Mem, op1, GetValue(true, Mem, op2)); - break; - - case VMCommands.VM_MOVSX: - SetValue(false, Mem, op1, (byte)GetValue(true, Mem, op2)); - break; - - case VMCommands.VM_XCHG: - - { - int value1 = GetValue(cmd.IsByteMode, Mem, op1); - SetValue(cmd.IsByteMode, Mem, op1, GetValue(cmd.IsByteMode, Mem, op2)); - SetValue(cmd.IsByteMode, Mem, op2, value1); - } - break; - - case VMCommands.VM_MUL: - - { - int result = (int)( - ( - GetValue(cmd.IsByteMode, Mem, op1) - & 0xFFffFFff * GetValue(cmd.IsByteMode, Mem, op2) - & unchecked((int)0xFFffFFff) - ) & unchecked((int)0xFFffFFff) - ); - SetValue(cmd.IsByteMode, Mem, op1, result); - } - break; - - case VMCommands.VM_DIV: - - { - int divider = GetValue(cmd.IsByteMode, Mem, op2); - if (divider != 0) - { - int result = GetValue(cmd.IsByteMode, Mem, op1) / divider; - SetValue(cmd.IsByteMode, Mem, op1, result); - } - } - break; - - case VMCommands.VM_ADC: - - { - int value1 = GetValue(cmd.IsByteMode, Mem, op1); - int FC = (int)(flags & VMFlags.VM_FC); - int result = (int)( - value1 - & 0xFFffFFff + GetValue(cmd.IsByteMode, Mem, op2) - & 0xFFffFFff + FC - & unchecked((int)0xFFffFFff) - ); - if (cmd.IsByteMode) - { - result &= 0xff; - } - - flags = (VMFlags)( - (result < value1 || result == value1 && FC != 0) - ? 1 - : 0 - | ( - result == 0 - ? (int)VMFlags.VM_FZ - : (result & (int)VMFlags.VM_FS) - ) - ); - SetValue(cmd.IsByteMode, Mem, op1, result); - } - break; - - case VMCommands.VM_SBB: - - { - int value1 = GetValue(cmd.IsByteMode, Mem, op1); - int FC = (int)(flags & VMFlags.VM_FC); - int result = (int)( - value1 - & 0xFFffFFff - GetValue(cmd.IsByteMode, Mem, op2) - & 0xFFffFFff - FC - & unchecked((int)0xFFffFFff) - ); - if (cmd.IsByteMode) - { - result &= 0xff; - } - flags = (VMFlags)( - (result > value1 || result == value1 && FC != 0) - ? 1 - : 0 - | ( - result == 0 - ? (int)VMFlags.VM_FZ - : (result & (int)VMFlags.VM_FS) - ) - ); - SetValue(cmd.IsByteMode, Mem, op1, result); - } - break; - - case VMCommands.VM_RET: - if (R[7] >= VM_MEMSIZE) - { - return (true); - } - setIP(GetValue(false, Mem, R[7] & VM_MEMMASK)); - R[7] += 4; - continue; - - case VMCommands.VM_STANDARD: - ExecuteStandardFilter((VMStandardFilters)(cmd.Op1.Data)); - break; - - case VMCommands.VM_PRINT: - break; - } - IP++; - --maxOpCount; + Mem[VM_GLOBALMEMADDR + i] = prg.GlobalData[i]; } } - - public void prepare(byte[] code, int codeSize, VMPreparedProgram prg) + var staticSize = + Math.Min(prg.StaticData.Count, VM_GLOBALMEMSIZE - globalSize) & 0xffFFffFF; + if (staticSize != 0) { - InitBitInput(); - int cpLength = Math.Min(MAX_SIZE, codeSize); - - // memcpy(inBuf,Code,Min(CodeSize,BitInput::MAX_SIZE)); - - Buffer.BlockCopy(code, 0, InBuf, 0, cpLength); - byte xorSum = 0; - for (int i = 1; i < codeSize; i++) + for (var i = 0; i < staticSize; i++) + // memcpy(Mem+VM_GLOBALMEMADDR+GlobalSize,&Prg->StaticData[0],StaticSize); { - xorSum ^= code[i]; + Mem[VM_GLOBALMEMADDR + (int)globalSize + i] = prg.StaticData[i]; } + } + R[7] = VM_MEMSIZE; + flags = 0; - AddBits(8); + //UPGRADE_NOTE: There is an untranslated Statement. Please refer to original code. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1153'" + var preparedCode = + prg.AltCommands.Count != 0 ? prg.AltCommands : prg.Commands; - prg.CommandCount = 0; - if (xorSum == code[0]) + if (!ExecuteCode(preparedCode, prg.CommandCount)) + { + preparedCode[0].OpCode = VMCommands.VM_RET; + } + var newBlockPos = GetValue(false, Mem, VM_GLOBALMEMADDR + 0x20) & VM_MEMMASK; + var newBlockSize = GetValue(false, Mem, VM_GLOBALMEMADDR + 0x1c) & VM_MEMMASK; + if ((newBlockPos + newBlockSize) >= VM_MEMSIZE) + { + newBlockPos = 0; + newBlockSize = 0; + } + + prg.FilteredDataOffset = newBlockPos; + prg.FilteredDataSize = newBlockSize; + + prg.GlobalData.Clear(); + + var dataSize = Math.Min( + GetValue(false, Mem, VM_GLOBALMEMADDR + 0x30), + VM_GLOBALMEMSIZE - VM_FIXEDGLOBALSIZE + ); + if (dataSize != 0) + { + //prg.GlobalData.Clear(); + // ->GlobalData.Add(dataSize+VM_FIXEDGLOBALSIZE); + prg.GlobalData.Capacity = dataSize + VM_FIXEDGLOBALSIZE; + for (var i = 0; i < dataSize + VM_FIXEDGLOBALSIZE; i++) + // memcpy(&Prg->GlobalData[0],&Mem[VM_GLOBALMEMADDR],DataSize+VM_FIXEDGLOBALSIZE); { - VMStandardFilters filterType = IsStandardFilter(code, codeSize); - if (filterType != VMStandardFilters.VMSF_NONE) + prg.GlobalData.Add(Mem[VM_GLOBALMEMADDR + i]); + } + } + } + + private bool setIP(int ip) + { + if ((ip) >= codeSize) + { + return (true); + } + + if (--maxOpCount <= 0) + { + return (false); + } + + IP = ip; + return true; + } + + private bool ExecuteCode(List preparedCode, int cmdCount) + { + maxOpCount = 25000000; + codeSize = cmdCount; + IP = 0; + + while (true) + { + var cmd = preparedCode[IP]; + var op1 = GetOperand(cmd.Op1); + var op2 = GetOperand(cmd.Op2); + switch (cmd.OpCode) + { + case VMCommands.VM_MOV: + SetValue(cmd.IsByteMode, Mem, op1, GetValue(cmd.IsByteMode, Mem, op2)); + + // SET_VALUE(Cmd->ByteMode,Op1,GET_VALUE(Cmd->ByteMode,Op2)); + break; + + case VMCommands.VM_MOVB: + SetValue(true, Mem, op1, GetValue(true, Mem, op2)); + break; + + case VMCommands.VM_MOVD: + SetValue(false, Mem, op1, GetValue(false, Mem, op2)); + break; + + case VMCommands.VM_CMP: + { - VMPreparedCommand curCmd = new VMPreparedCommand(); - curCmd.OpCode = VMCommands.VM_STANDARD; - curCmd.Op1.Data = (int)filterType; - curCmd.Op1.Type = VMOpType.VM_OPNONE; - curCmd.Op2.Type = VMOpType.VM_OPNONE; - codeSize = 0; - prg.Commands.Add(curCmd); - prg.CommandCount = prg.CommandCount + 1; + var value1 = (VMFlags)GetValue(cmd.IsByteMode, Mem, op1); + var result = value1 - GetValue(cmd.IsByteMode, Mem, op2); - // TODO - // curCmd->Op1.Data=FilterType; - // >>>>>> CurCmd->Op1.Addr=&CurCmd->Op1.Data; <<<<<<<<<< not set - // do i need to ? - // >>>>>> CurCmd->Op2.Addr=&CurCmd->Op2.Data; <<<<<<<<<< " - // CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE; - // CodeSize=0; - } - int dataFlag = GetBits(); - AddBits(1); - - // Read static data contained in DB operators. This data cannot be - // changed, - // it is a part of VM code, not a filter parameter. - - if ((dataFlag & 0x8000) != 0) - { - long dataSize = ReadData(this) & 0xffFFffFFL + 1L; - for (int i = 0; inAddr < codeSize && i < dataSize; i++) + if (result == 0) { - prg.StaticData.Add((byte)(GetBits() >> 8)); - AddBits(8); + flags = VMFlags.VM_FZ; + } + else + { + flags = (VMFlags)( + (result > value1) ? 1 : 0 | (int)(result & VMFlags.VM_FS) + ); } } + break; + + case VMCommands.VM_CMPB: - while (inAddr < codeSize) { - VMPreparedCommand curCmd = new VMPreparedCommand(); - int data = GetBits(); - if ((data & 0x8000) == 0) + var value1 = (VMFlags)GetValue(true, Mem, op1); + var result = value1 - GetValue(true, Mem, op2); + if (result == 0) { - curCmd.OpCode = (VMCommands)((data >> 12)); + flags = VMFlags.VM_FZ; + } + else + { + flags = (VMFlags)( + (result > value1) ? 1 : 0 | (int)(result & VMFlags.VM_FS) + ); + } + } + break; + + case VMCommands.VM_CMPD: + + { + var value1 = (VMFlags)GetValue(false, Mem, op1); + var result = value1 - GetValue(false, Mem, op2); + if (result == 0) + { + flags = VMFlags.VM_FZ; + } + else + { + flags = (VMFlags)( + (result > value1) ? 1 : 0 | (int)(result & VMFlags.VM_FS) + ); + } + } + break; + + case VMCommands.VM_ADD: + + { + var value1 = GetValue(cmd.IsByteMode, Mem, op1); + var result = (int)( + ((value1 + (long)GetValue(cmd.IsByteMode, Mem, op2))) + & unchecked((int)0xffffffff) + ); + if (cmd.IsByteMode) + { + result &= 0xff; + flags = (VMFlags)( + (result < value1) + ? 1 + : 0 + | ( + result == 0 + ? (int)VMFlags.VM_FZ + : ( + ((result & 0x80) != 0) + ? (int)VMFlags.VM_FS + : 0 + ) + ) + ); + + // Flags=(Result value1) ? 1 : 0 | (result & (int)VMFlags.VM_FS)) + ); + SetValue(cmd.IsByteMode, Mem, op1, result); // (Cmd->ByteMode,Op1,Result); + } + break; + + case VMCommands.VM_SUBB: + SetValue( + true, + Mem, + op1, + (int)( + GetValue(true, Mem, op1) + & (0xFFffFFff - GetValue(true, Mem, op2)) + & unchecked((int)0xFFffFFff) + ) + ); + break; + + case VMCommands.VM_SUBD: + SetValue( + false, + Mem, + op1, + (int)( + GetValue(false, Mem, op1) + & (0xFFffFFff - GetValue(false, Mem, op2)) + & unchecked((int)0xFFffFFff) + ) + ); + break; + + case VMCommands.VM_JZ: + if ((flags & VMFlags.VM_FZ) != 0) + { + setIP(GetValue(false, Mem, op1)); + continue; + } + break; + + case VMCommands.VM_JNZ: + if ((flags & VMFlags.VM_FZ) == 0) + { + setIP(GetValue(false, Mem, op1)); + continue; + } + break; + + case VMCommands.VM_INC: + + { + var result = (int)( + GetValue(cmd.IsByteMode, Mem, op1) & (0xFFffFFffL + 1L) + ); + if (cmd.IsByteMode) + { + result &= 0xff; + } + + SetValue(cmd.IsByteMode, Mem, op1, result); + flags = (VMFlags)( + result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS + ); + } + break; + + case VMCommands.VM_INCB: + SetValue( + true, + Mem, + op1, + (int)(GetValue(true, Mem, op1) & (0xFFffFFffL + 1L)) + ); + break; + + case VMCommands.VM_INCD: + SetValue( + false, + Mem, + op1, + (int)(GetValue(false, Mem, op1) & (0xFFffFFffL + 1L)) + ); + break; + + case VMCommands.VM_DEC: + + { + var result = (int)(GetValue(cmd.IsByteMode, Mem, op1) & (0xFFffFFff - 1)); + SetValue(cmd.IsByteMode, Mem, op1, result); + flags = (VMFlags)( + result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS + ); + } + break; + + case VMCommands.VM_DECB: + SetValue(true, Mem, op1, (int)(GetValue(true, Mem, op1) & (0xFFffFFff - 1))); + break; + + case VMCommands.VM_DECD: + SetValue( + false, + Mem, + op1, + (int)(GetValue(false, Mem, op1) & (0xFFffFFff - 1)) + ); + break; + + case VMCommands.VM_JMP: + setIP(GetValue(false, Mem, op1)); + continue; + + case VMCommands.VM_XOR: + + { + var result = + GetValue(cmd.IsByteMode, Mem, op1) + ^ GetValue(cmd.IsByteMode, Mem, op2); + flags = (VMFlags)( + result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS + ); + SetValue(cmd.IsByteMode, Mem, op1, result); + } + break; + + case VMCommands.VM_AND: + + { + var result = + GetValue(cmd.IsByteMode, Mem, op1) + & GetValue(cmd.IsByteMode, Mem, op2); + flags = (VMFlags)( + result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS + ); + SetValue(cmd.IsByteMode, Mem, op1, result); + } + break; + + case VMCommands.VM_OR: + + { + var result = + GetValue(cmd.IsByteMode, Mem, op1) + | GetValue(cmd.IsByteMode, Mem, op2); + flags = (VMFlags)( + result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS + ); + SetValue(cmd.IsByteMode, Mem, op1, result); + } + break; + + case VMCommands.VM_TEST: + + { + var result = + GetValue(cmd.IsByteMode, Mem, op1) + & GetValue(cmd.IsByteMode, Mem, op2); + flags = (VMFlags)( + result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS + ); + } + break; + + case VMCommands.VM_JS: + if ((flags & VMFlags.VM_FS) != 0) + { + setIP(GetValue(false, Mem, op1)); + continue; + } + break; + + case VMCommands.VM_JNS: + if ((flags & VMFlags.VM_FS) == 0) + { + setIP(GetValue(false, Mem, op1)); + continue; + } + break; + + case VMCommands.VM_JB: + if ((flags & VMFlags.VM_FC) != 0) + { + setIP(GetValue(false, Mem, op1)); + continue; + } + break; + + case VMCommands.VM_JBE: + if ((flags & (VMFlags.VM_FC | VMFlags.VM_FZ)) != 0) + { + setIP(GetValue(false, Mem, op1)); + continue; + } + break; + + case VMCommands.VM_JA: + if ((flags & (VMFlags.VM_FC | VMFlags.VM_FZ)) == 0) + { + setIP(GetValue(false, Mem, op1)); + continue; + } + break; + + case VMCommands.VM_JAE: + if ((flags & VMFlags.VM_FC) == 0) + { + setIP(GetValue(false, Mem, op1)); + continue; + } + break; + + case VMCommands.VM_PUSH: + R[7] -= 4; + SetValue(false, Mem, R[7] & VM_MEMMASK, GetValue(false, Mem, op1)); + break; + + case VMCommands.VM_POP: + SetValue(false, Mem, op1, GetValue(false, Mem, R[7] & VM_MEMMASK)); + R[7] += 4; + break; + + case VMCommands.VM_CALL: + R[7] -= 4; + SetValue(false, Mem, R[7] & VM_MEMMASK, IP + 1); + setIP(GetValue(false, Mem, op1)); + continue; + + case VMCommands.VM_NOT: + SetValue(cmd.IsByteMode, Mem, op1, ~GetValue(cmd.IsByteMode, Mem, op1)); + break; + + case VMCommands.VM_SHL: + + { + var value1 = GetValue(cmd.IsByteMode, Mem, op1); + var value2 = GetValue(cmd.IsByteMode, Mem, op2); + var result = value1 << value2; + flags = (VMFlags)( + (result == 0 ? (int)VMFlags.VM_FZ : (result & (int)VMFlags.VM_FS)) + | ( + ((value1 << (value2 - 1)) & unchecked((int)0x80000000)) != 0 + ? (int)VMFlags.VM_FC + : 0 + ) + ); + SetValue(cmd.IsByteMode, Mem, op1, result); + } + break; + + case VMCommands.VM_SHR: + + { + var value1 = GetValue(cmd.IsByteMode, Mem, op1); + var value2 = GetValue(cmd.IsByteMode, Mem, op2); + var result = Utility.URShift(value1, value2); + flags = (VMFlags)( + (result == 0 ? (int)VMFlags.VM_FZ : (result & (int)VMFlags.VM_FS)) + | ((Utility.URShift(value1, (value2 - 1))) & (int)VMFlags.VM_FC) + ); + SetValue(cmd.IsByteMode, Mem, op1, result); + } + break; + + case VMCommands.VM_SAR: + + { + var value1 = GetValue(cmd.IsByteMode, Mem, op1); + var value2 = GetValue(cmd.IsByteMode, Mem, op2); + var result = value1 >> value2; + flags = (VMFlags)( + (result == 0 ? (int)VMFlags.VM_FZ : (result & (int)VMFlags.VM_FS)) + | ((value1 >> (value2 - 1)) & (int)VMFlags.VM_FC) + ); + SetValue(cmd.IsByteMode, Mem, op1, result); + } + break; + + case VMCommands.VM_NEG: + + { + var result = -GetValue(cmd.IsByteMode, Mem, op1); + flags = (VMFlags)( + result == 0 + ? (int)VMFlags.VM_FZ + : (int)VMFlags.VM_FC | (result & (int)VMFlags.VM_FS) + ); + SetValue(cmd.IsByteMode, Mem, op1, result); + } + break; + + case VMCommands.VM_NEGB: + SetValue(true, Mem, op1, -GetValue(true, Mem, op1)); + break; + + case VMCommands.VM_NEGD: + SetValue(false, Mem, op1, -GetValue(false, Mem, op1)); + break; + + case VMCommands.VM_PUSHA: + + { + for (int i = 0, SP = R[7] - 4; i < regCount; i++, SP -= 4) + { + SetValue(false, Mem, SP & VM_MEMMASK, R[i]); + } + R[7] -= regCount * 4; + } + break; + + case VMCommands.VM_POPA: + + { + for (int i = 0, SP = R[7]; i < regCount; i++, SP += 4) + { + R[7 - i] = GetValue(false, Mem, SP & VM_MEMMASK); + } + } + break; + + case VMCommands.VM_PUSHF: + R[7] -= 4; + SetValue(false, Mem, R[7] & VM_MEMMASK, (int)flags); + break; + + case VMCommands.VM_POPF: + flags = (VMFlags)GetValue(false, Mem, R[7] & VM_MEMMASK); + R[7] += 4; + break; + + case VMCommands.VM_MOVZX: + SetValue(false, Mem, op1, GetValue(true, Mem, op2)); + break; + + case VMCommands.VM_MOVSX: + SetValue(false, Mem, op1, (byte)GetValue(true, Mem, op2)); + break; + + case VMCommands.VM_XCHG: + + { + var value1 = GetValue(cmd.IsByteMode, Mem, op1); + SetValue(cmd.IsByteMode, Mem, op1, GetValue(cmd.IsByteMode, Mem, op2)); + SetValue(cmd.IsByteMode, Mem, op2, value1); + } + break; + + case VMCommands.VM_MUL: + + { + var result = (int)( + ( + GetValue(cmd.IsByteMode, Mem, op1) + & (0xFFffFFff * GetValue(cmd.IsByteMode, Mem, op2)) + & unchecked((int)0xFFffFFff) + ) & unchecked((int)0xFFffFFff) + ); + SetValue(cmd.IsByteMode, Mem, op1, result); + } + break; + + case VMCommands.VM_DIV: + + { + var divider = GetValue(cmd.IsByteMode, Mem, op2); + if (divider != 0) + { + var result = GetValue(cmd.IsByteMode, Mem, op1) / divider; + SetValue(cmd.IsByteMode, Mem, op1, result); + } + } + break; + + case VMCommands.VM_ADC: + + { + var value1 = GetValue(cmd.IsByteMode, Mem, op1); + var FC = (int)(flags & VMFlags.VM_FC); + var result = (int)( + value1 + & (0xFFffFFff + GetValue(cmd.IsByteMode, Mem, op2)) + & (0xFFffFFff + FC) + & unchecked((int)0xFFffFFff) + ); + if (cmd.IsByteMode) + { + result &= 0xff; + } + + flags = (VMFlags)( + (result < value1 || result == value1 && FC != 0) + ? 1 + : 0 + | ( + result == 0 + ? (int)VMFlags.VM_FZ + : (result & (int)VMFlags.VM_FS) + ) + ); + SetValue(cmd.IsByteMode, Mem, op1, result); + } + break; + + case VMCommands.VM_SBB: + + { + var value1 = GetValue(cmd.IsByteMode, Mem, op1); + var FC = (int)(flags & VMFlags.VM_FC); + var result = (int)( + value1 + & (0xFFffFFff - GetValue(cmd.IsByteMode, Mem, op2)) + & (0xFFffFFff - FC) + & unchecked((int)0xFFffFFff) + ); + if (cmd.IsByteMode) + { + result &= 0xff; + } + flags = (VMFlags)( + (result > value1 || result == value1 && FC != 0) + ? 1 + : 0 + | ( + result == 0 + ? (int)VMFlags.VM_FZ + : (result & (int)VMFlags.VM_FS) + ) + ); + SetValue(cmd.IsByteMode, Mem, op1, result); + } + break; + + case VMCommands.VM_RET: + if (R[7] >= VM_MEMSIZE) + { + return (true); + } + setIP(GetValue(false, Mem, R[7] & VM_MEMMASK)); + R[7] += 4; + continue; + + case VMCommands.VM_STANDARD: + ExecuteStandardFilter((VMStandardFilters)(cmd.Op1.Data)); + break; + + case VMCommands.VM_PRINT: + break; + } + IP++; + --maxOpCount; + } + } + + public void prepare(byte[] code, int codeSize, VMPreparedProgram prg) + { + InitBitInput(); + var cpLength = Math.Min(MAX_SIZE, codeSize); + + // memcpy(inBuf,Code,Min(CodeSize,BitInput::MAX_SIZE)); + + Buffer.BlockCopy(code, 0, InBuf, 0, cpLength); + byte xorSum = 0; + for (var i = 1; i < codeSize; i++) + { + xorSum ^= code[i]; + } + + AddBits(8); + + prg.CommandCount = 0; + if (xorSum == code[0]) + { + var filterType = IsStandardFilter(code, codeSize); + if (filterType != VMStandardFilters.VMSF_NONE) + { + var curCmd = new VMPreparedCommand(); + curCmd.OpCode = VMCommands.VM_STANDARD; + curCmd.Op1.Data = (int)filterType; + curCmd.Op1.Type = VMOpType.VM_OPNONE; + curCmd.Op2.Type = VMOpType.VM_OPNONE; + codeSize = 0; + prg.Commands.Add(curCmd); + prg.CommandCount++; + + // TODO + // curCmd->Op1.Data=FilterType; + // >>>>>> CurCmd->Op1.Addr=&CurCmd->Op1.Data; <<<<<<<<<< not set + // do i need to ? + // >>>>>> CurCmd->Op2.Addr=&CurCmd->Op2.Data; <<<<<<<<<< " + // CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE; + // CodeSize=0; + } + var dataFlag = GetBits(); + AddBits(1); + + // Read static data contained in DB operators. This data cannot be + // changed, + // it is a part of VM code, not a filter parameter. + + if ((dataFlag & 0x8000) != 0) + { + var dataSize = ReadData(this) & (0xffFFffFFL + 1L); + for (var i = 0; inAddr < codeSize && i < dataSize; i++) + { + prg.StaticData.Add((byte)(GetBits() >> 8)); + AddBits(8); + } + } + + while (inAddr < codeSize) + { + var curCmd = new VMPreparedCommand(); + var data = GetBits(); + if ((data & 0x8000) == 0) + { + curCmd.OpCode = (VMCommands)((data >> 12)); + AddBits(4); + } + else + { + curCmd.OpCode = (VMCommands)((data >> 10) - 24); + AddBits(6); + } + if ( + (VMCmdFlags.VM_CmdFlags[(int)curCmd.OpCode] & VMCmdFlags.VMCF_BYTEMODE) != 0 + ) + { + curCmd.IsByteMode = (GetBits() >> 15) == 1; + AddBits(1); + } + else + { + curCmd.IsByteMode = false; + } + curCmd.Op1.Type = VMOpType.VM_OPNONE; + curCmd.Op2.Type = VMOpType.VM_OPNONE; + + var opNum = ( + VMCmdFlags.VM_CmdFlags[(int)curCmd.OpCode] & VMCmdFlags.VMCF_OPMASK + ); + + // TODO >>> CurCmd->Op1.Addr=CurCmd->Op2.Addr=NULL; << 0) + { + decodeArg(curCmd.Op1, curCmd.IsByteMode); + if (opNum == 2) + { + decodeArg(curCmd.Op2, curCmd.IsByteMode); + } + else + { + if ( + curCmd.Op1.Type == VMOpType.VM_OPINT + && ( + VMCmdFlags.VM_CmdFlags[(int)curCmd.OpCode] + & (VMCmdFlags.VMCF_JUMP | VMCmdFlags.VMCF_PROC) + ) != 0 + ) + { + var distance = curCmd.Op1.Data; + if (distance >= 256) + { + distance -= 256; + } + else + { + if (distance >= 136) + { + distance -= 264; + } + else + { + if (distance >= 16) + { + distance -= 8; + } + else + { + if (distance >= 8) + { + distance -= 16; + } + } + } + distance += prg.CommandCount; + } + curCmd.Op1.Data = distance; + } + } + } + prg.CommandCount++; + prg.Commands.Add(curCmd); + } + } + var curCmd2 = new VMPreparedCommand(); + curCmd2.OpCode = VMCommands.VM_RET; + + // TODO CurCmd->Op1.Addr=&CurCmd->Op1.Data; + // CurCmd->Op2.Addr=&CurCmd->Op2.Data; + curCmd2.Op1.Type = VMOpType.VM_OPNONE; + curCmd2.Op2.Type = VMOpType.VM_OPNONE; + + // for (int i=0;iCmd[I]; + // if (Cmd->Op1.Addr==NULL) + // Cmd->Op1.Addr=&Cmd->Op1.Data; + // if (Cmd->Op2.Addr==NULL) + // Cmd->Op2.Addr=&Cmd->Op2.Data; + // } + + prg.Commands.Add(curCmd2); + prg.CommandCount++; + + // #ifdef VM_OPTIMIZE + if (codeSize != 0) + { + optimize(prg); + } + } + + private void decodeArg(VMPreparedOperand op, bool byteMode) + { + var data = GetBits(); + if ((data & 0x8000) != 0) + { + op.Type = VMOpType.VM_OPREG; + op.Data = (data >> 12) & 7; + op.Offset = op.Data; + AddBits(4); + } + else + { + if ((data & 0xc000) == 0) + { + op.Type = VMOpType.VM_OPINT; + if (byteMode) + { + op.Data = (data >> 6) & 0xff; + AddBits(10); + } + else + { + AddBits(2); + op.Data = ReadData(this); + } + } + else + { + op.Type = VMOpType.VM_OPREGMEM; + if ((data & 0x2000) == 0) + { + op.Data = (data >> 10) & 7; + op.Offset = op.Data; + op.Base = 0; + AddBits(6); + } + else + { + if ((data & 0x1000) == 0) + { + op.Data = (data >> 9) & 7; + op.Offset = op.Data; + AddBits(7); + } + else + { + op.Data = 0; AddBits(4); } - else - { - curCmd.OpCode = (VMCommands)((data >> 10) - 24); - AddBits(6); - } - if ( - (VMCmdFlags.VM_CmdFlags[(int)curCmd.OpCode] & VMCmdFlags.VMCF_BYTEMODE) != 0 - ) - { - curCmd.IsByteMode = (GetBits() >> 15) == 1 ? true : false; - AddBits(1); - } - else - { - curCmd.IsByteMode = false; - } - curCmd.Op1.Type = VMOpType.VM_OPNONE; - curCmd.Op2.Type = VMOpType.VM_OPNONE; - - int opNum = ( - VMCmdFlags.VM_CmdFlags[(int)curCmd.OpCode] & VMCmdFlags.VMCF_OPMASK - ); - - // TODO >>> CurCmd->Op1.Addr=CurCmd->Op2.Addr=NULL; << 0) - { - decodeArg(curCmd.Op1, curCmd.IsByteMode); - if (opNum == 2) - { - decodeArg(curCmd.Op2, curCmd.IsByteMode); - } - else - { - if ( - curCmd.Op1.Type == VMOpType.VM_OPINT - && ( - VMCmdFlags.VM_CmdFlags[(int)curCmd.OpCode] - & (VMCmdFlags.VMCF_JUMP | VMCmdFlags.VMCF_PROC) - ) != 0 - ) - { - int distance = curCmd.Op1.Data; - if (distance >= 256) - { - distance -= 256; - } - else - { - if (distance >= 136) - { - distance -= 264; - } - else - { - if (distance >= 16) - { - distance -= 8; - } - else - { - if (distance >= 8) - { - distance -= 16; - } - } - } - distance += prg.CommandCount; - } - curCmd.Op1.Data = distance; - } - } - } - prg.CommandCount = (prg.CommandCount + 1); - prg.Commands.Add(curCmd); - } - } - VMPreparedCommand curCmd2 = new VMPreparedCommand(); - curCmd2.OpCode = VMCommands.VM_RET; - - // TODO CurCmd->Op1.Addr=&CurCmd->Op1.Data; - // CurCmd->Op2.Addr=&CurCmd->Op2.Data; - curCmd2.Op1.Type = VMOpType.VM_OPNONE; - curCmd2.Op2.Type = VMOpType.VM_OPNONE; - - // for (int i=0;iCmd[I]; - // if (Cmd->Op1.Addr==NULL) - // Cmd->Op1.Addr=&Cmd->Op1.Data; - // if (Cmd->Op2.Addr==NULL) - // Cmd->Op2.Addr=&Cmd->Op2.Data; - // } - - prg.Commands.Add(curCmd2); - prg.CommandCount = prg.CommandCount + 1; - - // #ifdef VM_OPTIMIZE - if (codeSize != 0) - { - optimize(prg); - } - } - - private void decodeArg(VMPreparedOperand op, bool byteMode) - { - int data = GetBits(); - if ((data & 0x8000) != 0) - { - op.Type = VMOpType.VM_OPREG; - op.Data = (data >> 12) & 7; - op.Offset = op.Data; - AddBits(4); - } - else - { - if ((data & 0xc000) == 0) - { - op.Type = VMOpType.VM_OPINT; - if (byteMode) - { - op.Data = (data >> 6) & 0xff; - AddBits(10); - } - else - { - AddBits(2); - op.Data = ReadData(this); - } - } - else - { - op.Type = VMOpType.VM_OPREGMEM; - if ((data & 0x2000) == 0) - { - op.Data = (data >> 10) & 7; - op.Offset = op.Data; - op.Base = 0; - AddBits(6); - } - else - { - if ((data & 0x1000) == 0) - { - op.Data = (data >> 9) & 7; - op.Offset = op.Data; - AddBits(7); - } - else - { - op.Data = 0; - AddBits(4); - } - op.Base = ReadData(this); - } - } - } - } - - private void optimize(VMPreparedProgram prg) - { - //UPGRADE_NOTE: There is an untranslated Statement. Please refer to original code. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1153'" - - List commands = prg.Commands; - - //UPGRADE_ISSUE: The following fragment of code could not be parsed and was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1156'" - foreach (VMPreparedCommand cmd in commands) - { - switch (cmd.OpCode) - { - case VMCommands.VM_MOV: - cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_MOVB : VMCommands.VM_MOVD; - continue; - - case VMCommands.VM_CMP: - cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_CMPB : VMCommands.VM_CMPD; - continue; - } - if ((VMCmdFlags.VM_CmdFlags[(int)cmd.OpCode] & VMCmdFlags.VMCF_CHFLAGS) == 0) - { - continue; - } - bool flagsRequired = false; - - for (int i = commands.IndexOf(cmd) + 1; i < commands.Count; i++) - { - int flags = VMCmdFlags.VM_CmdFlags[(int)commands[i].OpCode]; - if ( - ( - flags - & ( - VMCmdFlags.VMCF_JUMP - | VMCmdFlags.VMCF_PROC - | VMCmdFlags.VMCF_USEFLAGS - ) - ) != 0 - ) - { - flagsRequired = true; - break; - } - if ((flags & VMCmdFlags.VMCF_CHFLAGS) != 0) - { - break; - } - } - if (flagsRequired) - { - continue; - } - switch (cmd.OpCode) - { - case VMCommands.VM_ADD: - cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_ADDB : VMCommands.VM_ADDD; - continue; - - case VMCommands.VM_SUB: - cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_SUBB : VMCommands.VM_SUBD; - continue; - - case VMCommands.VM_INC: - cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_INCB : VMCommands.VM_INCD; - continue; - - case VMCommands.VM_DEC: - cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_DECB : VMCommands.VM_DECD; - continue; - - case VMCommands.VM_NEG: - cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_NEGB : VMCommands.VM_NEGD; - continue; - } - } - } - - internal static int ReadData(BitInput rarVM) - { - int data = rarVM.GetBits(); - switch (data & 0xc000) - { - case 0: - rarVM.AddBits(6); - return ((data >> 10) & 0xf); - - case 0x4000: - if ((data & 0x3c00) == 0) - { - data = unchecked((int)0xffffff00) | ((data >> 2) & 0xff); - rarVM.AddBits(14); - } - else - { - data = (data >> 6) & 0xff; - rarVM.AddBits(10); - } - return (data); - - case 0x8000: - rarVM.AddBits(2); - data = rarVM.GetBits(); - rarVM.AddBits(16); - return (data); - - default: - rarVM.AddBits(2); - data = (rarVM.GetBits() << 16); - rarVM.AddBits(16); - data |= rarVM.GetBits(); - rarVM.AddBits(16); - return (data); - } - } - - private VMStandardFilters IsStandardFilter(byte[] code, int codeSize) - { - VMStandardFilterSignature[] stdList = - { - new VMStandardFilterSignature(53, 0xad576887, VMStandardFilters.VMSF_E8), - new VMStandardFilterSignature(57, 0x3cd7e57e, VMStandardFilters.VMSF_E8E9), - new VMStandardFilterSignature(120, 0x3769893f, VMStandardFilters.VMSF_ITANIUM), - new VMStandardFilterSignature(29, 0x0e06077d, VMStandardFilters.VMSF_DELTA), - new VMStandardFilterSignature(149, 0x1c2c5dc8, VMStandardFilters.VMSF_RGB), - new VMStandardFilterSignature(216, 0xbc85e701, VMStandardFilters.VMSF_AUDIO), - new VMStandardFilterSignature(40, 0x46b9c560, VMStandardFilters.VMSF_UPCASE) - }; - uint CodeCRC = RarCRC.CheckCrc(0xffffffff, code, 0, code.Length) ^ 0xffffffff; - for (int i = 0; i < stdList.Length; i++) - { - if (stdList[i].CRC == CodeCRC && stdList[i].Length == code.Length) - { - return (stdList[i].Type); - } - } - return (VMStandardFilters.VMSF_NONE); - } - - private void ExecuteStandardFilter(VMStandardFilters filterType) - { - switch (filterType) - { - case VMStandardFilters.VMSF_E8: - case VMStandardFilters.VMSF_E8E9: - - { - int dataSize = R[4]; - long fileOffset = R[6] & unchecked((int)0xFFffFFff); - - if (dataSize >= VM_GLOBALMEMADDR) - { - break; - } - int fileSize = 0x1000000; - byte cmpByte2 = (byte)( - (filterType == VMStandardFilters.VMSF_E8E9) ? 0xe9 : 0xe8 - ); - for (int curPos = 0; curPos < dataSize - 4; ) - { - byte curByte = Mem[curPos++]; - if (curByte == 0xe8 || curByte == cmpByte2) - { - // #ifdef PRESENT_INT32 - // sint32 Offset=CurPos+FileOffset; - // sint32 Addr=GET_VALUE(false,Data); - // if (Addr<0) - // { - // if (Addr+Offset>=0) - // SET_VALUE(false,Data,Addr+FileSize); - // } - // else - // if (Addr= VM_GLOBALMEMADDR) - { - break; - } - int curPos = 0; - - //UPGRADE_NOTE: Final was removed from the declaration of 'Masks '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - byte[] Masks = { 4, 4, 6, 6, 0, 0, 7, 7, 4, 4, 0, 0, 4, 4, 0, 0 }; - fileOffset = Utility.URShift(fileOffset, 4); - - while (curPos < dataSize - 21) - { - int Byte = (Mem[curPos] & 0x1f) - 0x10; - if (Byte >= 0) - { - byte cmdMask = Masks[Byte]; - if (cmdMask != 0) - { - for (int i = 0; i <= 2; i++) - { - if ((cmdMask & (1 << i)) != 0) - { - int startPos = i * 41 + 5; - int opType = filterItanium_GetBits( - curPos, - startPos + 37, - 4 - ); - if (opType == 5) - { - int offset = filterItanium_GetBits( - curPos, - startPos + 13, - 20 - ); - filterItanium_SetBits( - curPos, - (int)(offset - fileOffset) & 0xfffff, - startPos + 13, - 20 - ); - } - } - } - } - } - curPos += 16; - fileOffset++; - } - } - break; - - case VMStandardFilters.VMSF_DELTA: - - { - int dataSize = R[4] & unchecked((int)0xFFffFFff); - int channels = R[0] & unchecked((int)0xFFffFFff); - int srcPos = 0; - int border = (dataSize * 2) & unchecked((int)0xFFffFFff); - SetValue(false, Mem, VM_GLOBALMEMADDR + 0x20, dataSize); - if (dataSize >= VM_GLOBALMEMADDR / 2) - { - break; - } - - // bytes from same channels are grouped to continual data blocks, - // so we need to place them back to their interleaving positions - - for (int curChannel = 0; curChannel < channels; curChannel++) - { - byte PrevByte = 0; - for ( - int destPos = dataSize + curChannel; - destPos < border; - destPos += channels - ) - { - Mem[destPos] = (PrevByte = (byte)(PrevByte - Mem[srcPos++])); - } - } - } - break; - - case VMStandardFilters.VMSF_RGB: - - { - // byte *SrcData=Mem,*DestData=SrcData+DataSize; - int dataSize = R[4], - width = R[0] - 3, - posR = R[1]; - int channels = 3; - int srcPos = 0; - int destDataPos = dataSize; - SetValue(false, Mem, VM_GLOBALMEMADDR + 0x20, dataSize); - if (dataSize >= VM_GLOBALMEMADDR / 2 || posR < 0) - { - break; - } - for (int curChannel = 0; curChannel < channels; curChannel++) - { - long prevByte = 0; - - for (int i = curChannel; i < dataSize; i += channels) - { - long predicted; - int upperPos = i - width; - if (upperPos >= 3) - { - int upperDataPos = destDataPos + upperPos; - int upperByte = Mem[upperDataPos] & 0xff; - int upperLeftByte = Mem[upperDataPos - 3] & 0xff; - predicted = prevByte + upperByte - upperLeftByte; - int pa = Math.Abs((int)(predicted - prevByte)); - int pb = Math.Abs((int)(predicted - upperByte)); - int pc = Math.Abs((int)(predicted - upperLeftByte)); - if (pa <= pb && pa <= pc) - { - predicted = prevByte; - } - else - { - if (pb <= pc) - { - predicted = upperByte; - } - else - { - predicted = upperLeftByte; - } - } - } - else - { - predicted = prevByte; - } - - prevByte = (predicted - Mem[srcPos++] & 0xff) & 0xff; - Mem[destDataPos + i] = (byte)(prevByte & 0xff); - } - } - for (int i = posR, border = dataSize - 2; i < border; i += 3) - { - byte G = Mem[destDataPos + i + 1]; - Mem[destDataPos + i] = (byte)(Mem[destDataPos + i] + G); - Mem[destDataPos + i + 2] = (byte)(Mem[destDataPos + i + 2] + G); - } - } - break; - - case VMStandardFilters.VMSF_AUDIO: - - { - int dataSize = R[4], - channels = R[0]; - int srcPos = 0; - int destDataPos = dataSize; - - //byte *SrcData=Mem,*DestData=SrcData+DataSize; - SetValue(false, Mem, VM_GLOBALMEMADDR + 0x20, dataSize); - if (dataSize >= VM_GLOBALMEMADDR / 2) - { - break; - } - for (int curChannel = 0; curChannel < channels; curChannel++) - { - long prevByte = 0; - long prevDelta = 0; - long[] Dif = new long[7]; - int D1 = 0, - D2 = 0, - D3; - int K1 = 0, - K2 = 0, - K3 = 0; - - for ( - int i = curChannel, byteCount = 0; - i < dataSize; - i += channels, byteCount++ - ) - { - D3 = D2; - D2 = (int)(prevDelta - D1); - D1 = (int)prevDelta; - - long predicted = 8 * prevByte + K1 * D1 + K2 * D2 + K3 * D3; - predicted = Utility.URShift(predicted, 3) & 0xff; - - long curByte = Mem[srcPos++]; - - predicted -= curByte; - Mem[destDataPos + i] = (byte)predicted; - prevDelta = (byte)(predicted - prevByte); - - //fix java byte - if (prevDelta >= 128) - { - prevDelta = 0 - (256 - prevDelta); - } - prevByte = predicted; - - //fix java byte - if (curByte >= 128) - { - curByte = 0 - (256 - curByte); - } - int D = ((int)curByte) << 3; - - Dif[0] += Math.Abs(D); - Dif[1] += Math.Abs(D - D1); - Dif[2] += Math.Abs(D + D1); - Dif[3] += Math.Abs(D - D2); - Dif[4] += Math.Abs(D + D2); - Dif[5] += Math.Abs(D - D3); - Dif[6] += Math.Abs(D + D3); - - if ((byteCount & 0x1f) == 0) - { - long minDif = Dif[0], - numMinDif = 0; - Dif[0] = 0; - for (int j = 1; j < Dif.Length; j++) - { - if (Dif[j] < minDif) - { - minDif = Dif[j]; - numMinDif = j; - } - Dif[j] = 0; - } - switch ((int)numMinDif) - { - case 1: - if (K1 >= -16) - { - K1--; - } - break; - - case 2: - if (K1 < 16) - { - K1++; - } - break; - - case 3: - if (K2 >= -16) - { - K2--; - } - break; - - case 4: - if (K2 < 16) - { - K2++; - } - break; - - case 5: - if (K3 >= -16) - { - K3--; - } - break; - - case 6: - if (K3 < 16) - { - K3++; - } - break; - } - } - } - } - } - break; - - case VMStandardFilters.VMSF_UPCASE: - - { - int dataSize = R[4], - srcPos = 0, - destPos = dataSize; - if (dataSize >= VM_GLOBALMEMADDR / 2) - { - break; - } - while (srcPos < dataSize) - { - byte curByte = Mem[srcPos++]; - if (curByte == 2 && (curByte = Mem[srcPos++]) != 2) - { - curByte = (byte)(curByte - 32); - } - Mem[destPos++] = curByte; - } - SetValue(false, Mem, VM_GLOBALMEMADDR + 0x1c, destPos - dataSize); - SetValue(false, Mem, VM_GLOBALMEMADDR + 0x20, dataSize); - } - break; - } - } - - private void filterItanium_SetBits(int curPos, int bitField, int bitPos, int bitCount) - { - int inAddr = bitPos / 8; - int inBit = bitPos & 7; - int andMask = Utility.URShift(unchecked((int)0xffffffff), (32 - bitCount)); - andMask = ~(andMask << inBit); - - bitField <<= inBit; - - for (int i = 0; i < 4; i++) - { - Mem[curPos + inAddr + i] &= (byte)(andMask); - Mem[curPos + inAddr + i] |= (byte)(bitField); - andMask = (Utility.URShift(andMask, 8)) | unchecked((int)0xff000000); - bitField = Utility.URShift(bitField, 8); - } - } - - private int filterItanium_GetBits(int curPos, int bitPos, int bitCount) - { - int inAddr = bitPos / 8; - int inBit = bitPos & 7; - int bitField = Mem[curPos + inAddr++] & 0xff; - bitField |= (Mem[curPos + inAddr++] & 0xff) << 8; - bitField |= (Mem[curPos + inAddr++] & 0xff) << 16; - bitField |= (Mem[curPos + inAddr] & 0xff) << 24; - bitField = Utility.URShift(bitField, inBit); - return (bitField & (Utility.URShift(unchecked((int)0xffffffff), (32 - bitCount)))); - } - - public void setMemory(int pos, byte[] data, int offset, int dataSize) - { - if (pos < VM_MEMSIZE) - { - //&& data!=Mem+Pos) - //memmove(Mem+Pos,Data,Min(DataSize,VM_MEMSIZE-Pos)); - for (int i = 0; i < Math.Min(data.Length - offset, dataSize); i++) - { - if ((VM_MEMSIZE - pos) < i) - { - break; - } - Mem[pos + i] = data[offset + i]; + op.Base = ReadData(this); } } } } - // + private void optimize(VMPreparedProgram prg) + { + //UPGRADE_NOTE: There is an untranslated Statement. Please refer to original code. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1153'" + + var commands = prg.Commands; + + //UPGRADE_ISSUE: The following fragment of code could not be parsed and was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1156'" + foreach (var cmd in commands) + { + switch (cmd.OpCode) + { + case VMCommands.VM_MOV: + cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_MOVB : VMCommands.VM_MOVD; + continue; + + case VMCommands.VM_CMP: + cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_CMPB : VMCommands.VM_CMPD; + continue; + } + if ((VMCmdFlags.VM_CmdFlags[(int)cmd.OpCode] & VMCmdFlags.VMCF_CHFLAGS) == 0) + { + continue; + } + var flagsRequired = false; + + for (var i = commands.IndexOf(cmd) + 1; i < commands.Count; i++) + { + int flags = VMCmdFlags.VM_CmdFlags[(int)commands[i].OpCode]; + if ( + ( + flags + & ( + VMCmdFlags.VMCF_JUMP + | VMCmdFlags.VMCF_PROC + | VMCmdFlags.VMCF_USEFLAGS + ) + ) != 0 + ) + { + flagsRequired = true; + break; + } + if ((flags & VMCmdFlags.VMCF_CHFLAGS) != 0) + { + break; + } + } + if (flagsRequired) + { + continue; + } + switch (cmd.OpCode) + { + case VMCommands.VM_ADD: + cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_ADDB : VMCommands.VM_ADDD; + continue; + + case VMCommands.VM_SUB: + cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_SUBB : VMCommands.VM_SUBD; + continue; + + case VMCommands.VM_INC: + cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_INCB : VMCommands.VM_INCD; + continue; + + case VMCommands.VM_DEC: + cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_DECB : VMCommands.VM_DECD; + continue; + + case VMCommands.VM_NEG: + cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_NEGB : VMCommands.VM_NEGD; + continue; + } + } + } + + internal static int ReadData(BitInput rarVM) + { + var data = rarVM.GetBits(); + switch (data & 0xc000) + { + case 0: + rarVM.AddBits(6); + return ((data >> 10) & 0xf); + + case 0x4000: + if ((data & 0x3c00) == 0) + { + data = unchecked((int)0xffffff00) | ((data >> 2) & 0xff); + rarVM.AddBits(14); + } + else + { + data = (data >> 6) & 0xff; + rarVM.AddBits(10); + } + return (data); + + case 0x8000: + rarVM.AddBits(2); + data = rarVM.GetBits(); + rarVM.AddBits(16); + return (data); + + default: + rarVM.AddBits(2); + data = (rarVM.GetBits() << 16); + rarVM.AddBits(16); + data |= rarVM.GetBits(); + rarVM.AddBits(16); + return (data); + } + } + + private VMStandardFilters IsStandardFilter(byte[] code, int codeSize) + { + VMStandardFilterSignature[] stdList = + { + new VMStandardFilterSignature(53, 0xad576887, VMStandardFilters.VMSF_E8), + new VMStandardFilterSignature(57, 0x3cd7e57e, VMStandardFilters.VMSF_E8E9), + new VMStandardFilterSignature(120, 0x3769893f, VMStandardFilters.VMSF_ITANIUM), + new VMStandardFilterSignature(29, 0x0e06077d, VMStandardFilters.VMSF_DELTA), + new VMStandardFilterSignature(149, 0x1c2c5dc8, VMStandardFilters.VMSF_RGB), + new VMStandardFilterSignature(216, 0xbc85e701, VMStandardFilters.VMSF_AUDIO), + new VMStandardFilterSignature(40, 0x46b9c560, VMStandardFilters.VMSF_UPCASE) + }; + var CodeCRC = RarCRC.CheckCrc(0xffffffff, code, 0, code.Length) ^ 0xffffffff; + for (var i = 0; i < stdList.Length; i++) + { + if (stdList[i].CRC == CodeCRC && stdList[i].Length == code.Length) + { + return (stdList[i].Type); + } + } + return (VMStandardFilters.VMSF_NONE); + } + + private void ExecuteStandardFilter(VMStandardFilters filterType) + { + switch (filterType) + { + case VMStandardFilters.VMSF_E8: + case VMStandardFilters.VMSF_E8E9: + + { + var dataSize = R[4]; + long fileOffset = R[6] & unchecked((int)0xFFffFFff); + + if (dataSize >= VM_GLOBALMEMADDR) + { + break; + } + var fileSize = 0x1000000; + var cmpByte2 = (byte)( + (filterType == VMStandardFilters.VMSF_E8E9) ? 0xe9 : 0xe8 + ); + for (var curPos = 0; curPos < dataSize - 4;) + { + var curByte = Mem[curPos++]; + if (curByte == 0xe8 || curByte == cmpByte2) + { + // #ifdef PRESENT_INT32 + // sint32 Offset=CurPos+FileOffset; + // sint32 Addr=GET_VALUE(false,Data); + // if (Addr<0) + // { + // if (Addr+Offset>=0) + // SET_VALUE(false,Data,Addr+FileSize); + // } + // else + // if (Addr= VM_GLOBALMEMADDR) + { + break; + } + var curPos = 0; + + //UPGRADE_NOTE: Final was removed from the declaration of 'Masks '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + byte[] Masks = { 4, 4, 6, 6, 0, 0, 7, 7, 4, 4, 0, 0, 4, 4, 0, 0 }; + fileOffset = Utility.URShift(fileOffset, 4); + + while (curPos < dataSize - 21) + { + var Byte = (Mem[curPos] & 0x1f) - 0x10; + if (Byte >= 0) + { + var cmdMask = Masks[Byte]; + if (cmdMask != 0) + { + for (var i = 0; i <= 2; i++) + { + if ((cmdMask & (1 << i)) != 0) + { + var startPos = (i * 41) + 5; + var opType = filterItanium_GetBits( + curPos, + startPos + 37, + 4 + ); + if (opType == 5) + { + var offset = filterItanium_GetBits( + curPos, + startPos + 13, + 20 + ); + filterItanium_SetBits( + curPos, + (int)(offset - fileOffset) & 0xfffff, + startPos + 13, + 20 + ); + } + } + } + } + } + curPos += 16; + fileOffset++; + } + } + break; + + case VMStandardFilters.VMSF_DELTA: + + { + var dataSize = R[4] & unchecked((int)0xFFffFFff); + var channels = R[0] & unchecked((int)0xFFffFFff); + var srcPos = 0; + var border = (dataSize * 2) & unchecked((int)0xFFffFFff); + SetValue(false, Mem, VM_GLOBALMEMADDR + 0x20, dataSize); + if (dataSize >= VM_GLOBALMEMADDR / 2) + { + break; + } + + // bytes from same channels are grouped to continual data blocks, + // so we need to place them back to their interleaving positions + + for (var curChannel = 0; curChannel < channels; curChannel++) + { + byte PrevByte = 0; + for ( + var destPos = dataSize + curChannel; + destPos < border; + destPos += channels + ) + { + Mem[destPos] = (PrevByte = (byte)(PrevByte - Mem[srcPos++])); + } + } + } + break; + + case VMStandardFilters.VMSF_RGB: + + { + // byte *SrcData=Mem,*DestData=SrcData+DataSize; + int dataSize = R[4], + width = R[0] - 3, + posR = R[1]; + var channels = 3; + var srcPos = 0; + var destDataPos = dataSize; + SetValue(false, Mem, VM_GLOBALMEMADDR + 0x20, dataSize); + if (dataSize >= VM_GLOBALMEMADDR / 2 || posR < 0) + { + break; + } + for (var curChannel = 0; curChannel < channels; curChannel++) + { + long prevByte = 0; + + for (var i = curChannel; i < dataSize; i += channels) + { + long predicted; + var upperPos = i - width; + if (upperPos >= 3) + { + var upperDataPos = destDataPos + upperPos; + var upperByte = Mem[upperDataPos] & 0xff; + var upperLeftByte = Mem[upperDataPos - 3] & 0xff; + predicted = prevByte + upperByte - upperLeftByte; + var pa = Math.Abs((int)(predicted - prevByte)); + var pb = Math.Abs((int)(predicted - upperByte)); + var pc = Math.Abs((int)(predicted - upperLeftByte)); + if (pa <= pb && pa <= pc) + { + predicted = prevByte; + } + else + { + if (pb <= pc) + { + predicted = upperByte; + } + else + { + predicted = upperLeftByte; + } + } + } + else + { + predicted = prevByte; + } + + prevByte = ((predicted - Mem[srcPos++]) & 0xff) & 0xff; + Mem[destDataPos + i] = (byte)(prevByte & 0xff); + } + } + for (int i = posR, border = dataSize - 2; i < border; i += 3) + { + var G = Mem[destDataPos + i + 1]; + Mem[destDataPos + i] = (byte)(Mem[destDataPos + i] + G); + Mem[destDataPos + i + 2] = (byte)(Mem[destDataPos + i + 2] + G); + } + } + break; + + case VMStandardFilters.VMSF_AUDIO: + + { + int dataSize = R[4], + channels = R[0]; + var srcPos = 0; + var destDataPos = dataSize; + + //byte *SrcData=Mem,*DestData=SrcData+DataSize; + SetValue(false, Mem, VM_GLOBALMEMADDR + 0x20, dataSize); + if (dataSize >= VM_GLOBALMEMADDR / 2) + { + break; + } + for (var curChannel = 0; curChannel < channels; curChannel++) + { + long prevByte = 0; + long prevDelta = 0; + var Dif = new long[7]; + int D1 = 0, + D2 = 0, + D3; + int K1 = 0, + K2 = 0, + K3 = 0; + + for ( + int i = curChannel, byteCount = 0; + i < dataSize; + i += channels, byteCount++ + ) + { + D3 = D2; + D2 = (int)(prevDelta - D1); + D1 = (int)prevDelta; + + var predicted = (8 * prevByte) + (K1 * D1) + (K2 * D2) + (K3 * D3); + predicted = Utility.URShift(predicted, 3) & 0xff; + + long curByte = Mem[srcPos++]; + + predicted -= curByte; + Mem[destDataPos + i] = (byte)predicted; + prevDelta = (byte)(predicted - prevByte); + + //fix java byte + if (prevDelta >= 128) + { + prevDelta = 0 - (256 - prevDelta); + } + prevByte = predicted; + + //fix java byte + if (curByte >= 128) + { + curByte = 0 - (256 - curByte); + } + var D = ((int)curByte) << 3; + + Dif[0] += Math.Abs(D); + Dif[1] += Math.Abs(D - D1); + Dif[2] += Math.Abs(D + D1); + Dif[3] += Math.Abs(D - D2); + Dif[4] += Math.Abs(D + D2); + Dif[5] += Math.Abs(D - D3); + Dif[6] += Math.Abs(D + D3); + + if ((byteCount & 0x1f) == 0) + { + long minDif = Dif[0], + numMinDif = 0; + Dif[0] = 0; + for (var j = 1; j < Dif.Length; j++) + { + if (Dif[j] < minDif) + { + minDif = Dif[j]; + numMinDif = j; + } + Dif[j] = 0; + } + switch ((int)numMinDif) + { + case 1: + if (K1 >= -16) + { + K1--; + } + break; + + case 2: + if (K1 < 16) + { + K1++; + } + break; + + case 3: + if (K2 >= -16) + { + K2--; + } + break; + + case 4: + if (K2 < 16) + { + K2++; + } + break; + + case 5: + if (K3 >= -16) + { + K3--; + } + break; + + case 6: + if (K3 < 16) + { + K3++; + } + break; + } + } + } + } + } + break; + + case VMStandardFilters.VMSF_UPCASE: + + { + int dataSize = R[4], + srcPos = 0, + destPos = dataSize; + if (dataSize >= VM_GLOBALMEMADDR / 2) + { + break; + } + while (srcPos < dataSize) + { + var curByte = Mem[srcPos++]; + if (curByte == 2 && (curByte = Mem[srcPos++]) != 2) + { + curByte = (byte)(curByte - 32); + } + Mem[destPos++] = curByte; + } + SetValue(false, Mem, VM_GLOBALMEMADDR + 0x1c, destPos - dataSize); + SetValue(false, Mem, VM_GLOBALMEMADDR + 0x20, dataSize); + } + break; + } + } + + private void filterItanium_SetBits(int curPos, int bitField, int bitPos, int bitCount) + { + var inAddr = bitPos / 8; + var inBit = bitPos & 7; + var andMask = Utility.URShift(unchecked((int)0xffffffff), (32 - bitCount)); + andMask = ~(andMask << inBit); + + bitField <<= inBit; + + for (var i = 0; i < 4; i++) + { + Mem[curPos + inAddr + i] &= (byte)(andMask); + Mem[curPos + inAddr + i] |= (byte)(bitField); + andMask = (Utility.URShift(andMask, 8)) | unchecked((int)0xff000000); + bitField = Utility.URShift(bitField, 8); + } + } + + private int filterItanium_GetBits(int curPos, int bitPos, int bitCount) + { + var inAddr = bitPos / 8; + var inBit = bitPos & 7; + var bitField = Mem[curPos + inAddr++] & 0xff; + bitField |= (Mem[curPos + inAddr++] & 0xff) << 8; + bitField |= (Mem[curPos + inAddr++] & 0xff) << 16; + bitField |= (Mem[curPos + inAddr] & 0xff) << 24; + bitField = Utility.URShift(bitField, inBit); + return (bitField & (Utility.URShift(unchecked((int)0xffffffff), (32 - bitCount)))); + } + + public void setMemory(int pos, byte[] data, int offset, int dataSize) + { + if (pos < VM_MEMSIZE) + { + //&& data!=Mem+Pos) + //memmove(Mem+Pos,Data,Min(DataSize,VM_MEMSIZE-Pos)); + for (var i = 0; i < Math.Min(data.Length - offset, dataSize); i++) + { + if ((VM_MEMSIZE - pos) < i) + { + break; + } + Mem[pos + i] = data[offset + i]; + } + } + } } + +// diff --git a/src/SharpCompress/Compressors/Rar/VM/VMCmdFlags.cs b/src/SharpCompress/Compressors/Rar/VM/VMCmdFlags.cs index d599a558..00de0b6a 100644 --- a/src/SharpCompress/Compressors/Rar/VM/VMCmdFlags.cs +++ b/src/SharpCompress/Compressors/Rar/VM/VMCmdFlags.cs @@ -1,59 +1,58 @@ -namespace SharpCompress.Compressors.Rar.VM -{ - internal class VMCmdFlags - { - public const byte VMCF_OP0 = 0; - public const byte VMCF_OP1 = 1; - public const byte VMCF_OP2 = 2; - public const byte VMCF_OPMASK = 3; - public const byte VMCF_BYTEMODE = 4; - public const byte VMCF_JUMP = 8; - public const byte VMCF_PROC = 16; - public const byte VMCF_USEFLAGS = 32; - public const byte VMCF_CHFLAGS = 64; +namespace SharpCompress.Compressors.Rar.VM; - public static byte[] VM_CmdFlags = - { - VMCF_OP2 | VMCF_BYTEMODE, - VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, - VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, - VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, - VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, - VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, - VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS, - VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS, - VMCF_OP1 | VMCF_JUMP, - VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, - VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, - VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, - VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, - VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, - VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, - VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, - VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, - VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, - VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, - VMCF_OP1, - VMCF_OP1, - VMCF_OP1 | VMCF_PROC, - VMCF_OP0 | VMCF_PROC, - VMCF_OP1 | VMCF_BYTEMODE, - VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, - VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, - VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, - VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS, - VMCF_OP0, - VMCF_OP0, - VMCF_OP0 | VMCF_USEFLAGS, - VMCF_OP0 | VMCF_CHFLAGS, - VMCF_OP2, - VMCF_OP2, - VMCF_OP2 | VMCF_BYTEMODE, - VMCF_OP2 | VMCF_BYTEMODE, - VMCF_OP2 | VMCF_BYTEMODE, - VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS, - VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS, - VMCF_OP0 - }; - } +internal class VMCmdFlags +{ + public const byte VMCF_OP0 = 0; + public const byte VMCF_OP1 = 1; + public const byte VMCF_OP2 = 2; + public const byte VMCF_OPMASK = 3; + public const byte VMCF_BYTEMODE = 4; + public const byte VMCF_JUMP = 8; + public const byte VMCF_PROC = 16; + public const byte VMCF_USEFLAGS = 32; + public const byte VMCF_CHFLAGS = 64; + + public static byte[] VM_CmdFlags = + { + VMCF_OP2 | VMCF_BYTEMODE, + VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, + VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, + VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, + VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, + VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, + VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS, + VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS, + VMCF_OP1 | VMCF_JUMP, + VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, + VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, + VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, + VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, + VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, + VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, + VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, + VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, + VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, + VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS, + VMCF_OP1, + VMCF_OP1, + VMCF_OP1 | VMCF_PROC, + VMCF_OP0 | VMCF_PROC, + VMCF_OP1 | VMCF_BYTEMODE, + VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, + VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, + VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS, + VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS, + VMCF_OP0, + VMCF_OP0, + VMCF_OP0 | VMCF_USEFLAGS, + VMCF_OP0 | VMCF_CHFLAGS, + VMCF_OP2, + VMCF_OP2, + VMCF_OP2 | VMCF_BYTEMODE, + VMCF_OP2 | VMCF_BYTEMODE, + VMCF_OP2 | VMCF_BYTEMODE, + VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS, + VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS, + VMCF_OP0 + }; } diff --git a/src/SharpCompress/Compressors/Rar/VM/VMCommands.cs b/src/SharpCompress/Compressors/Rar/VM/VMCommands.cs index 46fde3f7..7bb1a396 100644 --- a/src/SharpCompress/Compressors/Rar/VM/VMCommands.cs +++ b/src/SharpCompress/Compressors/Rar/VM/VMCommands.cs @@ -1,71 +1,70 @@ -namespace SharpCompress.Compressors.Rar.VM +namespace SharpCompress.Compressors.Rar.VM; + +internal enum VMCommands { - internal enum VMCommands - { - VM_MOV = 0, - VM_CMP = 1, - VM_ADD = 2, - VM_SUB = 3, - VM_JZ = 4, - VM_JNZ = 5, - VM_INC = 6, + VM_MOV = 0, + VM_CMP = 1, + VM_ADD = 2, + VM_SUB = 3, + VM_JZ = 4, + VM_JNZ = 5, + VM_INC = 6, - VM_DEC = 7, - VM_JMP = 8, - VM_XOR = 9, - VM_AND = 10, - VM_OR = 11, - VM_TEST = 12, + VM_DEC = 7, + VM_JMP = 8, + VM_XOR = 9, + VM_AND = 10, + VM_OR = 11, + VM_TEST = 12, - VM_JS = 13, - VM_JNS = 14, - VM_JB = 15, - VM_JBE = 16, - VM_JA = 17, - VM_JAE = 18, + VM_JS = 13, + VM_JNS = 14, + VM_JB = 15, + VM_JBE = 16, + VM_JA = 17, + VM_JAE = 18, - VM_PUSH = 19, - VM_POP = 20, - VM_CALL = 21, - VM_RET = 22, - VM_NOT = 23, - VM_SHL = 24, + VM_PUSH = 19, + VM_POP = 20, + VM_CALL = 21, + VM_RET = 22, + VM_NOT = 23, + VM_SHL = 24, - VM_SHR = 25, - VM_SAR = 26, - VM_NEG = 27, - VM_PUSHA = 28, - VM_POPA = 29, - VM_PUSHF = 30, + VM_SHR = 25, + VM_SAR = 26, + VM_NEG = 27, + VM_PUSHA = 28, + VM_POPA = 29, + VM_PUSHF = 30, - VM_POPF = 31, - VM_MOVZX = 32, - VM_MOVSX = 33, - VM_XCHG = 34, - VM_MUL = 35, - VM_DIV = 36, + VM_POPF = 31, + VM_MOVZX = 32, + VM_MOVSX = 33, + VM_XCHG = 34, + VM_MUL = 35, + VM_DIV = 36, - VM_ADC = 37, - VM_SBB = 38, - VM_PRINT = 39, + VM_ADC = 37, + VM_SBB = 38, + VM_PRINT = 39, - VM_MOVB = 40, - VM_MOVD = 41, - VM_CMPB = 42, - VM_CMPD = 43, + VM_MOVB = 40, + VM_MOVD = 41, + VM_CMPB = 42, + VM_CMPD = 43, - VM_ADDB = 44, - VM_ADDD = 45, - VM_SUBB = 46, - VM_SUBD = 47, - VM_INCB = 48, - VM_INCD = 49, + VM_ADDB = 44, + VM_ADDD = 45, + VM_SUBB = 46, + VM_SUBD = 47, + VM_INCB = 48, + VM_INCD = 49, - VM_DECB = 50, - VM_DECD = 51, - VM_NEGB = 52, - VM_NEGD = 53, + VM_DECB = 50, + VM_DECD = 51, + VM_NEGB = 52, + VM_NEGD = 53, - VM_STANDARD = 54 - } + VM_STANDARD = 54 } diff --git a/src/SharpCompress/Compressors/Rar/VM/VMFlags.cs b/src/SharpCompress/Compressors/Rar/VM/VMFlags.cs index d8b23ba8..fa9a72e7 100644 --- a/src/SharpCompress/Compressors/Rar/VM/VMFlags.cs +++ b/src/SharpCompress/Compressors/Rar/VM/VMFlags.cs @@ -1,10 +1,9 @@ -namespace SharpCompress.Compressors.Rar.VM +namespace SharpCompress.Compressors.Rar.VM; + +internal enum VMFlags { - internal enum VMFlags - { - None = 0, - VM_FC = 1, - VM_FZ = 2, - VM_FS = 80000000 - } + None = 0, + VM_FC = 1, + VM_FZ = 2, + VM_FS = 80000000 } diff --git a/src/SharpCompress/Compressors/Rar/VM/VMOpType.cs b/src/SharpCompress/Compressors/Rar/VM/VMOpType.cs index 8c72eb74..bcc4214e 100644 --- a/src/SharpCompress/Compressors/Rar/VM/VMOpType.cs +++ b/src/SharpCompress/Compressors/Rar/VM/VMOpType.cs @@ -1,10 +1,9 @@ -namespace SharpCompress.Compressors.Rar.VM +namespace SharpCompress.Compressors.Rar.VM; + +internal enum VMOpType { - internal enum VMOpType - { - VM_OPREG = 0, - VM_OPINT = 1, - VM_OPREGMEM = 2, - VM_OPNONE = 3 - } + VM_OPREG = 0, + VM_OPINT = 1, + VM_OPREGMEM = 2, + VM_OPNONE = 3 } diff --git a/src/SharpCompress/Compressors/Rar/VM/VMPreparedCommand.cs b/src/SharpCompress/Compressors/Rar/VM/VMPreparedCommand.cs index 69a459ca..e6fed0b1 100644 --- a/src/SharpCompress/Compressors/Rar/VM/VMPreparedCommand.cs +++ b/src/SharpCompress/Compressors/Rar/VM/VMPreparedCommand.cs @@ -1,17 +1,16 @@ -namespace SharpCompress.Compressors.Rar.VM +namespace SharpCompress.Compressors.Rar.VM; + +internal class VMPreparedCommand { - internal class VMPreparedCommand + internal VMPreparedCommand() { - internal VMPreparedCommand() - { - Op1 = new VMPreparedOperand(); - Op2 = new VMPreparedOperand(); - } - - internal VMCommands OpCode { get; set; } - internal bool IsByteMode { get; set; } - internal VMPreparedOperand Op1 { get; } - - internal VMPreparedOperand Op2 { get; } + Op1 = new VMPreparedOperand(); + Op2 = new VMPreparedOperand(); } + + internal VMCommands OpCode { get; set; } + internal bool IsByteMode { get; set; } + internal VMPreparedOperand Op1 { get; } + + internal VMPreparedOperand Op2 { get; } } diff --git a/src/SharpCompress/Compressors/Rar/VM/VMPreparedOperand.cs b/src/SharpCompress/Compressors/Rar/VM/VMPreparedOperand.cs index 2f20f306..af1be450 100644 --- a/src/SharpCompress/Compressors/Rar/VM/VMPreparedOperand.cs +++ b/src/SharpCompress/Compressors/Rar/VM/VMPreparedOperand.cs @@ -1,10 +1,9 @@ -namespace SharpCompress.Compressors.Rar.VM +namespace SharpCompress.Compressors.Rar.VM; + +internal class VMPreparedOperand { - internal class VMPreparedOperand - { - internal VMOpType Type { get; set; } - internal int Data { get; set; } - internal int Base { get; set; } - internal int Offset { get; set; } - } + internal VMOpType Type { get; set; } + internal int Data { get; set; } + internal int Base { get; set; } + internal int Offset { get; set; } } diff --git a/src/SharpCompress/Compressors/Rar/VM/VMPreparedProgram.cs b/src/SharpCompress/Compressors/Rar/VM/VMPreparedProgram.cs index de9e7054..c0006e09 100644 --- a/src/SharpCompress/Compressors/Rar/VM/VMPreparedProgram.cs +++ b/src/SharpCompress/Compressors/Rar/VM/VMPreparedProgram.cs @@ -1,21 +1,20 @@ using System.Collections.Generic; -namespace SharpCompress.Compressors.Rar.VM +namespace SharpCompress.Compressors.Rar.VM; + +internal class VMPreparedProgram { - internal class VMPreparedProgram - { - internal List Commands = new List(); - internal List AltCommands = new List(); + internal List Commands = new List(); + internal List AltCommands = new List(); - public int CommandCount { get; set; } + public int CommandCount { get; set; } - internal List GlobalData = new List(); - internal List StaticData = new List(); + internal List GlobalData = new List(); + internal List StaticData = new List(); - // static data contained in DB operators - internal int[] InitR = new int[7]; + // static data contained in DB operators + internal int[] InitR = new int[7]; - internal int FilteredDataOffset { get; set; } - internal int FilteredDataSize { get; set; } - } + internal int FilteredDataOffset { get; set; } + internal int FilteredDataSize { get; set; } } diff --git a/src/SharpCompress/Compressors/Rar/VM/VMStandardFilterSignature.cs b/src/SharpCompress/Compressors/Rar/VM/VMStandardFilterSignature.cs index cc55f167..a9096041 100644 --- a/src/SharpCompress/Compressors/Rar/VM/VMStandardFilterSignature.cs +++ b/src/SharpCompress/Compressors/Rar/VM/VMStandardFilterSignature.cs @@ -1,18 +1,17 @@ -namespace SharpCompress.Compressors.Rar.VM +namespace SharpCompress.Compressors.Rar.VM; + +internal class VMStandardFilterSignature { - internal class VMStandardFilterSignature + internal VMStandardFilterSignature(int length, uint crc, VMStandardFilters type) { - internal VMStandardFilterSignature(int length, uint crc, VMStandardFilters type) - { - Length = length; - CRC = crc; - Type = type; - } - - internal int Length { get; } - - internal uint CRC { get; } - - internal VMStandardFilters Type { get; } + Length = length; + CRC = crc; + Type = type; } + + internal int Length { get; } + + internal uint CRC { get; } + + internal VMStandardFilters Type { get; } } diff --git a/src/SharpCompress/Compressors/Rar/VM/VMStandardFilters.cs b/src/SharpCompress/Compressors/Rar/VM/VMStandardFilters.cs index 5f7c18ea..4b90b019 100644 --- a/src/SharpCompress/Compressors/Rar/VM/VMStandardFilters.cs +++ b/src/SharpCompress/Compressors/Rar/VM/VMStandardFilters.cs @@ -1,14 +1,13 @@ -namespace SharpCompress.Compressors.Rar.VM +namespace SharpCompress.Compressors.Rar.VM; + +internal enum VMStandardFilters { - internal enum VMStandardFilters - { - VMSF_NONE = 0, - VMSF_E8 = 1, - VMSF_E8E9 = 2, - VMSF_ITANIUM = 3, - VMSF_RGB = 4, - VMSF_AUDIO = 5, - VMSF_DELTA = 6, - VMSF_UPCASE = 7 - } + VMSF_NONE = 0, + VMSF_E8 = 1, + VMSF_E8E9 = 2, + VMSF_ITANIUM = 3, + VMSF_RGB = 4, + VMSF_AUDIO = 5, + VMSF_DELTA = 6, + VMSF_UPCASE = 7 } diff --git a/src/SharpCompress/Compressors/Xz/BinaryUtils.cs b/src/SharpCompress/Compressors/Xz/BinaryUtils.cs index a0a92171..b05c3bb1 100644 --- a/src/SharpCompress/Compressors/Xz/BinaryUtils.cs +++ b/src/SharpCompress/Compressors/Xz/BinaryUtils.cs @@ -1,61 +1,60 @@ -using System; +using System; using System.Buffers.Binary; using System.IO; -namespace SharpCompress.Compressors.Xz +namespace SharpCompress.Compressors.Xz; + +[CLSCompliant(false)] +public static class BinaryUtils { - [CLSCompliant(false)] - public static class BinaryUtils + public static int ReadLittleEndianInt32(this BinaryReader reader) { - public static int ReadLittleEndianInt32(this BinaryReader reader) + var bytes = reader.ReadBytes(4); + return BinaryPrimitives.ReadInt32LittleEndian(bytes); + } + + internal static uint ReadLittleEndianUInt32(this BinaryReader reader) + { + return unchecked((uint)ReadLittleEndianInt32(reader)); + } + + public static int ReadLittleEndianInt32(this Stream stream) + { + Span bytes = stackalloc byte[4]; + var read = stream.ReadFully(bytes); + if (!read) { - byte[] bytes = reader.ReadBytes(4); - return BinaryPrimitives.ReadInt32LittleEndian(bytes); + throw new EndOfStreamException(); + } + return BinaryPrimitives.ReadInt32LittleEndian(bytes); + } + + internal static uint ReadLittleEndianUInt32(this Stream stream) + { + return unchecked((uint)ReadLittleEndianInt32(stream)); + } + + internal static byte[] ToBigEndianBytes(this uint uint32) + { + var result = BitConverter.GetBytes(uint32); + + if (BitConverter.IsLittleEndian) + { + Array.Reverse(result); } - internal static uint ReadLittleEndianUInt32(this BinaryReader reader) + return result; + } + + public static byte[] ToLittleEndianBytes(this uint uint32) + { + var result = BitConverter.GetBytes(uint32); + + if (!BitConverter.IsLittleEndian) { - return unchecked((uint)ReadLittleEndianInt32(reader)); + Array.Reverse(result); } - public static int ReadLittleEndianInt32(this Stream stream) - { - Span bytes = stackalloc byte[4]; - var read = stream.ReadFully(bytes); - if (!read) - { - throw new EndOfStreamException(); - } - return BinaryPrimitives.ReadInt32LittleEndian(bytes); - } - - internal static uint ReadLittleEndianUInt32(this Stream stream) - { - return unchecked((uint)ReadLittleEndianInt32(stream)); - } - - internal static byte[] ToBigEndianBytes(this uint uint32) - { - var result = BitConverter.GetBytes(uint32); - - if (BitConverter.IsLittleEndian) - { - Array.Reverse(result); - } - - return result; - } - - public static byte[] ToLittleEndianBytes(this uint uint32) - { - var result = BitConverter.GetBytes(uint32); - - if (!BitConverter.IsLittleEndian) - { - Array.Reverse(result); - } - - return result; - } + return result; } } diff --git a/src/SharpCompress/Compressors/Xz/CheckType.cs b/src/SharpCompress/Compressors/Xz/CheckType.cs index 4176ac4b..a94f8261 100644 --- a/src/SharpCompress/Compressors/Xz/CheckType.cs +++ b/src/SharpCompress/Compressors/Xz/CheckType.cs @@ -1,10 +1,9 @@ -namespace SharpCompress.Compressors.Xz +namespace SharpCompress.Compressors.Xz; + +public enum CheckType : byte { - public enum CheckType : byte - { - NONE = 0x00, - CRC32 = 0x01, - CRC64 = 0x04, - SHA256 = 0x0A - } + NONE = 0x00, + CRC32 = 0x01, + CRC64 = 0x04, + SHA256 = 0x0A } diff --git a/src/SharpCompress/Compressors/Xz/Crc32.cs b/src/SharpCompress/Compressors/Xz/Crc32.cs index 51b4c7bf..5df276ae 100644 --- a/src/SharpCompress/Compressors/Xz/Crc32.cs +++ b/src/SharpCompress/Compressors/Xz/Crc32.cs @@ -1,76 +1,75 @@ -#nullable disable +#nullable disable using System; -namespace SharpCompress.Compressors.Xz +namespace SharpCompress.Compressors.Xz; + +[CLSCompliant(false)] +public static class Crc32 { - [CLSCompliant(false)] - public static class Crc32 + public const uint DefaultPolynomial = 0xedb88320u; + public const uint DefaultSeed = 0xffffffffu; + + private static uint[] defaultTable; + + public static uint Compute(byte[] buffer) { - public const UInt32 DefaultPolynomial = 0xedb88320u; - public const UInt32 DefaultSeed = 0xffffffffu; + return Compute(DefaultSeed, buffer); + } - private static UInt32[] defaultTable; + public static uint Compute(uint seed, byte[] buffer) + { + return Compute(DefaultPolynomial, seed, buffer); + } - public static UInt32 Compute(byte[] buffer) + public static uint Compute(uint polynomial, uint seed, byte[] buffer) + { + return ~CalculateHash(InitializeTable(polynomial), seed, buffer); + } + + private static uint[] InitializeTable(uint polynomial) + { + if (polynomial == DefaultPolynomial && defaultTable != null) { - return Compute(DefaultSeed, buffer); + return defaultTable; } - public static UInt32 Compute(UInt32 seed, byte[] buffer) + var createTable = new uint[256]; + for (var i = 0; i < 256; i++) { - return Compute(DefaultPolynomial, seed, buffer); - } - - public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer) - { - return ~CalculateHash(InitializeTable(polynomial), seed, buffer); - } - - private static UInt32[] InitializeTable(UInt32 polynomial) - { - if (polynomial == DefaultPolynomial && defaultTable != null) + var entry = (uint)i; + for (var j = 0; j < 8; j++) { - return defaultTable; - } - - var createTable = new UInt32[256]; - for (var i = 0; i < 256; i++) - { - var entry = (UInt32)i; - for (var j = 0; j < 8; j++) + if ((entry & 1) == 1) { - if ((entry & 1) == 1) - { - entry = (entry >> 1) ^ polynomial; - } - else - { - entry = entry >> 1; - } + entry = (entry >> 1) ^ polynomial; + } + else + { + entry >>= 1; } - - createTable[i] = entry; } - if (polynomial == DefaultPolynomial) - { - defaultTable = createTable; - } - - return createTable; + createTable[i] = entry; } - private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, ReadOnlySpan buffer) + if (polynomial == DefaultPolynomial) { - var crc = seed; - int len = buffer.Length; - for (var i = 0; i < len; i++) - { - crc = (crc >> 8) ^ table[(buffer[i] ^ crc) & 0xff]; - } - - return crc; + defaultTable = createTable; } + + return createTable; + } + + private static uint CalculateHash(uint[] table, uint seed, ReadOnlySpan buffer) + { + var crc = seed; + var len = buffer.Length; + for (var i = 0; i < len; i++) + { + crc = (crc >> 8) ^ table[(buffer[i] ^ crc) & 0xff]; + } + + return crc; } } diff --git a/src/SharpCompress/Compressors/Xz/Crc64.cs b/src/SharpCompress/Compressors/Xz/Crc64.cs index 6ea99940..6e578329 100644 --- a/src/SharpCompress/Compressors/Xz/Crc64.cs +++ b/src/SharpCompress/Compressors/Xz/Crc64.cs @@ -1,67 +1,65 @@ -#nullable disable +#nullable disable using System; -using System.Collections.Generic; -namespace SharpCompress.Compressors.Xz +namespace SharpCompress.Compressors.Xz; + +[CLSCompliant(false)] +public static class Crc64 { - [CLSCompliant(false)] - public static class Crc64 + public const ulong DefaultSeed = 0x0; + + internal static ulong[] Table; + + public const ulong Iso3309Polynomial = 0xD800000000000000; + + public static ulong Compute(byte[] buffer) { - public const UInt64 DefaultSeed = 0x0; + return Compute(DefaultSeed, buffer); + } - internal static UInt64[] Table; + public static ulong Compute(ulong seed, byte[] buffer) + { + Table ??= CreateTable(Iso3309Polynomial); - public const UInt64 Iso3309Polynomial = 0xD800000000000000; + return CalculateHash(seed, Table, buffer); + } - public static UInt64 Compute(byte[] buffer) + public static ulong CalculateHash(ulong seed, ulong[] table, ReadOnlySpan buffer) + { + var crc = seed; + var len = buffer.Length; + for (var i = 0; i < len; i++) { - return Compute(DefaultSeed, buffer); - } - - public static UInt64 Compute(UInt64 seed, byte[] buffer) - { - Table ??= CreateTable(Iso3309Polynomial); - - return CalculateHash(seed, Table, buffer); - } - - public static UInt64 CalculateHash(UInt64 seed, UInt64[] table, ReadOnlySpan buffer) - { - var crc = seed; - int len = buffer.Length; - for (var i = 0; i < len; i++) + unchecked { - unchecked + crc = (crc >> 8) ^ table[(buffer[i] ^ crc) & 0xff]; + } + } + + return crc; + } + + public static ulong[] CreateTable(ulong polynomial) + { + var createTable = new ulong[256]; + for (var i = 0; i < 256; ++i) + { + var entry = (ulong)i; + for (var j = 0; j < 8; ++j) + { + if ((entry & 1) == 1) { - crc = (crc >> 8) ^ table[(buffer[i] ^ crc) & 0xff]; + entry = (entry >> 1) ^ polynomial; + } + else + { + entry >>= 1; } } - return crc; - } - - public static ulong[] CreateTable(ulong polynomial) - { - var createTable = new UInt64[256]; - for (var i = 0; i < 256; ++i) - { - var entry = (UInt64)i; - for (var j = 0; j < 8; ++j) - { - if ((entry & 1) == 1) - { - entry = (entry >> 1) ^ polynomial; - } - else - { - entry = entry >> 1; - } - } - - createTable[i] = entry; - } - return createTable; + createTable[i] = entry; } + return createTable; } } diff --git a/src/SharpCompress/Compressors/Xz/Filters/ArmFilter.cs b/src/SharpCompress/Compressors/Xz/Filters/ArmFilter.cs index cefc1137..e30109f1 100644 --- a/src/SharpCompress/Compressors/Xz/Filters/ArmFilter.cs +++ b/src/SharpCompress/Compressors/Xz/Filters/ArmFilter.cs @@ -4,62 +4,56 @@ * @TODO Properties offset */ -using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using SharpCompress.Compressors.Filters; -namespace SharpCompress.Compressors.Xz.Filters +namespace SharpCompress.Compressors.Xz.Filters; + +public class ArmFilter : BlockFilter { - public class ArmFilter : BlockFilter + public override bool AllowAsLast => false; + + public override bool AllowAsNonLast => true; + + public override bool ChangesDataSize => false; + + private uint _ip = 0; + + //private UInt32 _offset = 0; + + public override void Init(byte[] properties) { - public override bool AllowAsLast => false; - - public override bool AllowAsNonLast => true; - - public override bool ChangesDataSize => false; - - private UInt32 _ip = 0; - - //private UInt32 _offset = 0; - - public override void Init(byte[] properties) + if (properties.Length != 0 && properties.Length != 4) { - if (properties.Length != 0 && properties.Length != 4) - { - throw new InvalidDataException("ARM properties unexpected length"); - } - - if (properties.Length == 4) - { - // Even XZ doesn't support it. - throw new InvalidDataException("ARM properties offset is not supported"); - - //_offset = BitConverter.ToUInt32(properties, 0); - // - //if (_offset % (UInt32)BranchExec.Alignment.ARCH_ARM_ALIGNMENT != 0) - //{ - // throw new InvalidDataException("Filter offset does not match alignment"); - //} - } + throw new InvalidDataException("ARM properties unexpected length"); } - public override void ValidateFilter() { } - - public override int Read(byte[] buffer, int offset, int count) + if (properties.Length == 4) { - int bytesRead = BaseStream.Read(buffer, offset, count); - BranchExecFilter.ARMConverter(buffer, _ip); - _ip += (UInt32)bytesRead; - return bytesRead; - } + // Even XZ doesn't support it. + throw new InvalidDataException("ARM properties offset is not supported"); - public override void SetBaseStream(Stream stream) - { - BaseStream = stream; + //_offset = BitConverter.ToUInt32(properties, 0); + // + //if (_offset % (UInt32)BranchExec.Alignment.ARCH_ARM_ALIGNMENT != 0) + //{ + // throw new InvalidDataException("Filter offset does not match alignment"); + //} } } + + public override void ValidateFilter() { } + + public override int Read(byte[] buffer, int offset, int count) + { + var bytesRead = BaseStream.Read(buffer, offset, count); + BranchExecFilter.ARMConverter(buffer, _ip); + _ip += (uint)bytesRead; + return bytesRead; + } + + public override void SetBaseStream(Stream stream) + { + BaseStream = stream; + } } diff --git a/src/SharpCompress/Compressors/Xz/Filters/ArmThumbFilter.cs b/src/SharpCompress/Compressors/Xz/Filters/ArmThumbFilter.cs index 3a70f6f7..586e3779 100644 --- a/src/SharpCompress/Compressors/Xz/Filters/ArmThumbFilter.cs +++ b/src/SharpCompress/Compressors/Xz/Filters/ArmThumbFilter.cs @@ -4,62 +4,56 @@ * @TODO Properties offset */ -using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using SharpCompress.Compressors.Filters; -namespace SharpCompress.Compressors.Xz.Filters +namespace SharpCompress.Compressors.Xz.Filters; + +public class ArmThumbFilter : BlockFilter { - public class ArmThumbFilter : BlockFilter + public override bool AllowAsLast => false; + + public override bool AllowAsNonLast => true; + + public override bool ChangesDataSize => false; + + private uint _ip = 0; + + //private UInt32 _offset = 0; + + public override void Init(byte[] properties) { - public override bool AllowAsLast => false; - - public override bool AllowAsNonLast => true; - - public override bool ChangesDataSize => false; - - private UInt32 _ip = 0; - - //private UInt32 _offset = 0; - - public override void Init(byte[] properties) + if (properties.Length != 0 && properties.Length != 4) { - if (properties.Length != 0 && properties.Length != 4) - { - throw new InvalidDataException("ARM Thumb properties unexpected length"); - } - - if (properties.Length == 4) - { - // Even XZ doesn't support it. - throw new InvalidDataException("ARM Thumb properties offset is not supported"); - - //_offset = BitConverter.ToUInt32(properties, 0); - // - //if (_offset % (UInt32)BranchExec.Alignment.ARCH_ARMTHUMB_ALIGNMENT != 0) - //{ - // throw new InvalidDataException("Filter offset does not match alignment"); - //} - } + throw new InvalidDataException("ARM Thumb properties unexpected length"); } - public override void ValidateFilter() { } - - public override int Read(byte[] buffer, int offset, int count) + if (properties.Length == 4) { - int bytesRead = BaseStream.Read(buffer, offset, count); - BranchExecFilter.ARMTConverter(buffer, _ip); - _ip += (UInt32)bytesRead; - return bytesRead; - } + // Even XZ doesn't support it. + throw new InvalidDataException("ARM Thumb properties offset is not supported"); - public override void SetBaseStream(Stream stream) - { - BaseStream = stream; + //_offset = BitConverter.ToUInt32(properties, 0); + // + //if (_offset % (UInt32)BranchExec.Alignment.ARCH_ARMTHUMB_ALIGNMENT != 0) + //{ + // throw new InvalidDataException("Filter offset does not match alignment"); + //} } } + + public override void ValidateFilter() { } + + public override int Read(byte[] buffer, int offset, int count) + { + var bytesRead = BaseStream.Read(buffer, offset, count); + BranchExecFilter.ARMTConverter(buffer, _ip); + _ip += (uint)bytesRead; + return bytesRead; + } + + public override void SetBaseStream(Stream stream) + { + BaseStream = stream; + } } diff --git a/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs b/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs index 79eddaaa..f141f4f2 100644 --- a/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs +++ b/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs @@ -2,67 +2,66 @@ using System; using System.Collections.Generic; using System.IO; -namespace SharpCompress.Compressors.Xz.Filters +namespace SharpCompress.Compressors.Xz.Filters; + +public abstract class BlockFilter : ReadOnlyStream { - public abstract class BlockFilter : ReadOnlyStream + [CLSCompliant(false)] + public enum FilterTypes : ulong { - [CLSCompliant(false)] - public enum FilterTypes : ulong - { - DELTA = 0x03, - ARCH_x86_FILTER = 0x04, - ARCH_PowerPC_FILTER = 0x05, - ARCH_IA64_FILTER = 0x06, - ARCH_ARM_FILTER = 0x07, - ARCH_ARMTHUMB_FILTER = 0x08, - ARCH_SPARC_FILTER = 0x09, - LZMA2 = 0x21 - } - - private static readonly Dictionary FilterMap = new Dictionary< - FilterTypes, - Type - > - { - { FilterTypes.ARCH_x86_FILTER, typeof(X86Filter) }, - { FilterTypes.ARCH_PowerPC_FILTER, typeof(PowerPCFilter) }, - { FilterTypes.ARCH_IA64_FILTER, typeof(IA64Filter) }, - { FilterTypes.ARCH_ARM_FILTER, typeof(ArmFilter) }, - { FilterTypes.ARCH_ARMTHUMB_FILTER, typeof(ArmThumbFilter) }, - { FilterTypes.ARCH_SPARC_FILTER, typeof(SparcFilter) }, - { FilterTypes.LZMA2, typeof(Lzma2Filter) } - }; - - public abstract bool AllowAsLast { get; } - public abstract bool AllowAsNonLast { get; } - public abstract bool ChangesDataSize { get; } - - public abstract void Init(byte[] properties); - public abstract void ValidateFilter(); - - public static BlockFilter Read(BinaryReader reader) - { - var filterType = (FilterTypes)reader.ReadXZInteger(); - if (!FilterMap.ContainsKey(filterType)) - { - throw new NotImplementedException( - $"Filter {filterType} has not yet been implemented" - ); - } - - var filter = (BlockFilter)Activator.CreateInstance(FilterMap[filterType])!; - - var sizeOfProperties = reader.ReadXZInteger(); - if (sizeOfProperties > int.MaxValue) - { - throw new InvalidDataException("Block filter information too large"); - } - - byte[] properties = reader.ReadBytes((int)sizeOfProperties); - filter.Init(properties); - return filter; - } - - public abstract void SetBaseStream(Stream stream); + DELTA = 0x03, + ARCH_x86_FILTER = 0x04, + ARCH_PowerPC_FILTER = 0x05, + ARCH_IA64_FILTER = 0x06, + ARCH_ARM_FILTER = 0x07, + ARCH_ARMTHUMB_FILTER = 0x08, + ARCH_SPARC_FILTER = 0x09, + LZMA2 = 0x21 } + + private static readonly Dictionary FilterMap = new Dictionary< + FilterTypes, + Type + > + { + { FilterTypes.ARCH_x86_FILTER, typeof(X86Filter) }, + { FilterTypes.ARCH_PowerPC_FILTER, typeof(PowerPCFilter) }, + { FilterTypes.ARCH_IA64_FILTER, typeof(IA64Filter) }, + { FilterTypes.ARCH_ARM_FILTER, typeof(ArmFilter) }, + { FilterTypes.ARCH_ARMTHUMB_FILTER, typeof(ArmThumbFilter) }, + { FilterTypes.ARCH_SPARC_FILTER, typeof(SparcFilter) }, + { FilterTypes.LZMA2, typeof(Lzma2Filter) } + }; + + public abstract bool AllowAsLast { get; } + public abstract bool AllowAsNonLast { get; } + public abstract bool ChangesDataSize { get; } + + public abstract void Init(byte[] properties); + public abstract void ValidateFilter(); + + public static BlockFilter Read(BinaryReader reader) + { + var filterType = (FilterTypes)reader.ReadXZInteger(); + if (!FilterMap.ContainsKey(filterType)) + { + throw new NotImplementedException( + $"Filter {filterType} has not yet been implemented" + ); + } + + var filter = (BlockFilter)Activator.CreateInstance(FilterMap[filterType])!; + + var sizeOfProperties = reader.ReadXZInteger(); + if (sizeOfProperties > int.MaxValue) + { + throw new InvalidDataException("Block filter information too large"); + } + + var properties = reader.ReadBytes((int)sizeOfProperties); + filter.Init(properties); + return filter; + } + + public abstract void SetBaseStream(Stream stream); } diff --git a/src/SharpCompress/Compressors/Xz/Filters/IA64Filter.cs b/src/SharpCompress/Compressors/Xz/Filters/IA64Filter.cs index 7c78ef26..b1f1b66c 100644 --- a/src/SharpCompress/Compressors/Xz/Filters/IA64Filter.cs +++ b/src/SharpCompress/Compressors/Xz/Filters/IA64Filter.cs @@ -4,62 +4,56 @@ * @TODO Properties offset */ -using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using SharpCompress.Compressors.Filters; -namespace SharpCompress.Compressors.Xz.Filters +namespace SharpCompress.Compressors.Xz.Filters; + +public class IA64Filter : BlockFilter { - public class IA64Filter : BlockFilter + public override bool AllowAsLast => false; + + public override bool AllowAsNonLast => true; + + public override bool ChangesDataSize => false; + + private uint _ip = 0; + + //private UInt32 _offset = 0; + + public override void Init(byte[] properties) { - public override bool AllowAsLast => false; - - public override bool AllowAsNonLast => true; - - public override bool ChangesDataSize => false; - - private UInt32 _ip = 0; - - //private UInt32 _offset = 0; - - public override void Init(byte[] properties) + if (properties.Length != 0 && properties.Length != 4) { - if (properties.Length != 0 && properties.Length != 4) - { - throw new InvalidDataException("IA64 properties unexpected length"); - } - - if (properties.Length == 4) - { - // Even XZ doesn't support it. - throw new InvalidDataException("IA64 properties offset is not supported"); - - //_offset = BitConverter.ToUInt32(properties, 0); - // - //if (_offset % (UInt32)BranchExec.Alignment.ARCH_IA64_ALIGNMENT != 0) - //{ - // throw new InvalidDataException("Filter offset does not match alignment"); - //} - } + throw new InvalidDataException("IA64 properties unexpected length"); } - public override void ValidateFilter() { } - - public override int Read(byte[] buffer, int offset, int count) + if (properties.Length == 4) { - int bytesRead = BaseStream.Read(buffer, offset, count); - BranchExecFilter.IA64Converter(buffer, _ip); - _ip += (UInt32)bytesRead; - return bytesRead; - } + // Even XZ doesn't support it. + throw new InvalidDataException("IA64 properties offset is not supported"); - public override void SetBaseStream(Stream stream) - { - BaseStream = stream; + //_offset = BitConverter.ToUInt32(properties, 0); + // + //if (_offset % (UInt32)BranchExec.Alignment.ARCH_IA64_ALIGNMENT != 0) + //{ + // throw new InvalidDataException("Filter offset does not match alignment"); + //} } } + + public override void ValidateFilter() { } + + public override int Read(byte[] buffer, int offset, int count) + { + var bytesRead = BaseStream.Read(buffer, offset, count); + BranchExecFilter.IA64Converter(buffer, _ip); + _ip += (uint)bytesRead; + return bytesRead; + } + + public override void SetBaseStream(Stream stream) + { + BaseStream = stream; + } } diff --git a/src/SharpCompress/Compressors/Xz/Filters/Lzma2Filter.cs b/src/SharpCompress/Compressors/Xz/Filters/Lzma2Filter.cs index be1f9ff4..7b4a2a1c 100644 --- a/src/SharpCompress/Compressors/Xz/Filters/Lzma2Filter.cs +++ b/src/SharpCompress/Compressors/Xz/Filters/Lzma2Filter.cs @@ -1,66 +1,65 @@ -using System; +using System; using System.IO; using SharpCompress.Compressors.LZMA; -namespace SharpCompress.Compressors.Xz.Filters +namespace SharpCompress.Compressors.Xz.Filters; + +[CLSCompliant(false)] +public class Lzma2Filter : BlockFilter { - [CLSCompliant(false)] - public class Lzma2Filter : BlockFilter + public override bool AllowAsLast => true; + public override bool AllowAsNonLast => false; + public override bool ChangesDataSize => true; + + private byte _dictionarySize; + public uint DictionarySize { - public override bool AllowAsLast => true; - public override bool AllowAsNonLast => false; - public override bool ChangesDataSize => true; - - private byte _dictionarySize; - public uint DictionarySize + get { - get + if (_dictionarySize > 40) { - if (_dictionarySize > 40) - { - throw new OverflowException("Dictionary size greater than UInt32.Max"); - } - - if (_dictionarySize == 40) - { - return uint.MaxValue; - } - int mantissa = 2 | (_dictionarySize & 1); - int exponent = _dictionarySize / 2 + 11; - return (uint)mantissa << exponent; - } - } - - public override void Init(byte[] properties) - { - if (properties.Length != 1) - { - throw new InvalidDataException("LZMA properties unexpected length"); + throw new OverflowException("Dictionary size greater than UInt32.Max"); } - _dictionarySize = (byte)(properties[0] & 0x3F); - var reserved = properties[0] & 0xC0; - if (reserved != 0) + if (_dictionarySize == 40) { - throw new InvalidDataException("Reserved bits used in LZMA properties"); + return uint.MaxValue; } - } - - public override void ValidateFilter() { } - - public override void SetBaseStream(Stream stream) - { - BaseStream = new LzmaStream(new[] { _dictionarySize }, stream); - } - - public override int Read(byte[] buffer, int offset, int count) - { - return BaseStream.Read(buffer, offset, count); - } - - public override int ReadByte() - { - return BaseStream.ReadByte(); + var mantissa = 2 | (_dictionarySize & 1); + var exponent = (_dictionarySize / 2) + 11; + return (uint)mantissa << exponent; } } + + public override void Init(byte[] properties) + { + if (properties.Length != 1) + { + throw new InvalidDataException("LZMA properties unexpected length"); + } + + _dictionarySize = (byte)(properties[0] & 0x3F); + var reserved = properties[0] & 0xC0; + if (reserved != 0) + { + throw new InvalidDataException("Reserved bits used in LZMA properties"); + } + } + + public override void ValidateFilter() { } + + public override void SetBaseStream(Stream stream) + { + BaseStream = new LzmaStream(new[] { _dictionarySize }, stream); + } + + public override int Read(byte[] buffer, int offset, int count) + { + return BaseStream.Read(buffer, offset, count); + } + + public override int ReadByte() + { + return BaseStream.ReadByte(); + } } diff --git a/src/SharpCompress/Compressors/Xz/Filters/PowerPCFilter.cs b/src/SharpCompress/Compressors/Xz/Filters/PowerPCFilter.cs index b9566c35..dfd7caa1 100644 --- a/src/SharpCompress/Compressors/Xz/Filters/PowerPCFilter.cs +++ b/src/SharpCompress/Compressors/Xz/Filters/PowerPCFilter.cs @@ -4,62 +4,56 @@ * @TODO Properties offset */ -using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using SharpCompress.Compressors.Filters; -namespace SharpCompress.Compressors.Xz.Filters +namespace SharpCompress.Compressors.Xz.Filters; + +public class PowerPCFilter : BlockFilter { - public class PowerPCFilter : BlockFilter + public override bool AllowAsLast => false; + + public override bool AllowAsNonLast => true; + + public override bool ChangesDataSize => false; + + private uint _ip = 0; + + //private UInt32 _offset = 0; + + public override void Init(byte[] properties) { - public override bool AllowAsLast => false; - - public override bool AllowAsNonLast => true; - - public override bool ChangesDataSize => false; - - private UInt32 _ip = 0; - - //private UInt32 _offset = 0; - - public override void Init(byte[] properties) + if (properties.Length != 0 && properties.Length != 4) { - if (properties.Length != 0 && properties.Length != 4) - { - throw new InvalidDataException("PPC properties unexpected length"); - } - - if (properties.Length == 4) - { - // Even XZ doesn't support it. - throw new InvalidDataException("PPC properties offset is not supported"); - - //_offset = BitConverter.ToUInt32(properties, 0); - // - //if (_offset % (UInt32)BranchExec.Alignment.ARCH_PowerPC_ALIGNMENT != 0) - //{ - // throw new InvalidDataException("Filter offset does not match alignment"); - //} - } + throw new InvalidDataException("PPC properties unexpected length"); } - public override void ValidateFilter() { } - - public override int Read(byte[] buffer, int offset, int count) + if (properties.Length == 4) { - int bytesRead = BaseStream.Read(buffer, offset, count); - BranchExecFilter.PowerPCConverter(buffer, _ip); - _ip += (UInt32)bytesRead; - return bytesRead; - } + // Even XZ doesn't support it. + throw new InvalidDataException("PPC properties offset is not supported"); - public override void SetBaseStream(Stream stream) - { - BaseStream = stream; + //_offset = BitConverter.ToUInt32(properties, 0); + // + //if (_offset % (UInt32)BranchExec.Alignment.ARCH_PowerPC_ALIGNMENT != 0) + //{ + // throw new InvalidDataException("Filter offset does not match alignment"); + //} } } + + public override void ValidateFilter() { } + + public override int Read(byte[] buffer, int offset, int count) + { + var bytesRead = BaseStream.Read(buffer, offset, count); + BranchExecFilter.PowerPCConverter(buffer, _ip); + _ip += (uint)bytesRead; + return bytesRead; + } + + public override void SetBaseStream(Stream stream) + { + BaseStream = stream; + } } diff --git a/src/SharpCompress/Compressors/Xz/Filters/SparcFilter.cs b/src/SharpCompress/Compressors/Xz/Filters/SparcFilter.cs index 1b7d4edd..141acb46 100644 --- a/src/SharpCompress/Compressors/Xz/Filters/SparcFilter.cs +++ b/src/SharpCompress/Compressors/Xz/Filters/SparcFilter.cs @@ -4,62 +4,56 @@ * @TODO Properties offset */ -using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using SharpCompress.Compressors.Filters; -namespace SharpCompress.Compressors.Xz.Filters +namespace SharpCompress.Compressors.Xz.Filters; + +public class SparcFilter : BlockFilter { - public class SparcFilter : BlockFilter + public override bool AllowAsLast => false; + + public override bool AllowAsNonLast => true; + + public override bool ChangesDataSize => false; + + private uint _ip = 0; + + //private UInt32 _offset = 0; + + public override void Init(byte[] properties) { - public override bool AllowAsLast => false; - - public override bool AllowAsNonLast => true; - - public override bool ChangesDataSize => false; - - private UInt32 _ip = 0; - - //private UInt32 _offset = 0; - - public override void Init(byte[] properties) + if (properties.Length != 0 && properties.Length != 4) { - if (properties.Length != 0 && properties.Length != 4) - { - throw new InvalidDataException("SPARC properties unexpected length"); - } - - if (properties.Length == 4) - { - // Even XZ doesn't support it. - throw new InvalidDataException("SPARC properties offset is not supported"); - - //_offset = BitConverter.ToUInt32(properties, 0); - // - //if (_offset % (UInt32)BranchExec.Alignment.ARCH_SPARC_ALIGNMENT != 0) - //{ - // throw new InvalidDataException("Filter offset does not match alignment"); - //} - } + throw new InvalidDataException("SPARC properties unexpected length"); } - public override void ValidateFilter() { } - - public override int Read(byte[] buffer, int offset, int count) + if (properties.Length == 4) { - int bytesRead = BaseStream.Read(buffer, offset, count); - BranchExecFilter.SPARCConverter(buffer, _ip); - _ip += (UInt32)bytesRead; - return bytesRead; - } + // Even XZ doesn't support it. + throw new InvalidDataException("SPARC properties offset is not supported"); - public override void SetBaseStream(Stream stream) - { - BaseStream = stream; + //_offset = BitConverter.ToUInt32(properties, 0); + // + //if (_offset % (UInt32)BranchExec.Alignment.ARCH_SPARC_ALIGNMENT != 0) + //{ + // throw new InvalidDataException("Filter offset does not match alignment"); + //} } } + + public override void ValidateFilter() { } + + public override int Read(byte[] buffer, int offset, int count) + { + var bytesRead = BaseStream.Read(buffer, offset, count); + BranchExecFilter.SPARCConverter(buffer, _ip); + _ip += (uint)bytesRead; + return bytesRead; + } + + public override void SetBaseStream(Stream stream) + { + BaseStream = stream; + } } diff --git a/src/SharpCompress/Compressors/Xz/Filters/X86Filter.cs b/src/SharpCompress/Compressors/Xz/Filters/X86Filter.cs index 46d5837a..2713a344 100644 --- a/src/SharpCompress/Compressors/Xz/Filters/X86Filter.cs +++ b/src/SharpCompress/Compressors/Xz/Filters/X86Filter.cs @@ -4,64 +4,58 @@ * @TODO Properties offset */ -using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using SharpCompress.Compressors.Filters; -namespace SharpCompress.Compressors.Xz.Filters +namespace SharpCompress.Compressors.Xz.Filters; + +public class X86Filter : BlockFilter { - public class X86Filter : BlockFilter + public override bool AllowAsLast => false; + + public override bool AllowAsNonLast => true; + + public override bool ChangesDataSize => false; + + private uint _ip = 0; + + private uint _state = 0; + + //private UInt32 _offset = 0; + + public override void Init(byte[] properties) { - public override bool AllowAsLast => false; - - public override bool AllowAsNonLast => true; - - public override bool ChangesDataSize => false; - - private UInt32 _ip = 0; - - private UInt32 _state = 0; - - //private UInt32 _offset = 0; - - public override void Init(byte[] properties) + if (properties.Length != 0 && properties.Length != 4) { - if (properties.Length != 0 && properties.Length != 4) - { - throw new InvalidDataException("X86 properties unexpected length"); - } - - if (properties.Length == 4) - { - // Even XZ doesn't support it. - throw new InvalidDataException("X86 properties offset is not supported"); - - //_offset = BitConverter.ToUInt32(properties, 0); - // - //if (_offset % (UInt32)BranchExec.Alignment.ARCH_x86_ALIGNMENT != 0) - //{ - // throw new InvalidDataException("Filter offset does not match alignment"); - //} - } + throw new InvalidDataException("X86 properties unexpected length"); } - public override void ValidateFilter() { } - - public override int Read(byte[] buffer, int offset, int count) + if (properties.Length == 4) { - int bytesRead = BaseStream.Read(buffer, offset, count); - BranchExecFilter.X86Converter(buffer, _ip, ref _state); - _ip += (UInt32)bytesRead; - return bytesRead; - } + // Even XZ doesn't support it. + throw new InvalidDataException("X86 properties offset is not supported"); - public override void SetBaseStream(Stream stream) - { - BaseStream = stream; + //_offset = BitConverter.ToUInt32(properties, 0); + // + //if (_offset % (UInt32)BranchExec.Alignment.ARCH_x86_ALIGNMENT != 0) + //{ + // throw new InvalidDataException("Filter offset does not match alignment"); + //} } } + + public override void ValidateFilter() { } + + public override int Read(byte[] buffer, int offset, int count) + { + var bytesRead = BaseStream.Read(buffer, offset, count); + BranchExecFilter.X86Converter(buffer, _ip, ref _state); + _ip += (uint)bytesRead; + return bytesRead; + } + + public override void SetBaseStream(Stream stream) + { + BaseStream = stream; + } } diff --git a/src/SharpCompress/Compressors/Xz/MultiByteIntegers.cs b/src/SharpCompress/Compressors/Xz/MultiByteIntegers.cs index 27f94644..a38505da 100644 --- a/src/SharpCompress/Compressors/Xz/MultiByteIntegers.cs +++ b/src/SharpCompress/Compressors/Xz/MultiByteIntegers.cs @@ -1,42 +1,41 @@ -using System; +using System; using System.IO; -namespace SharpCompress.Compressors.Xz +namespace SharpCompress.Compressors.Xz; + +internal static class MultiByteIntegers { - internal static class MultiByteIntegers + public static ulong ReadXZInteger(this BinaryReader reader, int MaxBytes = 9) { - public static ulong ReadXZInteger(this BinaryReader reader, int MaxBytes = 9) + if (MaxBytes <= 0) { - if (MaxBytes <= 0) - { - throw new ArgumentOutOfRangeException(nameof(MaxBytes)); - } - - if (MaxBytes > 9) - { - MaxBytes = 9; - } - - byte LastByte = reader.ReadByte(); - ulong Output = (ulong)LastByte & 0x7F; - - int i = 0; - while ((LastByte & 0x80) != 0) - { - if (++i >= MaxBytes) - { - throw new InvalidDataException(); - } - - LastByte = reader.ReadByte(); - if (LastByte == 0) - { - throw new InvalidDataException(); - } - - Output |= ((ulong)(LastByte & 0x7F)) << (i * 7); - } - return Output; + throw new ArgumentOutOfRangeException(nameof(MaxBytes)); } + + if (MaxBytes > 9) + { + MaxBytes = 9; + } + + var LastByte = reader.ReadByte(); + var Output = (ulong)LastByte & 0x7F; + + var i = 0; + while ((LastByte & 0x80) != 0) + { + if (++i >= MaxBytes) + { + throw new InvalidDataException(); + } + + LastByte = reader.ReadByte(); + if (LastByte == 0) + { + throw new InvalidDataException(); + } + + Output |= ((ulong)(LastByte & 0x7F)) << (i * 7); + } + return Output; } } diff --git a/src/SharpCompress/Compressors/Xz/ReadOnlyStream.cs b/src/SharpCompress/Compressors/Xz/ReadOnlyStream.cs index 0c1b53b4..c890b22d 100644 --- a/src/SharpCompress/Compressors/Xz/ReadOnlyStream.cs +++ b/src/SharpCompress/Compressors/Xz/ReadOnlyStream.cs @@ -1,46 +1,45 @@ -#nullable disable +#nullable disable using System; using System.IO; -namespace SharpCompress.Compressors.Xz +namespace SharpCompress.Compressors.Xz; + +public abstract class ReadOnlyStream : Stream { - public abstract class ReadOnlyStream : Stream + public Stream BaseStream { get; protected set; } + + public override bool CanRead => BaseStream.CanRead; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override long Length => throw new NotSupportedException(); + + public override long Position { - public Stream BaseStream { get; protected set; } + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } - public override bool CanRead => BaseStream.CanRead; + public override void Flush() + { + throw new NotSupportedException(); + } - public override bool CanSeek => false; + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } - public override bool CanWrite => false; + public override void SetLength(long value) + { + throw new NotSupportedException(); + } - public override long Length => throw new NotSupportedException(); - - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - - public override void Flush() - { - throw new NotSupportedException(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); } } diff --git a/src/SharpCompress/Compressors/Xz/XZBlock.cs b/src/SharpCompress/Compressors/Xz/XZBlock.cs index 38409ecf..5b948500 100644 --- a/src/SharpCompress/Compressors/Xz/XZBlock.cs +++ b/src/SharpCompress/Compressors/Xz/XZBlock.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable using System; using System.Collections.Generic; @@ -6,213 +6,212 @@ using System.IO; using System.Linq; using SharpCompress.Compressors.Xz.Filters; -namespace SharpCompress.Compressors.Xz +namespace SharpCompress.Compressors.Xz; + +[CLSCompliant(false)] +public sealed class XZBlock : XZReadOnlyStream { - [CLSCompliant(false)] - public sealed class XZBlock : XZReadOnlyStream + public int BlockHeaderSize => (_blockHeaderSizeByte + 1) * 4; + public ulong? CompressedSize { get; private set; } + public ulong? UncompressedSize { get; private set; } + public Stack Filters { get; private set; } = new Stack(); + public bool HeaderIsLoaded { get; private set; } + private CheckType _checkType; + private readonly int _checkSize; + private bool _streamConnected; + private int _numFilters; + private byte _blockHeaderSizeByte; + private Stream _decomStream; + private bool _endOfStream; + private bool _paddingSkipped; + private bool _crcChecked; + private ulong _bytesRead; + + public XZBlock(Stream stream, CheckType checkType, int checkSize) : base(stream) { - public int BlockHeaderSize => (_blockHeaderSizeByte + 1) * 4; - public ulong? CompressedSize { get; private set; } - public ulong? UncompressedSize { get; private set; } - public Stack Filters { get; private set; } = new Stack(); - public bool HeaderIsLoaded { get; private set; } - private CheckType _checkType; - private readonly int _checkSize; - private bool _streamConnected; - private int _numFilters; - private byte _blockHeaderSizeByte; - private Stream _decomStream; - private bool _endOfStream; - private bool _paddingSkipped; - private bool _crcChecked; - private ulong _bytesRead; + _checkType = checkType; + _checkSize = checkSize; + } - public XZBlock(Stream stream, CheckType checkType, int checkSize) : base(stream) + public override int Read(byte[] buffer, int offset, int count) + { + var bytesRead = 0; + if (!HeaderIsLoaded) { - _checkType = checkType; - _checkSize = checkSize; + LoadHeader(); } - public override int Read(byte[] buffer, int offset, int count) + if (!_streamConnected) { - int bytesRead = 0; - if (!HeaderIsLoaded) - { - LoadHeader(); - } - - if (!_streamConnected) - { - ConnectStream(); - } - - if (!_endOfStream) - { - bytesRead = _decomStream.Read(buffer, offset, count); - } - - if (bytesRead != count) - { - _endOfStream = true; - } - - if (_endOfStream && !_paddingSkipped) - { - SkipPadding(); - } - - if (_endOfStream && !_crcChecked) - { - CheckCrc(); - } - - _bytesRead += (ulong)bytesRead; - return bytesRead; + ConnectStream(); } - private void SkipPadding() + if (!_endOfStream) { - int bytes = (int)(BaseStream.Position % 4); - if (bytes > 0) - { - byte[] paddingBytes = new byte[4 - bytes]; - BaseStream.Read(paddingBytes, 0, paddingBytes.Length); - if (paddingBytes.Any(b => b != 0)) - { - throw new InvalidDataException("Padding bytes were non-null"); - } - } - _paddingSkipped = true; + bytesRead = _decomStream.Read(buffer, offset, count); } - private void CheckCrc() + if (bytesRead != count) { - byte[] crc = new byte[_checkSize]; - BaseStream.Read(crc, 0, _checkSize); - // Actually do a check (and read in the bytes - // into the function throughout the stream read). - _crcChecked = true; + _endOfStream = true; } - private void ConnectStream() + if (_endOfStream && !_paddingSkipped) { - _decomStream = BaseStream; - while (Filters.Any()) - { - BlockFilter filter = Filters.Pop(); - filter.SetBaseStream(_decomStream); - _decomStream = filter; - } - _streamConnected = true; + SkipPadding(); } - private void LoadHeader() + if (_endOfStream && !_crcChecked) { - ReadHeaderSize(); - byte[] headerCache = CacheHeader(); - - using (var cache = new MemoryStream(headerCache)) - using (var cachedReader = new BinaryReader(cache)) - { - cachedReader.BaseStream.Position = 1; // skip the header size byte - ReadBlockFlags(cachedReader); - ReadFilters(cachedReader); - } - HeaderIsLoaded = true; + CheckCrc(); } - private void ReadHeaderSize() + _bytesRead += (ulong)bytesRead; + return bytesRead; + } + + private void SkipPadding() + { + var bytes = (int)(BaseStream.Position % 4); + if (bytes > 0) { - _blockHeaderSizeByte = (byte)BaseStream.ReadByte(); - if (_blockHeaderSizeByte == 0) + var paddingBytes = new byte[4 - bytes]; + BaseStream.Read(paddingBytes, 0, paddingBytes.Length); + if (paddingBytes.Any(b => b != 0)) { - throw new XZIndexMarkerReachedException(); + throw new InvalidDataException("Padding bytes were non-null"); } } + _paddingSkipped = true; + } - private byte[] CacheHeader() + private void CheckCrc() + { + var crc = new byte[_checkSize]; + BaseStream.Read(crc, 0, _checkSize); + // Actually do a check (and read in the bytes + // into the function throughout the stream read). + _crcChecked = true; + } + + private void ConnectStream() + { + _decomStream = BaseStream; + while (Filters.Any()) { - byte[] blockHeaderWithoutCrc = new byte[BlockHeaderSize - 4]; - blockHeaderWithoutCrc[0] = _blockHeaderSizeByte; - var read = BaseStream.Read(blockHeaderWithoutCrc, 1, BlockHeaderSize - 5); - if (read != BlockHeaderSize - 5) - { - throw new EndOfStreamException("Reached end of stream unexectedly"); - } + var filter = Filters.Pop(); + filter.SetBaseStream(_decomStream); + _decomStream = filter; + } + _streamConnected = true; + } - uint crc = BaseStream.ReadLittleEndianUInt32(); - uint calcCrc = Crc32.Compute(blockHeaderWithoutCrc); - if (crc != calcCrc) - { - throw new InvalidDataException("Block header corrupt"); - } + private void LoadHeader() + { + ReadHeaderSize(); + var headerCache = CacheHeader(); - return blockHeaderWithoutCrc; + using (var cache = new MemoryStream(headerCache)) + using (var cachedReader = new BinaryReader(cache)) + { + cachedReader.BaseStream.Position = 1; // skip the header size byte + ReadBlockFlags(cachedReader); + ReadFilters(cachedReader); + } + HeaderIsLoaded = true; + } + + private void ReadHeaderSize() + { + _blockHeaderSizeByte = (byte)BaseStream.ReadByte(); + if (_blockHeaderSizeByte == 0) + { + throw new XZIndexMarkerReachedException(); + } + } + + private byte[] CacheHeader() + { + var blockHeaderWithoutCrc = new byte[BlockHeaderSize - 4]; + blockHeaderWithoutCrc[0] = _blockHeaderSizeByte; + var read = BaseStream.Read(blockHeaderWithoutCrc, 1, BlockHeaderSize - 5); + if (read != BlockHeaderSize - 5) + { + throw new EndOfStreamException("Reached end of stream unexectedly"); } - private void ReadBlockFlags(BinaryReader reader) + var crc = BaseStream.ReadLittleEndianUInt32(); + var calcCrc = Crc32.Compute(blockHeaderWithoutCrc); + if (crc != calcCrc) { - var blockFlags = reader.ReadByte(); - _numFilters = (blockFlags & 0x03) + 1; - byte reserved = (byte)(blockFlags & 0x3C); - - if (reserved != 0) - { - throw new InvalidDataException( - "Reserved bytes used, perhaps an unknown XZ implementation" - ); - } - - bool compressedSizePresent = (blockFlags & 0x40) != 0; - bool uncompressedSizePresent = (blockFlags & 0x80) != 0; - - if (compressedSizePresent) - { - CompressedSize = reader.ReadXZInteger(); - } - - if (uncompressedSizePresent) - { - UncompressedSize = reader.ReadXZInteger(); - } + throw new InvalidDataException("Block header corrupt"); } - private void ReadFilters(BinaryReader reader, long baseStreamOffset = 0) + return blockHeaderWithoutCrc; + } + + private void ReadBlockFlags(BinaryReader reader) + { + var blockFlags = reader.ReadByte(); + _numFilters = (blockFlags & 0x03) + 1; + var reserved = (byte)(blockFlags & 0x3C); + + if (reserved != 0) { - int nonLastSizeChangers = 0; - for (int i = 0; i < _numFilters; i++) - { - var filter = BlockFilter.Read(reader); - if ( - (i + 1 == _numFilters && !filter.AllowAsLast) - || (i + 1 < _numFilters && !filter.AllowAsNonLast) - ) - { - throw new InvalidDataException("Block Filters in bad order"); - } + throw new InvalidDataException( + "Reserved bytes used, perhaps an unknown XZ implementation" + ); + } - if (filter.ChangesDataSize && i + 1 < _numFilters) - { - nonLastSizeChangers++; - } + var compressedSizePresent = (blockFlags & 0x40) != 0; + var uncompressedSizePresent = (blockFlags & 0x80) != 0; - filter.ValidateFilter(); - Filters.Push(filter); - } - if (nonLastSizeChangers > 2) + if (compressedSizePresent) + { + CompressedSize = reader.ReadXZInteger(); + } + + if (uncompressedSizePresent) + { + UncompressedSize = reader.ReadXZInteger(); + } + } + + private void ReadFilters(BinaryReader reader, long baseStreamOffset = 0) + { + var nonLastSizeChangers = 0; + for (var i = 0; i < _numFilters; i++) + { + var filter = BlockFilter.Read(reader); + if ( + (i + 1 == _numFilters && !filter.AllowAsLast) + || (i + 1 < _numFilters && !filter.AllowAsNonLast) + ) { - throw new InvalidDataException( - "More than two non-last block filters cannot change stream size" - ); + throw new InvalidDataException("Block Filters in bad order"); } - int blockHeaderPaddingSize = - BlockHeaderSize - (4 + (int)(reader.BaseStream.Position - baseStreamOffset)); - byte[] blockHeaderPadding = reader.ReadBytes(blockHeaderPaddingSize); - if (!blockHeaderPadding.All(b => b == 0)) + if (filter.ChangesDataSize && i + 1 < _numFilters) { - throw new InvalidDataException("Block header contains unknown fields"); + nonLastSizeChangers++; } + + filter.ValidateFilter(); + Filters.Push(filter); + } + if (nonLastSizeChangers > 2) + { + throw new InvalidDataException( + "More than two non-last block filters cannot change stream size" + ); + } + + var blockHeaderPaddingSize = + BlockHeaderSize - (4 + (int)(reader.BaseStream.Position - baseStreamOffset)); + var blockHeaderPadding = reader.ReadBytes(blockHeaderPaddingSize); + if (!blockHeaderPadding.All(b => b == 0)) + { + throw new InvalidDataException("Block header contains unknown fields"); } } } diff --git a/src/SharpCompress/Compressors/Xz/XZFooter.cs b/src/SharpCompress/Compressors/Xz/XZFooter.cs index 40850f31..8632ec7e 100644 --- a/src/SharpCompress/Compressors/Xz/XZFooter.cs +++ b/src/SharpCompress/Compressors/Xz/XZFooter.cs @@ -4,52 +4,51 @@ using System.Linq; using System.Text; using SharpCompress.IO; -namespace SharpCompress.Compressors.Xz +namespace SharpCompress.Compressors.Xz; + +public class XZFooter { - public class XZFooter + private readonly BinaryReader _reader; + private static ReadOnlySpan _magicBytes => "YZ"u8; + public long StreamStartPosition { get; private set; } + public long BackwardSize { get; private set; } + public byte[]? StreamFlags { get; private set; } + + public XZFooter(BinaryReader reader) { - private readonly BinaryReader _reader; - private static ReadOnlySpan _magicBytes => new byte[] { 0x59, 0x5A }; - public long StreamStartPosition { get; private set; } - public long BackwardSize { get; private set; } - public byte[]? StreamFlags { get; private set; } + _reader = reader; + StreamStartPosition = reader.BaseStream.Position; + } - public XZFooter(BinaryReader reader) + public static XZFooter FromStream(Stream stream) + { + var footer = new XZFooter( + new BinaryReader(NonDisposingStream.Create(stream), Encoding.UTF8) + ); + footer.Process(); + return footer; + } + + public void Process() + { + var crc = _reader.ReadLittleEndianUInt32(); + var footerBytes = _reader.ReadBytes(6); + var myCrc = Crc32.Compute(footerBytes); + if (crc != myCrc) { - _reader = reader; - StreamStartPosition = reader.BaseStream.Position; + throw new InvalidDataException("Footer corrupt"); } - public static XZFooter FromStream(Stream stream) + using (var stream = new MemoryStream(footerBytes)) + using (var reader = new BinaryReader(stream)) { - var footer = new XZFooter( - new BinaryReader(NonDisposingStream.Create(stream), Encoding.UTF8) - ); - footer.Process(); - return footer; + BackwardSize = (reader.ReadLittleEndianUInt32() + 1) * 4; + StreamFlags = reader.ReadBytes(2); } - - public void Process() + var magBy = _reader.ReadBytes(2); + if (!magBy.AsSpan().SequenceEqual(_magicBytes)) { - uint crc = _reader.ReadLittleEndianUInt32(); - byte[] footerBytes = _reader.ReadBytes(6); - uint myCrc = Crc32.Compute(footerBytes); - if (crc != myCrc) - { - throw new InvalidDataException("Footer corrupt"); - } - - using (var stream = new MemoryStream(footerBytes)) - using (var reader = new BinaryReader(stream)) - { - BackwardSize = (reader.ReadLittleEndianUInt32() + 1) * 4; - StreamFlags = reader.ReadBytes(2); - } - byte[] magBy = _reader.ReadBytes(2); - if (!magBy.AsSpan().SequenceEqual(_magicBytes)) - { - throw new InvalidDataException("Magic footer missing"); - } + throw new InvalidDataException("Magic footer missing"); } } } diff --git a/src/SharpCompress/Compressors/Xz/XZHeader.cs b/src/SharpCompress/Compressors/Xz/XZHeader.cs index 5968cc62..16277dc6 100644 --- a/src/SharpCompress/Compressors/Xz/XZHeader.cs +++ b/src/SharpCompress/Compressors/Xz/XZHeader.cs @@ -4,60 +4,59 @@ using System.Linq; using System.Text; using SharpCompress.IO; -namespace SharpCompress.Compressors.Xz +namespace SharpCompress.Compressors.Xz; + +public class XZHeader { - public class XZHeader + private readonly BinaryReader _reader; + private readonly byte[] MagicHeader = { 0xFD, 0x37, 0x7A, 0x58, 0x5a, 0x00 }; + + public CheckType BlockCheckType { get; private set; } + public int BlockCheckSize => ((((int)BlockCheckType) + 2) / 3) * 4; + + public XZHeader(BinaryReader reader) { - private readonly BinaryReader _reader; - private readonly byte[] MagicHeader = { 0xFD, 0x37, 0x7A, 0x58, 0x5a, 0x00 }; + _reader = reader; + } - public CheckType BlockCheckType { get; private set; } - public int BlockCheckSize => ((((int)BlockCheckType) + 2) / 3) * 4; + public static XZHeader FromStream(Stream stream) + { + var header = new XZHeader( + new BinaryReader(NonDisposingStream.Create(stream), Encoding.UTF8) + ); + header.Process(); + return header; + } - public XZHeader(BinaryReader reader) + public void Process() + { + CheckMagicBytes(_reader.ReadBytes(6)); + ProcessStreamFlags(); + } + + private void ProcessStreamFlags() + { + var streamFlags = _reader.ReadBytes(2); + var crc = _reader.ReadLittleEndianUInt32(); + var calcCrc = Crc32.Compute(streamFlags); + if (crc != calcCrc) { - _reader = reader; + throw new InvalidDataException("Stream header corrupt"); } - public static XZHeader FromStream(Stream stream) + BlockCheckType = (CheckType)(streamFlags[1] & 0x0F); + var futureUse = (byte)(streamFlags[1] & 0xF0); + if (futureUse != 0 || streamFlags[0] != 0) { - var header = new XZHeader( - new BinaryReader(NonDisposingStream.Create(stream), Encoding.UTF8) - ); - header.Process(); - return header; + throw new InvalidDataException("Unknown XZ Stream Version"); } + } - public void Process() + private void CheckMagicBytes(byte[] header) + { + if (!header.SequenceEqual(MagicHeader)) { - CheckMagicBytes(_reader.ReadBytes(6)); - ProcessStreamFlags(); - } - - private void ProcessStreamFlags() - { - byte[] streamFlags = _reader.ReadBytes(2); - UInt32 crc = _reader.ReadLittleEndianUInt32(); - UInt32 calcCrc = Crc32.Compute(streamFlags); - if (crc != calcCrc) - { - throw new InvalidDataException("Stream header corrupt"); - } - - BlockCheckType = (CheckType)(streamFlags[1] & 0x0F); - byte futureUse = (byte)(streamFlags[1] & 0xF0); - if (futureUse != 0 || streamFlags[0] != 0) - { - throw new InvalidDataException("Unknown XZ Stream Version"); - } - } - - private void CheckMagicBytes(byte[] header) - { - if (!header.SequenceEqual(MagicHeader)) - { - throw new InvalidDataException("Invalid XZ Stream"); - } + throw new InvalidDataException("Invalid XZ Stream"); } } } diff --git a/src/SharpCompress/Compressors/Xz/XZIndex.cs b/src/SharpCompress/Compressors/Xz/XZIndex.cs index 7ad364ce..631f27b2 100644 --- a/src/SharpCompress/Compressors/Xz/XZIndex.cs +++ b/src/SharpCompress/Compressors/Xz/XZIndex.cs @@ -5,81 +5,80 @@ using System.Linq; using System.Text; using SharpCompress.IO; -namespace SharpCompress.Compressors.Xz +namespace SharpCompress.Compressors.Xz; + +[CLSCompliant(false)] +public class XZIndex { - [CLSCompliant(false)] - public class XZIndex + private readonly BinaryReader _reader; + public long StreamStartPosition { get; private set; } + public ulong NumberOfRecords { get; private set; } + public List Records { get; } = new List(); + + private readonly bool _indexMarkerAlreadyVerified; + + public XZIndex(BinaryReader reader, bool indexMarkerAlreadyVerified) { - private readonly BinaryReader _reader; - public long StreamStartPosition { get; private set; } - public ulong NumberOfRecords { get; private set; } - public List Records { get; } = new List(); - - private readonly bool _indexMarkerAlreadyVerified; - - public XZIndex(BinaryReader reader, bool indexMarkerAlreadyVerified) + _reader = reader; + _indexMarkerAlreadyVerified = indexMarkerAlreadyVerified; + StreamStartPosition = reader.BaseStream.Position; + if (indexMarkerAlreadyVerified) { - _reader = reader; - _indexMarkerAlreadyVerified = indexMarkerAlreadyVerified; - StreamStartPosition = reader.BaseStream.Position; - if (indexMarkerAlreadyVerified) - { - StreamStartPosition--; - } - } - - public static XZIndex FromStream(Stream stream, bool indexMarkerAlreadyVerified) - { - var index = new XZIndex( - new BinaryReader(NonDisposingStream.Create(stream), Encoding.UTF8), - indexMarkerAlreadyVerified - ); - index.Process(); - return index; - } - - public void Process() - { - if (!_indexMarkerAlreadyVerified) - { - VerifyIndexMarker(); - } - - NumberOfRecords = _reader.ReadXZInteger(); - for (ulong i = 0; i < NumberOfRecords; i++) - { - Records.Add(XZIndexRecord.FromBinaryReader(_reader)); - } - SkipPadding(); - VerifyCrc32(); - } - - private void VerifyIndexMarker() - { - byte marker = _reader.ReadByte(); - if (marker != 0) - { - throw new InvalidDataException("Not an index block"); - } - } - - private void SkipPadding() - { - int bytes = (int)(_reader.BaseStream.Position - StreamStartPosition) % 4; - if (bytes > 0) - { - byte[] paddingBytes = _reader.ReadBytes(4 - bytes); - if (paddingBytes.Any(b => b != 0)) - { - throw new InvalidDataException("Padding bytes were non-null"); - } - } - } - - private void VerifyCrc32() - { - uint crc = _reader.ReadLittleEndianUInt32(); - // TODO verify this matches + StreamStartPosition--; } } + + public static XZIndex FromStream(Stream stream, bool indexMarkerAlreadyVerified) + { + var index = new XZIndex( + new BinaryReader(NonDisposingStream.Create(stream), Encoding.UTF8), + indexMarkerAlreadyVerified + ); + index.Process(); + return index; + } + + public void Process() + { + if (!_indexMarkerAlreadyVerified) + { + VerifyIndexMarker(); + } + + NumberOfRecords = _reader.ReadXZInteger(); + for (ulong i = 0; i < NumberOfRecords; i++) + { + Records.Add(XZIndexRecord.FromBinaryReader(_reader)); + } + SkipPadding(); + VerifyCrc32(); + } + + private void VerifyIndexMarker() + { + var marker = _reader.ReadByte(); + if (marker != 0) + { + throw new InvalidDataException("Not an index block"); + } + } + + private void SkipPadding() + { + var bytes = (int)(_reader.BaseStream.Position - StreamStartPosition) % 4; + if (bytes > 0) + { + var paddingBytes = _reader.ReadBytes(4 - bytes); + if (paddingBytes.Any(b => b != 0)) + { + throw new InvalidDataException("Padding bytes were non-null"); + } + } + } + + private void VerifyCrc32() + { + var crc = _reader.ReadLittleEndianUInt32(); + // TODO verify this matches + } } diff --git a/src/SharpCompress/Compressors/Xz/XZIndexMarkerReachedException.cs b/src/SharpCompress/Compressors/Xz/XZIndexMarkerReachedException.cs index d5be9a82..bb006a35 100644 --- a/src/SharpCompress/Compressors/Xz/XZIndexMarkerReachedException.cs +++ b/src/SharpCompress/Compressors/Xz/XZIndexMarkerReachedException.cs @@ -1,6 +1,5 @@ -using System; +using System; -namespace SharpCompress.Compressors.Xz -{ - public class XZIndexMarkerReachedException : Exception { } -} +namespace SharpCompress.Compressors.Xz; + +public class XZIndexMarkerReachedException : Exception { } diff --git a/src/SharpCompress/Compressors/Xz/XZIndexRecord.cs b/src/SharpCompress/Compressors/Xz/XZIndexRecord.cs index f0ee443e..e05b988b 100644 --- a/src/SharpCompress/Compressors/Xz/XZIndexRecord.cs +++ b/src/SharpCompress/Compressors/Xz/XZIndexRecord.cs @@ -1,22 +1,21 @@ -using System; +using System; using System.IO; -namespace SharpCompress.Compressors.Xz +namespace SharpCompress.Compressors.Xz; + +[CLSCompliant(false)] +public class XZIndexRecord { - [CLSCompliant(false)] - public class XZIndexRecord + public ulong UnpaddedSize { get; private set; } + public ulong UncompressedSize { get; private set; } + + protected XZIndexRecord() { } + + public static XZIndexRecord FromBinaryReader(BinaryReader br) { - public ulong UnpaddedSize { get; private set; } - public ulong UncompressedSize { get; private set; } - - protected XZIndexRecord() { } - - public static XZIndexRecord FromBinaryReader(BinaryReader br) - { - var record = new XZIndexRecord(); - record.UnpaddedSize = br.ReadXZInteger(); - record.UncompressedSize = br.ReadXZInteger(); - return record; - } + var record = new XZIndexRecord(); + record.UnpaddedSize = br.ReadXZInteger(); + record.UncompressedSize = br.ReadXZInteger(); + return record; } } diff --git a/src/SharpCompress/Compressors/Xz/XZReadOnlyStream.cs b/src/SharpCompress/Compressors/Xz/XZReadOnlyStream.cs index fa376c51..4c1aa02b 100644 --- a/src/SharpCompress/Compressors/Xz/XZReadOnlyStream.cs +++ b/src/SharpCompress/Compressors/Xz/XZReadOnlyStream.cs @@ -1,16 +1,15 @@ -using System.IO; +using System.IO; -namespace SharpCompress.Compressors.Xz +namespace SharpCompress.Compressors.Xz; + +public abstract class XZReadOnlyStream : ReadOnlyStream { - public abstract class XZReadOnlyStream : ReadOnlyStream + public XZReadOnlyStream(Stream stream) { - public XZReadOnlyStream(Stream stream) + BaseStream = stream; + if (!BaseStream.CanRead) { - BaseStream = stream; - if (!BaseStream.CanRead) - { - throw new InvalidDataException("Must be able to read from stream"); - } + throw new InvalidDataException("Must be able to read from stream"); } } } diff --git a/src/SharpCompress/Compressors/Xz/XZStream.cs b/src/SharpCompress/Compressors/Xz/XZStream.cs index 41d968f5..f1157182 100644 --- a/src/SharpCompress/Compressors/Xz/XZStream.cs +++ b/src/SharpCompress/Compressors/Xz/XZStream.cs @@ -1,134 +1,133 @@ -#nullable disable +#nullable disable using System; using System.IO; -namespace SharpCompress.Compressors.Xz +namespace SharpCompress.Compressors.Xz; + +[CLSCompliant(false)] +public sealed class XZStream : XZReadOnlyStream { - [CLSCompliant(false)] - public sealed class XZStream : XZReadOnlyStream + public static bool IsXZStream(Stream stream) { - public static bool IsXZStream(Stream stream) + try + { + return null != XZHeader.FromStream(stream); + } + catch (Exception) + { + return false; + } + } + + private void AssertBlockCheckTypeIsSupported() + { + switch (Header.BlockCheckType) + { + case CheckType.NONE: + break; + case CheckType.CRC32: + break; + case CheckType.CRC64: + break; + case CheckType.SHA256: + throw new NotImplementedException(); + default: + throw new NotSupportedException( + "Check Type unknown to this version of decoder." + ); + } + } + + public XZHeader Header { get; private set; } + public XZIndex Index { get; private set; } + public XZFooter Footer { get; private set; } + public bool HeaderIsRead { get; private set; } + private XZBlock _currentBlock; + + private bool _endOfStream; + + public XZStream(Stream stream) : base(stream) { } + + public override int Read(byte[] buffer, int offset, int count) + { + var bytesRead = 0; + if (_endOfStream) + { + return bytesRead; + } + + if (!HeaderIsRead) + { + ReadHeader(); + } + + bytesRead = ReadBlocks(buffer, offset, count); + if (bytesRead < count) + { + _endOfStream = true; + ReadIndex(); + ReadFooter(); + } + return bytesRead; + } + + private void ReadHeader() + { + Header = XZHeader.FromStream(BaseStream); + AssertBlockCheckTypeIsSupported(); + HeaderIsRead = true; + } + + private void ReadIndex() + { + Index = XZIndex.FromStream(BaseStream, true); + // TODO veryfy Index + } + + private void ReadFooter() + { + Footer = XZFooter.FromStream(BaseStream); + // TODO verify footer + } + + private int ReadBlocks(byte[] buffer, int offset, int count) + { + var bytesRead = 0; + if (_currentBlock is null) + { + NextBlock(); + } + + for (; ; ) { try { - return null != XZHeader.FromStream(stream); - } - catch (Exception) - { - return false; - } - } - - private void AssertBlockCheckTypeIsSupported() - { - switch (Header.BlockCheckType) - { - case CheckType.NONE: - break; - case CheckType.CRC32: - break; - case CheckType.CRC64: - break; - case CheckType.SHA256: - throw new NotImplementedException(); - default: - throw new NotSupportedException( - "Check Type unknown to this version of decoder." - ); - } - } - - public XZHeader Header { get; private set; } - public XZIndex Index { get; private set; } - public XZFooter Footer { get; private set; } - public bool HeaderIsRead { get; private set; } - private XZBlock _currentBlock; - - private bool _endOfStream; - - public XZStream(Stream stream) : base(stream) { } - - public override int Read(byte[] buffer, int offset, int count) - { - int bytesRead = 0; - if (_endOfStream) - { - return bytesRead; - } - - if (!HeaderIsRead) - { - ReadHeader(); - } - - bytesRead = ReadBlocks(buffer, offset, count); - if (bytesRead < count) - { - _endOfStream = true; - ReadIndex(); - ReadFooter(); - } - return bytesRead; - } - - private void ReadHeader() - { - Header = XZHeader.FromStream(BaseStream); - AssertBlockCheckTypeIsSupported(); - HeaderIsRead = true; - } - - private void ReadIndex() - { - Index = XZIndex.FromStream(BaseStream, true); - // TODO veryfy Index - } - - private void ReadFooter() - { - Footer = XZFooter.FromStream(BaseStream); - // TODO verify footer - } - - private int ReadBlocks(byte[] buffer, int offset, int count) - { - int bytesRead = 0; - if (_currentBlock is null) - { - NextBlock(); - } - - for (; ; ) - { - try - { - if (bytesRead >= count) - { - break; - } - - int remaining = count - bytesRead; - int newOffset = offset + bytesRead; - int justRead = _currentBlock.Read(buffer, newOffset, remaining); - if (justRead < remaining) - { - NextBlock(); - } - - bytesRead += justRead; - } - catch (XZIndexMarkerReachedException) + if (bytesRead >= count) { break; } - } - return bytesRead; - } - private void NextBlock() - { - _currentBlock = new XZBlock(BaseStream, Header.BlockCheckType, Header.BlockCheckSize); + var remaining = count - bytesRead; + var newOffset = offset + bytesRead; + var justRead = _currentBlock.Read(buffer, newOffset, remaining); + if (justRead < remaining) + { + NextBlock(); + } + + bytesRead += justRead; + } + catch (XZIndexMarkerReachedException) + { + break; + } } + return bytesRead; + } + + private void NextBlock() + { + _currentBlock = new XZBlock(BaseStream, Header.BlockCheckType, Header.BlockCheckSize); } } diff --git a/src/SharpCompress/Crypto/Crc32Stream.cs b/src/SharpCompress/Crypto/Crc32Stream.cs index d3349bf5..827b1fad 100644 --- a/src/SharpCompress/Crypto/Crc32Stream.cs +++ b/src/SharpCompress/Crypto/Crc32Stream.cs @@ -1,145 +1,144 @@ -#nullable disable +#nullable disable using System; using System.IO; -namespace SharpCompress.Crypto +namespace SharpCompress.Crypto; + +[CLSCompliant(false)] +public sealed class Crc32Stream : Stream { - [CLSCompliant(false)] - public sealed class Crc32Stream : Stream + public const uint DefaultPolynomial = 0xedb88320u; + public const uint DefaultSeed = 0xffffffffu; + + private static uint[] defaultTable; + + private readonly uint[] table; + private uint hash; + + private readonly Stream stream; + + public Crc32Stream(Stream stream) : this(stream, DefaultPolynomial, DefaultSeed) { } + + public Crc32Stream(Stream stream, uint polynomial, uint seed) { - public const uint DefaultPolynomial = 0xedb88320u; - public const uint DefaultSeed = 0xffffffffu; + this.stream = stream; + table = InitializeTable(polynomial); + hash = seed; + } - private static uint[] defaultTable; + public Stream WrappedStream => stream; - private readonly uint[] table; - private uint hash; + public override void Flush() + { + stream.Flush(); + } - private readonly Stream stream; + public override int Read(byte[] buffer, int offset, int count) => + throw new NotSupportedException(); - public Crc32Stream(Stream stream) : this(stream, DefaultPolynomial, DefaultSeed) { } + public override long Seek(long offset, SeekOrigin origin) => + throw new NotSupportedException(); - public Crc32Stream(Stream stream, uint polynomial, uint seed) - { - this.stream = stream; - table = InitializeTable(polynomial); - hash = seed; - } - - public Stream WrappedStream => stream; - - public override void Flush() - { - stream.Flush(); - } - - public override int Read(byte[] buffer, int offset, int count) => - throw new NotSupportedException(); - - public override long Seek(long offset, SeekOrigin origin) => - throw new NotSupportedException(); - - public override void SetLength(long value) => throw new NotSupportedException(); + public override void SetLength(long value) => throw new NotSupportedException(); #if !NETFRAMEWORK && !NETSTANDARD2_0 - public override void Write(ReadOnlySpan buffer) - { - stream.Write(buffer); + public override void Write(ReadOnlySpan buffer) + { + stream.Write(buffer); - hash = CalculateCrc(table, hash, buffer); - } + hash = CalculateCrc(table, hash, buffer); + } #endif - public override void Write(byte[] buffer, int offset, int count) + public override void Write(byte[] buffer, int offset, int count) + { + stream.Write(buffer, offset, count); + hash = CalculateCrc(table, hash, buffer.AsSpan(offset, count)); + } + + public override void WriteByte(byte value) + { + stream.WriteByte(value); + hash = CalculateCrc(table, hash, value); + } + + public override bool CanRead => stream.CanRead; + public override bool CanSeek => false; + public override bool CanWrite => stream.CanWrite; + public override long Length => throw new NotSupportedException(); + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public uint Crc => ~hash; + + public static uint Compute(byte[] buffer) + { + return Compute(DefaultSeed, buffer); + } + + public static uint Compute(uint seed, byte[] buffer) + { + return Compute(DefaultPolynomial, seed, buffer); + } + + public static uint Compute(uint polynomial, uint seed, ReadOnlySpan buffer) + { + return ~CalculateCrc(InitializeTable(polynomial), seed, buffer); + } + + private static uint[] InitializeTable(uint polynomial) + { + if (polynomial == DefaultPolynomial && defaultTable != null) { - stream.Write(buffer, offset, count); - hash = CalculateCrc(table, hash, buffer.AsSpan(offset, count)); + return defaultTable; } - public override void WriteByte(byte value) + var createTable = new uint[256]; + for (var i = 0; i < 256; i++) { - stream.WriteByte(value); - hash = CalculateCrc(table, hash, value); - } - - public override bool CanRead => stream.CanRead; - public override bool CanSeek => false; - public override bool CanWrite => stream.CanWrite; - public override long Length => throw new NotSupportedException(); - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - - public uint Crc => ~hash; - - public static uint Compute(byte[] buffer) - { - return Compute(DefaultSeed, buffer); - } - - public static uint Compute(uint seed, byte[] buffer) - { - return Compute(DefaultPolynomial, seed, buffer); - } - - public static uint Compute(uint polynomial, uint seed, ReadOnlySpan buffer) - { - return ~CalculateCrc(InitializeTable(polynomial), seed, buffer); - } - - private static uint[] InitializeTable(uint polynomial) - { - if (polynomial == DefaultPolynomial && defaultTable != null) + var entry = (uint)i; + for (var j = 0; j < 8; j++) { - return defaultTable; - } - - var createTable = new uint[256]; - for (var i = 0; i < 256; i++) - { - var entry = (uint)i; - for (var j = 0; j < 8; j++) + if ((entry & 1) == 1) { - if ((entry & 1) == 1) - { - entry = (entry >> 1) ^ polynomial; - } - else - { - entry = entry >> 1; - } + entry = (entry >> 1) ^ polynomial; } - - createTable[i] = entry; - } - - if (polynomial == DefaultPolynomial) - { - defaultTable = createTable; - } - - return createTable; - } - - private static uint CalculateCrc(uint[] table, uint crc, ReadOnlySpan buffer) - { - unchecked - { - for (int i = 0; i < buffer.Length; i++) + else { - crc = CalculateCrc(table, crc, buffer[i]); + entry >>= 1; } } - return crc; + + createTable[i] = entry; } - private static uint CalculateCrc(uint[] table, uint crc, byte b) + if (polynomial == DefaultPolynomial) { - return (crc >> 8) ^ table[(crc ^ b) & 0xFF]; + defaultTable = createTable; } + + return createTable; + } + + private static uint CalculateCrc(uint[] table, uint crc, ReadOnlySpan buffer) + { + unchecked + { + for (var i = 0; i < buffer.Length; i++) + { + crc = CalculateCrc(table, crc, buffer[i]); + } + } + return crc; + } + + private static uint CalculateCrc(uint[] table, uint crc, byte b) + { + return (crc >> 8) ^ table[(crc ^ b) & 0xFF]; } } diff --git a/src/SharpCompress/Crypto/CryptoException.cs b/src/SharpCompress/Crypto/CryptoException.cs index f67e6a01..575c34d9 100644 --- a/src/SharpCompress/Crypto/CryptoException.cs +++ b/src/SharpCompress/Crypto/CryptoException.cs @@ -1,13 +1,12 @@ -using System; +using System; -namespace SharpCompress.Crypto +namespace SharpCompress.Crypto; + +public class CryptoException : Exception { - public class CryptoException : Exception - { - public CryptoException() { } + public CryptoException() { } - public CryptoException(string message) : base(message) { } + public CryptoException(string message) : base(message) { } - public CryptoException(string message, Exception exception) : base(message, exception) { } - } + public CryptoException(string message, Exception exception) : base(message, exception) { } } diff --git a/src/SharpCompress/Crypto/DataLengthException.cs b/src/SharpCompress/Crypto/DataLengthException.cs index d1069755..f751adf5 100644 --- a/src/SharpCompress/Crypto/DataLengthException.cs +++ b/src/SharpCompress/Crypto/DataLengthException.cs @@ -1,24 +1,23 @@ -using System; +using System; -namespace SharpCompress.Crypto +namespace SharpCompress.Crypto; + +public class DataLengthException : CryptoException { - public class DataLengthException : CryptoException - { - /** - * base constructor. - */ + /** + * base constructor. + */ - public DataLengthException() { } + public DataLengthException() { } - /** - * create a DataLengthException with the given message. - * - * @param message the message to be carried with the exception. - */ + /** + * create a DataLengthException with the given message. + * + * @param message the message to be carried with the exception. + */ - public DataLengthException(string message) : base(message) { } + public DataLengthException(string message) : base(message) { } - public DataLengthException(string message, Exception exception) : base(message, exception) - { } - } + public DataLengthException(string message, Exception exception) : base(message, exception) + { } } diff --git a/src/SharpCompress/Crypto/IBlockCipher.cs b/src/SharpCompress/Crypto/IBlockCipher.cs index eed5fc48..50eb5fb3 100644 --- a/src/SharpCompress/Crypto/IBlockCipher.cs +++ b/src/SharpCompress/Crypto/IBlockCipher.cs @@ -1,34 +1,33 @@ -using System; +using System; -namespace SharpCompress.Crypto +namespace SharpCompress.Crypto; + +/// Base interface for a symmetric key block cipher. +public interface IBlockCipher { - /// Base interface for a symmetric key block cipher. - public interface IBlockCipher - { - /// The name of the algorithm this cipher implements. - string AlgorithmName { get; } + /// The name of the algorithm this cipher implements. + string AlgorithmName { get; } - /// Initialise the cipher. - /// Initialise for encryption if true, for decryption if false. - /// The key or other data required by the cipher. - void Init(bool forEncryption, ICipherParameters parameters); + /// Initialise the cipher. + /// Initialise for encryption if true, for decryption if false. + /// The key or other data required by the cipher. + void Init(bool forEncryption, ICipherParameters parameters); - /// The block size for this cipher, in bytes. - int GetBlockSize(); + /// The block size for this cipher, in bytes. + int GetBlockSize(); - /// Indicates whether this cipher can handle partial blocks. - bool IsPartialBlockOkay { get; } + /// Indicates whether this cipher can handle partial blocks. + bool IsPartialBlockOkay { get; } - /// Process a block. - /// The input buffer. - /// The output buffer. - /// If input block is wrong size, or outBuf too small. - /// The number of bytes processed and produced. - int ProcessBlock(ReadOnlySpan inBuf, Span outBuf); + /// Process a block. + /// The input buffer. + /// The output buffer. + /// If input block is wrong size, or outBuf too small. + /// The number of bytes processed and produced. + int ProcessBlock(ReadOnlySpan inBuf, Span outBuf); - /// - /// Reset the cipher to the same state as it was after the last init (if there was one). - /// - void Reset(); - } + /// + /// Reset the cipher to the same state as it was after the last init (if there was one). + /// + void Reset(); } diff --git a/src/SharpCompress/Crypto/ICipherParameters.cs b/src/SharpCompress/Crypto/ICipherParameters.cs index dbccaa77..9934637a 100644 --- a/src/SharpCompress/Crypto/ICipherParameters.cs +++ b/src/SharpCompress/Crypto/ICipherParameters.cs @@ -1,4 +1,3 @@ -namespace SharpCompress.Crypto -{ - public interface ICipherParameters { } -} +namespace SharpCompress.Crypto; + +public interface ICipherParameters { } diff --git a/src/SharpCompress/Crypto/KeyParameter.cs b/src/SharpCompress/Crypto/KeyParameter.cs index 3fb36f46..e3a7934c 100644 --- a/src/SharpCompress/Crypto/KeyParameter.cs +++ b/src/SharpCompress/Crypto/KeyParameter.cs @@ -1,43 +1,42 @@ -using System; +using System; -namespace SharpCompress.Crypto +namespace SharpCompress.Crypto; + +public class KeyParameter : ICipherParameters { - public class KeyParameter : ICipherParameters + private readonly byte[] key; + + public KeyParameter(byte[] key) { - private readonly byte[] key; - - public KeyParameter(byte[] key) + if (key is null) { - if (key is null) - { - throw new ArgumentNullException(nameof(key)); - } - - this.key = (byte[])key.Clone(); + throw new ArgumentNullException(nameof(key)); } - public KeyParameter(byte[] key, int keyOff, int keyLen) - { - if (key is null) - { - throw new ArgumentNullException(nameof(key)); - } - if (keyOff < 0 || keyOff > key.Length) - { - throw new ArgumentOutOfRangeException(nameof(keyOff)); - } - if (keyLen < 0 || (keyOff + keyLen) > key.Length) - { - throw new ArgumentOutOfRangeException(nameof(keyLen)); - } + this.key = (byte[])key.Clone(); + } - this.key = new byte[keyLen]; - Array.Copy(key, keyOff, this.key, 0, keyLen); + public KeyParameter(byte[] key, int keyOff, int keyLen) + { + if (key is null) + { + throw new ArgumentNullException(nameof(key)); + } + if (keyOff < 0 || keyOff > key.Length) + { + throw new ArgumentOutOfRangeException(nameof(keyOff)); + } + if (keyLen < 0 || (keyOff + keyLen) > key.Length) + { + throw new ArgumentOutOfRangeException(nameof(keyLen)); } - public byte[] GetKey() - { - return (byte[])key.Clone(); - } + this.key = new byte[keyLen]; + Array.Copy(key, keyOff, this.key, 0, keyLen); + } + + public byte[] GetKey() + { + return (byte[])key.Clone(); } } diff --git a/src/SharpCompress/Crypto/RijndaelEngine.cs b/src/SharpCompress/Crypto/RijndaelEngine.cs index 6b91306b..bbcc3da1 100644 --- a/src/SharpCompress/Crypto/RijndaelEngine.cs +++ b/src/SharpCompress/Crypto/RijndaelEngine.cs @@ -1,1913 +1,1911 @@ -using System; +using System; -namespace SharpCompress.Crypto +namespace SharpCompress.Crypto; + +public sealed class RijndaelEngine : IBlockCipher { - public sealed class RijndaelEngine : IBlockCipher + private const int MAXROUNDS = 14; + + private const int MAXKC = (256 / 4); + + private static ReadOnlySpan Logtable => + new byte[] + { + 0, + 0, + 25, + 1, + 50, + 2, + 26, + 198, + 75, + 199, + 27, + 104, + 51, + 238, + 223, + 3, + 100, + 4, + 224, + 14, + 52, + 141, + 129, + 239, + 76, + 113, + 8, + 200, + 248, + 105, + 28, + 193, + 125, + 194, + 29, + 181, + 249, + 185, + 39, + 106, + 77, + 228, + 166, + 114, + 154, + 201, + 9, + 120, + 101, + 47, + 138, + 5, + 33, + 15, + 225, + 36, + 18, + 240, + 130, + 69, + 53, + 147, + 218, + 142, + 150, + 143, + 219, + 189, + 54, + 208, + 206, + 148, + 19, + 92, + 210, + 241, + 64, + 70, + 131, + 56, + 102, + 221, + 253, + 48, + 191, + 6, + 139, + 98, + 179, + 37, + 226, + 152, + 34, + 136, + 145, + 16, + 126, + 110, + 72, + 195, + 163, + 182, + 30, + 66, + 58, + 107, + 40, + 84, + 250, + 133, + 61, + 186, + 43, + 121, + 10, + 21, + 155, + 159, + 94, + 202, + 78, + 212, + 172, + 229, + 243, + 115, + 167, + 87, + 175, + 88, + 168, + 80, + 244, + 234, + 214, + 116, + 79, + 174, + 233, + 213, + 231, + 230, + 173, + 232, + 44, + 215, + 117, + 122, + 235, + 22, + 11, + 245, + 89, + 203, + 95, + 176, + 156, + 169, + 81, + 160, + 127, + 12, + 246, + 111, + 23, + 196, + 73, + 236, + 216, + 67, + 31, + 45, + 164, + 118, + 123, + 183, + 204, + 187, + 62, + 90, + 251, + 96, + 177, + 134, + 59, + 82, + 161, + 108, + 170, + 85, + 41, + 157, + 151, + 178, + 135, + 144, + 97, + 190, + 220, + 252, + 188, + 149, + 207, + 205, + 55, + 63, + 91, + 209, + 83, + 57, + 132, + 60, + 65, + 162, + 109, + 71, + 20, + 42, + 158, + 93, + 86, + 242, + 211, + 171, + 68, + 17, + 146, + 217, + 35, + 32, + 46, + 137, + 180, + 124, + 184, + 38, + 119, + 153, + 227, + 165, + 103, + 74, + 237, + 222, + 197, + 49, + 254, + 24, + 13, + 99, + 140, + 128, + 192, + 247, + 112, + 7 + }; + + private static ReadOnlySpan Alogtable => + new byte[] + { + 0, + 3, + 5, + 15, + 17, + 51, + 85, + 255, + 26, + 46, + 114, + 150, + 161, + 248, + 19, + 53, + 95, + 225, + 56, + 72, + 216, + 115, + 149, + 164, + 247, + 2, + 6, + 10, + 30, + 34, + 102, + 170, + 229, + 52, + 92, + 228, + 55, + 89, + 235, + 38, + 106, + 190, + 217, + 112, + 144, + 171, + 230, + 49, + 83, + 245, + 4, + 12, + 20, + 60, + 68, + 204, + 79, + 209, + 104, + 184, + 211, + 110, + 178, + 205, + 76, + 212, + 103, + 169, + 224, + 59, + 77, + 215, + 98, + 166, + 241, + 8, + 24, + 40, + 120, + 136, + 131, + 158, + 185, + 208, + 107, + 189, + 220, + 127, + 129, + 152, + 179, + 206, + 73, + 219, + 118, + 154, + 181, + 196, + 87, + 249, + 16, + 48, + 80, + 240, + 11, + 29, + 39, + 105, + 187, + 214, + 97, + 163, + 254, + 25, + 43, + 125, + 135, + 146, + 173, + 236, + 47, + 113, + 147, + 174, + 233, + 32, + 96, + 160, + 251, + 22, + 58, + 78, + 210, + 109, + 183, + 194, + 93, + 231, + 50, + 86, + 250, + 21, + 63, + 65, + 195, + 94, + 226, + 61, + 71, + 201, + 64, + 192, + 91, + 237, + 44, + 116, + 156, + 191, + 218, + 117, + 159, + 186, + 213, + 100, + 172, + 239, + 42, + 126, + 130, + 157, + 188, + 223, + 122, + 142, + 137, + 128, + 155, + 182, + 193, + 88, + 232, + 35, + 101, + 175, + 234, + 37, + 111, + 177, + 200, + 67, + 197, + 84, + 252, + 31, + 33, + 99, + 165, + 244, + 7, + 9, + 27, + 45, + 119, + 153, + 176, + 203, + 70, + 202, + 69, + 207, + 74, + 222, + 121, + 139, + 134, + 145, + 168, + 227, + 62, + 66, + 198, + 81, + 243, + 14, + 18, + 54, + 90, + 238, + 41, + 123, + 141, + 140, + 143, + 138, + 133, + 148, + 167, + 242, + 13, + 23, + 57, + 75, + 221, + 124, + 132, + 151, + 162, + 253, + 28, + 36, + 108, + 180, + 199, + 82, + 246, + 1, + 3, + 5, + 15, + 17, + 51, + 85, + 255, + 26, + 46, + 114, + 150, + 161, + 248, + 19, + 53, + 95, + 225, + 56, + 72, + 216, + 115, + 149, + 164, + 247, + 2, + 6, + 10, + 30, + 34, + 102, + 170, + 229, + 52, + 92, + 228, + 55, + 89, + 235, + 38, + 106, + 190, + 217, + 112, + 144, + 171, + 230, + 49, + 83, + 245, + 4, + 12, + 20, + 60, + 68, + 204, + 79, + 209, + 104, + 184, + 211, + 110, + 178, + 205, + 76, + 212, + 103, + 169, + 224, + 59, + 77, + 215, + 98, + 166, + 241, + 8, + 24, + 40, + 120, + 136, + 131, + 158, + 185, + 208, + 107, + 189, + 220, + 127, + 129, + 152, + 179, + 206, + 73, + 219, + 118, + 154, + 181, + 196, + 87, + 249, + 16, + 48, + 80, + 240, + 11, + 29, + 39, + 105, + 187, + 214, + 97, + 163, + 254, + 25, + 43, + 125, + 135, + 146, + 173, + 236, + 47, + 113, + 147, + 174, + 233, + 32, + 96, + 160, + 251, + 22, + 58, + 78, + 210, + 109, + 183, + 194, + 93, + 231, + 50, + 86, + 250, + 21, + 63, + 65, + 195, + 94, + 226, + 61, + 71, + 201, + 64, + 192, + 91, + 237, + 44, + 116, + 156, + 191, + 218, + 117, + 159, + 186, + 213, + 100, + 172, + 239, + 42, + 126, + 130, + 157, + 188, + 223, + 122, + 142, + 137, + 128, + 155, + 182, + 193, + 88, + 232, + 35, + 101, + 175, + 234, + 37, + 111, + 177, + 200, + 67, + 197, + 84, + 252, + 31, + 33, + 99, + 165, + 244, + 7, + 9, + 27, + 45, + 119, + 153, + 176, + 203, + 70, + 202, + 69, + 207, + 74, + 222, + 121, + 139, + 134, + 145, + 168, + 227, + 62, + 66, + 198, + 81, + 243, + 14, + 18, + 54, + 90, + 238, + 41, + 123, + 141, + 140, + 143, + 138, + 133, + 148, + 167, + 242, + 13, + 23, + 57, + 75, + 221, + 124, + 132, + 151, + 162, + 253, + 28, + 36, + 108, + 180, + 199, + 82, + 246, + 1 + }; + + private static ReadOnlySpan S => + new byte[] + { + 99, + 124, + 119, + 123, + 242, + 107, + 111, + 197, + 48, + 1, + 103, + 43, + 254, + 215, + 171, + 118, + 202, + 130, + 201, + 125, + 250, + 89, + 71, + 240, + 173, + 212, + 162, + 175, + 156, + 164, + 114, + 192, + 183, + 253, + 147, + 38, + 54, + 63, + 247, + 204, + 52, + 165, + 229, + 241, + 113, + 216, + 49, + 21, + 4, + 199, + 35, + 195, + 24, + 150, + 5, + 154, + 7, + 18, + 128, + 226, + 235, + 39, + 178, + 117, + 9, + 131, + 44, + 26, + 27, + 110, + 90, + 160, + 82, + 59, + 214, + 179, + 41, + 227, + 47, + 132, + 83, + 209, + 0, + 237, + 32, + 252, + 177, + 91, + 106, + 203, + 190, + 57, + 74, + 76, + 88, + 207, + 208, + 239, + 170, + 251, + 67, + 77, + 51, + 133, + 69, + 249, + 2, + 127, + 80, + 60, + 159, + 168, + 81, + 163, + 64, + 143, + 146, + 157, + 56, + 245, + 188, + 182, + 218, + 33, + 16, + 255, + 243, + 210, + 205, + 12, + 19, + 236, + 95, + 151, + 68, + 23, + 196, + 167, + 126, + 61, + 100, + 93, + 25, + 115, + 96, + 129, + 79, + 220, + 34, + 42, + 144, + 136, + 70, + 238, + 184, + 20, + 222, + 94, + 11, + 219, + 224, + 50, + 58, + 10, + 73, + 6, + 36, + 92, + 194, + 211, + 172, + 98, + 145, + 149, + 228, + 121, + 231, + 200, + 55, + 109, + 141, + 213, + 78, + 169, + 108, + 86, + 244, + 234, + 101, + 122, + 174, + 8, + 186, + 120, + 37, + 46, + 28, + 166, + 180, + 198, + 232, + 221, + 116, + 31, + 75, + 189, + 139, + 138, + 112, + 62, + 181, + 102, + 72, + 3, + 246, + 14, + 97, + 53, + 87, + 185, + 134, + 193, + 29, + 158, + 225, + 248, + 152, + 17, + 105, + 217, + 142, + 148, + 155, + 30, + 135, + 233, + 206, + 85, + 40, + 223, + 140, + 161, + 137, + 13, + 191, + 230, + 66, + 104, + 65, + 153, + 45, + 15, + 176, + 84, + 187, + 22 + }; + + private static ReadOnlySpan Si => + new byte[] + { + 82, + 9, + 106, + 213, + 48, + 54, + 165, + 56, + 191, + 64, + 163, + 158, + 129, + 243, + 215, + 251, + 124, + 227, + 57, + 130, + 155, + 47, + 255, + 135, + 52, + 142, + 67, + 68, + 196, + 222, + 233, + 203, + 84, + 123, + 148, + 50, + 166, + 194, + 35, + 61, + 238, + 76, + 149, + 11, + 66, + 250, + 195, + 78, + 8, + 46, + 161, + 102, + 40, + 217, + 36, + 178, + 118, + 91, + 162, + 73, + 109, + 139, + 209, + 37, + 114, + 248, + 246, + 100, + 134, + 104, + 152, + 22, + 212, + 164, + 92, + 204, + 93, + 101, + 182, + 146, + 108, + 112, + 72, + 80, + 253, + 237, + 185, + 218, + 94, + 21, + 70, + 87, + 167, + 141, + 157, + 132, + 144, + 216, + 171, + 0, + 140, + 188, + 211, + 10, + 247, + 228, + 88, + 5, + 184, + 179, + 69, + 6, + 208, + 44, + 30, + 143, + 202, + 63, + 15, + 2, + 193, + 175, + 189, + 3, + 1, + 19, + 138, + 107, + 58, + 145, + 17, + 65, + 79, + 103, + 220, + 234, + 151, + 242, + 207, + 206, + 240, + 180, + 230, + 115, + 150, + 172, + 116, + 34, + 231, + 173, + 53, + 133, + 226, + 249, + 55, + 232, + 28, + 117, + 223, + 110, + 71, + 241, + 26, + 113, + 29, + 41, + 197, + 137, + 111, + 183, + 98, + 14, + 170, + 24, + 190, + 27, + 252, + 86, + 62, + 75, + 198, + 210, + 121, + 32, + 154, + 219, + 192, + 254, + 120, + 205, + 90, + 244, + 31, + 221, + 168, + 51, + 136, + 7, + 199, + 49, + 177, + 18, + 16, + 89, + 39, + 128, + 236, + 95, + 96, + 81, + 127, + 169, + 25, + 181, + 74, + 13, + 45, + 229, + 122, + 159, + 147, + 201, + 156, + 239, + 160, + 224, + 59, + 77, + 174, + 42, + 245, + 176, + 200, + 235, + 187, + 60, + 131, + 83, + 153, + 97, + 23, + 43, + 4, + 126, + 186, + 119, + 214, + 38, + 225, + 105, + 20, + 99, + 85, + 33, + 12, + 125 + }; + + private static ReadOnlySpan rcon => + new byte[] + { + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91 + }; + + private static readonly byte[][] shifts0 = { - private const int MAXROUNDS = 14; + new byte[] { 0, 8, 16, 24 }, + new byte[] { 0, 8, 16, 24 }, + new byte[] { 0, 8, 16, 24 }, + new byte[] { 0, 8, 16, 32 }, + new byte[] { 0, 8, 24, 32 } + }; - private const int MAXKC = (256 / 4); + private static readonly byte[][] shifts1 = + { + new byte[] { 0, 24, 16, 8 }, + new byte[] { 0, 32, 24, 16 }, + new byte[] { 0, 40, 32, 24 }, + new byte[] { 0, 48, 40, 24 }, + new byte[] { 0, 56, 40, 32 } + }; - private static ReadOnlySpan Logtable => - new byte[] - { - 0, - 0, - 25, - 1, - 50, - 2, - 26, - 198, - 75, - 199, - 27, - 104, - 51, - 238, - 223, - 3, - 100, - 4, - 224, - 14, - 52, - 141, - 129, - 239, - 76, - 113, - 8, - 200, - 248, - 105, - 28, - 193, - 125, - 194, - 29, - 181, - 249, - 185, - 39, - 106, - 77, - 228, - 166, - 114, - 154, - 201, - 9, - 120, - 101, - 47, - 138, - 5, - 33, - 15, - 225, - 36, - 18, - 240, - 130, - 69, - 53, - 147, - 218, - 142, - 150, - 143, - 219, - 189, - 54, - 208, - 206, - 148, - 19, - 92, - 210, - 241, - 64, - 70, - 131, - 56, - 102, - 221, - 253, - 48, - 191, - 6, - 139, - 98, - 179, - 37, - 226, - 152, - 34, - 136, - 145, - 16, - 126, - 110, - 72, - 195, - 163, - 182, - 30, - 66, - 58, - 107, - 40, - 84, - 250, - 133, - 61, - 186, - 43, - 121, - 10, - 21, - 155, - 159, - 94, - 202, - 78, - 212, - 172, - 229, - 243, - 115, - 167, - 87, - 175, - 88, - 168, - 80, - 244, - 234, - 214, - 116, - 79, - 174, - 233, - 213, - 231, - 230, - 173, - 232, - 44, - 215, - 117, - 122, - 235, - 22, - 11, - 245, - 89, - 203, - 95, - 176, - 156, - 169, - 81, - 160, - 127, - 12, - 246, - 111, - 23, - 196, - 73, - 236, - 216, - 67, - 31, - 45, - 164, - 118, - 123, - 183, - 204, - 187, - 62, - 90, - 251, - 96, - 177, - 134, - 59, - 82, - 161, - 108, - 170, - 85, - 41, - 157, - 151, - 178, - 135, - 144, - 97, - 190, - 220, - 252, - 188, - 149, - 207, - 205, - 55, - 63, - 91, - 209, - 83, - 57, - 132, - 60, - 65, - 162, - 109, - 71, - 20, - 42, - 158, - 93, - 86, - 242, - 211, - 171, - 68, - 17, - 146, - 217, - 35, - 32, - 46, - 137, - 180, - 124, - 184, - 38, - 119, - 153, - 227, - 165, - 103, - 74, - 237, - 222, - 197, - 49, - 254, - 24, - 13, - 99, - 140, - 128, - 192, - 247, - 112, - 7 - }; + /** + * multiply two elements of GF(2^m) + * needed for MixColumn and InvMixColumn + */ - private static ReadOnlySpan Alogtable => - new byte[] - { - 0, - 3, - 5, - 15, - 17, - 51, - 85, - 255, - 26, - 46, - 114, - 150, - 161, - 248, - 19, - 53, - 95, - 225, - 56, - 72, - 216, - 115, - 149, - 164, - 247, - 2, - 6, - 10, - 30, - 34, - 102, - 170, - 229, - 52, - 92, - 228, - 55, - 89, - 235, - 38, - 106, - 190, - 217, - 112, - 144, - 171, - 230, - 49, - 83, - 245, - 4, - 12, - 20, - 60, - 68, - 204, - 79, - 209, - 104, - 184, - 211, - 110, - 178, - 205, - 76, - 212, - 103, - 169, - 224, - 59, - 77, - 215, - 98, - 166, - 241, - 8, - 24, - 40, - 120, - 136, - 131, - 158, - 185, - 208, - 107, - 189, - 220, - 127, - 129, - 152, - 179, - 206, - 73, - 219, - 118, - 154, - 181, - 196, - 87, - 249, - 16, - 48, - 80, - 240, - 11, - 29, - 39, - 105, - 187, - 214, - 97, - 163, - 254, - 25, - 43, - 125, - 135, - 146, - 173, - 236, - 47, - 113, - 147, - 174, - 233, - 32, - 96, - 160, - 251, - 22, - 58, - 78, - 210, - 109, - 183, - 194, - 93, - 231, - 50, - 86, - 250, - 21, - 63, - 65, - 195, - 94, - 226, - 61, - 71, - 201, - 64, - 192, - 91, - 237, - 44, - 116, - 156, - 191, - 218, - 117, - 159, - 186, - 213, - 100, - 172, - 239, - 42, - 126, - 130, - 157, - 188, - 223, - 122, - 142, - 137, - 128, - 155, - 182, - 193, - 88, - 232, - 35, - 101, - 175, - 234, - 37, - 111, - 177, - 200, - 67, - 197, - 84, - 252, - 31, - 33, - 99, - 165, - 244, - 7, - 9, - 27, - 45, - 119, - 153, - 176, - 203, - 70, - 202, - 69, - 207, - 74, - 222, - 121, - 139, - 134, - 145, - 168, - 227, - 62, - 66, - 198, - 81, - 243, - 14, - 18, - 54, - 90, - 238, - 41, - 123, - 141, - 140, - 143, - 138, - 133, - 148, - 167, - 242, - 13, - 23, - 57, - 75, - 221, - 124, - 132, - 151, - 162, - 253, - 28, - 36, - 108, - 180, - 199, - 82, - 246, - 1, - 3, - 5, - 15, - 17, - 51, - 85, - 255, - 26, - 46, - 114, - 150, - 161, - 248, - 19, - 53, - 95, - 225, - 56, - 72, - 216, - 115, - 149, - 164, - 247, - 2, - 6, - 10, - 30, - 34, - 102, - 170, - 229, - 52, - 92, - 228, - 55, - 89, - 235, - 38, - 106, - 190, - 217, - 112, - 144, - 171, - 230, - 49, - 83, - 245, - 4, - 12, - 20, - 60, - 68, - 204, - 79, - 209, - 104, - 184, - 211, - 110, - 178, - 205, - 76, - 212, - 103, - 169, - 224, - 59, - 77, - 215, - 98, - 166, - 241, - 8, - 24, - 40, - 120, - 136, - 131, - 158, - 185, - 208, - 107, - 189, - 220, - 127, - 129, - 152, - 179, - 206, - 73, - 219, - 118, - 154, - 181, - 196, - 87, - 249, - 16, - 48, - 80, - 240, - 11, - 29, - 39, - 105, - 187, - 214, - 97, - 163, - 254, - 25, - 43, - 125, - 135, - 146, - 173, - 236, - 47, - 113, - 147, - 174, - 233, - 32, - 96, - 160, - 251, - 22, - 58, - 78, - 210, - 109, - 183, - 194, - 93, - 231, - 50, - 86, - 250, - 21, - 63, - 65, - 195, - 94, - 226, - 61, - 71, - 201, - 64, - 192, - 91, - 237, - 44, - 116, - 156, - 191, - 218, - 117, - 159, - 186, - 213, - 100, - 172, - 239, - 42, - 126, - 130, - 157, - 188, - 223, - 122, - 142, - 137, - 128, - 155, - 182, - 193, - 88, - 232, - 35, - 101, - 175, - 234, - 37, - 111, - 177, - 200, - 67, - 197, - 84, - 252, - 31, - 33, - 99, - 165, - 244, - 7, - 9, - 27, - 45, - 119, - 153, - 176, - 203, - 70, - 202, - 69, - 207, - 74, - 222, - 121, - 139, - 134, - 145, - 168, - 227, - 62, - 66, - 198, - 81, - 243, - 14, - 18, - 54, - 90, - 238, - 41, - 123, - 141, - 140, - 143, - 138, - 133, - 148, - 167, - 242, - 13, - 23, - 57, - 75, - 221, - 124, - 132, - 151, - 162, - 253, - 28, - 36, - 108, - 180, - 199, - 82, - 246, - 1 - }; - - private static ReadOnlySpan S => - new byte[] - { - 99, - 124, - 119, - 123, - 242, - 107, - 111, - 197, - 48, - 1, - 103, - 43, - 254, - 215, - 171, - 118, - 202, - 130, - 201, - 125, - 250, - 89, - 71, - 240, - 173, - 212, - 162, - 175, - 156, - 164, - 114, - 192, - 183, - 253, - 147, - 38, - 54, - 63, - 247, - 204, - 52, - 165, - 229, - 241, - 113, - 216, - 49, - 21, - 4, - 199, - 35, - 195, - 24, - 150, - 5, - 154, - 7, - 18, - 128, - 226, - 235, - 39, - 178, - 117, - 9, - 131, - 44, - 26, - 27, - 110, - 90, - 160, - 82, - 59, - 214, - 179, - 41, - 227, - 47, - 132, - 83, - 209, - 0, - 237, - 32, - 252, - 177, - 91, - 106, - 203, - 190, - 57, - 74, - 76, - 88, - 207, - 208, - 239, - 170, - 251, - 67, - 77, - 51, - 133, - 69, - 249, - 2, - 127, - 80, - 60, - 159, - 168, - 81, - 163, - 64, - 143, - 146, - 157, - 56, - 245, - 188, - 182, - 218, - 33, - 16, - 255, - 243, - 210, - 205, - 12, - 19, - 236, - 95, - 151, - 68, - 23, - 196, - 167, - 126, - 61, - 100, - 93, - 25, - 115, - 96, - 129, - 79, - 220, - 34, - 42, - 144, - 136, - 70, - 238, - 184, - 20, - 222, - 94, - 11, - 219, - 224, - 50, - 58, - 10, - 73, - 6, - 36, - 92, - 194, - 211, - 172, - 98, - 145, - 149, - 228, - 121, - 231, - 200, - 55, - 109, - 141, - 213, - 78, - 169, - 108, - 86, - 244, - 234, - 101, - 122, - 174, - 8, - 186, - 120, - 37, - 46, - 28, - 166, - 180, - 198, - 232, - 221, - 116, - 31, - 75, - 189, - 139, - 138, - 112, - 62, - 181, - 102, - 72, - 3, - 246, - 14, - 97, - 53, - 87, - 185, - 134, - 193, - 29, - 158, - 225, - 248, - 152, - 17, - 105, - 217, - 142, - 148, - 155, - 30, - 135, - 233, - 206, - 85, - 40, - 223, - 140, - 161, - 137, - 13, - 191, - 230, - 66, - 104, - 65, - 153, - 45, - 15, - 176, - 84, - 187, - 22 - }; - - private static ReadOnlySpan Si => - new byte[] - { - 82, - 9, - 106, - 213, - 48, - 54, - 165, - 56, - 191, - 64, - 163, - 158, - 129, - 243, - 215, - 251, - 124, - 227, - 57, - 130, - 155, - 47, - 255, - 135, - 52, - 142, - 67, - 68, - 196, - 222, - 233, - 203, - 84, - 123, - 148, - 50, - 166, - 194, - 35, - 61, - 238, - 76, - 149, - 11, - 66, - 250, - 195, - 78, - 8, - 46, - 161, - 102, - 40, - 217, - 36, - 178, - 118, - 91, - 162, - 73, - 109, - 139, - 209, - 37, - 114, - 248, - 246, - 100, - 134, - 104, - 152, - 22, - 212, - 164, - 92, - 204, - 93, - 101, - 182, - 146, - 108, - 112, - 72, - 80, - 253, - 237, - 185, - 218, - 94, - 21, - 70, - 87, - 167, - 141, - 157, - 132, - 144, - 216, - 171, - 0, - 140, - 188, - 211, - 10, - 247, - 228, - 88, - 5, - 184, - 179, - 69, - 6, - 208, - 44, - 30, - 143, - 202, - 63, - 15, - 2, - 193, - 175, - 189, - 3, - 1, - 19, - 138, - 107, - 58, - 145, - 17, - 65, - 79, - 103, - 220, - 234, - 151, - 242, - 207, - 206, - 240, - 180, - 230, - 115, - 150, - 172, - 116, - 34, - 231, - 173, - 53, - 133, - 226, - 249, - 55, - 232, - 28, - 117, - 223, - 110, - 71, - 241, - 26, - 113, - 29, - 41, - 197, - 137, - 111, - 183, - 98, - 14, - 170, - 24, - 190, - 27, - 252, - 86, - 62, - 75, - 198, - 210, - 121, - 32, - 154, - 219, - 192, - 254, - 120, - 205, - 90, - 244, - 31, - 221, - 168, - 51, - 136, - 7, - 199, - 49, - 177, - 18, - 16, - 89, - 39, - 128, - 236, - 95, - 96, - 81, - 127, - 169, - 25, - 181, - 74, - 13, - 45, - 229, - 122, - 159, - 147, - 201, - 156, - 239, - 160, - 224, - 59, - 77, - 174, - 42, - 245, - 176, - 200, - 235, - 187, - 60, - 131, - 83, - 153, - 97, - 23, - 43, - 4, - 126, - 186, - 119, - 214, - 38, - 225, - 105, - 20, - 99, - 85, - 33, - 12, - 125 - }; - - private static ReadOnlySpan rcon => - new byte[] - { - 0x01, - 0x02, - 0x04, - 0x08, - 0x10, - 0x20, - 0x40, - 0x80, - 0x1b, - 0x36, - 0x6c, - 0xd8, - 0xab, - 0x4d, - 0x9a, - 0x2f, - 0x5e, - 0xbc, - 0x63, - 0xc6, - 0x97, - 0x35, - 0x6a, - 0xd4, - 0xb3, - 0x7d, - 0xfa, - 0xef, - 0xc5, - 0x91 - }; - - private static readonly byte[][] shifts0 = + private byte Mul0x2(int b) + { + if (b != 0) { - new byte[] { 0, 8, 16, 24 }, - new byte[] { 0, 8, 16, 24 }, - new byte[] { 0, 8, 16, 24 }, - new byte[] { 0, 8, 16, 32 }, - new byte[] { 0, 8, 24, 32 } + return Alogtable[25 + (Logtable[b] & 0xff)]; + } + return 0; + } + + private byte Mul0x3(int b) + { + if (b != 0) + { + return Alogtable[1 + (Logtable[b] & 0xff)]; + } + return 0; + } + + private byte Mul0x9(int b) + { + if (b >= 0) + { + return Alogtable[199 + b]; + } + return 0; + } + + private byte Mul0xb(int b) + { + if (b >= 0) + { + return Alogtable[104 + b]; + } + return 0; + } + + private byte Mul0xd(int b) + { + if (b >= 0) + { + return Alogtable[238 + b]; + } + return 0; + } + + private byte Mul0xe(int b) + { + if (b >= 0) + { + return Alogtable[223 + b]; + } + return 0; + } + + /** + * xor corresponding text input and round key input bytes + */ + + private void KeyAddition(long[] rk) + { + A0 ^= rk[0]; + A1 ^= rk[1]; + A2 ^= rk[2]; + A3 ^= rk[3]; + } + + private long Shift(long r, int shift) + { + //return (((long)((ulong) r >> shift) | (r << (BC - shift)))) & BC_MASK; + + var temp = (ulong)r >> shift; + + // NB: This corrects for Mono Bug #79087 (fixed in 1.1.17) + if (shift > 31) + { + temp &= 0xFFFFFFFFUL; + } + + return ((long)temp | (r << (BC - shift))) & BC_MASK; + } + + /** + * Row 0 remains unchanged + * The other three rows are shifted a variable amount + */ + + private void ShiftRow(byte[] shiftsSC) + { + A1 = Shift(A1, shiftsSC[1]); + A2 = Shift(A2, shiftsSC[2]); + A3 = Shift(A3, shiftsSC[3]); + } + + private long ApplyS(long r, ReadOnlySpan box) + { + long res = 0; + + for (var j = 0; j < BC; j += 8) + { + res |= (long)(box[(int)((r >> j) & 0xff)] & 0xff) << j; + } + + return res; + } + + /** + * Replace every byte of the input by the byte at that place + * in the nonlinear S-box + */ + + private void Substitution(ReadOnlySpan box) + { + A0 = ApplyS(A0, box); + A1 = ApplyS(A1, box); + A2 = ApplyS(A2, box); + A3 = ApplyS(A3, box); + } + + /** + * Mix the bytes of every column in a linear way + */ + + private void MixColumn() + { + long r0, + r1, + r2, + r3; + + r0 = r1 = r2 = r3 = 0; + + for (var j = 0; j < BC; j += 8) + { + var a0 = (int)((A0 >> j) & 0xff); + var a1 = (int)((A1 >> j) & 0xff); + var a2 = (int)((A2 >> j) & 0xff); + var a3 = (int)((A3 >> j) & 0xff); + + r0 |= (long)((Mul0x2(a0) ^ Mul0x3(a1) ^ a2 ^ a3) & 0xff) << j; + + r1 |= (long)((Mul0x2(a1) ^ Mul0x3(a2) ^ a3 ^ a0) & 0xff) << j; + + r2 |= (long)((Mul0x2(a2) ^ Mul0x3(a3) ^ a0 ^ a1) & 0xff) << j; + + r3 |= (long)((Mul0x2(a3) ^ Mul0x3(a0) ^ a1 ^ a2) & 0xff) << j; + } + + A0 = r0; + A1 = r1; + A2 = r2; + A3 = r3; + } + + /** + * Mix the bytes of every column in a linear way + * This is the opposite operation of Mixcolumn + */ + + private void InvMixColumn() + { + long r0, + r1, + r2, + r3; + + r0 = r1 = r2 = r3 = 0; + for (var j = 0; j < BC; j += 8) + { + var a0 = (int)((A0 >> j) & 0xff); + var a1 = (int)((A1 >> j) & 0xff); + var a2 = (int)((A2 >> j) & 0xff); + var a3 = (int)((A3 >> j) & 0xff); + + // + // pre-lookup the log table + // + a0 = (a0 != 0) ? (Logtable[a0 & 0xff] & 0xff) : -1; + a1 = (a1 != 0) ? (Logtable[a1 & 0xff] & 0xff) : -1; + a2 = (a2 != 0) ? (Logtable[a2 & 0xff] & 0xff) : -1; + a3 = (a3 != 0) ? (Logtable[a3 & 0xff] & 0xff) : -1; + + r0 |= (long)((Mul0xe(a0) ^ Mul0xb(a1) ^ Mul0xd(a2) ^ Mul0x9(a3)) & 0xff) << j; + + r1 |= (long)((Mul0xe(a1) ^ Mul0xb(a2) ^ Mul0xd(a3) ^ Mul0x9(a0)) & 0xff) << j; + + r2 |= (long)((Mul0xe(a2) ^ Mul0xb(a3) ^ Mul0xd(a0) ^ Mul0x9(a1)) & 0xff) << j; + + r3 |= (long)((Mul0xe(a3) ^ Mul0xb(a0) ^ Mul0xd(a1) ^ Mul0x9(a2)) & 0xff) << j; + } + + A0 = r0; + A1 = r1; + A2 = r2; + A3 = r3; + } + + /** + * Calculate the necessary round keys + * The number of calculations depends on keyBits and blockBits + */ + + private long[][] GenerateWorkingKey(byte[] key) + { + int t, + rconpointer = 0; + var keyBits = key.Length * 8; + var tk = new byte[4, MAXKC]; + + //long[,] W = new long[MAXROUNDS+1,4]; + var W = new long[MAXROUNDS + 1][]; + + for (var i = 0; i < MAXROUNDS + 1; i++) + { + W[i] = new long[4]; + } + + var KC = keyBits switch + { + 128 => 4, + 160 => 5, + 192 => 6, + 224 => 7, + 256 => 8, + _ => throw new ArgumentException("Key length not 128/160/192/224/256 bits."), }; - - private static readonly byte[][] shifts1 = + if (keyBits >= blockBits) { - new byte[] { 0, 24, 16, 8 }, - new byte[] { 0, 32, 24, 16 }, - new byte[] { 0, 40, 32, 24 }, - new byte[] { 0, 48, 40, 24 }, - new byte[] { 0, 56, 40, 32 } - }; - - /** - * multiply two elements of GF(2^m) - * needed for MixColumn and InvMixColumn - */ - - private byte Mul0x2(int b) + ROUNDS = KC + 6; + } + else { - if (b != 0) + ROUNDS = (BC / 8) + 6; + } + + // + // copy the key into the processing area + // + var index = 0; + + for (var i = 0; i < key.Length; i++) + { + tk[i % 4, i / 4] = key[index++]; + } + + t = 0; + + // + // copy values into round key array + // + for (var j = 0; (j < KC) && (t < (ROUNDS + 1) * (BC / 8)); j++, t++) + { + for (var i = 0; i < 4; i++) { - return Alogtable[25 + (Logtable[b] & 0xff)]; + W[t / (BC / 8)][i] |= (long)(tk[i, j] & 0xff) << ((t * 8) % BC); } - return 0; } - private byte Mul0x3(int b) + // + // while not enough round key material calculated + // calculate new values + // + while (t < (ROUNDS + 1) * (BC / 8)) { - if (b != 0) + for (var i = 0; i < 4; i++) { - return Alogtable[1 + (Logtable[b] & 0xff)]; + tk[i, 0] ^= S[tk[(i + 1) % 4, KC - 1] & 0xff]; } - return 0; - } + tk[0, 0] ^= rcon[rconpointer++]; - private byte Mul0x9(int b) - { - if (b >= 0) + if (KC <= 6) { - return Alogtable[199 + b]; - } - return 0; - } - - private byte Mul0xb(int b) - { - if (b >= 0) - { - return Alogtable[104 + b]; - } - return 0; - } - - private byte Mul0xd(int b) - { - if (b >= 0) - { - return Alogtable[238 + b]; - } - return 0; - } - - private byte Mul0xe(int b) - { - if (b >= 0) - { - return Alogtable[223 + b]; - } - return 0; - } - - /** - * xor corresponding text input and round key input bytes - */ - - private void KeyAddition(long[] rk) - { - A0 ^= rk[0]; - A1 ^= rk[1]; - A2 ^= rk[2]; - A3 ^= rk[3]; - } - - private long Shift(long r, int shift) - { - //return (((long)((ulong) r >> shift) | (r << (BC - shift)))) & BC_MASK; - - ulong temp = (ulong)r >> shift; - - // NB: This corrects for Mono Bug #79087 (fixed in 1.1.17) - if (shift > 31) - { - temp &= 0xFFFFFFFFUL; - } - - return ((long)temp | (r << (BC - shift))) & BC_MASK; - } - - /** - * Row 0 remains unchanged - * The other three rows are shifted a variable amount - */ - - private void ShiftRow(byte[] shiftsSC) - { - A1 = Shift(A1, shiftsSC[1]); - A2 = Shift(A2, shiftsSC[2]); - A3 = Shift(A3, shiftsSC[3]); - } - - private long ApplyS(long r, ReadOnlySpan box) - { - long res = 0; - - for (int j = 0; j < BC; j += 8) - { - res |= (long)(box[(int)((r >> j) & 0xff)] & 0xff) << j; - } - - return res; - } - - /** - * Replace every byte of the input by the byte at that place - * in the nonlinear S-box - */ - - private void Substitution(ReadOnlySpan box) - { - A0 = ApplyS(A0, box); - A1 = ApplyS(A1, box); - A2 = ApplyS(A2, box); - A3 = ApplyS(A3, box); - } - - /** - * Mix the bytes of every column in a linear way - */ - - private void MixColumn() - { - long r0, - r1, - r2, - r3; - - r0 = r1 = r2 = r3 = 0; - - for (int j = 0; j < BC; j += 8) - { - int a0 = (int)((A0 >> j) & 0xff); - int a1 = (int)((A1 >> j) & 0xff); - int a2 = (int)((A2 >> j) & 0xff); - int a3 = (int)((A3 >> j) & 0xff); - - r0 |= (long)((Mul0x2(a0) ^ Mul0x3(a1) ^ a2 ^ a3) & 0xff) << j; - - r1 |= (long)((Mul0x2(a1) ^ Mul0x3(a2) ^ a3 ^ a0) & 0xff) << j; - - r2 |= (long)((Mul0x2(a2) ^ Mul0x3(a3) ^ a0 ^ a1) & 0xff) << j; - - r3 |= (long)((Mul0x2(a3) ^ Mul0x3(a0) ^ a1 ^ a2) & 0xff) << j; - } - - A0 = r0; - A1 = r1; - A2 = r2; - A3 = r3; - } - - /** - * Mix the bytes of every column in a linear way - * This is the opposite operation of Mixcolumn - */ - - private void InvMixColumn() - { - long r0, - r1, - r2, - r3; - - r0 = r1 = r2 = r3 = 0; - for (int j = 0; j < BC; j += 8) - { - int a0 = (int)((A0 >> j) & 0xff); - int a1 = (int)((A1 >> j) & 0xff); - int a2 = (int)((A2 >> j) & 0xff); - int a3 = (int)((A3 >> j) & 0xff); - - // - // pre-lookup the log table - // - a0 = (a0 != 0) ? (Logtable[a0 & 0xff] & 0xff) : -1; - a1 = (a1 != 0) ? (Logtable[a1 & 0xff] & 0xff) : -1; - a2 = (a2 != 0) ? (Logtable[a2 & 0xff] & 0xff) : -1; - a3 = (a3 != 0) ? (Logtable[a3 & 0xff] & 0xff) : -1; - - r0 |= (long)((Mul0xe(a0) ^ Mul0xb(a1) ^ Mul0xd(a2) ^ Mul0x9(a3)) & 0xff) << j; - - r1 |= (long)((Mul0xe(a1) ^ Mul0xb(a2) ^ Mul0xd(a3) ^ Mul0x9(a0)) & 0xff) << j; - - r2 |= (long)((Mul0xe(a2) ^ Mul0xb(a3) ^ Mul0xd(a0) ^ Mul0x9(a1)) & 0xff) << j; - - r3 |= (long)((Mul0xe(a3) ^ Mul0xb(a0) ^ Mul0xd(a1) ^ Mul0x9(a2)) & 0xff) << j; - } - - A0 = r0; - A1 = r1; - A2 = r2; - A3 = r3; - } - - /** - * Calculate the necessary round keys - * The number of calculations depends on keyBits and blockBits - */ - - private long[][] GenerateWorkingKey(byte[] key) - { - int t, - rconpointer = 0; - int keyBits = key.Length * 8; - byte[,] tk = new byte[4, MAXKC]; - - //long[,] W = new long[MAXROUNDS+1,4]; - long[][] W = new long[MAXROUNDS + 1][]; - - for (int i = 0; i < MAXROUNDS + 1; i++) - { - W[i] = new long[4]; - } - - var KC = keyBits switch - { - 128 => 4, - 160 => 5, - 192 => 6, - 224 => 7, - 256 => 8, - _ => throw new ArgumentException("Key length not 128/160/192/224/256 bits."), - }; - if (keyBits >= blockBits) - { - ROUNDS = KC + 6; + for (var j = 1; j < KC; j++) + { + for (var i = 0; i < 4; i++) + { + tk[i, j] ^= tk[i, j - 1]; + } + } } else { - ROUNDS = (BC / 8) + 6; + for (var j = 1; j < 4; j++) + { + for (var i = 0; i < 4; i++) + { + tk[i, j] ^= tk[i, j - 1]; + } + } + for (var i = 0; i < 4; i++) + { + tk[i, 4] ^= S[tk[i, 3] & 0xff]; + } + for (var j = 5; j < KC; j++) + { + for (var i = 0; i < 4; i++) + { + tk[i, j] ^= tk[i, j - 1]; + } + } } - // - // copy the key into the processing area - // - int index = 0; - - for (int i = 0; i < key.Length; i++) - { - tk[i % 4, i / 4] = key[index++]; - } - - t = 0; - // // copy values into round key array // - for (int j = 0; (j < KC) && (t < (ROUNDS + 1) * (BC / 8)); j++, t++) + for (var j = 0; (j < KC) && (t < (ROUNDS + 1) * (BC / 8)); j++, t++) { - for (int i = 0; i < 4; i++) + for (var i = 0; i < 4; i++) { - W[t / (BC / 8)][i] |= (long)(tk[i, j] & 0xff) << ((t * 8) % BC); + W[t / (BC / 8)][i] |= (long)(tk[i, j] & 0xff) << ((t * 8) % (BC)); } } + } + return W; + } - // - // while not enough round key material calculated - // calculate new values - // - while (t < (ROUNDS + 1) * (BC / 8)) - { - for (int i = 0; i < 4; i++) - { - tk[i, 0] ^= S[tk[(i + 1) % 4, KC - 1] & 0xff]; - } - tk[0, 0] ^= rcon[rconpointer++]; + private readonly int BC; + private readonly long BC_MASK; + private int ROUNDS; + private readonly int blockBits; + private long[][]? workingKey; + private long A0, + A1, + A2, + A3; + private bool forEncryption; + private readonly byte[] shifts0SC; + private readonly byte[] shifts1SC; - if (KC <= 6) - { - for (int j = 1; j < KC; j++) - { - for (int i = 0; i < 4; i++) - { - tk[i, j] ^= tk[i, j - 1]; - } - } - } - else - { - for (int j = 1; j < 4; j++) - { - for (int i = 0; i < 4; i++) - { - tk[i, j] ^= tk[i, j - 1]; - } - } - for (int i = 0; i < 4; i++) - { - tk[i, 4] ^= S[tk[i, 3] & 0xff]; - } - for (int j = 5; j < KC; j++) - { - for (int i = 0; i < 4; i++) - { - tk[i, j] ^= tk[i, j - 1]; - } - } - } + /** + * default constructor - 128 bit block size. + */ - // - // copy values into round key array - // - for (int j = 0; (j < KC) && (t < (ROUNDS + 1) * (BC / 8)); j++, t++) - { - for (int i = 0; i < 4; i++) - { - W[t / (BC / 8)][i] |= (long)(tk[i, j] & 0xff) << ((t * 8) % (BC)); - } - } - } - return W; + public RijndaelEngine() : this(128) { } + + /** + * basic constructor - set the cipher up for a given blocksize + * + * @param blocksize the blocksize in bits, must be 128, 192, or 256. + */ + + public RijndaelEngine(int blockBits) + { + switch (blockBits) + { + case 128: + BC = 32; + BC_MASK = 0xffffffffL; + shifts0SC = shifts0[0]; + shifts1SC = shifts1[0]; + break; + case 160: + BC = 40; + BC_MASK = 0xffffffffffL; + shifts0SC = shifts0[1]; + shifts1SC = shifts1[1]; + break; + case 192: + BC = 48; + BC_MASK = 0xffffffffffffL; + shifts0SC = shifts0[2]; + shifts1SC = shifts1[2]; + break; + case 224: + BC = 56; + BC_MASK = 0xffffffffffffffL; + shifts0SC = shifts0[3]; + shifts1SC = shifts1[3]; + break; + case 256: + BC = 64; + BC_MASK = unchecked((long)0xffffffffffffffffL); + shifts0SC = shifts0[4]; + shifts1SC = shifts1[4]; + break; + default: + throw new ArgumentException("unknown blocksize to Rijndael"); } - private readonly int BC; - private readonly long BC_MASK; - private int ROUNDS; - private readonly int blockBits; - private long[][]? workingKey; - private long A0, - A1, - A2, - A3; - private bool forEncryption; - private readonly byte[] shifts0SC; - private readonly byte[] shifts1SC; + this.blockBits = blockBits; + } - /** - * default constructor - 128 bit block size. - */ + /** + * initialise a Rijndael cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ - public RijndaelEngine() : this(128) { } - - /** - * basic constructor - set the cipher up for a given blocksize - * - * @param blocksize the blocksize in bits, must be 128, 192, or 256. - */ - - public RijndaelEngine(int blockBits) + public void Init(bool forEncryption, ICipherParameters parameters) + { + if (parameters is KeyParameter parameter) { - switch (blockBits) - { - case 128: - BC = 32; - BC_MASK = 0xffffffffL; - shifts0SC = shifts0[0]; - shifts1SC = shifts1[0]; - break; - case 160: - BC = 40; - BC_MASK = 0xffffffffffL; - shifts0SC = shifts0[1]; - shifts1SC = shifts1[1]; - break; - case 192: - BC = 48; - BC_MASK = 0xffffffffffffL; - shifts0SC = shifts0[2]; - shifts1SC = shifts1[2]; - break; - case 224: - BC = 56; - BC_MASK = 0xffffffffffffffL; - shifts0SC = shifts0[3]; - shifts1SC = shifts1[3]; - break; - case 256: - BC = 64; - BC_MASK = unchecked((long)0xffffffffffffffffL); - shifts0SC = shifts0[4]; - shifts1SC = shifts1[4]; - break; - default: - throw new ArgumentException("unknown blocksize to Rijndael"); - } - - this.blockBits = blockBits; + workingKey = GenerateWorkingKey(parameter.GetKey()); + this.forEncryption = forEncryption; + return; } - /** - * initialise a Rijndael cipher. - * - * @param forEncryption whether or not we are for encryption. - * @param parameters the parameters required to set up the cipher. - * @exception ArgumentException if the parameters argument is - * inappropriate. - */ + throw new ArgumentException( + "invalid parameter passed to Rijndael init - " + parameters.GetType() + ); + } - public void Init(bool forEncryption, ICipherParameters parameters) + public string AlgorithmName => "Rijndael"; + + public bool IsPartialBlockOkay => false; + + public int GetBlockSize() + { + return BC / 2; + } + + public int ProcessBlock(ReadOnlySpan input, Span output) + { + if (workingKey is null) { - var parameter = parameters as KeyParameter; - if (parameter != null) - { - workingKey = GenerateWorkingKey(parameter.GetKey()); - this.forEncryption = forEncryption; - return; - } - - throw new ArgumentException( - "invalid parameter passed to Rijndael init - " + parameters.GetType() - ); + throw new InvalidOperationException("Rijndael engine not initialised"); } - public string AlgorithmName => "Rijndael"; - - public bool IsPartialBlockOkay => false; - - public int GetBlockSize() + if (BC / 2 > input.Length) { - return BC / 2; + throw new DataLengthException("input buffer too short"); } - public int ProcessBlock(ReadOnlySpan input, Span output) + if (BC / 2 > output.Length) { - if (workingKey is null) - { - throw new InvalidOperationException("Rijndael engine not initialised"); - } - - if (BC / 2 > input.Length) - { - throw new DataLengthException("input buffer too short"); - } - - if (BC / 2 > output.Length) - { - throw new DataLengthException("output buffer too short"); - } - - UnPackBlock(input); - - if (forEncryption) - { - EncryptBlock(workingKey); - } - else - { - DecryptBlock(workingKey); - } - - PackBlock(output); - - return BC / 2; + throw new DataLengthException("output buffer too short"); } - public void Reset() { } + UnPackBlock(input); - private void UnPackBlock(ReadOnlySpan bytes) + if (forEncryption) { - int index = 0; - - A0 = bytes[index++] & 0xff; - A1 = bytes[index++] & 0xff; - A2 = bytes[index++] & 0xff; - A3 = bytes[index++] & 0xff; - - for (int j = 8; j != BC; j += 8) - { - A0 |= (long)(bytes[index++] & 0xff) << j; - A1 |= (long)(bytes[index++] & 0xff) << j; - A2 |= (long)(bytes[index++] & 0xff) << j; - A3 |= (long)(bytes[index++] & 0xff) << j; - } + EncryptBlock(workingKey); + } + else + { + DecryptBlock(workingKey); } - private void PackBlock(Span bytes) + PackBlock(output); + + return BC / 2; + } + + public void Reset() { } + + private void UnPackBlock(ReadOnlySpan bytes) + { + var index = 0; + + A0 = bytes[index++] & 0xff; + A1 = bytes[index++] & 0xff; + A2 = bytes[index++] & 0xff; + A3 = bytes[index++] & 0xff; + + for (var j = 8; j != BC; j += 8) { - int index = 0; - - for (int j = 0; j != BC; j += 8) - { - bytes[index++] = (byte)(A0 >> j); - bytes[index++] = (byte)(A1 >> j); - bytes[index++] = (byte)(A2 >> j); - bytes[index++] = (byte)(A3 >> j); - } - } - - private void EncryptBlock(long[][] rk) - { - int r; - - // - // begin with a key addition - // - KeyAddition(rk[0]); - - // - // ROUNDS-1 ordinary rounds - // - for (r = 1; r < ROUNDS; r++) - { - Substitution(S); - ShiftRow(shifts0SC); - MixColumn(); - KeyAddition(rk[r]); - } - - // - // Last round is special: there is no MixColumn - // - Substitution(S); - ShiftRow(shifts0SC); - KeyAddition(rk[ROUNDS]); - } - - private void DecryptBlock(long[][] rk) - { - int r; - - // To decrypt: apply the inverse operations of the encrypt routine, - // in opposite order - // - // (KeyAddition is an involution: it 's equal to its inverse) - // (the inverse of Substitution with table S is Substitution with the inverse table of S) - // (the inverse of Shiftrow is Shiftrow over a suitable distance) - // - - // First the special round: - // without InvMixColumn - // with extra KeyAddition - // - KeyAddition(rk[ROUNDS]); - Substitution(Si); - ShiftRow(shifts1SC); - - // - // ROUNDS-1 ordinary rounds - // - for (r = ROUNDS - 1; r > 0; r--) - { - KeyAddition(rk[r]); - InvMixColumn(); - Substitution(Si); - ShiftRow(shifts1SC); - } - - // - // End with the extra key addition - // - KeyAddition(rk[0]); + A0 |= (long)(bytes[index++] & 0xff) << j; + A1 |= (long)(bytes[index++] & 0xff) << j; + A2 |= (long)(bytes[index++] & 0xff) << j; + A3 |= (long)(bytes[index++] & 0xff) << j; } } + + private void PackBlock(Span bytes) + { + var index = 0; + + for (var j = 0; j != BC; j += 8) + { + bytes[index++] = (byte)(A0 >> j); + bytes[index++] = (byte)(A1 >> j); + bytes[index++] = (byte)(A2 >> j); + bytes[index++] = (byte)(A3 >> j); + } + } + + private void EncryptBlock(long[][] rk) + { + int r; + + // + // begin with a key addition + // + KeyAddition(rk[0]); + + // + // ROUNDS-1 ordinary rounds + // + for (r = 1; r < ROUNDS; r++) + { + Substitution(S); + ShiftRow(shifts0SC); + MixColumn(); + KeyAddition(rk[r]); + } + + // + // Last round is special: there is no MixColumn + // + Substitution(S); + ShiftRow(shifts0SC); + KeyAddition(rk[ROUNDS]); + } + + private void DecryptBlock(long[][] rk) + { + int r; + + // To decrypt: apply the inverse operations of the encrypt routine, + // in opposite order + // + // (KeyAddition is an involution: it 's equal to its inverse) + // (the inverse of Substitution with table S is Substitution with the inverse table of S) + // (the inverse of Shiftrow is Shiftrow over a suitable distance) + // + + // First the special round: + // without InvMixColumn + // with extra KeyAddition + // + KeyAddition(rk[ROUNDS]); + Substitution(Si); + ShiftRow(shifts1SC); + + // + // ROUNDS-1 ordinary rounds + // + for (r = ROUNDS - 1; r > 0; r--) + { + KeyAddition(rk[r]); + InvMixColumn(); + Substitution(Si); + ShiftRow(shifts1SC); + } + + // + // End with the extra key addition + // + KeyAddition(rk[0]); + } } diff --git a/src/SharpCompress/Factories/Factory.cs b/src/SharpCompress/Factories/Factory.cs index ba0bae88..b092a23a 100644 --- a/src/SharpCompress/Factories/Factory.cs +++ b/src/SharpCompress/Factories/Factory.cs @@ -1,96 +1,90 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using SharpCompress.Archives; using SharpCompress.Common; using SharpCompress.IO; using SharpCompress.Readers; -namespace SharpCompress.Factories +namespace SharpCompress.Factories; + +/// +public abstract class Factory : IFactory { - /// - public abstract class Factory : IFactory + static Factory() { - static Factory() + RegisterFactory(new ZipFactory()); + RegisterFactory(new RarFactory()); + RegisterFactory(new SevenZipFactory()); + RegisterFactory(new GZipFactory()); + RegisterFactory(new TarFactory()); + } + + private static readonly HashSet _factories = new HashSet(); + + /// + /// Gets the collection of registered . + /// + public static IEnumerable Factories => _factories; + + /// + /// Registers an archive factory. + /// + /// The factory to register. + /// must not be null. + public static void RegisterFactory(Factory factory) + { + factory.CheckNotNull(nameof(factory)); + + _factories.Add(factory); + } + + /// + public abstract string Name { get; } + + /// + public virtual ArchiveType? KnownArchiveType => null; + + /// + public abstract IEnumerable GetSupportedExtensions(); + + /// + public abstract bool IsArchive(Stream stream, string? password = null); + + /// + public virtual FileInfo? GetFilePart(int index, FileInfo part1) + { + return null; + } + + /// + /// Tries to open an from a . + /// + /// + /// This method provides extra insight to support loading compressed TAR files. + /// + /// + /// + /// + /// + internal virtual bool TryOpenReader( + RewindableStream rewindableStream, + ReaderOptions options, + out IReader? reader + ) + { + reader = null; + + if (this is IReaderFactory readerFactory) { - RegisterFactory(new ZipFactory()); - RegisterFactory(new RarFactory()); - RegisterFactory(new SevenZipFactory()); - RegisterFactory(new GZipFactory()); - RegisterFactory(new TarFactory()); - } - - private static readonly HashSet _factories = new HashSet(); - - /// - /// Gets the collection of registered . - /// - public static IEnumerable Factories => _factories; - - /// - /// Registers an archive factory. - /// - /// The factory to register. - /// must not be null. - public static void RegisterFactory(Factory factory) - { - factory.CheckNotNull(nameof(factory)); - - _factories.Add(factory); - } - - /// - public abstract string Name { get; } - - /// - public virtual ArchiveType? KnownArchiveType => null; - - /// - public abstract IEnumerable GetSupportedExtensions(); - - /// - public abstract bool IsArchive(Stream stream, string? password = null); - - /// - public virtual FileInfo? GetFilePart(int index, FileInfo part1) - { - return null; - } - - /// - /// Tries to open an from a . - /// - /// - /// This method provides extra insight to support loading compressed TAR files. - /// - /// - /// - /// - /// - internal virtual bool TryOpenReader( - RewindableStream rewindableStream, - ReaderOptions options, - out IReader? reader - ) - { - reader = null; - - if (this is IReaderFactory readerFactory) + rewindableStream.Rewind(false); + if (IsArchive(rewindableStream, options.Password)) { - rewindableStream.Rewind(false); - if (this.IsArchive(rewindableStream, options.Password)) - { - rewindableStream.Rewind(true); - reader = readerFactory.OpenReader(rewindableStream, options); - return true; - } + rewindableStream.Rewind(true); + reader = readerFactory.OpenReader(rewindableStream, options); + return true; } - - return false; } + + return false; } } diff --git a/src/SharpCompress/Factories/GZipFactory.cs b/src/SharpCompress/Factories/GZipFactory.cs index 1c473e55..e504e577 100644 --- a/src/SharpCompress/Factories/GZipFactory.cs +++ b/src/SharpCompress/Factories/GZipFactory.cs @@ -1,8 +1,6 @@ -using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; -using System.Linq; using SharpCompress.Archives; using SharpCompress.Archives.GZip; @@ -15,136 +13,135 @@ using SharpCompress.Readers.Tar; using SharpCompress.Writers; using SharpCompress.Writers.GZip; -namespace SharpCompress.Factories +namespace SharpCompress.Factories; + +/// +/// Represents the foundation factory of GZip archive. +/// +public class GZipFactory + : Factory, + IArchiveFactory, + IMultiArchiveFactory, + IReaderFactory, + IWriterFactory, + IWriteableArchiveFactory { - /// - /// Represents the foundation factory of GZip archive. - /// - public class GZipFactory - : Factory, - IArchiveFactory, - IMultiArchiveFactory, - IReaderFactory, - IWriterFactory, - IWriteableArchiveFactory + #region IFactory + + /// + public override string Name => "GZip"; + + /// + public override ArchiveType? KnownArchiveType => ArchiveType.GZip; + + /// + public override IEnumerable GetSupportedExtensions() { - #region IFactory + yield return "gz"; + } - /// - public override string Name => "GZip"; + /// + public override bool IsArchive(Stream stream, string? password = null) + { + return GZipArchive.IsGZipFile(stream); + } - /// - public override ArchiveType? KnownArchiveType => ArchiveType.GZip; + #endregion - /// - public override IEnumerable GetSupportedExtensions() + #region IArchiveFactory + + /// + public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) + { + return GZipArchive.Open(stream, readerOptions); + } + + /// + public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + { + return GZipArchive.Open(fileInfo, readerOptions); + } + + #endregion + + #region IMultiArchiveFactory + + /// + public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) + { + return GZipArchive.Open(streams, readerOptions); + } + + /// + public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) + { + return GZipArchive.Open(fileInfos, readerOptions); + } + + #endregion + + #region IReaderFactory + + /// + internal override bool TryOpenReader( + RewindableStream rewindableStream, + ReaderOptions options, + out IReader? reader + ) + { + reader = null; + + rewindableStream.Rewind(false); + if (GZipArchive.IsGZipFile(rewindableStream)) { - yield return "gz"; - } - - /// - public override bool IsArchive(Stream stream, string? password = null) - { - return GZipArchive.IsGZipFile(stream); - } - - #endregion - - #region IArchiveFactory - - /// - public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) - { - return GZipArchive.Open(stream, readerOptions); - } - - /// - public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) - { - return GZipArchive.Open(fileInfo, readerOptions); - } - - #endregion - - #region IMultiArchiveFactory - - /// - public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) - { - return GZipArchive.Open(streams, readerOptions); - } - - /// - public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) - { - return GZipArchive.Open(fileInfos, readerOptions); - } - - #endregion - - #region IReaderFactory - - /// - internal override bool TryOpenReader( - RewindableStream rewindableStream, - ReaderOptions options, - out IReader? reader - ) - { - reader = null; - rewindableStream.Rewind(false); - if (GZipArchive.IsGZipFile(rewindableStream)) + var testStream = new GZipStream(rewindableStream, CompressionMode.Decompress); + if (TarArchive.IsTarFile(testStream)) { - rewindableStream.Rewind(false); - var testStream = new GZipStream(rewindableStream, CompressionMode.Decompress); - if (TarArchive.IsTarFile(testStream)) - { - rewindableStream.Rewind(true); - reader = new TarReader(rewindableStream, options, CompressionType.GZip); - return true; - } - rewindableStream.Rewind(true); - reader = OpenReader(rewindableStream, options); + reader = new TarReader(rewindableStream, options, CompressionType.GZip); return true; } - return false; + rewindableStream.Rewind(true); + reader = OpenReader(rewindableStream, options); + return true; } - /// - public IReader OpenReader(Stream stream, ReaderOptions? options) - { - return GZipReader.Open(stream, options); - } - - #endregion - - #region IWriterFactory - - /// - public IWriter Open(Stream stream, WriterOptions writerOptions) - { - if (writerOptions.CompressionType != CompressionType.GZip) - { - throw new InvalidFormatException( - "GZip archives only support GZip compression type." - ); - } - return new GZipWriter(stream, new GZipWriterOptions(writerOptions)); - } - - #endregion - - #region IWriteableArchiveFactory - - /// - public IWritableArchive CreateWriteableArchive() - { - return GZipArchive.Create(); - } - - #endregion + return false; } + + /// + public IReader OpenReader(Stream stream, ReaderOptions? options) + { + return GZipReader.Open(stream, options); + } + + #endregion + + #region IWriterFactory + + /// + public IWriter Open(Stream stream, WriterOptions writerOptions) + { + if (writerOptions.CompressionType != CompressionType.GZip) + { + throw new InvalidFormatException( + "GZip archives only support GZip compression type." + ); + } + return new GZipWriter(stream, new GZipWriterOptions(writerOptions)); + } + + #endregion + + #region IWriteableArchiveFactory + + /// + public IWritableArchive CreateWriteableArchive() + { + return GZipArchive.Create(); + } + + #endregion } diff --git a/src/SharpCompress/Factories/IFactory.cs b/src/SharpCompress/Factories/IFactory.cs index 39c2c384..08bedd50 100644 --- a/src/SharpCompress/Factories/IFactory.cs +++ b/src/SharpCompress/Factories/IFactory.cs @@ -1,54 +1,49 @@ -using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace SharpCompress.Factories +namespace SharpCompress.Factories; + +/// +/// Represents the foundation of an archive factory. +/// +/// +/// To get extended functionality, this type can be cast to:
+///
+///
+///
+///
+public interface IFactory { /// - /// Represents the foundation of an archive factory. + /// Gets the archive Type name /// - /// - /// To get extended functionality, this type can be cast to:
- ///
- ///
- ///
- ///
- public interface IFactory - { - /// - /// Gets the archive Type name - /// - string Name { get; } + string Name { get; } - /// - /// Gets the archive Type in case it is a well known archive format. - /// - Common.ArchiveType? KnownArchiveType { get; } + /// + /// Gets the archive Type in case it is a well known archive format. + /// + Common.ArchiveType? KnownArchiveType { get; } - /// - /// returns the extensions typically used by this archive type. - /// - /// - IEnumerable GetSupportedExtensions(); + /// + /// returns the extensions typically used by this archive type. + /// + /// + IEnumerable GetSupportedExtensions(); - /// - /// Returns true if the stream represents an archive of the format defined by this type. - /// - /// A stream, pointing to the beginning of the archive. - /// optional password - bool IsArchive(Stream stream, string? password = null); + /// + /// Returns true if the stream represents an archive of the format defined by this type. + /// + /// A stream, pointing to the beginning of the archive. + /// optional password + bool IsArchive(Stream stream, string? password = null); - /// - /// From a passed in archive (zip, rar, 7z, 001), return all parts. - /// - /// Path to the first part. - /// - /// The path to the requested part, - /// or NULL if the part does not exist. - /// - FileInfo? GetFilePart(int index, FileInfo part1); - } + /// + /// From a passed in archive (zip, rar, 7z, 001), return all parts. + /// + /// Path to the first part. + /// + /// The path to the requested part, + /// or NULL if the part does not exist. + /// + FileInfo? GetFilePart(int index, FileInfo part1); } diff --git a/src/SharpCompress/Factories/RarFactory.cs b/src/SharpCompress/Factories/RarFactory.cs index f9ffdb8c..77c04932 100644 --- a/src/SharpCompress/Factories/RarFactory.cs +++ b/src/SharpCompress/Factories/RarFactory.cs @@ -1,90 +1,86 @@ -using System; using System.Collections.Generic; using System.IO; -using System.Linq; using SharpCompress.Archives; using SharpCompress.Archives.Rar; using SharpCompress.Common; -using SharpCompress.IO; using SharpCompress.Readers; using SharpCompress.Readers.Rar; -namespace SharpCompress.Factories +namespace SharpCompress.Factories; + +/// +/// Represents the foundation factory of RAR archive. +/// +public class RarFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReaderFactory { - /// - /// Represents the foundation factory of RAR archive. - /// - public class RarFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReaderFactory + #region IArchive + + /// + public override string Name => "Rar"; + + /// + public override ArchiveType? KnownArchiveType => ArchiveType.Rar; + + /// + public override IEnumerable GetSupportedExtensions() { - #region IArchive - - /// - public override string Name => "Rar"; - - /// - public override ArchiveType? KnownArchiveType => ArchiveType.Rar; - - /// - public override IEnumerable GetSupportedExtensions() - { - yield return "rar"; - yield return "cbr"; - } - - /// - public override bool IsArchive(Stream stream, string? password = null) - { - return RarArchive.IsRarFile(stream); - } - - /// - public override FileInfo? GetFilePart(int index, FileInfo part1) - { - return RarArchiveVolumeFactory.GetFilePart(index, part1); - } - - #endregion - - #region IArchiveFactory - - /// - public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) - { - return RarArchive.Open(stream, readerOptions); - } - - /// - public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) - { - return RarArchive.Open(fileInfo, readerOptions); - } - - #endregion - - #region IMultiArchiveFactory - - /// - public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) - { - return RarArchive.Open(streams, readerOptions); - } - - /// - public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) - { - return RarArchive.Open(fileInfos, readerOptions); - } - - #endregion - - #region IReaderFactory - - /// - public IReader OpenReader(Stream stream, ReaderOptions? options) - { - return RarReader.Open(stream, options); - } - - #endregion + yield return "rar"; + yield return "cbr"; } + + /// + public override bool IsArchive(Stream stream, string? password = null) + { + return RarArchive.IsRarFile(stream); + } + + /// + public override FileInfo? GetFilePart(int index, FileInfo part1) + { + return RarArchiveVolumeFactory.GetFilePart(index, part1); + } + + #endregion + + #region IArchiveFactory + + /// + public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) + { + return RarArchive.Open(stream, readerOptions); + } + + /// + public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + { + return RarArchive.Open(fileInfo, readerOptions); + } + + #endregion + + #region IMultiArchiveFactory + + /// + public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) + { + return RarArchive.Open(streams, readerOptions); + } + + /// + public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) + { + return RarArchive.Open(fileInfos, readerOptions); + } + + #endregion + + #region IReaderFactory + + /// + public IReader OpenReader(Stream stream, ReaderOptions? options) + { + return RarReader.Open(stream, options); + } + + #endregion } diff --git a/src/SharpCompress/Factories/SevenZipFactory.cs b/src/SharpCompress/Factories/SevenZipFactory.cs index 8bdbb8d0..7e06d9e0 100644 --- a/src/SharpCompress/Factories/SevenZipFactory.cs +++ b/src/SharpCompress/Factories/SevenZipFactory.cs @@ -1,87 +1,83 @@ -using System; using System.Collections.Generic; using System.IO; -using System.Linq; using SharpCompress.Archives; using SharpCompress.Archives.SevenZip; using SharpCompress.Common; -using SharpCompress.Common.SevenZip; using SharpCompress.IO; using SharpCompress.Readers; -namespace SharpCompress.Factories +namespace SharpCompress.Factories; + +/// +/// Represents the foundation factory of 7Zip archive. +/// +public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory { - /// - /// Represents the foundation factory of 7Zip archive. - /// - public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory + #region IFactory + + /// + public override string Name => "7Zip"; + + /// + public override ArchiveType? KnownArchiveType => ArchiveType.SevenZip; + + /// + public override IEnumerable GetSupportedExtensions() { - #region IFactory - - /// - public override string Name => "7Zip"; - - /// - public override ArchiveType? KnownArchiveType => ArchiveType.SevenZip; - - /// - public override IEnumerable GetSupportedExtensions() - { - yield return "7z"; - } - - /// - public override bool IsArchive(Stream stream, string? password = null) - { - return SevenZipArchive.IsSevenZipFile(stream); - } - - #endregion - - #region IArchiveFactory - - /// - public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) - { - return SevenZipArchive.Open(stream, readerOptions); - } - - /// - public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) - { - return SevenZipArchive.Open(fileInfo, readerOptions); - } - - #endregion - - #region IMultiArchiveFactory - - /// - public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) - { - return SevenZipArchive.Open(streams, readerOptions); - } - - /// - public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) - { - return SevenZipArchive.Open(fileInfos, readerOptions); - } - - #endregion - - #region reader - - internal override bool TryOpenReader( - RewindableStream rewindableStream, - ReaderOptions options, - out IReader? reader - ) - { - reader = null; - return false; - } - - #endregion + yield return "7z"; } + + /// + public override bool IsArchive(Stream stream, string? password = null) + { + return SevenZipArchive.IsSevenZipFile(stream); + } + + #endregion + + #region IArchiveFactory + + /// + public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) + { + return SevenZipArchive.Open(stream, readerOptions); + } + + /// + public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + { + return SevenZipArchive.Open(fileInfo, readerOptions); + } + + #endregion + + #region IMultiArchiveFactory + + /// + public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) + { + return SevenZipArchive.Open(streams, readerOptions); + } + + /// + public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) + { + return SevenZipArchive.Open(fileInfos, readerOptions); + } + + #endregion + + #region reader + + internal override bool TryOpenReader( + RewindableStream rewindableStream, + ReaderOptions options, + out IReader? reader + ) + { + reader = null; + return false; + } + + #endregion } diff --git a/src/SharpCompress/Factories/TarFactory.cs b/src/SharpCompress/Factories/TarFactory.cs index b2322d56..747ec17f 100644 --- a/src/SharpCompress/Factories/TarFactory.cs +++ b/src/SharpCompress/Factories/TarFactory.cs @@ -1,11 +1,8 @@ -using System; using System.Collections.Generic; using System.IO; -using System.Linq; using SharpCompress.Archives; using SharpCompress.Archives.Tar; -using SharpCompress.Archives.Zip; using SharpCompress.Common; using SharpCompress.Compressors; using SharpCompress.Compressors.BZip2; @@ -17,192 +14,191 @@ using SharpCompress.Readers.Tar; using SharpCompress.Writers; using SharpCompress.Writers.Tar; -namespace SharpCompress.Factories +namespace SharpCompress.Factories; + +/// +/// Represents the foundation factory of TAR archive. +/// +public class TarFactory + : Factory, + IArchiveFactory, + IMultiArchiveFactory, + IReaderFactory, + IWriterFactory, + IWriteableArchiveFactory { - /// - /// Represents the foundation factory of TAR archive. - /// - public class TarFactory - : Factory, - IArchiveFactory, - IMultiArchiveFactory, - IReaderFactory, - IWriterFactory, - IWriteableArchiveFactory + #region IFactory + + /// + public override string Name => "Tar"; + + /// + public override ArchiveType? KnownArchiveType => ArchiveType.Tar; + + /// + public override IEnumerable GetSupportedExtensions() { - #region IFactory + // from https://en.wikipedia.org/wiki/Tar_(computing)#Suffixes_for_compressed_files - /// - public override string Name => "Tar"; + yield return "tar"; - /// - public override ArchiveType? KnownArchiveType => ArchiveType.Tar; + // gzip + yield return "taz"; + yield return "tgz"; - /// - public override IEnumerable GetSupportedExtensions() + // bzip2 + yield return "tb2"; + yield return "tbz"; + yield return "tbz2"; + yield return "tz2"; + + // lzma + // yield return "tlz"; // unsupported + + // xz + // yield return "txz"; // unsupported + + // compress + yield return "tZ"; + yield return "taZ"; + + // zstd + // yield return "tzst"; // unsupported + } + + /// + public override bool IsArchive(Stream stream, string? password = null) + { + return TarArchive.IsTarFile(stream); + } + + #endregion + + #region IArchiveFactory + + /// + public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) + { + return TarArchive.Open(stream, readerOptions); + } + + /// + public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + { + return TarArchive.Open(fileInfo, readerOptions); + } + + #endregion + + #region IMultiArchiveFactory + + /// + public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) + { + return TarArchive.Open(streams, readerOptions); + } + + /// + public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) + { + return TarArchive.Open(fileInfos, readerOptions); + } + + #endregion + + #region IReaderFactory + + /// + internal override bool TryOpenReader( + RewindableStream rewindableStream, + ReaderOptions options, + out IReader? reader + ) + { + reader = null; + + rewindableStream.Rewind(false); + if (TarArchive.IsTarFile(rewindableStream)) { - // from https://en.wikipedia.org/wiki/Tar_(computing)#Suffixes_for_compressed_files - - yield return "tar"; - - // gzip - yield return "taz"; - yield return "tgz"; - - // bzip2 - yield return "tb2"; - yield return "tbz"; - yield return "tbz2"; - yield return "tz2"; - - // lzma - // yield return "tlz"; // unsupported - - // xz - // yield return "txz"; // unsupported - - // compress - yield return "tZ"; - yield return "taZ"; - - // zstd - // yield return "tzst"; // unsupported + rewindableStream.Rewind(true); + reader = OpenReader(rewindableStream, options); + return true; } - /// - public override bool IsArchive(Stream stream, string? password = null) + rewindableStream.Rewind(false); + if (BZip2Stream.IsBZip2(rewindableStream)) { - return TarArchive.IsTarFile(stream); - } - - #endregion - - #region IArchiveFactory - - /// - public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) - { - return TarArchive.Open(stream, readerOptions); - } - - /// - public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) - { - return TarArchive.Open(fileInfo, readerOptions); - } - - #endregion - - #region IMultiArchiveFactory - - /// - public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) - { - return TarArchive.Open(streams, readerOptions); - } - - /// - public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) - { - return TarArchive.Open(fileInfos, readerOptions); - } - - #endregion - - #region IReaderFactory - - /// - internal override bool TryOpenReader( - RewindableStream rewindableStream, - ReaderOptions options, - out IReader? reader - ) - { - reader = null; - rewindableStream.Rewind(false); - if (TarArchive.IsTarFile(rewindableStream)) + var testStream = new BZip2Stream( + NonDisposingStream.Create(rewindableStream), + CompressionMode.Decompress, + false + ); + if (TarArchive.IsTarFile(testStream)) { rewindableStream.Rewind(true); - reader = OpenReader(rewindableStream, options); + reader = new TarReader(rewindableStream, options, CompressionType.BZip2); return true; } + } + rewindableStream.Rewind(false); + if (LZipStream.IsLZipFile(rewindableStream)) + { rewindableStream.Rewind(false); - if (BZip2Stream.IsBZip2(rewindableStream)) - { - rewindableStream.Rewind(false); - var testStream = new BZip2Stream( - NonDisposingStream.Create(rewindableStream), - CompressionMode.Decompress, - false - ); - if (TarArchive.IsTarFile(testStream)) - { - rewindableStream.Rewind(true); - reader = new TarReader(rewindableStream, options, CompressionType.BZip2); - return true; - } - } - - rewindableStream.Rewind(false); - if (LZipStream.IsLZipFile(rewindableStream)) - { - rewindableStream.Rewind(false); - var testStream = new LZipStream( - NonDisposingStream.Create(rewindableStream), - CompressionMode.Decompress - ); - if (TarArchive.IsTarFile(testStream)) - { - rewindableStream.Rewind(true); - reader = new TarReader(rewindableStream, options, CompressionType.LZip); - return true; - } - } - - rewindableStream.Rewind(false); - if (XZStream.IsXZStream(rewindableStream)) + var testStream = new LZipStream( + NonDisposingStream.Create(rewindableStream), + CompressionMode.Decompress + ); + if (TarArchive.IsTarFile(testStream)) { rewindableStream.Rewind(true); - var testStream = new XZStream(rewindableStream); - if (TarArchive.IsTarFile(testStream)) - { - rewindableStream.Rewind(true); - reader = new TarReader(rewindableStream, options, CompressionType.Xz); - return true; - } + reader = new TarReader(rewindableStream, options, CompressionType.LZip); + return true; } - - return false; } - /// - public IReader OpenReader(Stream stream, ReaderOptions? options) + rewindableStream.Rewind(false); + if (XZStream.IsXZStream(rewindableStream)) { - return TarReader.Open(stream, options); + rewindableStream.Rewind(true); + var testStream = new XZStream(rewindableStream); + if (TarArchive.IsTarFile(testStream)) + { + rewindableStream.Rewind(true); + reader = new TarReader(rewindableStream, options, CompressionType.Xz); + return true; + } } - #endregion - - #region IWriterFactory - - /// - public IWriter Open(Stream stream, WriterOptions writerOptions) - { - return new TarWriter(stream, new TarWriterOptions(writerOptions)); - } - - #endregion - - #region IWriteableArchiveFactory - - /// - public IWritableArchive CreateWriteableArchive() - { - return TarArchive.Create(); - } - - #endregion + return false; } + + /// + public IReader OpenReader(Stream stream, ReaderOptions? options) + { + return TarReader.Open(stream, options); + } + + #endregion + + #region IWriterFactory + + /// + public IWriter Open(Stream stream, WriterOptions writerOptions) + { + return new TarWriter(stream, new TarWriterOptions(writerOptions)); + } + + #endregion + + #region IWriteableArchiveFactory + + /// + public IWritableArchive CreateWriteableArchive() + { + return TarArchive.Create(); + } + + #endregion } diff --git a/src/SharpCompress/Factories/ZipFactory.cs b/src/SharpCompress/Factories/ZipFactory.cs index 66b274cb..64162b68 100644 --- a/src/SharpCompress/Factories/ZipFactory.cs +++ b/src/SharpCompress/Factories/ZipFactory.cs @@ -1,139 +1,140 @@ -using System; using System.Collections.Generic; using System.IO; -using System.Linq; using SharpCompress.Archives; using SharpCompress.Archives.Zip; using SharpCompress.Common; -using SharpCompress.Common.Tar.Headers; -using SharpCompress.IO; using SharpCompress.Readers; using SharpCompress.Readers.Zip; using SharpCompress.Writers; using SharpCompress.Writers.Zip; -namespace SharpCompress.Factories +namespace SharpCompress.Factories; + +/// +/// Represents the foundation factory of ZIP archive. +/// +public class ZipFactory + : Factory, + IArchiveFactory, + IMultiArchiveFactory, + IReaderFactory, + IWriterFactory, + IWriteableArchiveFactory { - /// - /// Represents the foundation factory of ZIP archive. - /// - public class ZipFactory - : Factory, - IArchiveFactory, - IMultiArchiveFactory, - IReaderFactory, - IWriterFactory, - IWriteableArchiveFactory + #region IFactory + + /// + public override string Name => "Zip"; + + /// + public override ArchiveType? KnownArchiveType => ArchiveType.Zip; + + /// + public override IEnumerable GetSupportedExtensions() { - #region IFactory + yield return "zip"; + yield return "zipx"; + yield return "cbz"; + } - /// - public override string Name => "Zip"; + /// + public override bool IsArchive(Stream stream, string? password = null) + { + var startPosition = stream.CanSeek ? stream.Position : -1; - /// - public override ArchiveType? KnownArchiveType => ArchiveType.Zip; + // probe for single volume zip - /// - public override IEnumerable GetSupportedExtensions() + if (ZipArchive.IsZipFile(stream, password)) { - yield return "zip"; - yield return "zipx"; - yield return "cbz"; + return true; } - /// - public override bool IsArchive(Stream stream, string? password = null) + // probe for a multipart zip + + if (!stream.CanSeek) { - var startPosition = stream.CanSeek ? stream.Position : -1; - - // probe for single volume zip - - if (ZipArchive.IsZipFile(stream, password)) - return true; - - // probe for a multipart zip - - if (!stream.CanSeek) - return false; - - stream.Position = startPosition; - - //test the zip (last) file of a multipart zip - if (ZipArchive.IsZipMulti(stream)) - return true; - return false; } - /// - public override FileInfo? GetFilePart(int index, FileInfo part1) + stream.Position = startPosition; + + //test the zip (last) file of a multipart zip + if (ZipArchive.IsZipMulti(stream)) { - return ZipArchiveVolumeFactory.GetFilePart(index, part1); + return true; } - #endregion - - #region IArchiveFactory - - /// - public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) - { - return ZipArchive.Open(stream, readerOptions); - } - - /// - public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) - { - return ZipArchive.Open(fileInfo, readerOptions); - } - - #endregion - - #region IMultiArchiveFactory - - /// - public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) - { - return ZipArchive.Open(streams, readerOptions); - } - - /// - public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) - { - return ZipArchive.Open(fileInfos, readerOptions); - } - - #endregion - - #region IReaderFactory - - /// - public IReader OpenReader(Stream stream, ReaderOptions? options) - { - return ZipReader.Open(stream, options); - } - - #endregion - - #region IWriterFactory - - /// - public IWriter Open(Stream stream, WriterOptions writerOptions) - { - return new ZipWriter(stream, new ZipWriterOptions(writerOptions)); - } - - #endregion - - #region IWriteableArchiveFactory - - /// - public IWritableArchive CreateWriteableArchive() - { - return ZipArchive.Create(); - } - - #endregion + return false; } + + /// + public override FileInfo? GetFilePart(int index, FileInfo part1) + { + return ZipArchiveVolumeFactory.GetFilePart(index, part1); + } + + #endregion + + #region IArchiveFactory + + /// + public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) + { + return ZipArchive.Open(stream, readerOptions); + } + + /// + public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + { + return ZipArchive.Open(fileInfo, readerOptions); + } + + #endregion + + #region IMultiArchiveFactory + + /// + public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) + { + return ZipArchive.Open(streams, readerOptions); + } + + /// + public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) + { + return ZipArchive.Open(fileInfos, readerOptions); + } + + #endregion + + #region IReaderFactory + + /// + public IReader OpenReader(Stream stream, ReaderOptions? options) + { + return ZipReader.Open(stream, options); + } + + #endregion + + #region IWriterFactory + + /// + public IWriter Open(Stream stream, WriterOptions writerOptions) + { + return new ZipWriter(stream, new ZipWriterOptions(writerOptions)); + } + + #endregion + + #region IWriteableArchiveFactory + + /// + public IWritableArchive CreateWriteableArchive() + { + return ZipArchive.Create(); + } + + #endregion } diff --git a/src/SharpCompress/IO/BufferedSubStream.cs b/src/SharpCompress/IO/BufferedSubStream.cs index 94d70b17..37e7e0bb 100644 --- a/src/SharpCompress/IO/BufferedSubStream.cs +++ b/src/SharpCompress/IO/BufferedSubStream.cs @@ -1,88 +1,87 @@ -using System; +using System; using System.IO; -namespace SharpCompress.IO +namespace SharpCompress.IO; + +internal class BufferedSubStream : NonDisposingStream { - internal class BufferedSubStream : NonDisposingStream + private long position; + private int cacheOffset; + private int cacheLength; + private readonly byte[] cache; + + public BufferedSubStream(Stream stream, long origin, long bytesToRead) + : base(stream, throwOnDispose: false) { - private long position; - private int cacheOffset; - private int cacheLength; - private readonly byte[] cache; + position = origin; + BytesLeftToRead = bytesToRead; + cache = new byte[32 << 10]; + } - public BufferedSubStream(Stream stream, long origin, long bytesToRead) - : base(stream, throwOnDispose: false) + private long BytesLeftToRead { get; set; } + + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override void Flush() + { + throw new NotSupportedException(); + } + + public override long Length => BytesLeftToRead; + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count > BytesLeftToRead) { - position = origin; - BytesLeftToRead = bytesToRead; - cache = new byte[32 << 10]; + count = (int)BytesLeftToRead; } - private long BytesLeftToRead { get; set; } - - public override bool CanRead => true; - - public override bool CanSeek => false; - - public override bool CanWrite => false; - - public override void Flush() + if (count > 0) { - throw new NotSupportedException(); - } - - public override long Length => BytesLeftToRead; - - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - if (count > BytesLeftToRead) + if (cacheLength == 0) { - count = (int)BytesLeftToRead; + cacheOffset = 0; + Stream.Position = position; + cacheLength = Stream.Read(cache, 0, cache.Length); + position += cacheLength; } - if (count > 0) + if (count > cacheLength) { - if (cacheLength == 0) - { - cacheOffset = 0; - Stream.Position = position; - cacheLength = Stream.Read(cache, 0, cache.Length); - position += cacheLength; - } - - if (count > cacheLength) - { - count = cacheLength; - } - - Buffer.BlockCopy(cache, cacheOffset, buffer, offset, count); - cacheOffset += count; - cacheLength -= count; - BytesLeftToRead -= count; + count = cacheLength; } - return count; + Buffer.BlockCopy(cache, cacheOffset, buffer, offset, count); + cacheOffset += count; + cacheLength -= count; + BytesLeftToRead -= count; } - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } + return count; + } - public override void SetLength(long value) - { - throw new NotSupportedException(); - } + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); } } diff --git a/src/SharpCompress/IO/CountingWritableSubStream.cs b/src/SharpCompress/IO/CountingWritableSubStream.cs index a5aa4465..5a3a990d 100644 --- a/src/SharpCompress/IO/CountingWritableSubStream.cs +++ b/src/SharpCompress/IO/CountingWritableSubStream.cs @@ -1,58 +1,57 @@ using System; using System.IO; -namespace SharpCompress.IO +namespace SharpCompress.IO; + +internal class CountingWritableSubStream : NonDisposingStream { - internal class CountingWritableSubStream : NonDisposingStream + internal CountingWritableSubStream(Stream stream) : base(stream, throwOnDispose: false) { } + + public ulong Count { get; private set; } + + public override bool CanRead => false; + + public override bool CanSeek => false; + + public override bool CanWrite => true; + + public override void Flush() { - internal CountingWritableSubStream(Stream stream) : base(stream, throwOnDispose: false) { } + Stream.Flush(); + } - public ulong Count { get; private set; } + public override long Length => throw new NotSupportedException(); - public override bool CanRead => false; + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } - public override bool CanSeek => false; + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } - public override bool CanWrite => true; + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } - public override void Flush() - { - Stream.Flush(); - } + public override void SetLength(long value) + { + throw new NotSupportedException(); + } - public override long Length => throw new NotSupportedException(); + public override void Write(byte[] buffer, int offset, int count) + { + Stream.Write(buffer, offset, count); + Count += (uint)count; + } - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - Stream.Write(buffer, offset, count); - Count += (uint)count; - } - - public override void WriteByte(byte value) - { - Stream.WriteByte(value); - ++Count; - } + public override void WriteByte(byte value) + { + Stream.WriteByte(value); + ++Count; } } diff --git a/src/SharpCompress/IO/ListeningStream.cs b/src/SharpCompress/IO/ListeningStream.cs index d914bec4..0cccdc6b 100644 --- a/src/SharpCompress/IO/ListeningStream.cs +++ b/src/SharpCompress/IO/ListeningStream.cs @@ -1,89 +1,88 @@ -using System.IO; +using System.IO; using SharpCompress.Common; -namespace SharpCompress.IO +namespace SharpCompress.IO; + +internal class ListeningStream : Stream { - internal class ListeningStream : Stream + private long currentEntryTotalReadBytes; + private readonly IExtractionListener listener; + + public ListeningStream(IExtractionListener listener, Stream stream) { - private long currentEntryTotalReadBytes; - private readonly IExtractionListener listener; + Stream = stream; + this.listener = listener; + } - public ListeningStream(IExtractionListener listener, Stream stream) + protected override void Dispose(bool disposing) + { + if (disposing) { - Stream = stream; - this.listener = listener; + Stream.Dispose(); + } + base.Dispose(disposing); + } + + public Stream Stream { get; } + + public override bool CanRead => Stream.CanRead; + + public override bool CanSeek => Stream.CanSeek; + + public override bool CanWrite => Stream.CanWrite; + + public override void Flush() + { + Stream.Flush(); + } + + public override long Length => Stream.Length; + + public override long Position + { + get => Stream.Position; + set => Stream.Position = value; + } + + public override int Read(byte[] buffer, int offset, int count) + { + var read = Stream.Read(buffer, offset, count); + currentEntryTotalReadBytes += read; + listener.FireCompressedBytesRead( + currentEntryTotalReadBytes, + currentEntryTotalReadBytes + ); + return read; + } + + public override int ReadByte() + { + var value = Stream.ReadByte(); + if (value == -1) + { + return -1; } - protected override void Dispose(bool disposing) - { - if (disposing) - { - Stream.Dispose(); - } - base.Dispose(disposing); - } + ++currentEntryTotalReadBytes; + listener.FireCompressedBytesRead( + currentEntryTotalReadBytes, + currentEntryTotalReadBytes + ); + return value; + } - public Stream Stream { get; } + public override long Seek(long offset, SeekOrigin origin) + { + return Stream.Seek(offset, origin); + } - public override bool CanRead => Stream.CanRead; + public override void SetLength(long value) + { + Stream.SetLength(value); + } - public override bool CanSeek => Stream.CanSeek; - - public override bool CanWrite => Stream.CanWrite; - - public override void Flush() - { - Stream.Flush(); - } - - public override long Length => Stream.Length; - - public override long Position - { - get => Stream.Position; - set => Stream.Position = value; - } - - public override int Read(byte[] buffer, int offset, int count) - { - int read = Stream.Read(buffer, offset, count); - currentEntryTotalReadBytes += read; - listener.FireCompressedBytesRead( - currentEntryTotalReadBytes, - currentEntryTotalReadBytes - ); - return read; - } - - public override int ReadByte() - { - int value = Stream.ReadByte(); - if (value == -1) - { - return -1; - } - - ++currentEntryTotalReadBytes; - listener.FireCompressedBytesRead( - currentEntryTotalReadBytes, - currentEntryTotalReadBytes - ); - return value; - } - - public override long Seek(long offset, SeekOrigin origin) - { - return Stream.Seek(offset, origin); - } - - public override void SetLength(long value) - { - Stream.SetLength(value); - } - - public override void Write(byte[] buffer, int offset, int count) - { - Stream.Write(buffer, offset, count); - } + public override void Write(byte[] buffer, int offset, int count) + { + Stream.Write(buffer, offset, count); } } diff --git a/src/SharpCompress/IO/MarkingBinaryReader.cs b/src/SharpCompress/IO/MarkingBinaryReader.cs index 01847518..144a702b 100644 --- a/src/SharpCompress/IO/MarkingBinaryReader.cs +++ b/src/SharpCompress/IO/MarkingBinaryReader.cs @@ -1,214 +1,213 @@ -using System; +using System; using System.Buffers.Binary; using System.IO; -namespace SharpCompress.IO +namespace SharpCompress.IO; + +internal class MarkingBinaryReader : BinaryReader { - internal class MarkingBinaryReader : BinaryReader + public MarkingBinaryReader(Stream stream) : base(stream) { } + + public virtual long CurrentReadByteCount { get; protected set; } + + public virtual void Mark() { - public MarkingBinaryReader(Stream stream) : base(stream) { } + CurrentReadByteCount = 0; + } - public virtual long CurrentReadByteCount { get; protected set; } + public override int Read() + { + throw new NotSupportedException(); + } - public virtual void Mark() + public override int Read(byte[] buffer, int index, int count) + { + throw new NotSupportedException(); + } + + public override int Read(char[] buffer, int index, int count) + { + throw new NotSupportedException(); + } + + public override bool ReadBoolean() + { + return ReadByte() != 0; + } + + // NOTE: there is a somewhat fragile dependency on the internals of this class + // with RarCrcBinaryReader and RarCryptoBinaryReader. + // + // RarCrcBinaryReader/RarCryptoBinaryReader need to override any specific methods + // that call directly to the base BinaryReader and do not delegate to other methods + // in this class so that it can track the each byte being read. + // + // if altering this class in a way that changes the implementation be sure to + // update RarCrcBinaryReader/RarCryptoBinaryReader. + public override byte ReadByte() + { + CurrentReadByteCount++; + return base.ReadByte(); + } + + public override byte[] ReadBytes(int count) + { + CurrentReadByteCount += count; + var bytes = base.ReadBytes(count); + if (bytes.Length != count) { - CurrentReadByteCount = 0; + throw new EndOfStreamException( + string.Format( + "Could not read the requested amount of bytes. End of stream reached. Requested: {0} Read: {1}", + count, + bytes.Length + ) + ); } + return bytes; + } - public override int Read() - { - throw new NotSupportedException(); - } + public override char ReadChar() + { + throw new NotSupportedException(); + } - public override int Read(byte[] buffer, int index, int count) - { - throw new NotSupportedException(); - } + public override char[] ReadChars(int count) + { + throw new NotSupportedException(); + } - public override int Read(char[] buffer, int index, int count) - { - throw new NotSupportedException(); - } + public override double ReadDouble() + { + throw new NotSupportedException(); + } - public override bool ReadBoolean() - { - return ReadByte() != 0; - } + public override short ReadInt16() + { + return BinaryPrimitives.ReadInt16LittleEndian(ReadBytes(2)); + } - // NOTE: there is a somewhat fragile dependency on the internals of this class - // with RarCrcBinaryReader and RarCryptoBinaryReader. - // - // RarCrcBinaryReader/RarCryptoBinaryReader need to override any specific methods - // that call directly to the base BinaryReader and do not delegate to other methods - // in this class so that it can track the each byte being read. - // - // if altering this class in a way that changes the implementation be sure to - // update RarCrcBinaryReader/RarCryptoBinaryReader. - public override byte ReadByte() - { - CurrentReadByteCount++; - return base.ReadByte(); - } + public override int ReadInt32() + { + return BinaryPrimitives.ReadInt32LittleEndian(ReadBytes(4)); + } - public override byte[] ReadBytes(int count) + public override long ReadInt64() + { + return BinaryPrimitives.ReadInt64LittleEndian(ReadBytes(8)); + } + + public override sbyte ReadSByte() + { + return (sbyte)ReadByte(); + } + + public override float ReadSingle() + { + throw new NotSupportedException(); + } + + public override string ReadString() + { + throw new NotSupportedException(); + } + + public override ushort ReadUInt16() + { + return BinaryPrimitives.ReadUInt16LittleEndian(ReadBytes(2)); + } + + public override uint ReadUInt32() + { + return BinaryPrimitives.ReadUInt32LittleEndian(ReadBytes(4)); + } + + public override ulong ReadUInt64() + { + return BinaryPrimitives.ReadUInt64LittleEndian(ReadBytes(8)); + } + + // RAR5 style variable length encoded value + // maximum value of 0xffffffffffffffff (64 bits) + // technote: "implies max 10 bytes consumed" -- but not really because we could extend indefinitely using 0x80 0x80 ... 0x80 00 + // + // Variable length integer. Can include one or more bytes, where lower 7 bits of every byte contain integer data + // and highest bit in every byte is the continuation flag. If highest bit is 0, this is the last byte in sequence. + // So first byte contains 7 least significant bits of integer and continuation flag. Second byte, if present, + // contains next 7 bits and so on. + public ulong ReadRarVInt(int maxBytes = 10) + { + // hopefully this gets inlined + return DoReadRarVInt((maxBytes - 1) * 7); + } + + private ulong DoReadRarVInt(int maxShift) + { + var shift = 0; + ulong result = 0; + do { - CurrentReadByteCount += count; - var bytes = base.ReadBytes(count); - if (bytes.Length != count) + var b0 = ReadByte(); + var b1 = ((uint)b0) & 0x7f; + ulong n = b1; + var shifted = n << shift; + if (n != shifted >> shift) { - throw new EndOfStreamException( - string.Format( - "Could not read the requested amount of bytes. End of stream reached. Requested: {0} Read: {1}", - count, - bytes.Length - ) - ); + // overflow + break; } - return bytes; - } - - public override char ReadChar() - { - throw new NotSupportedException(); - } - - public override char[] ReadChars(int count) - { - throw new NotSupportedException(); - } - - public override double ReadDouble() - { - throw new NotSupportedException(); - } - - public override short ReadInt16() - { - return BinaryPrimitives.ReadInt16LittleEndian(ReadBytes(2)); - } - - public override int ReadInt32() - { - return BinaryPrimitives.ReadInt32LittleEndian(ReadBytes(4)); - } - - public override long ReadInt64() - { - return BinaryPrimitives.ReadInt64LittleEndian(ReadBytes(8)); - } - - public override sbyte ReadSByte() - { - return (sbyte)ReadByte(); - } - - public override float ReadSingle() - { - throw new NotSupportedException(); - } - - public override string ReadString() - { - throw new NotSupportedException(); - } - - public override ushort ReadUInt16() - { - return BinaryPrimitives.ReadUInt16LittleEndian(ReadBytes(2)); - } - - public override uint ReadUInt32() - { - return BinaryPrimitives.ReadUInt32LittleEndian(ReadBytes(4)); - } - - public override ulong ReadUInt64() - { - return BinaryPrimitives.ReadUInt64LittleEndian(ReadBytes(8)); - } - - // RAR5 style variable length encoded value - // maximum value of 0xffffffffffffffff (64 bits) - // technote: "implies max 10 bytes consumed" -- but not really because we could extend indefinitely using 0x80 0x80 ... 0x80 00 - // - // Variable length integer. Can include one or more bytes, where lower 7 bits of every byte contain integer data - // and highest bit in every byte is the continuation flag. If highest bit is 0, this is the last byte in sequence. - // So first byte contains 7 least significant bits of integer and continuation flag. Second byte, if present, - // contains next 7 bits and so on. - public ulong ReadRarVInt(int maxBytes = 10) - { - // hopefully this gets inlined - return DoReadRarVInt((maxBytes - 1) * 7); - } - - private ulong DoReadRarVInt(int maxShift) - { - int shift = 0; - ulong result = 0; - do + result |= shifted; + if (b0 == b1) { - byte b0 = ReadByte(); - uint b1 = ((uint)b0) & 0x7f; - ulong n = b1; - ulong shifted = n << shift; - if (n != shifted >> shift) - { - // overflow - break; - } - result |= shifted; - if (b0 == b1) - { - return result; - } - shift += 7; - } while (shift <= maxShift); + return result; + } + shift += 7; + } while (shift <= maxShift); - throw new FormatException("malformed vint"); - } + throw new FormatException("malformed vint"); + } - public uint ReadRarVIntUInt32(int maxBytes = 5) + public uint ReadRarVIntUInt32(int maxBytes = 5) + { + // hopefully this gets inlined + return DoReadRarVIntUInt32((maxBytes - 1) * 7); + } + + public ushort ReadRarVIntUInt16(int maxBytes = 3) + { + // hopefully this gets inlined + return checked((ushort)DoReadRarVIntUInt32((maxBytes - 1) * 7)); + } + + public byte ReadRarVIntByte(int maxBytes = 2) + { + // hopefully this gets inlined + return checked((byte)DoReadRarVIntUInt32((maxBytes - 1) * 7)); + } + + private uint DoReadRarVIntUInt32(int maxShift) + { + var shift = 0; + uint result = 0; + do { - // hopefully this gets inlined - return DoReadRarVIntUInt32((maxBytes - 1) * 7); - } - - public ushort ReadRarVIntUInt16(int maxBytes = 3) - { - // hopefully this gets inlined - return checked((ushort)DoReadRarVIntUInt32((maxBytes - 1) * 7)); - } - - public byte ReadRarVIntByte(int maxBytes = 2) - { - // hopefully this gets inlined - return checked((byte)DoReadRarVIntUInt32((maxBytes - 1) * 7)); - } - - private uint DoReadRarVIntUInt32(int maxShift) - { - int shift = 0; - uint result = 0; - do + var b0 = ReadByte(); + var b1 = ((uint)b0) & 0x7f; + var n = b1; + var shifted = n << shift; + if (n != shifted >> shift) { - byte b0 = ReadByte(); - uint b1 = ((uint)b0) & 0x7f; - uint n = b1; - uint shifted = n << shift; - if (n != shifted >> shift) - { - // overflow - break; - } - result |= shifted; - if (b0 == b1) - { - return result; - } - shift += 7; - } while (shift <= maxShift); + // overflow + break; + } + result |= shifted; + if (b0 == b1) + { + return result; + } + shift += 7; + } while (shift <= maxShift); - throw new FormatException("malformed vint"); - } + throw new FormatException("malformed vint"); } } diff --git a/src/SharpCompress/IO/NonDisposingStream.cs b/src/SharpCompress/IO/NonDisposingStream.cs index a3b74469..78063c78 100644 --- a/src/SharpCompress/IO/NonDisposingStream.cs +++ b/src/SharpCompress/IO/NonDisposingStream.cs @@ -1,93 +1,92 @@ using System; using System.IO; -namespace SharpCompress.IO +namespace SharpCompress.IO; + +public class NonDisposingStream : Stream { - public class NonDisposingStream : Stream + public static NonDisposingStream Create(Stream stream, bool throwOnDispose = false) { - public static NonDisposingStream Create(Stream stream, bool throwOnDispose = false) + if ( + stream is NonDisposingStream nonDisposingStream + && nonDisposingStream.ThrowOnDispose == throwOnDispose + ) { - if ( - stream is NonDisposingStream nonDisposingStream - && nonDisposingStream.ThrowOnDispose == throwOnDispose - ) - { - return nonDisposingStream; - } - return new NonDisposingStream(stream, throwOnDispose); + return nonDisposingStream; } + return new NonDisposingStream(stream, throwOnDispose); + } - protected NonDisposingStream(Stream stream, bool throwOnDispose = false) + protected NonDisposingStream(Stream stream, bool throwOnDispose = false) + { + Stream = stream; + ThrowOnDispose = throwOnDispose; + } + + public bool ThrowOnDispose { get; set; } + + protected override void Dispose(bool disposing) + { + if (ThrowOnDispose) { - Stream = stream; - ThrowOnDispose = throwOnDispose; + throw new InvalidOperationException( + $"Attempt to dispose of a {nameof(NonDisposingStream)} when {nameof(ThrowOnDispose)} is {ThrowOnDispose}" + ); } + } - public bool ThrowOnDispose { get; set; } + protected Stream Stream { get; } - protected override void Dispose(bool disposing) - { - if (ThrowOnDispose) - { - throw new InvalidOperationException( - $"Attempt to dispose of a {nameof(NonDisposingStream)} when {nameof(ThrowOnDispose)} is {ThrowOnDispose}" - ); - } - } + public override bool CanRead => Stream.CanRead; - protected Stream Stream { get; } + public override bool CanSeek => Stream.CanSeek; - public override bool CanRead => Stream.CanRead; + public override bool CanWrite => Stream.CanWrite; - public override bool CanSeek => Stream.CanSeek; + public override void Flush() + { + Stream.Flush(); + } - public override bool CanWrite => Stream.CanWrite; + public override long Length => Stream.Length; - public override void Flush() - { - Stream.Flush(); - } + public override long Position + { + get => Stream.Position; + set => Stream.Position = value; + } - public override long Length => Stream.Length; + public override int Read(byte[] buffer, int offset, int count) + { + return Stream.Read(buffer, offset, count); + } - public override long Position - { - get => Stream.Position; - set => Stream.Position = value; - } + public override long Seek(long offset, SeekOrigin origin) + { + return Stream.Seek(offset, origin); + } - public override int Read(byte[] buffer, int offset, int count) - { - return Stream.Read(buffer, offset, count); - } + public override void SetLength(long value) + { + Stream.SetLength(value); + } - public override long Seek(long offset, SeekOrigin origin) - { - return Stream.Seek(offset, origin); - } - - public override void SetLength(long value) - { - Stream.SetLength(value); - } - - public override void Write(byte[] buffer, int offset, int count) - { - Stream.Write(buffer, offset, count); - } + public override void Write(byte[] buffer, int offset, int count) + { + Stream.Write(buffer, offset, count); + } #if !NETFRAMEWORK && !NETSTANDARD2_0 - public override int Read(Span buffer) - { - return Stream.Read(buffer); - } + public override int Read(Span buffer) + { + return Stream.Read(buffer); + } - public override void Write(ReadOnlySpan buffer) - { - Stream.Write(buffer); - } + public override void Write(ReadOnlySpan buffer) + { + Stream.Write(buffer); + } #endif - } } diff --git a/src/SharpCompress/IO/ReadOnlySubStream.cs b/src/SharpCompress/IO/ReadOnlySubStream.cs index f098db4c..acdb6e4a 100644 --- a/src/SharpCompress/IO/ReadOnlySubStream.cs +++ b/src/SharpCompress/IO/ReadOnlySubStream.cs @@ -1,98 +1,97 @@ using System; using System.IO; -namespace SharpCompress.IO +namespace SharpCompress.IO; + +internal class ReadOnlySubStream : NonDisposingStream { - internal class ReadOnlySubStream : NonDisposingStream + public ReadOnlySubStream(Stream stream, long bytesToRead) : this(stream, null, bytesToRead) + { } + + public ReadOnlySubStream(Stream stream, long? origin, long bytesToRead) + : base(stream, throwOnDispose: false) { - public ReadOnlySubStream(Stream stream, long bytesToRead) : this(stream, null, bytesToRead) - { } - - public ReadOnlySubStream(Stream stream, long? origin, long bytesToRead) - : base(stream, throwOnDispose: false) + if (origin != null) { - if (origin != null) - { - stream.Position = origin.Value; - } - BytesLeftToRead = bytesToRead; + stream.Position = origin.Value; } + BytesLeftToRead = bytesToRead; + } - private long BytesLeftToRead { get; set; } + private long BytesLeftToRead { get; set; } - public override bool CanRead => true; + public override bool CanRead => true; - public override bool CanSeek => false; + public override bool CanSeek => false; - public override bool CanWrite => false; + public override bool CanWrite => false; - public override void Flush() + public override void Flush() + { + throw new NotSupportedException(); + } + + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (BytesLeftToRead < count) { - throw new NotSupportedException(); + count = (int)BytesLeftToRead; } - - public override long Length => throw new NotSupportedException(); - - public override long Position + var read = Stream.Read(buffer, offset, count); + if (read > 0) { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); + BytesLeftToRead -= read; } + return read; + } - public override int Read(byte[] buffer, int offset, int count) + public override int ReadByte() + { + if (BytesLeftToRead <= 0) { - if (BytesLeftToRead < count) - { - count = (int)BytesLeftToRead; - } - int read = Stream.Read(buffer, offset, count); - if (read > 0) - { - BytesLeftToRead -= read; - } - return read; + return -1; } - - public override int ReadByte() + var value = Stream.ReadByte(); + if (value != -1) { - if (BytesLeftToRead <= 0) - { - return -1; - } - int value = Stream.ReadByte(); - if (value != -1) - { - --BytesLeftToRead; - } - return value; + --BytesLeftToRead; } + return value; + } #if !NETFRAMEWORK && !NETSTANDARD2_0 - public override int Read(Span buffer) + public override int Read(Span buffer) + { + var slice_len = BytesLeftToRead < buffer.Length ? BytesLeftToRead : buffer.Length; + var read = Stream.Read(buffer.Slice(0, (int)slice_len)); + if (read > 0) { - var slice_len = BytesLeftToRead < buffer.Length ? BytesLeftToRead : buffer.Length; - var read = Stream.Read(buffer.Slice(0, (int)slice_len)); - if (read > 0) - { - BytesLeftToRead -= read; - } - return read; + BytesLeftToRead -= read; } + return read; + } #endif - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } - public override void SetLength(long value) - { - throw new NotSupportedException(); - } + public override void SetLength(long value) + { + throw new NotSupportedException(); + } - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); } } diff --git a/src/SharpCompress/IO/RewindableStream.cs b/src/SharpCompress/IO/RewindableStream.cs index 281f08ea..aab9f459 100644 --- a/src/SharpCompress/IO/RewindableStream.cs +++ b/src/SharpCompress/IO/RewindableStream.cs @@ -1,164 +1,163 @@ using System; using System.IO; -namespace SharpCompress.IO +namespace SharpCompress.IO; + +public class RewindableStream : Stream { - public class RewindableStream : Stream + private readonly Stream stream; + private MemoryStream bufferStream = new MemoryStream(); + private bool isRewound; + private bool isDisposed; + + public RewindableStream(Stream stream) { - private readonly Stream stream; - private MemoryStream bufferStream = new MemoryStream(); - private bool isRewound; - private bool isDisposed; + this.stream = stream; + } - public RewindableStream(Stream stream) + internal bool IsRecording { get; private set; } + + protected override void Dispose(bool disposing) + { + if (isDisposed) { - this.stream = stream; + return; } - - internal bool IsRecording { get; private set; } - - protected override void Dispose(bool disposing) + isDisposed = true; + base.Dispose(disposing); + if (disposing) { - if (isDisposed) - { - return; - } - isDisposed = true; - base.Dispose(disposing); - if (disposing) - { - stream.Dispose(); - } + stream.Dispose(); } + } - public void Rewind(bool stopRecording) + public void Rewind(bool stopRecording) + { + isRewound = true; + IsRecording = !stopRecording; + bufferStream.Position = 0; + } + + public void Rewind(MemoryStream buffer) + { + if (bufferStream.Position >= buffer.Length) { - isRewound = true; - IsRecording = !stopRecording; + bufferStream.Position -= buffer.Length; + } + else + { + bufferStream.TransferTo(buffer); + //create new memorystream to allow proper resizing as memorystream could be a user provided buffer + //https://github.com/adamhathcock/sharpcompress/issues/306 + bufferStream = new MemoryStream(); + buffer.Position = 0; + buffer.TransferTo(bufferStream); bufferStream.Position = 0; } + isRewound = true; + } - public void Rewind(MemoryStream buffer) + public void StartRecording() + { + //if (isRewound && bufferStream.Position != 0) + // throw new System.NotImplementedException(); + if (bufferStream.Position != 0) { - if (bufferStream.Position >= buffer.Length) + var data = bufferStream.ToArray(); + var position = bufferStream.Position; + bufferStream.SetLength(0); + bufferStream.Write(data, (int)position, data.Length - (int)position); + bufferStream.Position = 0; + } + IsRecording = true; + } + + public override bool CanRead => true; + + public override bool CanSeek => stream.CanSeek; + + public override bool CanWrite => false; + + public override void Flush() + { + throw new NotSupportedException(); + } + + public override long Length => stream.Length; + + public override long Position + { + get => stream.Position + bufferStream.Position - bufferStream.Length; + set + { + if (!isRewound) { - bufferStream.Position -= buffer.Length; + stream.Position = value; + } + else if (value < stream.Position - bufferStream.Length || value >= stream.Position) + { + stream.Position = value; + isRewound = false; + bufferStream.SetLength(0); } else { - bufferStream.TransferTo(buffer); - //create new memorystream to allow proper resizing as memorystream could be a user provided buffer - //https://github.com/adamhathcock/sharpcompress/issues/306 - bufferStream = new MemoryStream(); - buffer.Position = 0; - buffer.TransferTo(bufferStream); - bufferStream.Position = 0; + bufferStream.Position = value - stream.Position + bufferStream.Length; } - isRewound = true; } + } - public void StartRecording() + public override int Read(byte[] buffer, int offset, int count) + { + //don't actually read if we don't really want to read anything + //currently a network stream bug on Windows for .NET Core + if (count == 0) { - //if (isRewound && bufferStream.Position != 0) - // throw new System.NotImplementedException(); - if (bufferStream.Position != 0) + return 0; + } + int read; + if (isRewound && bufferStream.Position != bufferStream.Length) + { + // don't read more than left + var readCount = Math.Min(count, (int)(bufferStream.Length - bufferStream.Position)); + read = bufferStream.Read(buffer, offset, readCount); + if (read < readCount) { - byte[] data = bufferStream.ToArray(); - long position = bufferStream.Position; + var tempRead = stream.Read(buffer, offset + read, count - read); + if (IsRecording) + { + bufferStream.Write(buffer, offset + read, tempRead); + } + read += tempRead; + } + if (bufferStream.Position == bufferStream.Length && !IsRecording) + { + isRewound = false; bufferStream.SetLength(0); - bufferStream.Write(data, (int)position, data.Length - (int)position); - bufferStream.Position = 0; - } - IsRecording = true; - } - - public override bool CanRead => true; - - public override bool CanSeek => stream.CanSeek; - - public override bool CanWrite => false; - - public override void Flush() - { - throw new NotSupportedException(); - } - - public override long Length => stream.Length; - - public override long Position - { - get => stream.Position + bufferStream.Position - bufferStream.Length; - set - { - if (!isRewound) - { - stream.Position = value; - } - else if (value < stream.Position - bufferStream.Length || value >= stream.Position) - { - stream.Position = value; - isRewound = false; - bufferStream.SetLength(0); - } - else - { - bufferStream.Position = value - stream.Position + bufferStream.Length; - } - } - } - - public override int Read(byte[] buffer, int offset, int count) - { - //don't actually read if we don't really want to read anything - //currently a network stream bug on Windows for .NET Core - if (count == 0) - { - return 0; - } - int read; - if (isRewound && bufferStream.Position != bufferStream.Length) - { - // don't read more than left - int readCount = Math.Min(count, (int)(bufferStream.Length - bufferStream.Position)); - read = bufferStream.Read(buffer, offset, readCount); - if (read < readCount) - { - int tempRead = stream.Read(buffer, offset + read, count - read); - if (IsRecording) - { - bufferStream.Write(buffer, offset + read, tempRead); - } - read += tempRead; - } - if (bufferStream.Position == bufferStream.Length && !IsRecording) - { - isRewound = false; - bufferStream.SetLength(0); - } - return read; - } - - read = stream.Read(buffer, offset, count); - if (IsRecording) - { - bufferStream.Write(buffer, offset, read); } return read; } - public override long Seek(long offset, SeekOrigin origin) + read = stream.Read(buffer, offset, count); + if (IsRecording) { - throw new NotSupportedException(); + bufferStream.Write(buffer, offset, read); } + return read; + } - public override void SetLength(long value) - { - throw new NotSupportedException(); - } + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); } } diff --git a/src/SharpCompress/IO/SourceStream.cs b/src/SharpCompress/IO/SourceStream.cs index 35fba988..5f722b65 100644 --- a/src/SharpCompress/IO/SourceStream.cs +++ b/src/SharpCompress/IO/SourceStream.cs @@ -2,237 +2,246 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; using SharpCompress.Readers; -namespace SharpCompress.IO +namespace SharpCompress.IO; + +public class SourceStream : Stream { - public class SourceStream : Stream + private long _prevSize; + private List _files; + private List _streams; + private Func _getFilePart; + private Func _getStreamPart; + private int _stream; + + public SourceStream(FileInfo file, Func getPart, ReaderOptions options) + : this(null, null, file, getPart, options) { } + + public SourceStream(Stream stream, Func getPart, ReaderOptions options) + : this(stream, getPart, null, null, options) { } + + private SourceStream( + Stream? stream, + Func? getStreamPart, + FileInfo? file, + Func? getFilePart, + ReaderOptions options + ) { - private long _prevSize; - private List _files; - private List _streams; - private Func _getFilePart; - private Func _getStreamPart; - private int _stream; + ReaderOptions = options; + _files = new List(); + _streams = new List(); + IsFileMode = file != null; + IsVolumes = false; - public SourceStream(FileInfo file, Func getPart, ReaderOptions options) - : this(null, null, file, getPart, options) { } - - public SourceStream(Stream stream, Func getPart, ReaderOptions options) - : this(stream, getPart, null, null, options) { } - - private SourceStream( - Stream? stream, - Func? getStreamPart, - FileInfo? file, - Func? getFilePart, - ReaderOptions options - ) + if (!IsFileMode) { - this.ReaderOptions = options; - _files = new List(); - _streams = new List(); - IsFileMode = file != null; - IsVolumes = false; - - if (!IsFileMode) + _streams.Add(stream!); + _getStreamPart = getStreamPart!; + _getFilePart = new Func(a => null!); + if (stream is FileStream) { - _streams.Add(stream!); - _getStreamPart = getStreamPart!; - _getFilePart = new Func(a => null!); - if (stream! is FileStream) - _files.Add(new FileInfo(((FileStream)stream!).Name)); + _files.Add(new FileInfo(((FileStream)stream!).Name)); + } + } + else + { + _files.Add(file!); + _streams.Add(_files[0].OpenRead()); + _getFilePart = getFilePart!; + _getStreamPart = new Func(a => null!); + } + _stream = 0; + _prevSize = 0; + } + + public void LoadAllParts() + { + for (var i = 1; SetStream(i); i++) + { } + SetStream(0); + } + + public bool IsVolumes { get; set; } + + public ReaderOptions ReaderOptions { get; } + public bool IsFileMode { get; } + + public IEnumerable Files => _files; + public IEnumerable Streams => _streams; + + private Stream Current => _streams[_stream]; + + public bool LoadStream(int index) //ensure all parts to id are loaded + { + while (_streams.Count <= index) + { + if (IsFileMode) + { + var f = _getFilePart(_streams.Count); + if (f == null) + { + _stream = _streams.Count - 1; + return false; + } + //throw new Exception($"File part {idx} not available."); + _files.Add(f); + _streams.Add(_files.Last().OpenRead()); } else { - _files.Add(file!); - _streams.Add(_files[0].OpenRead()); - _getFilePart = getFilePart!; - _getStreamPart = new Func(a => null!); + var s = _getStreamPart(_streams.Count); + if (s == null) + { + _stream = _streams.Count - 1; + return false; + } + //throw new Exception($"Stream part {idx} not available."); + _streams.Add(s); + if (s is FileStream) + { + _files.Add(new FileInfo(((FileStream)s).Name)); + } } - _stream = 0; - _prevSize = 0; + } + return true; + } + + public bool SetStream(int idx) //allow caller to switch part in multipart + { + if (LoadStream(idx)) + { + _stream = idx; } - public void LoadAllParts() + return _stream == idx; + } + + public override bool CanRead => true; + + public override bool CanSeek => true; + + public override bool CanWrite => false; + + public override long Length => (!IsVolumes ? _streams.Sum(a => a.Length) : Current.Length); + + public override long Position + { + get => _prevSize + Current.Position; //_prevSize is 0 for multivolume + set => Seek(value, SeekOrigin.Begin); + } + + public override void Flush() + { + Current.Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count <= 0) + { + return 0; + } + + var total = count; + var r = -1; + + while (count != 0 && r != 0) + { + r = Current.Read( + buffer, + offset, + (int)Math.Min(count, Current.Length - Current.Position) + ); + count -= r; + offset += r; + + if (!IsVolumes && count != 0 && Current.Position == Current.Length) + { + var length = Current.Length; + + // Load next file if present + if (!SetStream(_stream + 1)) + { + break; + } + + // Current stream switched + // Add length of previous stream + _prevSize += length; + Current.Seek(0, SeekOrigin.Begin); + } + } + + return total - count; + } + + public override long Seek(long offset, SeekOrigin origin) + { + var pos = Position; + switch (origin) + { + case SeekOrigin.Begin: + pos = offset; + break; + case SeekOrigin.Current: + pos += offset; + break; + case SeekOrigin.End: + pos = Length + offset; + break; + } + + _prevSize = 0; + if (!IsVolumes) { - for (int i = 1; SetStream(i); i++) { } SetStream(0); - } - - public bool IsVolumes { get; set; } - - public ReaderOptions ReaderOptions { get; } - public bool IsFileMode { get; } - - public IEnumerable Files => _files; - public IEnumerable Streams => _streams; - - private Stream Current => _streams[_stream]; - - public bool LoadStream(int index) //ensure all parts to id are loaded - { - while (_streams.Count <= index) + while (_prevSize + Current.Length < pos) { - if (IsFileMode) - { - FileInfo? f = _getFilePart(_streams.Count); - if (f == null) - { - _stream = _streams.Count - 1; - return false; - } - //throw new Exception($"File part {idx} not available."); - _files.Add(f); - _streams.Add(_files.Last().OpenRead()); - } - else - { - Stream? s = _getStreamPart(_streams.Count); - if (s == null) - { - _stream = _streams.Count - 1; - return false; - } - //throw new Exception($"Stream part {idx} not available."); - _streams.Add(s); - if (s is FileStream) - _files.Add(new FileInfo(((FileStream)s).Name)); - } - } - return true; - } - - public bool SetStream(int idx) //allow caller to switch part in multipart - { - if (LoadStream(idx)) - _stream = idx; - return _stream == idx; - } - - public override bool CanRead => true; - - public override bool CanSeek => true; - - public override bool CanWrite => false; - - public override long Length => (!IsVolumes ? _streams.Sum(a => a.Length) : Current.Length); - - public override long Position - { - get => _prevSize + Current.Position; //_prevSize is 0 for multivolume - set => Seek(value, SeekOrigin.Begin); - } - - public override void Flush() - { - Current.Flush(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - if (count <= 0) - return 0; - - int total = count; - int r = -1; - - while (count != 0 && r != 0) - { - r = Current.Read( - buffer, - offset, - (int)Math.Min(count, Current.Length - Current.Position) - ); - count -= r; - offset += r; - - if (!IsVolumes && count != 0 && Current.Position == Current.Length) - { - var length = Current.Length; - - // Load next file if present - if (!SetStream(_stream + 1)) - { - break; - } - - // Current stream switched - // Add length of previous stream - _prevSize += length; - Current.Seek(0, SeekOrigin.Begin); - } - } - - return total - count; - } - - public override long Seek(long offset, SeekOrigin origin) - { - long pos = this.Position; - switch (origin) - { - case SeekOrigin.Begin: - pos = offset; - break; - case SeekOrigin.Current: - pos += offset; - break; - case SeekOrigin.End: - pos = Length + offset; - break; - } - - _prevSize = 0; - if (!IsVolumes) - { - SetStream(0); - while (_prevSize + Current.Length < pos) - { - _prevSize += Current.Length; - SetStream(_stream + 1); - } - } - - if (pos != _prevSize + Current.Position) - Current.Seek(pos - _prevSize, SeekOrigin.Begin); - return pos; - } - - public override void SetLength(long value) - { - throw new NotImplementedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotImplementedException(); - } - - public override void Close() - { - if (this.IsFileMode || !this.ReaderOptions.LeaveStreamOpen) //close if file mode or options specify it - { - foreach (Stream stream in _streams) - { - try - { - if (stream != null) - stream.Dispose(); - } - catch { } - } - _streams.Clear(); - _files.Clear(); + _prevSize += Current.Length; + SetStream(_stream + 1); } } - protected override void Dispose(bool disposing) + if (pos != _prevSize + Current.Position) { - this.Close(); - base.Dispose(disposing); + Current.Seek(pos - _prevSize, SeekOrigin.Begin); + } + + return pos; + } + + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override void Close() + { + if (IsFileMode || !ReaderOptions.LeaveStreamOpen) //close if file mode or options specify it + { + foreach (var stream in _streams) + { + try + { + stream?.Dispose(); + } + catch { } + } + _streams.Clear(); + _files.Clear(); } } + + protected override void Dispose(bool disposing) + { + Close(); + base.Dispose(disposing); + } } diff --git a/src/SharpCompress/IO/StreamingMode.cs b/src/SharpCompress/IO/StreamingMode.cs index d2a8a823..0fdb674d 100644 --- a/src/SharpCompress/IO/StreamingMode.cs +++ b/src/SharpCompress/IO/StreamingMode.cs @@ -1,8 +1,7 @@ -namespace SharpCompress.IO +namespace SharpCompress.IO; + +public enum StreamingMode { - public enum StreamingMode - { - Streaming, - Seekable - } + Streaming, + Seekable } diff --git a/src/SharpCompress/Lazy.cs b/src/SharpCompress/Lazy.cs index 3ddd47d2..d7e5159a 100644 --- a/src/SharpCompress/Lazy.cs +++ b/src/SharpCompress/Lazy.cs @@ -2,30 +2,29 @@ using System; -namespace SharpCompress +namespace SharpCompress; + +public class Lazy { - public class Lazy + private readonly Func _lazyFunc; + private bool _evaluated; + private T _value; + + public Lazy(Func lazyFunc) { - private readonly Func _lazyFunc; - private bool _evaluated; - private T _value; + _lazyFunc = lazyFunc; + } - public Lazy(Func lazyFunc) + public T Value + { + get { - _lazyFunc = lazyFunc; - } - - public T Value - { - get + if (!_evaluated) { - if (!_evaluated) - { - _value = _lazyFunc(); - _evaluated = true; - } - return _value; + _value = _lazyFunc(); + _evaluated = true; } + return _value; } } } diff --git a/src/SharpCompress/LazyReadOnlyCollection.cs b/src/SharpCompress/LazyReadOnlyCollection.cs index 415e9904..6fc4ff87 100644 --- a/src/SharpCompress/LazyReadOnlyCollection.cs +++ b/src/SharpCompress/LazyReadOnlyCollection.cs @@ -1,153 +1,152 @@ -#nullable disable +#nullable disable using System; using System.Collections; using System.Collections.Generic; -namespace SharpCompress +namespace SharpCompress; + +internal sealed class LazyReadOnlyCollection : ICollection { - internal sealed class LazyReadOnlyCollection : ICollection + private readonly List backing = new List(); + private readonly IEnumerator source; + private bool fullyLoaded; + + public LazyReadOnlyCollection(IEnumerable source) { - private readonly List backing = new List(); - private readonly IEnumerator source; - private bool fullyLoaded; + this.source = source.GetEnumerator(); + } - public LazyReadOnlyCollection(IEnumerable source) + private class LazyLoader : IEnumerator + { + private readonly LazyReadOnlyCollection lazyReadOnlyCollection; + private bool disposed; + private int index = -1; + + internal LazyLoader(LazyReadOnlyCollection lazyReadOnlyCollection) { - this.source = source.GetEnumerator(); + this.lazyReadOnlyCollection = lazyReadOnlyCollection; } - private class LazyLoader : IEnumerator - { - private readonly LazyReadOnlyCollection lazyReadOnlyCollection; - private bool disposed; - private int index = -1; + #region IEnumerator Members - internal LazyLoader(LazyReadOnlyCollection lazyReadOnlyCollection) + public T Current => lazyReadOnlyCollection.backing[index]; + + #endregion + + #region IDisposable Members + + public void Dispose() + { + if (!disposed) { - this.lazyReadOnlyCollection = lazyReadOnlyCollection; + disposed = true; } - - #region IEnumerator Members - - public T Current => lazyReadOnlyCollection.backing[index]; - - #endregion - - #region IDisposable Members - - public void Dispose() - { - if (!disposed) - { - disposed = true; - } - } - - #endregion - - #region IEnumerator Members - - object IEnumerator.Current => Current; - - public bool MoveNext() - { - if (index + 1 < lazyReadOnlyCollection.backing.Count) - { - index++; - return true; - } - if (!lazyReadOnlyCollection.fullyLoaded && lazyReadOnlyCollection.source.MoveNext()) - { - lazyReadOnlyCollection.backing.Add(lazyReadOnlyCollection.source.Current); - index++; - return true; - } - lazyReadOnlyCollection.fullyLoaded = true; - return false; - } - - public void Reset() - { - throw new NotSupportedException(); - } - - #endregion - } - - internal void EnsureFullyLoaded() - { - if (!fullyLoaded) - { - this.ForEach(x => { }); - fullyLoaded = true; - } - } - - internal IEnumerable GetLoaded() - { - return backing; - } - - #region ICollection Members - - public void Add(T item) - { - throw new NotSupportedException(); - } - - public void Clear() - { - throw new NotSupportedException(); - } - - public bool Contains(T item) - { - EnsureFullyLoaded(); - return backing.Contains(item); - } - - public void CopyTo(T[] array, int arrayIndex) - { - EnsureFullyLoaded(); - backing.CopyTo(array, arrayIndex); - } - - public int Count - { - get - { - EnsureFullyLoaded(); - return backing.Count; - } - } - - public bool IsReadOnly => true; - - public bool Remove(T item) - { - throw new NotSupportedException(); } #endregion - #region IEnumerable Members + #region IEnumerator Members - //TODO check for concurrent access - public IEnumerator GetEnumerator() + object IEnumerator.Current => Current; + + public bool MoveNext() { - return new LazyLoader(this); + if (index + 1 < lazyReadOnlyCollection.backing.Count) + { + index++; + return true; + } + if (!lazyReadOnlyCollection.fullyLoaded && lazyReadOnlyCollection.source.MoveNext()) + { + lazyReadOnlyCollection.backing.Add(lazyReadOnlyCollection.source.Current); + index++; + return true; + } + lazyReadOnlyCollection.fullyLoaded = true; + return false; } - #endregion - - #region IEnumerable Members - - IEnumerator IEnumerable.GetEnumerator() + public void Reset() { - return GetEnumerator(); + throw new NotSupportedException(); } #endregion } + + internal void EnsureFullyLoaded() + { + if (!fullyLoaded) + { + this.ForEach(x => { }); + fullyLoaded = true; + } + } + + internal IEnumerable GetLoaded() + { + return backing; + } + + #region ICollection Members + + public void Add(T item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(T item) + { + EnsureFullyLoaded(); + return backing.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + EnsureFullyLoaded(); + backing.CopyTo(array, arrayIndex); + } + + public int Count + { + get + { + EnsureFullyLoaded(); + return backing.Count; + } + } + + public bool IsReadOnly => true; + + public bool Remove(T item) + { + throw new NotSupportedException(); + } + + #endregion + + #region IEnumerable Members + + //TODO check for concurrent access + public IEnumerator GetEnumerator() + { + return new LazyLoader(this); + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion } diff --git a/src/SharpCompress/Polyfills/StreamExtensions.cs b/src/SharpCompress/Polyfills/StreamExtensions.cs index 6dacef75..ab118a4f 100644 --- a/src/SharpCompress/Polyfills/StreamExtensions.cs +++ b/src/SharpCompress/Polyfills/StreamExtensions.cs @@ -1,45 +1,44 @@ -#if NETFRAMEWORK || NETSTANDARD2_0 +#if NETFRAMEWORK || NETSTANDARD2_0 using System; using System.Buffers; using System.IO; -namespace SharpCompress +namespace SharpCompress; + +internal static class StreamExtensions { - internal static class StreamExtensions + internal static int Read(this Stream stream, Span buffer) { - internal static int Read(this Stream stream, Span buffer) + var temp = ArrayPool.Shared.Rent(buffer.Length); + + try { - byte[] temp = ArrayPool.Shared.Rent(buffer.Length); + var read = stream.Read(temp, 0, buffer.Length); - try - { - int read = stream.Read(temp, 0, buffer.Length); + temp.AsSpan(0, read).CopyTo(buffer); - temp.AsSpan(0, read).CopyTo(buffer); - - return read; - } - finally - { - ArrayPool.Shared.Return(temp); - } + return read; } - - internal static void Write(this Stream stream, ReadOnlySpan buffer) + finally { - byte[] temp = ArrayPool.Shared.Rent(buffer.Length); + ArrayPool.Shared.Return(temp); + } + } - buffer.CopyTo(temp); + internal static void Write(this Stream stream, ReadOnlySpan buffer) + { + var temp = ArrayPool.Shared.Rent(buffer.Length); - try - { - stream.Write(temp, 0, buffer.Length); - } - finally - { - ArrayPool.Shared.Return(temp); - } + buffer.CopyTo(temp); + + try + { + stream.Write(temp, 0, buffer.Length); + } + finally + { + ArrayPool.Shared.Return(temp); } } } diff --git a/src/SharpCompress/Polyfills/StringExtensions.cs b/src/SharpCompress/Polyfills/StringExtensions.cs index c6283707..72d0abc6 100644 --- a/src/SharpCompress/Polyfills/StringExtensions.cs +++ b/src/SharpCompress/Polyfills/StringExtensions.cs @@ -1,18 +1,17 @@ -#if NETFRAMEWORK || NETSTANDARD2_0 +#if NETFRAMEWORK || NETSTANDARD2_0 -namespace SharpCompress +namespace SharpCompress; + +internal static class StringExtensions { - internal static class StringExtensions + internal static bool EndsWith(this string text, char value) { - internal static bool EndsWith(this string text, char value) - { - return text.Length > 0 && text[text.Length - 1] == value; - } + return text.Length > 0 && text[text.Length - 1] == value; + } - internal static bool Contains(this string text, char value) - { - return text.IndexOf(value) > -1; - } + internal static bool Contains(this string text, char value) + { + return text.IndexOf(value) > -1; } } diff --git a/src/SharpCompress/ReadOnlyCollection.cs b/src/SharpCompress/ReadOnlyCollection.cs index a116a056..b4c46716 100644 --- a/src/SharpCompress/ReadOnlyCollection.cs +++ b/src/SharpCompress/ReadOnlyCollection.cs @@ -1,55 +1,54 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; -namespace SharpCompress +namespace SharpCompress; + +public class ReadOnlyCollection : ICollection { - public class ReadOnlyCollection : ICollection + private readonly ICollection collection; + + public ReadOnlyCollection(ICollection collection) { - private readonly ICollection collection; + this.collection = collection; + } - public ReadOnlyCollection(ICollection collection) - { - this.collection = collection; - } + public void Add(T item) + { + throw new NotSupportedException(); + } - public void Add(T item) - { - throw new NotSupportedException(); - } + public void Clear() + { + throw new NotSupportedException(); + } - public void Clear() - { - throw new NotSupportedException(); - } + public bool Contains(T item) + { + return collection.Contains(item); + } - public bool Contains(T item) - { - return collection.Contains(item); - } + public void CopyTo(T[] array, int arrayIndex) + { + collection.CopyTo(array, arrayIndex); + } - public void CopyTo(T[] array, int arrayIndex) - { - collection.CopyTo(array, arrayIndex); - } + public int Count => collection.Count; - public int Count => collection.Count; + public bool IsReadOnly => true; - public bool IsReadOnly => true; + public bool Remove(T item) + { + throw new NotSupportedException(); + } - public bool Remove(T item) - { - throw new NotSupportedException(); - } + public IEnumerator GetEnumerator() + { + return collection.GetEnumerator(); + } - public IEnumerator GetEnumerator() - { - return collection.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - throw new NotSupportedException(); - } + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotSupportedException(); } } diff --git a/src/SharpCompress/Readers/AbstractReader.cs b/src/SharpCompress/Readers/AbstractReader.cs index f8d91ca1..5c383cb2 100644 --- a/src/SharpCompress/Readers/AbstractReader.cs +++ b/src/SharpCompress/Readers/AbstractReader.cs @@ -4,263 +4,258 @@ using System.IO; using System.Linq; using SharpCompress.Common; -namespace SharpCompress.Readers +namespace SharpCompress.Readers; + +/// +/// A generic push reader that reads unseekable comrpessed streams. +/// +public abstract class AbstractReader : IReader, IReaderExtractionListener + where TEntry : Entry + where TVolume : Volume { - /// - /// A generic push reader that reads unseekable comrpessed streams. - /// - public abstract class AbstractReader : IReader, IReaderExtractionListener - where TEntry : Entry - where TVolume : Volume + private bool completed; + private IEnumerator? entriesForCurrentReadStream; + private bool wroteCurrentEntry; + + public event EventHandler>? EntryExtractionProgress; + + public event EventHandler? CompressedBytesRead; + public event EventHandler? FilePartExtractionBegin; + + internal AbstractReader(ReaderOptions options, ArchiveType archiveType) { - private bool completed; - private IEnumerator? entriesForCurrentReadStream; - private bool wroteCurrentEntry; + ArchiveType = archiveType; + Options = options; + } - public event EventHandler>? EntryExtractionProgress; + internal ReaderOptions Options { get; } - public event EventHandler? CompressedBytesRead; - public event EventHandler? FilePartExtractionBegin; + public ArchiveType ArchiveType { get; } - internal AbstractReader(ReaderOptions options, ArchiveType archiveType) + /// + /// Current volume that the current entry resides in + /// + public abstract TVolume Volume { get; } + + /// + /// Current file entry + /// + public TEntry Entry => entriesForCurrentReadStream!.Current; + + #region IDisposable Members + + public void Dispose() + { + entriesForCurrentReadStream?.Dispose(); + Volume?.Dispose(); + } + + #endregion + + public bool Cancelled { get; private set; } + + /// + /// Indicates that the remaining entries are not required. + /// On dispose of an EntryStream, the stream will not skip to the end of the entry. + /// An attempt to move to the next entry will throw an exception, as the compressed stream is not positioned at an entry boundary. + /// + public void Cancel() + { + if (!completed) { - ArchiveType = archiveType; - Options = options; - } - - internal ReaderOptions Options { get; } - - public ArchiveType ArchiveType { get; } - - /// - /// Current volume that the current entry resides in - /// - public abstract TVolume Volume { get; } - - /// - /// Current file entry - /// - public TEntry Entry => entriesForCurrentReadStream!.Current; - - #region IDisposable Members - - public void Dispose() - { - entriesForCurrentReadStream?.Dispose(); - Volume?.Dispose(); - } - - #endregion - - public bool Cancelled { get; private set; } - - /// - /// Indicates that the remaining entries are not required. - /// On dispose of an EntryStream, the stream will not skip to the end of the entry. - /// An attempt to move to the next entry will throw an exception, as the compressed stream is not positioned at an entry boundary. - /// - public void Cancel() - { - if (!completed) - { - Cancelled = true; - } - } - - public bool MoveToNextEntry() - { - if (completed) - { - return false; - } - if (Cancelled) - { - throw new InvalidOperationException("Reader has been cancelled."); - } - if (entriesForCurrentReadStream is null) - { - return LoadStreamForReading(RequestInitialStream()); - } - if (!wroteCurrentEntry) - { - SkipEntry(); - } - wroteCurrentEntry = false; - if (NextEntryForCurrentStream()) - { - return true; - } - completed = true; - return false; - } - - protected bool LoadStreamForReading(Stream stream) - { - entriesForCurrentReadStream?.Dispose(); - if ((stream is null) || (!stream.CanRead)) - { - throw new MultipartStreamRequiredException( - "File is split into multiple archives: '" - + Entry.Key - + "'. A new readable stream is required. Use Cancel if it was intended." - ); - } - entriesForCurrentReadStream = GetEntries(stream).GetEnumerator(); - return entriesForCurrentReadStream.MoveNext(); - } - - protected virtual Stream RequestInitialStream() - { - return Volume.Stream; - } - - internal virtual bool NextEntryForCurrentStream() - { - return entriesForCurrentReadStream!.MoveNext(); - } - - protected abstract IEnumerable GetEntries(Stream stream); - - #region Entry Skip/Write - - private void SkipEntry() - { - if (!Entry.IsDirectory) - { - Skip(); - } - } - - private void Skip() - { - var part = Entry.Parts.First(); - - if (ArchiveType != ArchiveType.Rar && !Entry.IsSolid && Entry.CompressedSize > 0) - { - //not solid and has a known compressed size then we can skip raw bytes. - var rawStream = part.GetRawStream(); - - if (rawStream != null) - { - var bytesToAdvance = Entry.CompressedSize; - rawStream.Skip(bytesToAdvance); - part.Skipped = true; - return; - } - } - //don't know the size so we have to try to decompress to skip - using (var s = OpenEntryStream()) - { - s.SkipEntry(); - } - } - - public void WriteEntryTo(Stream writableStream) - { - if (wroteCurrentEntry) - { - throw new ArgumentException( - "WriteEntryTo or OpenEntryStream can only be called once." - ); - } - - if (writableStream is null) - { - throw new ArgumentNullException(nameof(writableStream)); - } - if (!writableStream.CanWrite) - { - throw new ArgumentException( - "A writable Stream was required. Use Cancel if that was intended." - ); - } - - Write(writableStream); - wroteCurrentEntry = true; - } - - internal void Write(Stream writeStream) - { - var streamListener = this as IReaderExtractionListener; - using (Stream s = OpenEntryStream()) - { - s.TransferTo(writeStream, Entry, streamListener); - } - } - - public EntryStream OpenEntryStream() - { - if (wroteCurrentEntry) - { - throw new ArgumentException( - "WriteEntryTo or OpenEntryStream can only be called once." - ); - } - var stream = GetEntryStream(); - wroteCurrentEntry = true; - return stream; - } - - /// - /// Retains a reference to the entry stream, so we can check whether it completed later. - /// - protected EntryStream CreateEntryStream(Stream decompressed) - { - return new EntryStream(this, decompressed); - } - - protected virtual EntryStream GetEntryStream() - { - return CreateEntryStream(Entry.Parts.First().GetCompressedStream()); - } - - #endregion - - IEntry IReader.Entry => Entry; - - void IExtractionListener.FireCompressedBytesRead( - long currentPartCompressedBytes, - long compressedReadBytes - ) - { - CompressedBytesRead?.Invoke( - this, - new CompressedBytesReadEventArgs( - currentFilePartCompressedBytesRead: currentPartCompressedBytes, - compressedBytesRead: compressedReadBytes - ) - ); - } - - void IExtractionListener.FireFilePartExtractionBegin( - string name, - long size, - long compressedSize - ) - { - FilePartExtractionBegin?.Invoke( - this, - new FilePartExtractionBeginEventArgs( - compressedSize: compressedSize, - size: size, - name: name - ) - ); - } - - void IReaderExtractionListener.FireEntryExtractionProgress( - Entry entry, - long bytesTransferred, - int iterations - ) - { - EntryExtractionProgress?.Invoke( - this, - new ReaderExtractionEventArgs( - entry, - new ReaderProgress(entry, bytesTransferred, iterations) - ) - ); + Cancelled = true; } } + + public bool MoveToNextEntry() + { + if (completed) + { + return false; + } + if (Cancelled) + { + throw new InvalidOperationException("Reader has been cancelled."); + } + if (entriesForCurrentReadStream is null) + { + return LoadStreamForReading(RequestInitialStream()); + } + if (!wroteCurrentEntry) + { + SkipEntry(); + } + wroteCurrentEntry = false; + if (NextEntryForCurrentStream()) + { + return true; + } + completed = true; + return false; + } + + protected bool LoadStreamForReading(Stream stream) + { + entriesForCurrentReadStream?.Dispose(); + if ((stream is null) || (!stream.CanRead)) + { + throw new MultipartStreamRequiredException( + "File is split into multiple archives: '" + + Entry.Key + + "'. A new readable stream is required. Use Cancel if it was intended." + ); + } + entriesForCurrentReadStream = GetEntries(stream).GetEnumerator(); + return entriesForCurrentReadStream.MoveNext(); + } + + protected virtual Stream RequestInitialStream() + { + return Volume.Stream; + } + + internal virtual bool NextEntryForCurrentStream() + { + return entriesForCurrentReadStream!.MoveNext(); + } + + protected abstract IEnumerable GetEntries(Stream stream); + + #region Entry Skip/Write + + private void SkipEntry() + { + if (!Entry.IsDirectory) + { + Skip(); + } + } + + private void Skip() + { + var part = Entry.Parts.First(); + + if (ArchiveType != ArchiveType.Rar && !Entry.IsSolid && Entry.CompressedSize > 0) + { + //not solid and has a known compressed size then we can skip raw bytes. + var rawStream = part.GetRawStream(); + + if (rawStream != null) + { + var bytesToAdvance = Entry.CompressedSize; + rawStream.Skip(bytesToAdvance); + part.Skipped = true; + return; + } + } + //don't know the size so we have to try to decompress to skip + using var s = OpenEntryStream(); + s.SkipEntry(); + } + + public void WriteEntryTo(Stream writableStream) + { + if (wroteCurrentEntry) + { + throw new ArgumentException( + "WriteEntryTo or OpenEntryStream can only be called once." + ); + } + + if (writableStream is null) + { + throw new ArgumentNullException(nameof(writableStream)); + } + if (!writableStream.CanWrite) + { + throw new ArgumentException( + "A writable Stream was required. Use Cancel if that was intended." + ); + } + + Write(writableStream); + wroteCurrentEntry = true; + } + + internal void Write(Stream writeStream) + { + var streamListener = this as IReaderExtractionListener; + using Stream s = OpenEntryStream(); + s.TransferTo(writeStream, Entry, streamListener); + } + + public EntryStream OpenEntryStream() + { + if (wroteCurrentEntry) + { + throw new ArgumentException( + "WriteEntryTo or OpenEntryStream can only be called once." + ); + } + var stream = GetEntryStream(); + wroteCurrentEntry = true; + return stream; + } + + /// + /// Retains a reference to the entry stream, so we can check whether it completed later. + /// + protected EntryStream CreateEntryStream(Stream decompressed) + { + return new EntryStream(this, decompressed); + } + + protected virtual EntryStream GetEntryStream() + { + return CreateEntryStream(Entry.Parts.First().GetCompressedStream()); + } + + #endregion + + IEntry IReader.Entry => Entry; + + void IExtractionListener.FireCompressedBytesRead( + long currentPartCompressedBytes, + long compressedReadBytes + ) + { + CompressedBytesRead?.Invoke( + this, + new CompressedBytesReadEventArgs( + currentFilePartCompressedBytesRead: currentPartCompressedBytes, + compressedBytesRead: compressedReadBytes + ) + ); + } + + void IExtractionListener.FireFilePartExtractionBegin( + string name, + long size, + long compressedSize + ) + { + FilePartExtractionBegin?.Invoke( + this, + new FilePartExtractionBeginEventArgs( + compressedSize: compressedSize, + size: size, + name: name + ) + ); + } + + void IReaderExtractionListener.FireEntryExtractionProgress( + Entry entry, + long bytesTransferred, + int iterations + ) + { + EntryExtractionProgress?.Invoke( + this, + new ReaderExtractionEventArgs( + entry, + new ReaderProgress(entry, bytesTransferred, iterations) + ) + ); + } } diff --git a/src/SharpCompress/Readers/GZip/GZipReader.cs b/src/SharpCompress/Readers/GZip/GZipReader.cs index d9a823f0..c757e351 100644 --- a/src/SharpCompress/Readers/GZip/GZipReader.cs +++ b/src/SharpCompress/Readers/GZip/GZipReader.cs @@ -1,38 +1,37 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using SharpCompress.Common; using SharpCompress.Common.GZip; -namespace SharpCompress.Readers.GZip +namespace SharpCompress.Readers.GZip; + +public class GZipReader : AbstractReader { - public class GZipReader : AbstractReader + internal GZipReader(Stream stream, ReaderOptions options) : base(options, ArchiveType.GZip) { - internal GZipReader(Stream stream, ReaderOptions options) : base(options, ArchiveType.GZip) - { - Volume = new GZipVolume(stream, options); - } + Volume = new GZipVolume(stream, options); + } - public override GZipVolume Volume { get; } + public override GZipVolume Volume { get; } - #region Open + #region Open - /// - /// Opens a GZipReader for Non-seeking usage with a single volume - /// - /// - /// - /// - public static GZipReader Open(Stream stream, ReaderOptions? options = null) - { - stream.CheckNotNull(nameof(stream)); - return new GZipReader(stream, options ?? new ReaderOptions()); - } + /// + /// Opens a GZipReader for Non-seeking usage with a single volume + /// + /// + /// + /// + public static GZipReader Open(Stream stream, ReaderOptions? options = null) + { + stream.CheckNotNull(nameof(stream)); + return new GZipReader(stream, options ?? new ReaderOptions()); + } - #endregion Open + #endregion Open - protected override IEnumerable GetEntries(Stream stream) - { - return GZipEntry.GetEntries(stream, Options); - } + protected override IEnumerable GetEntries(Stream stream) + { + return GZipEntry.GetEntries(stream, Options); } } diff --git a/src/SharpCompress/Readers/IReader.cs b/src/SharpCompress/Readers/IReader.cs index a4beaa80..50fc7f4d 100644 --- a/src/SharpCompress/Readers/IReader.cs +++ b/src/SharpCompress/Readers/IReader.cs @@ -1,39 +1,38 @@ -using System; +using System; using System.IO; using SharpCompress.Common; -namespace SharpCompress.Readers +namespace SharpCompress.Readers; + +public interface IReader : IDisposable { - public interface IReader : IDisposable - { - event EventHandler> EntryExtractionProgress; + event EventHandler> EntryExtractionProgress; - event EventHandler CompressedBytesRead; - event EventHandler FilePartExtractionBegin; + event EventHandler CompressedBytesRead; + event EventHandler FilePartExtractionBegin; - ArchiveType ArchiveType { get; } + ArchiveType ArchiveType { get; } - IEntry Entry { get; } + IEntry Entry { get; } - /// - /// Decompresses the current entry to the stream. This cannot be called twice for the current entry. - /// - /// - void WriteEntryTo(Stream writableStream); + /// + /// Decompresses the current entry to the stream. This cannot be called twice for the current entry. + /// + /// + void WriteEntryTo(Stream writableStream); - bool Cancelled { get; } - void Cancel(); + bool Cancelled { get; } + void Cancel(); - /// - /// Moves to the next entry by reading more data from the underlying stream. This skips if data has not been read. - /// - /// - bool MoveToNextEntry(); + /// + /// Moves to the next entry by reading more data from the underlying stream. This skips if data has not been read. + /// + /// + bool MoveToNextEntry(); - /// - /// Opens the current entry as a stream that will decompress as it is read. - /// Read the entire stream or use SkipEntry on EntryStream. - /// - EntryStream OpenEntryStream(); - } + /// + /// Opens the current entry as a stream that will decompress as it is read. + /// Read the entire stream or use SkipEntry on EntryStream. + /// + EntryStream OpenEntryStream(); } diff --git a/src/SharpCompress/Readers/IReaderExtensions.cs b/src/SharpCompress/Readers/IReaderExtensions.cs index 29f11ef1..5ef89352 100644 --- a/src/SharpCompress/Readers/IReaderExtensions.cs +++ b/src/SharpCompress/Readers/IReaderExtensions.cs @@ -1,79 +1,72 @@ -using System.IO; +using System.IO; using SharpCompress.Common; -namespace SharpCompress.Readers +namespace SharpCompress.Readers; + +public static class IReaderExtensions { - public static class IReaderExtensions + public static void WriteEntryTo(this IReader reader, string filePath) { - public static void WriteEntryTo(this IReader reader, string filePath) - { - using (Stream stream = File.Open(filePath, FileMode.Create, FileAccess.Write)) - { - reader.WriteEntryTo(stream); - } - } + using Stream stream = File.Open(filePath, FileMode.Create, FileAccess.Write); + reader.WriteEntryTo(stream); + } - public static void WriteEntryTo(this IReader reader, FileInfo filePath) - { - using (Stream stream = filePath.Open(FileMode.Create)) - { - reader.WriteEntryTo(stream); - } - } + public static void WriteEntryTo(this IReader reader, FileInfo filePath) + { + using Stream stream = filePath.Open(FileMode.Create); + reader.WriteEntryTo(stream); + } - /// - /// Extract all remaining unread entries to specific directory, retaining filename - /// - public static void WriteAllToDirectory( - this IReader reader, - string destinationDirectory, - ExtractionOptions? options = null - ) + /// + /// Extract all remaining unread entries to specific directory, retaining filename + /// + public static void WriteAllToDirectory( + this IReader reader, + string destinationDirectory, + ExtractionOptions? options = null + ) + { + while (reader.MoveToNextEntry()) { - while (reader.MoveToNextEntry()) - { - reader.WriteEntryToDirectory(destinationDirectory, options); - } - } - - /// - /// Extract to specific directory, retaining filename - /// - public static void WriteEntryToDirectory( - this IReader reader, - string destinationDirectory, - ExtractionOptions? options = null - ) - { - ExtractionMethods.WriteEntryToDirectory( - reader.Entry, - destinationDirectory, - options, - reader.WriteEntryToFile - ); - } - - /// - /// Extract to specific file - /// - public static void WriteEntryToFile( - this IReader reader, - string destinationFileName, - ExtractionOptions? options = null - ) - { - ExtractionMethods.WriteEntryToFile( - reader.Entry, - destinationFileName, - options, - (x, fm) => - { - using (FileStream fs = File.Open(destinationFileName, fm)) - { - reader.WriteEntryTo(fs); - } - } - ); + reader.WriteEntryToDirectory(destinationDirectory, options); } } + + /// + /// Extract to specific directory, retaining filename + /// + public static void WriteEntryToDirectory( + this IReader reader, + string destinationDirectory, + ExtractionOptions? options = null + ) + { + ExtractionMethods.WriteEntryToDirectory( + reader.Entry, + destinationDirectory, + options, + reader.WriteEntryToFile + ); + } + + /// + /// Extract to specific file + /// + public static void WriteEntryToFile( + this IReader reader, + string destinationFileName, + ExtractionOptions? options = null + ) + { + ExtractionMethods.WriteEntryToFile( + reader.Entry, + destinationFileName, + options, + (x, fm) => + { + using var fs = File.Open(destinationFileName, fm); + reader.WriteEntryTo(fs); + } + ); + } } diff --git a/src/SharpCompress/Readers/IReaderExtractionListener.cs b/src/SharpCompress/Readers/IReaderExtractionListener.cs index 2edbef17..49e5dae4 100644 --- a/src/SharpCompress/Readers/IReaderExtractionListener.cs +++ b/src/SharpCompress/Readers/IReaderExtractionListener.cs @@ -1,9 +1,8 @@ -using SharpCompress.Common; +using SharpCompress.Common; -namespace SharpCompress.Readers +namespace SharpCompress.Readers; + +public interface IReaderExtractionListener : IExtractionListener { - public interface IReaderExtractionListener : IExtractionListener - { - void FireEntryExtractionProgress(Entry entry, long sizeTransferred, int iterations); - } + void FireEntryExtractionProgress(Entry entry, long sizeTransferred, int iterations); } diff --git a/src/SharpCompress/Readers/IReaderFactory.cs b/src/SharpCompress/Readers/IReaderFactory.cs index 4587cca6..08c190a3 100644 --- a/src/SharpCompress/Readers/IReaderFactory.cs +++ b/src/SharpCompress/Readers/IReaderFactory.cs @@ -1,22 +1,14 @@ -using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using SharpCompress.IO; +namespace SharpCompress.Readers; -namespace SharpCompress.Readers +public interface IReaderFactory : Factories.IFactory { - public interface IReaderFactory : Factories.IFactory - { - /// - /// Opens a Reader for Non-seeking usage - /// - /// - /// - /// - IReader OpenReader(Stream stream, ReaderOptions? options); - } + /// + /// Opens a Reader for Non-seeking usage + /// + /// + /// + /// + IReader OpenReader(Stream stream, ReaderOptions? options); } diff --git a/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs b/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs index 4e857626..2c29fb5f 100644 --- a/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs +++ b/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable using System.Collections; using System.Collections.Generic; @@ -7,117 +7,116 @@ using System.Linq; using SharpCompress.Common; using SharpCompress.Common.Rar; -namespace SharpCompress.Readers.Rar +namespace SharpCompress.Readers.Rar; + +internal class MultiVolumeRarReader : RarReader { - internal class MultiVolumeRarReader : RarReader + private readonly IEnumerator streams; + private Stream tempStream; + + internal MultiVolumeRarReader(IEnumerable streams, ReaderOptions options) + : base(options) { - private readonly IEnumerator streams; + this.streams = streams.GetEnumerator(); + } + + internal override void ValidateArchive(RarVolume archive) { } + + protected override Stream RequestInitialStream() + { + if (streams.MoveNext()) + { + return streams.Current; + } + throw new MultiVolumeExtractionException( + "No stream provided when requested by MultiVolumeRarReader" + ); + } + + internal override bool NextEntryForCurrentStream() + { + if (!base.NextEntryForCurrentStream()) + { + // if we're got another stream to try to process then do so + return streams.MoveNext() && LoadStreamForReading(streams.Current); + } + return true; + } + + protected override IEnumerable CreateFilePartEnumerableForCurrentEntry() + { + var enumerator = new MultiVolumeStreamEnumerator(this, streams, tempStream); + tempStream = null; + return enumerator; + } + + private class MultiVolumeStreamEnumerator : IEnumerable, IEnumerator + { + private readonly MultiVolumeRarReader reader; + private readonly IEnumerator nextReadableStreams; private Stream tempStream; + private bool isFirst = true; - internal MultiVolumeRarReader(IEnumerable streams, ReaderOptions options) - : base(options) + internal MultiVolumeStreamEnumerator( + MultiVolumeRarReader r, + IEnumerator nextReadableStreams, + Stream tempStream + ) { - this.streams = streams.GetEnumerator(); + reader = r; + this.nextReadableStreams = nextReadableStreams; + this.tempStream = tempStream; } - internal override void ValidateArchive(RarVolume archive) { } - - protected override Stream RequestInitialStream() + public IEnumerator GetEnumerator() { - if (streams.MoveNext()) - { - return streams.Current; - } - throw new MultiVolumeExtractionException( - "No stream provided when requested by MultiVolumeRarReader" - ); + return this; } - internal override bool NextEntryForCurrentStream() + IEnumerator IEnumerable.GetEnumerator() { - if (!base.NextEntryForCurrentStream()) - { - // if we're got another stream to try to process then do so - return streams.MoveNext() && LoadStreamForReading(streams.Current); - } - return true; + return this; } - protected override IEnumerable CreateFilePartEnumerableForCurrentEntry() + public FilePart Current { get; private set; } + + public void Dispose() { } + + object IEnumerator.Current => Current; + + public bool MoveNext() { - var enumerator = new MultiVolumeStreamEnumerator(this, streams, tempStream); - tempStream = null; - return enumerator; - } - - private class MultiVolumeStreamEnumerator : IEnumerable, IEnumerator - { - private readonly MultiVolumeRarReader reader; - private readonly IEnumerator nextReadableStreams; - private Stream tempStream; - private bool isFirst = true; - - internal MultiVolumeStreamEnumerator( - MultiVolumeRarReader r, - IEnumerator nextReadableStreams, - Stream tempStream - ) + if (isFirst) { - reader = r; - this.nextReadableStreams = nextReadableStreams; - this.tempStream = tempStream; - } - - public IEnumerator GetEnumerator() - { - return this; - } - - IEnumerator IEnumerable.GetEnumerator() - { - return this; - } - - public FilePart Current { get; private set; } - - public void Dispose() { } - - object IEnumerator.Current => Current; - - public bool MoveNext() - { - if (isFirst) - { - Current = reader.Entry.Parts.First(); - isFirst = false; //first stream already to go - return true; - } - - if (!reader.Entry.IsSplitAfter) - { - return false; - } - if (tempStream != null) - { - reader.LoadStreamForReading(tempStream); - tempStream = null; - } - else if (!nextReadableStreams.MoveNext()) - { - throw new MultiVolumeExtractionException( - "No stream provided when requested by MultiVolumeRarReader" - ); - } - else - { - reader.LoadStreamForReading(nextReadableStreams.Current); - } - Current = reader.Entry.Parts.First(); + isFirst = false; //first stream already to go return true; } - public void Reset() { } + if (!reader.Entry.IsSplitAfter) + { + return false; + } + if (tempStream != null) + { + reader.LoadStreamForReading(tempStream); + tempStream = null; + } + else if (!nextReadableStreams.MoveNext()) + { + throw new MultiVolumeExtractionException( + "No stream provided when requested by MultiVolumeRarReader" + ); + } + else + { + reader.LoadStreamForReading(nextReadableStreams.Current); + } + + Current = reader.Entry.Parts.First(); + return true; } + + public void Reset() { } } } diff --git a/src/SharpCompress/Readers/Rar/NonSeekableStreamFilePart.cs b/src/SharpCompress/Readers/Rar/NonSeekableStreamFilePart.cs index 5c545540..99db4583 100644 --- a/src/SharpCompress/Readers/Rar/NonSeekableStreamFilePart.cs +++ b/src/SharpCompress/Readers/Rar/NonSeekableStreamFilePart.cs @@ -2,19 +2,18 @@ using System.IO; using SharpCompress.Common.Rar; using SharpCompress.Common.Rar.Headers; -namespace SharpCompress.Readers.Rar +namespace SharpCompress.Readers.Rar; + +internal class NonSeekableStreamFilePart : RarFilePart { - internal class NonSeekableStreamFilePart : RarFilePart + internal NonSeekableStreamFilePart(MarkHeader mh, FileHeader fh, int index = 0) + : base(mh, fh, index) { } + + internal override Stream GetCompressedStream() { - internal NonSeekableStreamFilePart(MarkHeader mh, FileHeader fh, int index = 0) - : base(mh, fh, index) { } - - internal override Stream GetCompressedStream() - { - return FileHeader.PackedStream; - } - - internal override string FilePartName => - "Unknown Stream - File Entry: " + FileHeader.FileName; + return FileHeader.PackedStream; } + + internal override string FilePartName => + "Unknown Stream - File Entry: " + FileHeader.FileName; } diff --git a/src/SharpCompress/Readers/Rar/RarReader.cs b/src/SharpCompress/Readers/Rar/RarReader.cs index 32bf4124..b665ddf9 100644 --- a/src/SharpCompress/Readers/Rar/RarReader.cs +++ b/src/SharpCompress/Readers/Rar/RarReader.cs @@ -5,77 +5,76 @@ using SharpCompress.Common; using SharpCompress.Common.Rar; using SharpCompress.Compressors.Rar; -namespace SharpCompress.Readers.Rar +namespace SharpCompress.Readers.Rar; + +/// +/// This class faciliates Reading a Rar Archive in a non-seekable forward-only manner +/// +public abstract class RarReader : AbstractReader { + private RarVolume? volume; + internal Lazy UnpackV2017 { get; } = + new Lazy(() => new Compressors.Rar.UnpackV2017.Unpack()); + internal Lazy UnpackV1 { get; } = + new Lazy(() => new Compressors.Rar.UnpackV1.Unpack()); + + internal RarReader(ReaderOptions options) : base(options, ArchiveType.Rar) { } + + internal abstract void ValidateArchive(RarVolume archive); + + public override RarVolume Volume => volume!; + /// - /// This class faciliates Reading a Rar Archive in a non-seekable forward-only manner + /// Opens a RarReader for Non-seeking usage with a single volume /// - public abstract class RarReader : AbstractReader + /// + /// + /// + public static RarReader Open(Stream stream, ReaderOptions? options = null) { - private RarVolume? volume; - internal Lazy UnpackV2017 { get; } = - new Lazy(() => new SharpCompress.Compressors.Rar.UnpackV2017.Unpack()); - internal Lazy UnpackV1 { get; } = - new Lazy(() => new SharpCompress.Compressors.Rar.UnpackV1.Unpack()); + stream.CheckNotNull(nameof(stream)); + return new SingleVolumeRarReader(stream, options ?? new ReaderOptions()); + } - internal RarReader(ReaderOptions options) : base(options, ArchiveType.Rar) { } + /// + /// Opens a RarReader for Non-seeking usage with multiple volumes + /// + /// + /// + /// + public static RarReader Open(IEnumerable streams, ReaderOptions? options = null) + { + streams.CheckNotNull(nameof(streams)); + return new MultiVolumeRarReader(streams, options ?? new ReaderOptions()); + } - internal abstract void ValidateArchive(RarVolume archive); - - public override RarVolume Volume => volume!; - - /// - /// Opens a RarReader for Non-seeking usage with a single volume - /// - /// - /// - /// - public static RarReader Open(Stream stream, ReaderOptions? options = null) + protected override IEnumerable GetEntries(Stream stream) + { + volume = new RarReaderVolume(stream, Options); + foreach (var fp in volume.ReadFileParts()) { - stream.CheckNotNull(nameof(stream)); - return new SingleVolumeRarReader(stream, options ?? new ReaderOptions()); - } - - /// - /// Opens a RarReader for Non-seeking usage with multiple volumes - /// - /// - /// - /// - public static RarReader Open(IEnumerable streams, ReaderOptions? options = null) - { - streams.CheckNotNull(nameof(streams)); - return new MultiVolumeRarReader(streams, options ?? new ReaderOptions()); - } - - protected override IEnumerable GetEntries(Stream stream) - { - volume = new RarReaderVolume(stream, Options); - foreach (RarFilePart fp in volume.ReadFileParts()) - { - ValidateArchive(volume); - yield return new RarReaderEntry(volume.IsSolidArchive, fp); - } - } - - protected virtual IEnumerable CreateFilePartEnumerableForCurrentEntry() - { - return Entry.Parts; - } - - protected override EntryStream GetEntryStream() - { - var stream = new MultiVolumeReadOnlyStream( - CreateFilePartEnumerableForCurrentEntry().Cast(), - this - ); - if (Entry.IsRarV3) - { - return CreateEntryStream( - new RarCrcStream(UnpackV1.Value, Entry.FileHeader, stream) - ); - } - return CreateEntryStream(new RarCrcStream(UnpackV2017.Value, Entry.FileHeader, stream)); + ValidateArchive(volume); + yield return new RarReaderEntry(volume.IsSolidArchive, fp); } } + + protected virtual IEnumerable CreateFilePartEnumerableForCurrentEntry() + { + return Entry.Parts; + } + + protected override EntryStream GetEntryStream() + { + var stream = new MultiVolumeReadOnlyStream( + CreateFilePartEnumerableForCurrentEntry().Cast(), + this + ); + if (Entry.IsRarV3) + { + return CreateEntryStream( + new RarCrcStream(UnpackV1.Value, Entry.FileHeader, stream) + ); + } + return CreateEntryStream(new RarCrcStream(UnpackV2017.Value, Entry.FileHeader, stream)); + } } diff --git a/src/SharpCompress/Readers/Rar/RarReaderEntry.cs b/src/SharpCompress/Readers/Rar/RarReaderEntry.cs index 7a67392e..d6f7222f 100644 --- a/src/SharpCompress/Readers/Rar/RarReaderEntry.cs +++ b/src/SharpCompress/Readers/Rar/RarReaderEntry.cs @@ -1,34 +1,33 @@ -using System.Collections.Generic; +using System.Collections.Generic; using SharpCompress.Common; using SharpCompress.Common.Rar; using SharpCompress.Common.Rar.Headers; -namespace SharpCompress.Readers.Rar +namespace SharpCompress.Readers.Rar; + +public class RarReaderEntry : RarEntry { - public class RarReaderEntry : RarEntry + internal RarReaderEntry(bool solid, RarFilePart part) { - internal RarReaderEntry(bool solid, RarFilePart part) - { - Part = part; - IsSolid = solid; - } - - internal RarFilePart Part { get; } - - internal override IEnumerable Parts => Part.AsEnumerable(); - - internal override FileHeader FileHeader => Part.FileHeader; - - public override CompressionType CompressionType => CompressionType.Rar; - - /// - /// The compressed file size - /// - public override long CompressedSize => Part.FileHeader.CompressedSize; - - /// - /// The uncompressed file size - /// - public override long Size => Part.FileHeader.UncompressedSize; + Part = part; + IsSolid = solid; } + + internal RarFilePart Part { get; } + + internal override IEnumerable Parts => Part.AsEnumerable(); + + internal override FileHeader FileHeader => Part.FileHeader; + + public override CompressionType CompressionType => CompressionType.Rar; + + /// + /// The compressed file size + /// + public override long CompressedSize => Part.FileHeader.CompressedSize; + + /// + /// The uncompressed file size + /// + public override long Size => Part.FileHeader.UncompressedSize; } diff --git a/src/SharpCompress/Readers/Rar/RarReaderVolume.cs b/src/SharpCompress/Readers/Rar/RarReaderVolume.cs index 61e73ecb..b0f82bc3 100644 --- a/src/SharpCompress/Readers/Rar/RarReaderVolume.cs +++ b/src/SharpCompress/Readers/Rar/RarReaderVolume.cs @@ -4,21 +4,20 @@ using SharpCompress.Common.Rar; using SharpCompress.Common.Rar.Headers; using SharpCompress.IO; -namespace SharpCompress.Readers.Rar +namespace SharpCompress.Readers.Rar; + +public class RarReaderVolume : RarVolume { - public class RarReaderVolume : RarVolume + internal RarReaderVolume(Stream stream, ReaderOptions options, int index = 0) + : base(StreamingMode.Streaming, stream, options, index) { } + + internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) { - internal RarReaderVolume(Stream stream, ReaderOptions options, int index = 0) - : base(StreamingMode.Streaming, stream, options, index) { } + return new NonSeekableStreamFilePart(markHeader, fileHeader, Index); + } - internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) - { - return new NonSeekableStreamFilePart(markHeader, fileHeader, this.Index); - } - - internal override IEnumerable ReadFileParts() - { - return GetVolumeFileParts(); - } + internal override IEnumerable ReadFileParts() + { + return GetVolumeFileParts(); } } diff --git a/src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs b/src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs index 45bf884b..b7a9eff3 100644 --- a/src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs +++ b/src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs @@ -1,31 +1,30 @@ -using System.IO; +using System.IO; using SharpCompress.Common; using SharpCompress.Common.Rar; -namespace SharpCompress.Readers.Rar +namespace SharpCompress.Readers.Rar; + +internal class SingleVolumeRarReader : RarReader { - internal class SingleVolumeRarReader : RarReader + private readonly Stream stream; + + internal SingleVolumeRarReader(Stream stream, ReaderOptions options) : base(options) { - private readonly Stream stream; + this.stream = stream; + } - internal SingleVolumeRarReader(Stream stream, ReaderOptions options) : base(options) + internal override void ValidateArchive(RarVolume archive) + { + if (archive.IsMultiVolume) { - this.stream = stream; - } - - internal override void ValidateArchive(RarVolume archive) - { - if (archive.IsMultiVolume) - { - var msg = - "Streamed archive is a Multi-volume archive. Use different RarReader method to extract."; - throw new MultiVolumeExtractionException(msg); - } - } - - protected override Stream RequestInitialStream() - { - return stream; + var msg = + "Streamed archive is a Multi-volume archive. Use different RarReader method to extract."; + throw new MultiVolumeExtractionException(msg); } } + + protected override Stream RequestInitialStream() + { + return stream; + } } diff --git a/src/SharpCompress/Readers/ReaderFactory.cs b/src/SharpCompress/Readers/ReaderFactory.cs index d7e58f68..3fcb2dcd 100644 --- a/src/SharpCompress/Readers/ReaderFactory.cs +++ b/src/SharpCompress/Readers/ReaderFactory.cs @@ -1,40 +1,39 @@ using System; using System.IO; using System.Linq; - -using SharpCompress.Common; using SharpCompress.IO; -namespace SharpCompress.Readers +namespace SharpCompress.Readers; + +public static class ReaderFactory { - public static class ReaderFactory + /// + /// Opens a Reader for Non-seeking usage + /// + /// + /// + /// + public static IReader Open(Stream stream, ReaderOptions? options = null) { - /// - /// Opens a Reader for Non-seeking usage - /// - /// - /// - /// - public static IReader Open(Stream stream, ReaderOptions? options = null) + stream.CheckNotNull(nameof(stream)); + options ??= new ReaderOptions() { LeaveStreamOpen = false }; + + var rewindableStream = new RewindableStream(stream); + rewindableStream.StartRecording(); + + foreach (var factory in Factories.Factory.Factories.OfType()) { - stream.CheckNotNull(nameof(stream)); - options = options ?? new ReaderOptions() { LeaveStreamOpen = false }; - - RewindableStream rewindableStream = new RewindableStream(stream); - rewindableStream.StartRecording(); - - foreach (var factory in Factories.Factory.Factories.OfType()) + if ( + factory.TryOpenReader(rewindableStream, options, out var reader) + && reader != null + ) { - if ( - factory.TryOpenReader(rewindableStream, options, out var reader) - && reader != null - ) - return reader; + return reader; } - - throw new InvalidOperationException( - "Cannot determine compressed stream type. Supported Reader Formats: Zip, GZip, BZip2, Tar, Rar, LZip, XZ" - ); } + + throw new InvalidOperationException( + "Cannot determine compressed stream type. Supported Reader Formats: Zip, GZip, BZip2, Tar, Rar, LZip, XZ" + ); } } diff --git a/src/SharpCompress/Readers/ReaderOptions.cs b/src/SharpCompress/Readers/ReaderOptions.cs index 3891781d..706829cb 100644 --- a/src/SharpCompress/Readers/ReaderOptions.cs +++ b/src/SharpCompress/Readers/ReaderOptions.cs @@ -1,16 +1,15 @@ -using SharpCompress.Common; +using SharpCompress.Common; -namespace SharpCompress.Readers +namespace SharpCompress.Readers; + +public class ReaderOptions : OptionsBase { - public class ReaderOptions : OptionsBase - { - /// - /// Look for RarArchive (Check for self-extracting archives or cases where RarArchive isn't at the start of the file) - /// - public bool LookForHeader { get; set; } + /// + /// Look for RarArchive (Check for self-extracting archives or cases where RarArchive isn't at the start of the file) + /// + public bool LookForHeader { get; set; } - public string? Password { get; set; } + public string? Password { get; set; } - public bool DisableCheckIncomplete { get; set; } - } + public bool DisableCheckIncomplete { get; set; } } diff --git a/src/SharpCompress/Readers/ReaderProgress.cs b/src/SharpCompress/Readers/ReaderProgress.cs index 794929dd..2cffab9a 100644 --- a/src/SharpCompress/Readers/ReaderProgress.cs +++ b/src/SharpCompress/Readers/ReaderProgress.cs @@ -1,22 +1,21 @@ -using System; +using System; using SharpCompress.Common; -namespace SharpCompress.Readers +namespace SharpCompress.Readers; + +public class ReaderProgress { - public class ReaderProgress + private readonly IEntry _entry; + public long BytesTransferred { get; } + public int Iterations { get; } + + public int PercentageRead => (int)Math.Round(PercentageReadExact); + public double PercentageReadExact => (float)BytesTransferred / _entry.Size * 100; + + public ReaderProgress(IEntry entry, long bytesTransferred, int iterations) { - private readonly IEntry _entry; - public long BytesTransferred { get; } - public int Iterations { get; } - - public int PercentageRead => (int)Math.Round(PercentageReadExact); - public double PercentageReadExact => (float)BytesTransferred / _entry.Size * 100; - - public ReaderProgress(IEntry entry, long bytesTransferred, int iterations) - { - _entry = entry; - BytesTransferred = bytesTransferred; - Iterations = iterations; - } + _entry = entry; + BytesTransferred = bytesTransferred; + Iterations = iterations; } } diff --git a/src/SharpCompress/Readers/Tar/TarReader.cs b/src/SharpCompress/Readers/Tar/TarReader.cs index 707c2e58..b72b5982 100644 --- a/src/SharpCompress/Readers/Tar/TarReader.cs +++ b/src/SharpCompress/Readers/Tar/TarReader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using SharpCompress.Archives.GZip; diff --git a/src/SharpCompress/Readers/Zip/ZipReader.cs b/src/SharpCompress/Readers/Zip/ZipReader.cs index 111b3860..2f0e34bb 100644 --- a/src/SharpCompress/Readers/Zip/ZipReader.cs +++ b/src/SharpCompress/Readers/Zip/ZipReader.cs @@ -1,62 +1,61 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using SharpCompress.Common; using SharpCompress.Common.Zip; using SharpCompress.Common.Zip.Headers; -namespace SharpCompress.Readers.Zip +namespace SharpCompress.Readers.Zip; + +public class ZipReader : AbstractReader { - public class ZipReader : AbstractReader + private readonly StreamingZipHeaderFactory _headerFactory; + + private ZipReader(Stream stream, ReaderOptions options) : base(options, ArchiveType.Zip) { - private readonly StreamingZipHeaderFactory _headerFactory; + Volume = new ZipVolume(stream, options); + _headerFactory = new StreamingZipHeaderFactory( + options.Password, + options.ArchiveEncoding + ); + } - private ZipReader(Stream stream, ReaderOptions options) : base(options, ArchiveType.Zip) + public override ZipVolume Volume { get; } + + #region Open + + /// + /// Opens a ZipReader for Non-seeking usage with a single volume + /// + /// + /// + /// + public static ZipReader Open(Stream stream, ReaderOptions? options = null) + { + stream.CheckNotNull(nameof(stream)); + return new ZipReader(stream, options ?? new ReaderOptions()); + } + + #endregion Open + + protected override IEnumerable GetEntries(Stream stream) + { + foreach (var h in _headerFactory.ReadStreamHeader(stream)) { - Volume = new ZipVolume(stream, options); - _headerFactory = new StreamingZipHeaderFactory( - options.Password, - options.ArchiveEncoding - ); - } - - public override ZipVolume Volume { get; } - - #region Open - - /// - /// Opens a ZipReader for Non-seeking usage with a single volume - /// - /// - /// - /// - public static ZipReader Open(Stream stream, ReaderOptions? options = null) - { - stream.CheckNotNull(nameof(stream)); - return new ZipReader(stream, options ?? new ReaderOptions()); - } - - #endregion Open - - protected override IEnumerable GetEntries(Stream stream) - { - foreach (ZipHeader h in _headerFactory.ReadStreamHeader(stream)) + if (h != null) { - if (h != null) + switch (h.ZipHeaderType) { - switch (h.ZipHeaderType) - { - case ZipHeaderType.LocalEntry: + case ZipHeaderType.LocalEntry: - { - yield return new ZipEntry( - new StreamingZipFilePart((LocalEntryHeader)h, stream) - ); - } - break; - case ZipHeaderType.DirectoryEnd: - { - yield break; - } + { + yield return new ZipEntry( + new StreamingZipFilePart((LocalEntryHeader)h, stream) + ); + } + break; + case ZipHeaderType.DirectoryEnd: + { + yield break; } } } diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index ff059ae6..a40da612 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -4,406 +4,406 @@ using System.Collections.Generic; using System.IO; using SharpCompress.Readers; -namespace SharpCompress +namespace SharpCompress; + +[CLSCompliant(false)] +public static class Utility { - [CLSCompliant(false)] - public static class Utility + public static ReadOnlyCollection ToReadOnly(this ICollection items) { - public static ReadOnlyCollection ToReadOnly(this ICollection items) - { - return new ReadOnlyCollection(items); - } + return new ReadOnlyCollection(items); + } - /// - /// Performs an unsigned bitwise right shift with the specified number - /// - /// Number to operate on - /// Amount of bits to shift - /// The resulting number from the shift operation - public static int URShift(int number, int bits) + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Amount of bits to shift + /// The resulting number from the shift operation + public static int URShift(int number, int bits) + { + if (number >= 0) { - if (number >= 0) - { - return number >> bits; - } - return (number >> bits) + (2 << ~bits); + return number >> bits; } + return (number >> bits) + (2 << ~bits); + } - /// - /// Performs an unsigned bitwise right shift with the specified number - /// - /// Number to operate on - /// Amount of bits to shift - /// The resulting number from the shift operation - public static long URShift(long number, int bits) + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Amount of bits to shift + /// The resulting number from the shift operation + public static long URShift(long number, int bits) + { + if (number >= 0) { - if (number >= 0) - { - return number >> bits; - } - return (number >> bits) + (2L << ~bits); + return number >> bits; } + return (number >> bits) + (2L << ~bits); + } - public static void SetSize(this List list, int count) + public static void SetSize(this List list, int count) + { + if (count > list.Count) { - if (count > list.Count) + // Ensure the list only needs to grow once + list.Capacity = count; + for (var i = list.Count; i < count; i++) { - // Ensure the list only needs to grow once - list.Capacity = count; - for (int i = list.Count; i < count; i++) - { - list.Add(0x0); - } - } - else - { - list.RemoveRange(count, list.Count - count); + list.Add(0x0); } } - - public static void ForEach(this IEnumerable items, Action action) + else { - foreach (T item in items) - { - action(item); - } - } - - public static void Copy( - Array sourceArray, - long sourceIndex, - Array destinationArray, - long destinationIndex, - long length - ) - { - if (sourceIndex > Int32.MaxValue || sourceIndex < Int32.MinValue) - { - throw new ArgumentOutOfRangeException(nameof(sourceIndex)); - } - - if (destinationIndex > Int32.MaxValue || destinationIndex < Int32.MinValue) - { - throw new ArgumentOutOfRangeException(nameof(destinationIndex)); - } - - if (length > Int32.MaxValue || length < Int32.MinValue) - { - throw new ArgumentOutOfRangeException(nameof(length)); - } - - Array.Copy( - sourceArray, - (int)sourceIndex, - destinationArray, - (int)destinationIndex, - (int)length - ); - } - - public static IEnumerable AsEnumerable(this T item) - { - yield return item; - } - - public static void CheckNotNull(this object obj, string name) - { - if (obj is null) - { - throw new ArgumentNullException(name); - } - } - - public static void CheckNotNullOrEmpty(this string obj, string name) - { - obj.CheckNotNull(name); - if (obj.Length == 0) - { - throw new ArgumentException("String is empty.", name); - } - } - - public static void Skip(this Stream source, long advanceAmount) - { - if (source.CanSeek) - { - source.Position += advanceAmount; - return; - } - - byte[] buffer = GetTransferByteArray(); - try - { - int read = 0; - int readCount = 0; - do - { - readCount = buffer.Length; - if (readCount > advanceAmount) - { - readCount = (int)advanceAmount; - } - read = source.Read(buffer, 0, readCount); - if (read <= 0) - { - break; - } - advanceAmount -= read; - if (advanceAmount == 0) - { - break; - } - } while (true); - } - finally - { - ArrayPool.Shared.Return(buffer); - } - } - - public static void Skip(this Stream source) - { - byte[] buffer = GetTransferByteArray(); - try - { - do { } while (source.Read(buffer, 0, buffer.Length) == buffer.Length); - } - finally - { - ArrayPool.Shared.Return(buffer); - } - } - - public static bool Find(this Stream source, byte[] array) - { - byte[] buffer = GetTransferByteArray(); - try - { - int count = 0; - var len = source.Read(buffer, 0, buffer.Length); - - do - { - for (int i = 0; i < len; i++) - { - if (array[count] == buffer[i]) - { - count++; - if (count == array.Length) - { - source.Position = source.Position - len + i - array.Length + 1; - return true; - } - } - } - } while ((len = source.Read(buffer, 0, buffer.Length)) > 0); - } - finally - { - ArrayPool.Shared.Return(buffer); - } - - return false; - } - - public static DateTime DosDateToDateTime(UInt16 iDate, UInt16 iTime) - { - int year = iDate / 512 + 1980; - int month = iDate % 512 / 32; - int day = iDate % 512 % 32; - int hour = iTime / 2048; - int minute = iTime % 2048 / 32; - int second = iTime % 2048 % 32 * 2; - - if (iDate == UInt16.MaxValue || month == 0 || day == 0) - { - year = 1980; - month = 1; - day = 1; - } - - if (iTime == UInt16.MaxValue) - { - hour = minute = second = 0; - } - - DateTime dt; - try - { - dt = new DateTime(year, month, day, hour, minute, second, DateTimeKind.Local); - } - catch - { - dt = new DateTime(); - } - return dt; - } - - public static uint DateTimeToDosTime(this DateTime? dateTime) - { - if (dateTime is null) - { - return 0; - } - - var localDateTime = dateTime.Value.ToLocalTime(); - - return (uint)( - (localDateTime.Second / 2) - | (localDateTime.Minute << 5) - | (localDateTime.Hour << 11) - | (localDateTime.Day << 16) - | (localDateTime.Month << 21) - | ((localDateTime.Year - 1980) << 25) - ); - } - - public static DateTime DosDateToDateTime(UInt32 iTime) - { - return DosDateToDateTime((UInt16)(iTime / 65536), (UInt16)(iTime % 65536)); - } - - /// - /// Convert Unix time value to a DateTime object. - /// - /// The Unix time stamp you want to convert to DateTime. - /// Returns a DateTime object that represents value of the Unix time. - public static DateTime UnixTimeToDateTime(long unixtime) - { - DateTime sTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - return sTime.AddSeconds(unixtime); - } - - public static long TransferTo(this Stream source, Stream destination) - { - byte[] array = GetTransferByteArray(); - try - { - long total = 0; - while (ReadTransferBlock(source, array, out int count)) - { - total += count; - destination.Write(array, 0, count); - } - return total; - } - finally - { - ArrayPool.Shared.Return(array); - } - } - - public static long TransferTo( - this Stream source, - Stream destination, - Common.Entry entry, - IReaderExtractionListener readerExtractionListener - ) - { - byte[] array = GetTransferByteArray(); - try - { - var iterations = 0; - long total = 0; - while (ReadTransferBlock(source, array, out int count)) - { - total += count; - destination.Write(array, 0, count); - iterations++; - readerExtractionListener.FireEntryExtractionProgress(entry, total, iterations); - } - return total; - } - finally - { - ArrayPool.Shared.Return(array); - } - } - - private static bool ReadTransferBlock(Stream source, byte[] array, out int count) - { - return (count = source.Read(array, 0, array.Length)) != 0; - } - - private static byte[] GetTransferByteArray() - { - return ArrayPool.Shared.Rent(81920); - } - - public static bool ReadFully(this Stream stream, byte[] buffer) - { - int total = 0; - int read; - while ((read = stream.Read(buffer, total, buffer.Length - total)) > 0) - { - total += read; - if (total >= buffer.Length) - { - return true; - } - } - return (total >= buffer.Length); - } - - public static bool ReadFully(this Stream stream, Span buffer) - { - int total = 0; - int read; - while ((read = stream.Read(buffer.Slice(total, buffer.Length - total))) > 0) - { - total += read; - if (total >= buffer.Length) - { - return true; - } - } - return (total >= buffer.Length); - } - - public static string TrimNulls(this string source) - { - return source.Replace('\0', ' ').Trim(); - } - - /// - /// Swap the endianness of a UINT32 - /// - /// The UINT32 you want to swap his endianness - /// Return the new UINT32 in the other endianness format - public static UInt32 SwapUINT32(UInt32 number) - { - return (number >> 24) - | ((number << 8) & 0x00FF0000) - | ((number >> 8) & 0x0000FF00) - | (number << 24); - } - - /// - /// Insert a little endian UINT32 into a byte array - /// - /// The buffer to insert into - /// The UINT32 to insert - /// Offset of the buffer to insert into - public static void SetLittleUInt32(ref byte[] buffer, UInt32 number, long offset) - { - buffer[offset] = (byte)(number); - buffer[offset + 1] = (byte)(number >> 8); - buffer[offset + 2] = (byte)(number >> 16); - buffer[offset + 3] = (byte)(number >> 24); - } - - /// - /// Insert a big endian UINT32 into a byte array - /// - /// The buffer to insert into - /// The UINT32 to insert - /// Offset of the buffer to insert into - public static void SetBigUInt32(ref byte[] buffer, UInt32 number, long offset) - { - buffer[offset] = (byte)(number >> 24); - buffer[offset + 1] = (byte)(number >> 16); - buffer[offset + 2] = (byte)(number >> 8); - buffer[offset + 3] = (byte)number; + list.RemoveRange(count, list.Count - count); } } + + public static void ForEach(this IEnumerable items, Action action) + { + foreach (var item in items) + { + action(item); + } + } + + public static void Copy( + Array sourceArray, + long sourceIndex, + Array destinationArray, + long destinationIndex, + long length + ) + { + if (sourceIndex > int.MaxValue || sourceIndex < int.MinValue) + { + throw new ArgumentOutOfRangeException(nameof(sourceIndex)); + } + + if (destinationIndex > int.MaxValue || destinationIndex < int.MinValue) + { + throw new ArgumentOutOfRangeException(nameof(destinationIndex)); + } + + if (length > int.MaxValue || length < int.MinValue) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + Array.Copy( + sourceArray, + (int)sourceIndex, + destinationArray, + (int)destinationIndex, + (int)length + ); + } + + public static IEnumerable AsEnumerable(this T item) + { + yield return item; + } + + public static void CheckNotNull(this object obj, string name) + { + if (obj is null) + { + throw new ArgumentNullException(name); + } + } + + public static void CheckNotNullOrEmpty(this string obj, string name) + { + obj.CheckNotNull(name); + if (obj.Length == 0) + { + throw new ArgumentException("String is empty.", name); + } + } + + public static void Skip(this Stream source, long advanceAmount) + { + if (source.CanSeek) + { + source.Position += advanceAmount; + return; + } + + var buffer = GetTransferByteArray(); + try + { + var read = 0; + var readCount = 0; + do + { + readCount = buffer.Length; + if (readCount > advanceAmount) + { + readCount = (int)advanceAmount; + } + read = source.Read(buffer, 0, readCount); + if (read <= 0) + { + break; + } + advanceAmount -= read; + if (advanceAmount == 0) + { + break; + } + } while (true); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + + public static void Skip(this Stream source) + { + var buffer = GetTransferByteArray(); + try + { + do + { } while (source.Read(buffer, 0, buffer.Length) == buffer.Length); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + + public static bool Find(this Stream source, byte[] array) + { + var buffer = GetTransferByteArray(); + try + { + var count = 0; + var len = source.Read(buffer, 0, buffer.Length); + + do + { + for (var i = 0; i < len; i++) + { + if (array[count] == buffer[i]) + { + count++; + if (count == array.Length) + { + source.Position = source.Position - len + i - array.Length + 1; + return true; + } + } + } + } while ((len = source.Read(buffer, 0, buffer.Length)) > 0); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + + return false; + } + + public static DateTime DosDateToDateTime(ushort iDate, ushort iTime) + { + var year = (iDate / 512) + 1980; + var month = iDate % 512 / 32; + var day = iDate % 512 % 32; + var hour = iTime / 2048; + var minute = iTime % 2048 / 32; + var second = iTime % 2048 % 32 * 2; + + if (iDate == ushort.MaxValue || month == 0 || day == 0) + { + year = 1980; + month = 1; + day = 1; + } + + if (iTime == ushort.MaxValue) + { + hour = minute = second = 0; + } + + DateTime dt; + try + { + dt = new DateTime(year, month, day, hour, minute, second, DateTimeKind.Local); + } + catch + { + dt = new DateTime(); + } + return dt; + } + + public static uint DateTimeToDosTime(this DateTime? dateTime) + { + if (dateTime is null) + { + return 0; + } + + var localDateTime = dateTime.Value.ToLocalTime(); + + return (uint)( + (localDateTime.Second / 2) + | (localDateTime.Minute << 5) + | (localDateTime.Hour << 11) + | (localDateTime.Day << 16) + | (localDateTime.Month << 21) + | ((localDateTime.Year - 1980) << 25) + ); + } + + public static DateTime DosDateToDateTime(uint iTime) + { + return DosDateToDateTime((ushort)(iTime / 65536), (ushort)(iTime % 65536)); + } + + /// + /// Convert Unix time value to a DateTime object. + /// + /// The Unix time stamp you want to convert to DateTime. + /// Returns a DateTime object that represents value of the Unix time. + public static DateTime UnixTimeToDateTime(long unixtime) + { + var sTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + return sTime.AddSeconds(unixtime); + } + + public static long TransferTo(this Stream source, Stream destination) + { + var array = GetTransferByteArray(); + try + { + long total = 0; + while (ReadTransferBlock(source, array, out var count)) + { + total += count; + destination.Write(array, 0, count); + } + return total; + } + finally + { + ArrayPool.Shared.Return(array); + } + } + + public static long TransferTo( + this Stream source, + Stream destination, + Common.Entry entry, + IReaderExtractionListener readerExtractionListener + ) + { + var array = GetTransferByteArray(); + try + { + var iterations = 0; + long total = 0; + while (ReadTransferBlock(source, array, out var count)) + { + total += count; + destination.Write(array, 0, count); + iterations++; + readerExtractionListener.FireEntryExtractionProgress(entry, total, iterations); + } + return total; + } + finally + { + ArrayPool.Shared.Return(array); + } + } + + private static bool ReadTransferBlock(Stream source, byte[] array, out int count) + { + return (count = source.Read(array, 0, array.Length)) != 0; + } + + private static byte[] GetTransferByteArray() + { + return ArrayPool.Shared.Rent(81920); + } + + public static bool ReadFully(this Stream stream, byte[] buffer) + { + var total = 0; + int read; + while ((read = stream.Read(buffer, total, buffer.Length - total)) > 0) + { + total += read; + if (total >= buffer.Length) + { + return true; + } + } + return (total >= buffer.Length); + } + + public static bool ReadFully(this Stream stream, Span buffer) + { + var total = 0; + int read; + while ((read = stream.Read(buffer.Slice(total, buffer.Length - total))) > 0) + { + total += read; + if (total >= buffer.Length) + { + return true; + } + } + return (total >= buffer.Length); + } + + public static string TrimNulls(this string source) + { + return source.Replace('\0', ' ').Trim(); + } + + /// + /// Swap the endianness of a UINT32 + /// + /// The UINT32 you want to swap his endianness + /// Return the new UINT32 in the other endianness format + public static uint SwapUINT32(uint number) + { + return (number >> 24) + | ((number << 8) & 0x00FF0000) + | ((number >> 8) & 0x0000FF00) + | (number << 24); + } + + /// + /// Insert a little endian UINT32 into a byte array + /// + /// The buffer to insert into + /// The UINT32 to insert + /// Offset of the buffer to insert into + public static void SetLittleUInt32(ref byte[] buffer, uint number, long offset) + { + buffer[offset] = (byte)(number); + buffer[offset + 1] = (byte)(number >> 8); + buffer[offset + 2] = (byte)(number >> 16); + buffer[offset + 3] = (byte)(number >> 24); + } + + /// + /// Insert a big endian UINT32 into a byte array + /// + /// The buffer to insert into + /// The UINT32 to insert + /// Offset of the buffer to insert into + public static void SetBigUInt32(ref byte[] buffer, uint number, long offset) + { + buffer[offset] = (byte)(number >> 24); + buffer[offset + 1] = (byte)(number >> 16); + buffer[offset + 2] = (byte)(number >> 8); + buffer[offset + 3] = (byte)number; + } } diff --git a/src/SharpCompress/Writers/AbstractWriter.cs b/src/SharpCompress/Writers/AbstractWriter.cs index 0c910f69..0fe95c5b 100644 --- a/src/SharpCompress/Writers/AbstractWriter.cs +++ b/src/SharpCompress/Writers/AbstractWriter.cs @@ -1,59 +1,58 @@ -#nullable disable +#nullable disable using System; using System.IO; using SharpCompress.Common; -namespace SharpCompress.Writers +namespace SharpCompress.Writers; + +public abstract class AbstractWriter : IWriter { - public abstract class AbstractWriter : IWriter + private bool _isDisposed; + + protected AbstractWriter(ArchiveType type, WriterOptions writerOptions) { - private bool _isDisposed; + WriterType = type; + WriterOptions = writerOptions; + } - protected AbstractWriter(ArchiveType type, WriterOptions writerOptions) + protected void InitalizeStream(Stream stream) + { + OutputStream = stream; + } + + protected Stream OutputStream { get; private set; } + + public ArchiveType WriterType { get; } + + protected WriterOptions WriterOptions { get; } + + public abstract void Write(string filename, Stream source, DateTime? modificationTime); + + protected virtual void Dispose(bool isDisposing) + { + if (isDisposing) { - WriterType = type; - WriterOptions = writerOptions; + OutputStream.Dispose(); } + } - protected void InitalizeStream(Stream stream) + public void Dispose() + { + if (!_isDisposed) { - OutputStream = stream; + GC.SuppressFinalize(this); + Dispose(true); + _isDisposed = true; } + } - protected Stream OutputStream { get; private set; } - - public ArchiveType WriterType { get; } - - protected WriterOptions WriterOptions { get; } - - public abstract void Write(string filename, Stream source, DateTime? modificationTime); - - protected virtual void Dispose(bool isDisposing) + ~AbstractWriter() + { + if (!_isDisposed) { - if (isDisposing) - { - OutputStream.Dispose(); - } - } - - public void Dispose() - { - if (!_isDisposed) - { - GC.SuppressFinalize(this); - Dispose(true); - _isDisposed = true; - } - } - - ~AbstractWriter() - { - if (!_isDisposed) - { - Dispose(false); - _isDisposed = true; - } + Dispose(false); + _isDisposed = true; } } } diff --git a/src/SharpCompress/Writers/GZip/GZipWriter.cs b/src/SharpCompress/Writers/GZip/GZipWriter.cs index b7d32ed2..5c990a7d 100644 --- a/src/SharpCompress/Writers/GZip/GZipWriter.cs +++ b/src/SharpCompress/Writers/GZip/GZipWriter.cs @@ -5,50 +5,49 @@ using SharpCompress.Compressors; using SharpCompress.Compressors.Deflate; using SharpCompress.IO; -namespace SharpCompress.Writers.GZip +namespace SharpCompress.Writers.GZip; + +public sealed class GZipWriter : AbstractWriter { - public sealed class GZipWriter : AbstractWriter + private bool _wroteToStream; + + public GZipWriter(Stream destination, GZipWriterOptions? options = null) + : base(ArchiveType.GZip, options ?? new GZipWriterOptions()) { - private bool _wroteToStream; - - public GZipWriter(Stream destination, GZipWriterOptions? options = null) - : base(ArchiveType.GZip, options ?? new GZipWriterOptions()) + if (WriterOptions.LeaveStreamOpen) { - if (WriterOptions.LeaveStreamOpen) - { - destination = NonDisposingStream.Create(destination); - } - InitalizeStream( - new GZipStream( - destination, - CompressionMode.Compress, - options?.CompressionLevel ?? CompressionLevel.Default, - WriterOptions.ArchiveEncoding.GetEncoding() - ) - ); + destination = NonDisposingStream.Create(destination); } + InitalizeStream( + new GZipStream( + destination, + CompressionMode.Compress, + options?.CompressionLevel ?? CompressionLevel.Default, + WriterOptions.ArchiveEncoding.GetEncoding() + ) + ); + } - protected override void Dispose(bool isDisposing) + protected override void Dispose(bool isDisposing) + { + if (isDisposing) { - if (isDisposing) - { - //dispose here to finish the GZip, GZip won't close the underlying stream - OutputStream.Dispose(); - } - base.Dispose(isDisposing); + //dispose here to finish the GZip, GZip won't close the underlying stream + OutputStream.Dispose(); } + base.Dispose(isDisposing); + } - public override void Write(string filename, Stream source, DateTime? modificationTime) + public override void Write(string filename, Stream source, DateTime? modificationTime) + { + if (_wroteToStream) { - if (_wroteToStream) - { - throw new ArgumentException("Can only write a single stream to a GZip file."); - } - GZipStream stream = (GZipStream)OutputStream; - stream.FileName = filename; - stream.LastModified = modificationTime; - source.TransferTo(stream); - _wroteToStream = true; + throw new ArgumentException("Can only write a single stream to a GZip file."); } + var stream = (GZipStream)OutputStream; + stream.FileName = filename; + stream.LastModified = modificationTime; + source.TransferTo(stream); + _wroteToStream = true; } } diff --git a/src/SharpCompress/Writers/GZip/GZipWriterOptions.cs b/src/SharpCompress/Writers/GZip/GZipWriterOptions.cs index cef2744a..faff1cc0 100644 --- a/src/SharpCompress/Writers/GZip/GZipWriterOptions.cs +++ b/src/SharpCompress/Writers/GZip/GZipWriterOptions.cs @@ -1,24 +1,22 @@ -using SharpCompress.Common; +using SharpCompress.Common; using SharpCompress.Compressors.Deflate; -namespace SharpCompress.Writers.GZip +namespace SharpCompress.Writers.GZip; + +public class GZipWriterOptions : WriterOptions { - public class GZipWriterOptions : WriterOptions + public GZipWriterOptions() : base(CompressionType.GZip) { } + + internal GZipWriterOptions(WriterOptions options) : base(options.CompressionType) { - public GZipWriterOptions() : base(CompressionType.GZip) { } + LeaveStreamOpen = options.LeaveStreamOpen; + ArchiveEncoding = options.ArchiveEncoding; - internal GZipWriterOptions(WriterOptions options) : base(options.CompressionType) + if (options is GZipWriterOptions writerOptions) { - LeaveStreamOpen = options.LeaveStreamOpen; - ArchiveEncoding = options.ArchiveEncoding; - - var writerOptions = options as GZipWriterOptions; - if (writerOptions != null) - { - CompressionLevel = writerOptions.CompressionLevel; - } + CompressionLevel = writerOptions.CompressionLevel; } - - public CompressionLevel CompressionLevel { get; set; } = CompressionLevel.Default; } + + public CompressionLevel CompressionLevel { get; set; } = CompressionLevel.Default; } diff --git a/src/SharpCompress/Writers/IWriter.cs b/src/SharpCompress/Writers/IWriter.cs index 0c8cb5ab..bde2fcd9 100644 --- a/src/SharpCompress/Writers/IWriter.cs +++ b/src/SharpCompress/Writers/IWriter.cs @@ -1,12 +1,11 @@ -using System; +using System; using System.IO; using SharpCompress.Common; -namespace SharpCompress.Writers +namespace SharpCompress.Writers; + +public interface IWriter : IDisposable { - public interface IWriter : IDisposable - { - ArchiveType WriterType { get; } - void Write(string filename, Stream source, DateTime? modificationTime); - } + ArchiveType WriterType { get; } + void Write(string filename, Stream source, DateTime? modificationTime); } diff --git a/src/SharpCompress/Writers/IWriterExtensions.cs b/src/SharpCompress/Writers/IWriterExtensions.cs index 9335c46e..f61daf13 100644 --- a/src/SharpCompress/Writers/IWriterExtensions.cs +++ b/src/SharpCompress/Writers/IWriterExtensions.cs @@ -1,69 +1,62 @@ -using System; +using System; using System.IO; using System.Linq; -using System.Linq.Expressions; -namespace SharpCompress.Writers +namespace SharpCompress.Writers; + +public static class IWriterExtensions { - public static class IWriterExtensions + public static void Write(this IWriter writer, string entryPath, Stream source) { - public static void Write(this IWriter writer, string entryPath, Stream source) + writer.Write(entryPath, source, null); + } + + public static void Write(this IWriter writer, string entryPath, FileInfo source) + { + if (!source.Exists) { - writer.Write(entryPath, source, null); + throw new ArgumentException("Source does not exist: " + source.FullName); + } + using var stream = source.OpenRead(); + writer.Write(entryPath, stream, source.LastWriteTime); + } + + public static void Write(this IWriter writer, string entryPath, string source) + { + writer.Write(entryPath, new FileInfo(source)); + } + + public static void WriteAll( + this IWriter writer, + string directory, + string searchPattern = "*", + SearchOption option = SearchOption.TopDirectoryOnly + ) + { + writer.WriteAll(directory, searchPattern, null, option); + } + + public static void WriteAll( + this IWriter writer, + string directory, + string searchPattern = "*", + Func? fileSearchFunc = null, + SearchOption option = SearchOption.TopDirectoryOnly + ) + { + if (!Directory.Exists(directory)) + { + throw new ArgumentException("Directory does not exist: " + directory); } - public static void Write(this IWriter writer, string entryPath, FileInfo source) - { - if (!source.Exists) - { - throw new ArgumentException("Source does not exist: " + source.FullName); - } - using (var stream = source.OpenRead()) - { - writer.Write(entryPath, stream, source.LastWriteTime); - } - } - - public static void Write(this IWriter writer, string entryPath, string source) - { - writer.Write(entryPath, new FileInfo(source)); - } - - public static void WriteAll( - this IWriter writer, - string directory, - string searchPattern = "*", - SearchOption option = SearchOption.TopDirectoryOnly + fileSearchFunc ??= n => true; + foreach ( + var file in Directory + .EnumerateFiles(directory, searchPattern, option) + .Where(fileSearchFunc) ) { - writer.WriteAll(directory, searchPattern, null, option); - } - - public static void WriteAll( - this IWriter writer, - string directory, - string searchPattern = "*", - Func? fileSearchFunc = null, - SearchOption option = SearchOption.TopDirectoryOnly - ) - { - if (!Directory.Exists(directory)) - { - throw new ArgumentException("Directory does not exist: " + directory); - } - - if (fileSearchFunc is null) - { - fileSearchFunc = n => true; - } - foreach ( - var file in Directory - .EnumerateFiles(directory, searchPattern, option) - .Where(fileSearchFunc) - ) - { - writer.Write(file.Substring(directory.Length), file); - } + writer.Write(file.Substring(directory.Length), file); } } } diff --git a/src/SharpCompress/Writers/IWriterFactory.cs b/src/SharpCompress/Writers/IWriterFactory.cs index 1aed7c72..8e7bc701 100644 --- a/src/SharpCompress/Writers/IWriterFactory.cs +++ b/src/SharpCompress/Writers/IWriterFactory.cs @@ -1,16 +1,10 @@ -using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using SharpCompress.Factories; -namespace SharpCompress.Writers +namespace SharpCompress.Writers; + +public interface IWriterFactory : IFactory { - public interface IWriterFactory : IFactory - { - IWriter Open(Stream stream, WriterOptions writerOptions); - } + IWriter Open(Stream stream, WriterOptions writerOptions); } diff --git a/src/SharpCompress/Writers/Tar/TarWriter.cs b/src/SharpCompress/Writers/Tar/TarWriter.cs index b6b4da5c..223e6303 100644 --- a/src/SharpCompress/Writers/Tar/TarWriter.cs +++ b/src/SharpCompress/Writers/Tar/TarWriter.cs @@ -8,125 +8,124 @@ using SharpCompress.Compressors.Deflate; using SharpCompress.Compressors.LZMA; using SharpCompress.IO; -namespace SharpCompress.Writers.Tar +namespace SharpCompress.Writers.Tar; + +public class TarWriter : AbstractWriter { - public class TarWriter : AbstractWriter + private readonly bool finalizeArchiveOnClose; + + public TarWriter(Stream destination, TarWriterOptions options) + : base(ArchiveType.Tar, options) { - private readonly bool finalizeArchiveOnClose; + finalizeArchiveOnClose = options.FinalizeArchiveOnClose; - public TarWriter(Stream destination, TarWriterOptions options) - : base(ArchiveType.Tar, options) + if (!destination.CanWrite) { - finalizeArchiveOnClose = options.FinalizeArchiveOnClose; + throw new ArgumentException("Tars require writable streams."); + } + if (WriterOptions.LeaveStreamOpen) + { + destination = NonDisposingStream.Create(destination); + } + switch (options.CompressionType) + { + case CompressionType.None: + break; + case CompressionType.BZip2: - if (!destination.CanWrite) { - throw new ArgumentException("Tars require writable streams."); + destination = new BZip2Stream(destination, CompressionMode.Compress, false); } - if (WriterOptions.LeaveStreamOpen) + break; + case CompressionType.GZip: + { - destination = NonDisposingStream.Create(destination); + destination = new GZipStream(destination, CompressionMode.Compress); } - switch (options.CompressionType) + break; + case CompressionType.LZip: + { - case CompressionType.None: - break; - case CompressionType.BZip2: + destination = new LZipStream(destination, CompressionMode.Compress); + } + break; + default: + { + throw new InvalidFormatException( + "Tar does not support compression: " + options.CompressionType + ); + } + } + InitalizeStream(destination); + } - { - destination = new BZip2Stream(destination, CompressionMode.Compress, false); - } - break; - case CompressionType.GZip: + public override void Write(string filename, Stream source, DateTime? modificationTime) + { + Write(filename, source, modificationTime, null); + } - { - destination = new GZipStream(destination, CompressionMode.Compress); - } - break; - case CompressionType.LZip: + private string NormalizeFilename(string filename) + { + filename = filename.Replace('\\', '/'); - { - destination = new LZipStream(destination, CompressionMode.Compress); - } - break; - default: + var pos = filename.IndexOf(':'); + if (pos >= 0) + { + filename = filename.Remove(0, pos + 1); + } + + return filename.Trim('/'); + } + + public void Write(string filename, Stream source, DateTime? modificationTime, long? size) + { + if (!source.CanSeek && size is null) + { + throw new ArgumentException("Seekable stream is required if no size is given."); + } + + var realSize = size ?? source.Length; + + var header = new TarHeader(WriterOptions.ArchiveEncoding); + + header.LastModifiedTime = modificationTime ?? TarHeader.EPOCH; + header.Name = NormalizeFilename(filename); + header.Size = realSize; + header.Write(OutputStream); + + size = source.TransferTo(OutputStream); + PadTo512(size.Value); + } + + private void PadTo512(long size) + { + var zeros = unchecked((int)(((size + 511L) & ~511L) - size)); + + OutputStream.Write(stackalloc byte[zeros]); + } + + protected override void Dispose(bool isDisposing) + { + if (isDisposing) + { + if (finalizeArchiveOnClose) + { + OutputStream.Write(stackalloc byte[1024]); + } + switch (OutputStream) + { + case BZip2Stream b: { - throw new InvalidFormatException( - "Tar does not support compression: " + options.CompressionType - ); + b.Finish(); + break; + } + case LZipStream l: + { + l.Finish(); + break; } } - InitalizeStream(destination); - } - - public override void Write(string filename, Stream source, DateTime? modificationTime) - { - Write(filename, source, modificationTime, null); - } - - private string NormalizeFilename(string filename) - { - filename = filename.Replace('\\', '/'); - - int pos = filename.IndexOf(':'); - if (pos >= 0) - { - filename = filename.Remove(0, pos + 1); - } - - return filename.Trim('/'); - } - - public void Write(string filename, Stream source, DateTime? modificationTime, long? size) - { - if (!source.CanSeek && size is null) - { - throw new ArgumentException("Seekable stream is required if no size is given."); - } - - long realSize = size ?? source.Length; - - TarHeader header = new TarHeader(WriterOptions.ArchiveEncoding); - - header.LastModifiedTime = modificationTime ?? TarHeader.EPOCH; - header.Name = NormalizeFilename(filename); - header.Size = realSize; - header.Write(OutputStream); - - size = source.TransferTo(OutputStream); - PadTo512(size.Value); - } - - private void PadTo512(long size) - { - int zeros = unchecked((int)(((size + 511L) & ~511L) - size)); - - OutputStream.Write(stackalloc byte[zeros]); - } - - protected override void Dispose(bool isDisposing) - { - if (isDisposing) - { - if (finalizeArchiveOnClose) - { - OutputStream.Write(stackalloc byte[1024]); - } - switch (OutputStream) - { - case BZip2Stream b: - { - b.Finish(); - break; - } - case LZipStream l: - { - l.Finish(); - break; - } - } - } - base.Dispose(isDisposing); } + base.Dispose(isDisposing); } } diff --git a/src/SharpCompress/Writers/Tar/TarWriterOptions.cs b/src/SharpCompress/Writers/Tar/TarWriterOptions.cs index f3cc26db..c95ceba4 100755 --- a/src/SharpCompress/Writers/Tar/TarWriterOptions.cs +++ b/src/SharpCompress/Writers/Tar/TarWriterOptions.cs @@ -1,23 +1,22 @@ using SharpCompress.Common; -namespace SharpCompress.Writers.Tar +namespace SharpCompress.Writers.Tar; + +public class TarWriterOptions : WriterOptions { - public class TarWriterOptions : WriterOptions + /// + /// Indicates if archive should be finalized (by 2 empty blocks) on close. + /// + public bool FinalizeArchiveOnClose { get; } + + public TarWriterOptions(CompressionType compressionType, bool finalizeArchiveOnClose) + : base(compressionType) { - /// - /// Indicates if archive should be finalized (by 2 empty blocks) on close. - /// - public bool FinalizeArchiveOnClose { get; } + FinalizeArchiveOnClose = finalizeArchiveOnClose; + } - public TarWriterOptions(CompressionType compressionType, bool finalizeArchiveOnClose) - : base(compressionType) - { - FinalizeArchiveOnClose = finalizeArchiveOnClose; - } - - internal TarWriterOptions(WriterOptions options) : this(options.CompressionType, true) - { - ArchiveEncoding = options.ArchiveEncoding; - } + internal TarWriterOptions(WriterOptions options) : this(options.CompressionType, true) + { + ArchiveEncoding = options.ArchiveEncoding; } } diff --git a/src/SharpCompress/Writers/WriterFactory.cs b/src/SharpCompress/Writers/WriterFactory.cs index cd7102c5..fc4f1319 100644 --- a/src/SharpCompress/Writers/WriterFactory.cs +++ b/src/SharpCompress/Writers/WriterFactory.cs @@ -4,24 +4,25 @@ using System.Linq; using SharpCompress.Common; -namespace SharpCompress.Writers +namespace SharpCompress.Writers; + +public static class WriterFactory { - public static class WriterFactory + public static IWriter Open( + Stream stream, + ArchiveType archiveType, + WriterOptions writerOptions + ) { - public static IWriter Open( - Stream stream, - ArchiveType archiveType, - WriterOptions writerOptions - ) + var factory = Factories.Factory.Factories + .OfType() + .FirstOrDefault(item => item.KnownArchiveType == archiveType); + + if (factory != null) { - var factory = Factories.Factory.Factories - .OfType() - .FirstOrDefault(item => item.KnownArchiveType == archiveType); - - if (factory != null) - return factory.Open(stream, writerOptions); - - throw new NotSupportedException("Archive Type does not have a Writer: " + archiveType); + return factory.Open(stream, writerOptions); } + + throw new NotSupportedException("Archive Type does not have a Writer: " + archiveType); } } diff --git a/src/SharpCompress/Writers/WriterOptions.cs b/src/SharpCompress/Writers/WriterOptions.cs index db3b83c7..01b21bea 100644 --- a/src/SharpCompress/Writers/WriterOptions.cs +++ b/src/SharpCompress/Writers/WriterOptions.cs @@ -1,19 +1,18 @@ -using SharpCompress.Common; +using SharpCompress.Common; -namespace SharpCompress.Writers +namespace SharpCompress.Writers; + +public class WriterOptions : OptionsBase { - public class WriterOptions : OptionsBase + public WriterOptions(CompressionType compressionType) { - public WriterOptions(CompressionType compressionType) - { - CompressionType = compressionType; - } + CompressionType = compressionType; + } - public CompressionType CompressionType { get; set; } + public CompressionType CompressionType { get; set; } - public static implicit operator WriterOptions(CompressionType compressionType) - { - return new WriterOptions(compressionType); - } + public static implicit operator WriterOptions(CompressionType compressionType) + { + return new WriterOptions(compressionType); } } diff --git a/src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs b/src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs index de54e622..111c7239 100644 --- a/src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs +++ b/src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs @@ -6,170 +6,169 @@ using SharpCompress.Common; using SharpCompress.Common.Zip; using SharpCompress.Common.Zip.Headers; -namespace SharpCompress.Writers.Zip +namespace SharpCompress.Writers.Zip; + +internal class ZipCentralDirectoryEntry { - internal class ZipCentralDirectoryEntry + private readonly ZipCompressionMethod compression; + private readonly string fileName; + private readonly ArchiveEncoding archiveEncoding; + + public ZipCentralDirectoryEntry( + ZipCompressionMethod compression, + string fileName, + ulong headerOffset, + ArchiveEncoding archiveEncoding + ) { - private readonly ZipCompressionMethod compression; - private readonly string fileName; - private readonly ArchiveEncoding archiveEncoding; + this.compression = compression; + this.fileName = fileName; + HeaderOffset = headerOffset; + this.archiveEncoding = archiveEncoding; + } - public ZipCentralDirectoryEntry( - ZipCompressionMethod compression, - string fileName, - ulong headerOffset, - ArchiveEncoding archiveEncoding - ) + internal DateTime? ModificationTime { get; set; } + internal string? Comment { get; set; } + internal uint Crc { get; set; } + internal ulong Compressed { get; set; } + internal ulong Decompressed { get; set; } + internal ushort Zip64HeaderOffset { get; set; } + internal ulong HeaderOffset { get; } + + internal uint Write(Stream outputStream) + { + var encodedFilename = archiveEncoding.Encode(fileName); + var encodedComment = archiveEncoding.Encode(Comment ?? string.Empty); + + var zip64_stream = Compressed >= uint.MaxValue || Decompressed >= uint.MaxValue; + var zip64 = zip64_stream || HeaderOffset >= uint.MaxValue; + var usedCompression = compression; + + var compressedvalue = zip64 ? uint.MaxValue : (uint)Compressed; + var decompressedvalue = zip64 ? uint.MaxValue : (uint)Decompressed; + var headeroffsetvalue = zip64 ? uint.MaxValue : (uint)HeaderOffset; + var extralength = zip64 ? (2 + 2 + 8 + 8 + 8 + 4) : 0; + var version = (byte)(zip64 ? 45 : 20); // Version 20 required for deflate/encryption + + var flags = Equals(archiveEncoding.GetEncoding(), Encoding.UTF8) + ? HeaderFlags.Efs + : HeaderFlags.None; + if (!outputStream.CanSeek) { - this.compression = compression; - this.fileName = fileName; - HeaderOffset = headerOffset; - this.archiveEncoding = archiveEncoding; + // Cannot use data descriptors with zip64: + // https://blogs.oracle.com/xuemingshen/entry/is_zipinput_outputstream_handling_of + + // We check that streams are not written too large in the ZipWritingStream, + // so this extra guard is not required, but kept to simplify changing the code + // once the zip64 post-data issue is resolved + if (!zip64_stream) + { + flags |= HeaderFlags.UsePostDataDescriptor; + } + + if (usedCompression == ZipCompressionMethod.LZMA) + { + flags |= HeaderFlags.Bit1; // eos marker + } } - internal DateTime? ModificationTime { get; set; } - internal string? Comment { get; set; } - internal uint Crc { get; set; } - internal ulong Compressed { get; set; } - internal ulong Decompressed { get; set; } - internal ushort Zip64HeaderOffset { get; set; } - internal ulong HeaderOffset { get; } - - internal uint Write(Stream outputStream) + // Support for zero byte files + if (Decompressed == 0 && Compressed == 0) { - byte[] encodedFilename = archiveEncoding.Encode(fileName); - byte[] encodedComment = archiveEncoding.Encode(Comment ?? string.Empty); + usedCompression = ZipCompressionMethod.None; + } - var zip64_stream = Compressed >= uint.MaxValue || Decompressed >= uint.MaxValue; - var zip64 = zip64_stream || HeaderOffset >= uint.MaxValue; - var usedCompression = compression; + Span intBuf = stackalloc byte[] { 80, 75, 1, 2, version, 0, version, 0 }; + //constant sig, then version made by, then version to extract + outputStream.Write(intBuf); - var compressedvalue = zip64 ? uint.MaxValue : (uint)Compressed; - var decompressedvalue = zip64 ? uint.MaxValue : (uint)Decompressed; - var headeroffsetvalue = zip64 ? uint.MaxValue : (uint)HeaderOffset; - var extralength = zip64 ? (2 + 2 + 8 + 8 + 8 + 4) : 0; - var version = (byte)(zip64 ? 45 : 20); // Version 20 required for deflate/encryption + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)flags); + outputStream.Write(intBuf.Slice(0, 2)); + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)usedCompression); + outputStream.Write(intBuf.Slice(0, 2)); // zipping method + BinaryPrimitives.WriteUInt32LittleEndian(intBuf, ModificationTime.DateTimeToDosTime()); + outputStream.Write(intBuf.Slice(0, 4)); - HeaderFlags flags = Equals(archiveEncoding.GetEncoding(), Encoding.UTF8) - ? HeaderFlags.Efs - : HeaderFlags.None; - if (!outputStream.CanSeek) - { - // Cannot use data descriptors with zip64: - // https://blogs.oracle.com/xuemingshen/entry/is_zipinput_outputstream_handling_of + // zipping date and time + BinaryPrimitives.WriteUInt32LittleEndian(intBuf, Crc); + outputStream.Write(intBuf.Slice(0, 4)); // file CRC + BinaryPrimitives.WriteUInt32LittleEndian(intBuf, compressedvalue); + outputStream.Write(intBuf.Slice(0, 4)); // compressed file size + BinaryPrimitives.WriteUInt32LittleEndian(intBuf, decompressedvalue); + outputStream.Write(intBuf.Slice(0, 4)); // uncompressed file size + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)encodedFilename.Length); + outputStream.Write(intBuf.Slice(0, 2)); // Filename in zip + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)extralength); + outputStream.Write(intBuf.Slice(0, 2)); // extra length + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)encodedComment.Length); + outputStream.Write(intBuf.Slice(0, 2)); - // We check that streams are not written too large in the ZipWritingStream, - // so this extra guard is not required, but kept to simplify changing the code - // once the zip64 post-data issue is resolved - if (!zip64_stream) - { - flags |= HeaderFlags.UsePostDataDescriptor; - } + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 0); + outputStream.Write(intBuf.Slice(0, 2)); // disk=0 - if (usedCompression == ZipCompressionMethod.LZMA) - { - flags |= HeaderFlags.Bit1; // eos marker - } - } + // Internal file attributes: + // Bit 0: apparent ASCII/ text file + // Bit 1: reserved + // Bit 2: control field records precede logical records + // Bits 3 - 16: unused + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 0); + outputStream.Write(intBuf.Slice(0, 2)); // file type: binary, Internal file attributes - // Support for zero byte files - if (Decompressed == 0 && Compressed == 0) - { - usedCompression = ZipCompressionMethod.None; - } + // External flags are host-dependent, this might match DOS + // Bit 0: Read-Only + // Bit 1: Hidden + // Bit 2: System + // Bit 3: Label + // Bit 4: Directory + // Bit 5: Archive + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 0); + outputStream.Write(intBuf.Slice(0, 2)); // External file attributes + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 0x8100); + outputStream.Write(intBuf.Slice(0, 2)); - Span intBuf = stackalloc byte[] { 80, 75, 1, 2, version, 0, version, 0 }; - //constant sig, then version made by, then version to extract + // External file attributes (normal/readable) + BinaryPrimitives.WriteUInt32LittleEndian(intBuf, headeroffsetvalue); + outputStream.Write(intBuf.Slice(0, 4)); // Offset of header + + outputStream.Write(encodedFilename, 0, encodedFilename.Length); + if (zip64) + { + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 0x0001); + outputStream.Write(intBuf.Slice(0, 2)); + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)(extralength - 4)); + outputStream.Write(intBuf.Slice(0, 2)); + + BinaryPrimitives.WriteUInt64LittleEndian(intBuf, Decompressed); outputStream.Write(intBuf); - - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)flags); - outputStream.Write(intBuf.Slice(0, 2)); - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)usedCompression); - outputStream.Write(intBuf.Slice(0, 2)); // zipping method - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, ModificationTime.DateTimeToDosTime()); - outputStream.Write(intBuf.Slice(0, 4)); - - // zipping date and time - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, Crc); - outputStream.Write(intBuf.Slice(0, 4)); // file CRC - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, compressedvalue); - outputStream.Write(intBuf.Slice(0, 4)); // compressed file size - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, decompressedvalue); - outputStream.Write(intBuf.Slice(0, 4)); // uncompressed file size - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)encodedFilename.Length); - outputStream.Write(intBuf.Slice(0, 2)); // Filename in zip - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)extralength); - outputStream.Write(intBuf.Slice(0, 2)); // extra length - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)encodedComment.Length); - outputStream.Write(intBuf.Slice(0, 2)); - - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 0); - outputStream.Write(intBuf.Slice(0, 2)); // disk=0 - - // Internal file attributes: - // Bit 0: apparent ASCII/ text file - // Bit 1: reserved - // Bit 2: control field records precede logical records - // Bits 3 - 16: unused - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 0); - outputStream.Write(intBuf.Slice(0, 2)); // file type: binary, Internal file attributes - - // External flags are host-dependent, this might match DOS - // Bit 0: Read-Only - // Bit 1: Hidden - // Bit 2: System - // Bit 3: Label - // Bit 4: Directory - // Bit 5: Archive - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 0); - outputStream.Write(intBuf.Slice(0, 2)); // External file attributes - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 0x8100); - outputStream.Write(intBuf.Slice(0, 2)); - - // External file attributes (normal/readable) - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, headeroffsetvalue); - outputStream.Write(intBuf.Slice(0, 4)); // Offset of header - - outputStream.Write(encodedFilename, 0, encodedFilename.Length); - if (zip64) - { - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 0x0001); - outputStream.Write(intBuf.Slice(0, 2)); - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)(extralength - 4)); - outputStream.Write(intBuf.Slice(0, 2)); - - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, Decompressed); - outputStream.Write(intBuf); - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, Compressed); - outputStream.Write(intBuf); - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, HeaderOffset); - outputStream.Write(intBuf); - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, 0); - outputStream.Write(intBuf.Slice(0, 4)); // VolumeNumber = 0 - } - - outputStream.Write(encodedComment, 0, encodedComment.Length); - - return (uint)( - 8 - + 2 - + 2 - + 4 - + 4 - + 4 - + 4 - + 2 - + 2 - + 2 - + 2 - + 2 - + 2 - + 2 - + 4 - + encodedFilename.Length - + extralength - + encodedComment.Length - ); + BinaryPrimitives.WriteUInt64LittleEndian(intBuf, Compressed); + outputStream.Write(intBuf); + BinaryPrimitives.WriteUInt64LittleEndian(intBuf, HeaderOffset); + outputStream.Write(intBuf); + BinaryPrimitives.WriteUInt32LittleEndian(intBuf, 0); + outputStream.Write(intBuf.Slice(0, 4)); // VolumeNumber = 0 } + + outputStream.Write(encodedComment, 0, encodedComment.Length); + + return (uint)( + 8 + + 2 + + 2 + + 4 + + 4 + + 4 + + 4 + + 2 + + 2 + + 2 + + 2 + + 2 + + 2 + + 2 + + 4 + + encodedFilename.Length + + extralength + + encodedComment.Length + ); } } diff --git a/src/SharpCompress/Writers/Zip/ZipWriter.cs b/src/SharpCompress/Writers/Zip/ZipWriter.cs index fb04e7a4..afc34800 100644 --- a/src/SharpCompress/Writers/Zip/ZipWriter.cs +++ b/src/SharpCompress/Writers/Zip/ZipWriter.cs @@ -13,595 +13,592 @@ using SharpCompress.Compressors.LZMA; using SharpCompress.Compressors.PPMd; using SharpCompress.IO; -namespace SharpCompress.Writers.Zip +namespace SharpCompress.Writers.Zip; + +public class ZipWriter : AbstractWriter { - public class ZipWriter : AbstractWriter + private readonly CompressionType compressionType; + private readonly CompressionLevel compressionLevel; + private readonly List entries = + new List(); + private readonly string zipComment; + private long streamPosition; + private PpmdProperties? ppmdProps; + private readonly bool isZip64; + + public ZipWriter(Stream destination, ZipWriterOptions zipWriterOptions) + : base(ArchiveType.Zip, zipWriterOptions) { - private readonly CompressionType compressionType; - private readonly CompressionLevel compressionLevel; - private readonly List entries = - new List(); - private readonly string zipComment; - private long streamPosition; - private PpmdProperties? ppmdProps; - private readonly bool isZip64; - - public ZipWriter(Stream destination, ZipWriterOptions zipWriterOptions) - : base(ArchiveType.Zip, zipWriterOptions) + zipComment = zipWriterOptions.ArchiveComment ?? string.Empty; + isZip64 = zipWriterOptions.UseZip64; + if (destination.CanSeek) { - zipComment = zipWriterOptions.ArchiveComment ?? string.Empty; - isZip64 = zipWriterOptions.UseZip64; - if (destination.CanSeek) + streamPosition = destination.Position; + } + + compressionType = zipWriterOptions.CompressionType; + compressionLevel = zipWriterOptions.DeflateCompressionLevel; + + if (WriterOptions.LeaveStreamOpen) + { + destination = NonDisposingStream.Create(destination); + } + InitalizeStream(destination); + } + + private PpmdProperties PpmdProperties + { + get { return ppmdProps ??= new PpmdProperties(); } + } + + protected override void Dispose(bool isDisposing) + { + if (isDisposing) + { + ulong size = 0; + foreach (var entry in entries) { - streamPosition = destination.Position; + size += entry.Write(OutputStream); } + WriteEndRecord(size); + } + base.Dispose(isDisposing); + } - compressionType = zipWriterOptions.CompressionType; - compressionLevel = zipWriterOptions.DeflateCompressionLevel; - - if (WriterOptions.LeaveStreamOpen) + private static ZipCompressionMethod ToZipCompressionMethod(CompressionType compressionType) + { + switch (compressionType) + { + case CompressionType.None: { - destination = NonDisposingStream.Create(destination); + return ZipCompressionMethod.None; } - InitalizeStream(destination); - } - - private PpmdProperties PpmdProperties - { - get { return ppmdProps ??= new PpmdProperties(); } - } - - protected override void Dispose(bool isDisposing) - { - if (isDisposing) + case CompressionType.Deflate: { - ulong size = 0; - foreach (ZipCentralDirectoryEntry entry in entries) - { - size += entry.Write(OutputStream); - } - WriteEndRecord(size); + return ZipCompressionMethod.Deflate; } - base.Dispose(isDisposing); - } - - private static ZipCompressionMethod ToZipCompressionMethod(CompressionType compressionType) - { - switch (compressionType) + case CompressionType.BZip2: { - case CompressionType.None: - { - return ZipCompressionMethod.None; - } - case CompressionType.Deflate: - { - return ZipCompressionMethod.Deflate; - } - case CompressionType.BZip2: - { - return ZipCompressionMethod.BZip2; - } - case CompressionType.LZMA: - { - return ZipCompressionMethod.LZMA; - } - case CompressionType.PPMd: - { - return ZipCompressionMethod.PPMd; - } - default: - throw new InvalidFormatException( - "Invalid compression method: " + compressionType - ); + return ZipCompressionMethod.BZip2; } - } - - public override void Write(string entryPath, Stream source, DateTime? modificationTime) - { - Write( - entryPath, - source, - new ZipWriterEntryOptions() { ModificationDateTime = modificationTime } - ); - } - - public void Write( - string entryPath, - Stream source, - ZipWriterEntryOptions zipWriterEntryOptions - ) - { - using (Stream output = WriteToStream(entryPath, zipWriterEntryOptions)) + case CompressionType.LZMA: { - source.TransferTo(output); + return ZipCompressionMethod.LZMA; } - } - - public Stream WriteToStream(string entryPath, ZipWriterEntryOptions options) - { - var compression = ToZipCompressionMethod(options.CompressionType ?? compressionType); - - entryPath = NormalizeFilename(entryPath); - options.ModificationDateTime ??= DateTime.Now; - options.EntryComment ??= string.Empty; - var entry = new ZipCentralDirectoryEntry( - compression, - entryPath, - (ulong)streamPosition, - WriterOptions.ArchiveEncoding - ) + case CompressionType.PPMd: { - Comment = options.EntryComment, - ModificationTime = options.ModificationDateTime - }; - - // Use the archive default setting for zip64 and allow overrides - var useZip64 = isZip64; - if (options.EnableZip64.HasValue) - { - useZip64 = options.EnableZip64.Value; + return ZipCompressionMethod.PPMd; } - - var headersize = (uint)WriteHeader(entryPath, options, entry, useZip64); - streamPosition += headersize; - return new ZipWritingStream( - this, - OutputStream, - entry, - compression, - options.DeflateCompressionLevel ?? compressionLevel - ); - } - - private string NormalizeFilename(string filename) - { - filename = filename.Replace('\\', '/'); - - int pos = filename.IndexOf(':'); - if (pos >= 0) - { - filename = filename.Remove(0, pos + 1); - } - - return filename.Trim('/'); - } - - private int WriteHeader( - string filename, - ZipWriterEntryOptions zipWriterEntryOptions, - ZipCentralDirectoryEntry entry, - bool useZip64 - ) - { - // We err on the side of caution until the zip specification clarifies how to support this - if (!OutputStream.CanSeek && useZip64) - { - throw new NotSupportedException( - "Zip64 extensions are not supported on non-seekable streams" + default: + throw new InvalidFormatException( + "Invalid compression method: " + compressionType ); - } + } + } - var explicitZipCompressionInfo = ToZipCompressionMethod( - zipWriterEntryOptions.CompressionType ?? compressionType + public override void Write(string entryPath, Stream source, DateTime? modificationTime) + { + Write( + entryPath, + source, + new ZipWriterEntryOptions() { ModificationDateTime = modificationTime } + ); + } + + public void Write( + string entryPath, + Stream source, + ZipWriterEntryOptions zipWriterEntryOptions + ) + { + using var output = WriteToStream(entryPath, zipWriterEntryOptions); + source.TransferTo(output); + } + + public Stream WriteToStream(string entryPath, ZipWriterEntryOptions options) + { + var compression = ToZipCompressionMethod(options.CompressionType ?? compressionType); + + entryPath = NormalizeFilename(entryPath); + options.ModificationDateTime ??= DateTime.Now; + options.EntryComment ??= string.Empty; + var entry = new ZipCentralDirectoryEntry( + compression, + entryPath, + (ulong)streamPosition, + WriterOptions.ArchiveEncoding + ) + { + Comment = options.EntryComment, + ModificationTime = options.ModificationDateTime + }; + + // Use the archive default setting for zip64 and allow overrides + var useZip64 = isZip64; + if (options.EnableZip64.HasValue) + { + useZip64 = options.EnableZip64.Value; + } + + var headersize = (uint)WriteHeader(entryPath, options, entry, useZip64); + streamPosition += headersize; + return new ZipWritingStream( + this, + OutputStream, + entry, + compression, + options.DeflateCompressionLevel ?? compressionLevel + ); + } + + private string NormalizeFilename(string filename) + { + filename = filename.Replace('\\', '/'); + + var pos = filename.IndexOf(':'); + if (pos >= 0) + { + filename = filename.Remove(0, pos + 1); + } + + return filename.Trim('/'); + } + + private int WriteHeader( + string filename, + ZipWriterEntryOptions zipWriterEntryOptions, + ZipCentralDirectoryEntry entry, + bool useZip64 + ) + { + // We err on the side of caution until the zip specification clarifies how to support this + if (!OutputStream.CanSeek && useZip64) + { + throw new NotSupportedException( + "Zip64 extensions are not supported on non-seekable streams" ); - byte[] encodedFilename = WriterOptions.ArchiveEncoding.Encode(filename); + } - Span intBuf = stackalloc byte[4]; - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, ZipHeaderFactory.ENTRY_HEADER_BYTES); - OutputStream.Write(intBuf); - if (explicitZipCompressionInfo == ZipCompressionMethod.Deflate) + var explicitZipCompressionInfo = ToZipCompressionMethod( + zipWriterEntryOptions.CompressionType ?? compressionType + ); + var encodedFilename = WriterOptions.ArchiveEncoding.Encode(filename); + + Span intBuf = stackalloc byte[4]; + BinaryPrimitives.WriteUInt32LittleEndian(intBuf, ZipHeaderFactory.ENTRY_HEADER_BYTES); + OutputStream.Write(intBuf); + if (explicitZipCompressionInfo == ZipCompressionMethod.Deflate) + { + if (OutputStream.CanSeek && useZip64) { - if (OutputStream.CanSeek && useZip64) - { - OutputStream.Write(stackalloc byte[] { 45, 0 }); //smallest allowed version for zip64 - } - else - { - OutputStream.Write(stackalloc byte[] { 20, 0 }); //older version which is more compatible - } + OutputStream.Write(stackalloc byte[] { 45, 0 }); //smallest allowed version for zip64 } else { - OutputStream.Write(stackalloc byte[] { 63, 0 }); //version says we used PPMd or LZMA + OutputStream.Write(stackalloc byte[] { 20, 0 }); //older version which is more compatible } - HeaderFlags flags = Equals(WriterOptions.ArchiveEncoding.GetEncoding(), Encoding.UTF8) - ? HeaderFlags.Efs - : 0; - if (!OutputStream.CanSeek) + } + else + { + OutputStream.Write(stackalloc byte[] { 63, 0 }); //version says we used PPMd or LZMA + } + var flags = Equals(WriterOptions.ArchiveEncoding.GetEncoding(), Encoding.UTF8) + ? HeaderFlags.Efs + : 0; + if (!OutputStream.CanSeek) + { + flags |= HeaderFlags.UsePostDataDescriptor; + + if (explicitZipCompressionInfo == ZipCompressionMethod.LZMA) { - flags |= HeaderFlags.UsePostDataDescriptor; - - if (explicitZipCompressionInfo == ZipCompressionMethod.LZMA) - { - flags |= HeaderFlags.Bit1; // eos marker - } + flags |= HeaderFlags.Bit1; // eos marker } + } - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)flags); - OutputStream.Write(intBuf.Slice(0, 2)); - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)explicitZipCompressionInfo); - OutputStream.Write(intBuf.Slice(0, 2)); // zipping method - BinaryPrimitives.WriteUInt32LittleEndian( - intBuf, - zipWriterEntryOptions.ModificationDateTime.DateTimeToDosTime() + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)flags); + OutputStream.Write(intBuf.Slice(0, 2)); + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)explicitZipCompressionInfo); + OutputStream.Write(intBuf.Slice(0, 2)); // zipping method + BinaryPrimitives.WriteUInt32LittleEndian( + intBuf, + zipWriterEntryOptions.ModificationDateTime.DateTimeToDosTime() + ); + OutputStream.Write(intBuf); + + // zipping date and time + OutputStream.Write(stackalloc byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); + + // unused CRC, un/compressed size, updated later + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)encodedFilename.Length); + OutputStream.Write(intBuf.Slice(0, 2)); // filename length + + var extralength = 0; + if (OutputStream.CanSeek && useZip64) + { + extralength = 2 + 2 + 8 + 8; + } + + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)extralength); + OutputStream.Write(intBuf.Slice(0, 2)); // extra length + OutputStream.Write(encodedFilename, 0, encodedFilename.Length); + + if (extralength != 0) + { + OutputStream.Write(new byte[extralength], 0, extralength); // reserve space for zip64 data + entry.Zip64HeaderOffset = (ushort)( + 6 + 2 + 2 + 4 + 12 + 2 + 2 + encodedFilename.Length ); - OutputStream.Write(intBuf); - - // zipping date and time - OutputStream.Write(stackalloc byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); - - // unused CRC, un/compressed size, updated later - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)encodedFilename.Length); - OutputStream.Write(intBuf.Slice(0, 2)); // filename length - - var extralength = 0; - if (OutputStream.CanSeek && useZip64) - { - extralength = 2 + 2 + 8 + 8; - } - - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)extralength); - OutputStream.Write(intBuf.Slice(0, 2)); // extra length - OutputStream.Write(encodedFilename, 0, encodedFilename.Length); - - if (extralength != 0) - { - OutputStream.Write(new byte[extralength], 0, extralength); // reserve space for zip64 data - entry.Zip64HeaderOffset = (ushort)( - 6 + 2 + 2 + 4 + 12 + 2 + 2 + encodedFilename.Length - ); - } - - return 6 + 2 + 2 + 4 + 12 + 2 + 2 + encodedFilename.Length + extralength; } - private void WriteFooter(uint crc, uint compressed, uint uncompressed) + return 6 + 2 + 2 + 4 + 12 + 2 + 2 + encodedFilename.Length + extralength; + } + + private void WriteFooter(uint crc, uint compressed, uint uncompressed) + { + Span intBuf = stackalloc byte[4]; + BinaryPrimitives.WriteUInt32LittleEndian(intBuf, crc); + OutputStream.Write(intBuf); + BinaryPrimitives.WriteUInt32LittleEndian(intBuf, compressed); + OutputStream.Write(intBuf); + BinaryPrimitives.WriteUInt32LittleEndian(intBuf, uncompressed); + OutputStream.Write(intBuf); + } + + private void WriteEndRecord(ulong size) + { + var zip64EndOfCentralDirectoryNeeded = + entries.Count > ushort.MaxValue + || streamPosition >= uint.MaxValue + || size >= uint.MaxValue; + + var sizevalue = size >= uint.MaxValue ? uint.MaxValue : (uint)size; + var streampositionvalue = + streamPosition >= uint.MaxValue ? uint.MaxValue : (uint)streamPosition; + + Span intBuf = stackalloc byte[8]; + if (zip64EndOfCentralDirectoryNeeded) { - Span intBuf = stackalloc byte[4]; - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, crc); - OutputStream.Write(intBuf); - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, compressed); - OutputStream.Write(intBuf); - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, uncompressed); - OutputStream.Write(intBuf); + var recordlen = 2 + 2 + 4 + 4 + 8 + 8 + 8 + 8; + + // Write zip64 end of central directory record + OutputStream.Write(stackalloc byte[] { 80, 75, 6, 6 }); + + BinaryPrimitives.WriteUInt64LittleEndian(intBuf, (ulong)recordlen); + OutputStream.Write(intBuf); // Size of zip64 end of central directory record + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 45); + OutputStream.Write(intBuf.Slice(0, 2)); // Made by + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 45); + OutputStream.Write(intBuf.Slice(0, 2)); // Version needed + + BinaryPrimitives.WriteUInt32LittleEndian(intBuf, 0); + OutputStream.Write(intBuf.Slice(0, 4)); // Disk number + OutputStream.Write(intBuf.Slice(0, 4)); // Central dir disk + + // TODO: entries.Count is int, so max 2^31 files + BinaryPrimitives.WriteUInt64LittleEndian(intBuf, (ulong)entries.Count); + OutputStream.Write(intBuf); // Entries in this disk + OutputStream.Write(intBuf); // Total entries + BinaryPrimitives.WriteUInt64LittleEndian(intBuf, size); + OutputStream.Write(intBuf); // Central Directory size + BinaryPrimitives.WriteUInt64LittleEndian(intBuf, (ulong)streamPosition); + OutputStream.Write(intBuf); // Disk offset + + // Write zip64 end of central directory locator + OutputStream.Write(stackalloc byte[] { 80, 75, 6, 7 }); + + BinaryPrimitives.WriteUInt32LittleEndian(intBuf, 0); + OutputStream.Write(intBuf.Slice(0, 4)); // Entry disk + BinaryPrimitives.WriteUInt64LittleEndian(intBuf, (ulong)streamPosition + size); + OutputStream.Write(intBuf); // Offset to the zip64 central directory + BinaryPrimitives.WriteUInt32LittleEndian(intBuf, 1); + OutputStream.Write(intBuf.Slice(0, 4)); // Number of disks + + streamPosition += 4 + 8 + recordlen + (4 + 4 + 8 + 4); } - private void WriteEndRecord(ulong size) + // Write normal end of central directory record + OutputStream.Write(stackalloc byte[] { 80, 75, 5, 6, 0, 0, 0, 0 }); + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)entries.Count); + OutputStream.Write(intBuf.Slice(0, 2)); + OutputStream.Write(intBuf.Slice(0, 2)); + BinaryPrimitives.WriteUInt32LittleEndian(intBuf, sizevalue); + OutputStream.Write(intBuf.Slice(0, 4)); + BinaryPrimitives.WriteUInt32LittleEndian(intBuf, streampositionvalue); + OutputStream.Write(intBuf.Slice(0, 4)); + var encodedComment = WriterOptions.ArchiveEncoding.Encode(zipComment); + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)encodedComment.Length); + OutputStream.Write(intBuf.Slice(0, 2)); + OutputStream.Write(encodedComment, 0, encodedComment.Length); + } + + #region Nested type: ZipWritingStream + + internal class ZipWritingStream : Stream + { + private readonly CRC32 crc = new CRC32(); + private readonly ZipCentralDirectoryEntry entry; + private readonly Stream originalStream; + private readonly Stream writeStream; + private readonly ZipWriter writer; + private readonly ZipCompressionMethod zipCompressionMethod; + private readonly CompressionLevel compressionLevel; + private CountingWritableSubStream? counting; + private ulong decompressed; + + // Flag to prevent throwing exceptions on Dispose + private bool limitsExceeded; + private bool isDisposed; + + internal ZipWritingStream( + ZipWriter writer, + Stream originalStream, + ZipCentralDirectoryEntry entry, + ZipCompressionMethod zipCompressionMethod, + CompressionLevel compressionLevel + ) { - var zip64EndOfCentralDirectoryNeeded = - entries.Count > ushort.MaxValue - || streamPosition >= uint.MaxValue - || size >= uint.MaxValue; - - var sizevalue = size >= uint.MaxValue ? uint.MaxValue : (uint)size; - var streampositionvalue = - streamPosition >= uint.MaxValue ? uint.MaxValue : (uint)streamPosition; - - Span intBuf = stackalloc byte[8]; - if (zip64EndOfCentralDirectoryNeeded) - { - var recordlen = 2 + 2 + 4 + 4 + 8 + 8 + 8 + 8; - - // Write zip64 end of central directory record - OutputStream.Write(stackalloc byte[] { 80, 75, 6, 6 }); - - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, (ulong)recordlen); - OutputStream.Write(intBuf); // Size of zip64 end of central directory record - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 45); - OutputStream.Write(intBuf.Slice(0, 2)); // Made by - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 45); - OutputStream.Write(intBuf.Slice(0, 2)); // Version needed - - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, 0); - OutputStream.Write(intBuf.Slice(0, 4)); // Disk number - OutputStream.Write(intBuf.Slice(0, 4)); // Central dir disk - - // TODO: entries.Count is int, so max 2^31 files - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, (ulong)entries.Count); - OutputStream.Write(intBuf); // Entries in this disk - OutputStream.Write(intBuf); // Total entries - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, size); - OutputStream.Write(intBuf); // Central Directory size - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, (ulong)streamPosition); - OutputStream.Write(intBuf); // Disk offset - - // Write zip64 end of central directory locator - OutputStream.Write(stackalloc byte[] { 80, 75, 6, 7 }); - - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, 0); - OutputStream.Write(intBuf.Slice(0, 4)); // Entry disk - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, (ulong)streamPosition + size); - OutputStream.Write(intBuf); // Offset to the zip64 central directory - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, 1); - OutputStream.Write(intBuf.Slice(0, 4)); // Number of disks - - streamPosition += 4 + 8 + recordlen + (4 + 4 + 8 + 4); - } - - // Write normal end of central directory record - OutputStream.Write(stackalloc byte[] { 80, 75, 5, 6, 0, 0, 0, 0 }); - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)entries.Count); - OutputStream.Write(intBuf.Slice(0, 2)); - OutputStream.Write(intBuf.Slice(0, 2)); - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, sizevalue); - OutputStream.Write(intBuf.Slice(0, 4)); - BinaryPrimitives.WriteUInt32LittleEndian(intBuf, streampositionvalue); - OutputStream.Write(intBuf.Slice(0, 4)); - byte[] encodedComment = WriterOptions.ArchiveEncoding.Encode(zipComment); - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, (ushort)encodedComment.Length); - OutputStream.Write(intBuf.Slice(0, 2)); - OutputStream.Write(encodedComment, 0, encodedComment.Length); + this.writer = writer; + this.originalStream = originalStream; + this.writer = writer; + this.entry = entry; + this.zipCompressionMethod = zipCompressionMethod; + this.compressionLevel = compressionLevel; + writeStream = GetWriteStream(originalStream); } - #region Nested type: ZipWritingStream + public override bool CanRead => false; - internal class ZipWritingStream : Stream + public override bool CanSeek => false; + + public override bool CanWrite => true; + + public override long Length => throw new NotSupportedException(); + + public override long Position { - private readonly CRC32 crc = new CRC32(); - private readonly ZipCentralDirectoryEntry entry; - private readonly Stream originalStream; - private readonly Stream writeStream; - private readonly ZipWriter writer; - private readonly ZipCompressionMethod zipCompressionMethod; - private readonly CompressionLevel compressionLevel; - private CountingWritableSubStream? counting; - private ulong decompressed; + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } - // Flag to prevent throwing exceptions on Dispose - private bool limitsExceeded; - private bool isDisposed; - - internal ZipWritingStream( - ZipWriter writer, - Stream originalStream, - ZipCentralDirectoryEntry entry, - ZipCompressionMethod zipCompressionMethod, - CompressionLevel compressionLevel - ) + private Stream GetWriteStream(Stream writeStream) + { + counting = new CountingWritableSubStream(writeStream); + Stream output = counting; + switch (zipCompressionMethod) { - this.writer = writer; - this.originalStream = originalStream; - this.writer = writer; - this.entry = entry; - this.zipCompressionMethod = zipCompressionMethod; - this.compressionLevel = compressionLevel; - writeStream = GetWriteStream(originalStream); - } - - public override bool CanRead => false; - - public override bool CanSeek => false; - - public override bool CanWrite => true; - - public override long Length => throw new NotSupportedException(); - - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - - private Stream GetWriteStream(Stream writeStream) - { - counting = new CountingWritableSubStream(writeStream); - Stream output = counting; - switch (zipCompressionMethod) + case ZipCompressionMethod.None: { - case ZipCompressionMethod.None: - { - return output; - } - case ZipCompressionMethod.Deflate: - { - return new DeflateStream( - counting, - CompressionMode.Compress, - compressionLevel - ); - } - case ZipCompressionMethod.BZip2: - { - return new BZip2Stream(counting, CompressionMode.Compress, false); - } - case ZipCompressionMethod.LZMA: - { - counting.WriteByte(9); - counting.WriteByte(20); - counting.WriteByte(5); - counting.WriteByte(0); + return output; + } + case ZipCompressionMethod.Deflate: + { + return new DeflateStream( + counting, + CompressionMode.Compress, + compressionLevel + ); + } + case ZipCompressionMethod.BZip2: + { + return new BZip2Stream(counting, CompressionMode.Compress, false); + } + case ZipCompressionMethod.LZMA: + { + counting.WriteByte(9); + counting.WriteByte(20); + counting.WriteByte(5); + counting.WriteByte(0); - LzmaStream lzmaStream = new LzmaStream( - new LzmaEncoderProperties(!originalStream.CanSeek), - false, - counting - ); - counting.Write(lzmaStream.Properties, 0, lzmaStream.Properties.Length); - return lzmaStream; - } - case ZipCompressionMethod.PPMd: - { - counting.Write(writer.PpmdProperties.Properties, 0, 2); - return new PpmdStream(writer.PpmdProperties, counting, true); - } - default: - { - throw new NotSupportedException( - "CompressionMethod: " + zipCompressionMethod - ); - } + var lzmaStream = new LzmaStream( + new LzmaEncoderProperties(!originalStream.CanSeek), + false, + counting + ); + counting.Write(lzmaStream.Properties, 0, lzmaStream.Properties.Length); + return lzmaStream; + } + case ZipCompressionMethod.PPMd: + { + counting.Write(writer.PpmdProperties.Properties, 0, 2); + return new PpmdStream(writer.PpmdProperties, counting, true); + } + default: + { + throw new NotSupportedException( + "CompressionMethod: " + zipCompressionMethod + ); } } + } - protected override void Dispose(bool disposing) + protected override void Dispose(bool disposing) + { + if (isDisposed) { - if (isDisposed) + return; + } + + isDisposed = true; + + base.Dispose(disposing); + if (disposing) + { + writeStream.Dispose(); + + if (limitsExceeded) { + // We have written invalid data into the archive, + // so we destroy it now, instead of allowing the user to continue + // with a defunct archive + originalStream.Dispose(); return; } - isDisposed = true; + entry.Crc = (uint)crc.Crc32Result; + entry.Compressed = counting!.Count; + entry.Decompressed = decompressed; - base.Dispose(disposing); - if (disposing) + var zip64 = + entry.Compressed >= uint.MaxValue || entry.Decompressed >= uint.MaxValue; + var compressedvalue = zip64 ? uint.MaxValue : (uint)counting.Count; + var decompressedvalue = zip64 ? uint.MaxValue : (uint)entry.Decompressed; + + if (originalStream.CanSeek) { - writeStream.Dispose(); + originalStream.Position = (long)(entry.HeaderOffset + 6); + originalStream.WriteByte(0); - if (limitsExceeded) + if (counting.Count == 0 && entry.Decompressed == 0) { - // We have written invalid data into the archive, - // so we destroy it now, instead of allowing the user to continue - // with a defunct archive - originalStream.Dispose(); - return; - } - - entry.Crc = (uint)crc.Crc32Result; - entry.Compressed = counting!.Count; - entry.Decompressed = decompressed; - - var zip64 = - entry.Compressed >= uint.MaxValue || entry.Decompressed >= uint.MaxValue; - var compressedvalue = zip64 ? uint.MaxValue : (uint)counting.Count; - var decompressedvalue = zip64 ? uint.MaxValue : (uint)entry.Decompressed; - - if (originalStream.CanSeek) - { - originalStream.Position = (long)(entry.HeaderOffset + 6); + // set compression to STORED for zero byte files (no compression data) + originalStream.Position = (long)(entry.HeaderOffset + 8); + originalStream.WriteByte(0); originalStream.WriteByte(0); - - if (counting.Count == 0 && entry.Decompressed == 0) - { - // set compression to STORED for zero byte files (no compression data) - originalStream.Position = (long)(entry.HeaderOffset + 8); - originalStream.WriteByte(0); - originalStream.WriteByte(0); - } - - originalStream.Position = (long)(entry.HeaderOffset + 14); - - writer.WriteFooter(entry.Crc, compressedvalue, decompressedvalue); - - // Ideally, we should not throw from Dispose() - // We should not get here as the Write call checks the limits - if (zip64 && entry.Zip64HeaderOffset == 0) - { - throw new NotSupportedException( - "Attempted to write a stream that is larger than 4GiB without setting the zip64 option" - ); - } - - // If we have pre-allocated space for zip64 data, - // fill it out, even if it is not required - if (entry.Zip64HeaderOffset != 0) - { - originalStream.Position = (long)( - entry.HeaderOffset + entry.Zip64HeaderOffset - ); - Span intBuf = stackalloc byte[8]; - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 0x0001); - originalStream.Write(intBuf.Slice(0, 2)); - BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 8 + 8); - originalStream.Write(intBuf.Slice(0, 2)); - - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, entry.Decompressed); - originalStream.Write(intBuf); - BinaryPrimitives.WriteUInt64LittleEndian(intBuf, entry.Compressed); - originalStream.Write(intBuf); - } - - originalStream.Position = writer.streamPosition + (long)entry.Compressed; - writer.streamPosition += (long)entry.Compressed; } - else + + originalStream.Position = (long)(entry.HeaderOffset + 14); + + writer.WriteFooter(entry.Crc, compressedvalue, decompressedvalue); + + // Ideally, we should not throw from Dispose() + // We should not get here as the Write call checks the limits + if (zip64 && entry.Zip64HeaderOffset == 0) { - // We have a streaming archive, so we should add a post-data-descriptor, - // but we cannot as it does not hold the zip64 values - // Throwing an exception until the zip specification is clarified - - // Ideally, we should not throw from Dispose() - // We should not get here as the Write call checks the limits - if (zip64) - { - throw new NotSupportedException( - "Streams larger than 4GiB are not supported for non-seekable streams" - ); - } - - Span intBuf = stackalloc byte[4]; - BinaryPrimitives.WriteUInt32LittleEndian( - intBuf, - ZipHeaderFactory.POST_DATA_DESCRIPTOR + throw new NotSupportedException( + "Attempted to write a stream that is larger than 4GiB without setting the zip64 option" ); + } + + // If we have pre-allocated space for zip64 data, + // fill it out, even if it is not required + if (entry.Zip64HeaderOffset != 0) + { + originalStream.Position = (long)( + entry.HeaderOffset + entry.Zip64HeaderOffset + ); + Span intBuf = stackalloc byte[8]; + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 0x0001); + originalStream.Write(intBuf.Slice(0, 2)); + BinaryPrimitives.WriteUInt16LittleEndian(intBuf, 8 + 8); + originalStream.Write(intBuf.Slice(0, 2)); + + BinaryPrimitives.WriteUInt64LittleEndian(intBuf, entry.Decompressed); + originalStream.Write(intBuf); + BinaryPrimitives.WriteUInt64LittleEndian(intBuf, entry.Compressed); originalStream.Write(intBuf); - writer.WriteFooter(entry.Crc, compressedvalue, decompressedvalue); - writer.streamPosition += (long)entry.Compressed + 16; } - writer.entries.Add(entry); + + originalStream.Position = writer.streamPosition + (long)entry.Compressed; + writer.streamPosition += (long)entry.Compressed; } - } - - public override void Flush() - { - writeStream.Flush(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - // We check the limits first, because we can keep the archive consistent - // if we can prevent the writes from happening - if (entry.Zip64HeaderOffset == 0) + else { - // Pre-check, the counting.Count is not exact, as we do not know the size before having actually compressed it - if ( - limitsExceeded - || ((decompressed + (uint)count) > uint.MaxValue) - || (counting!.Count + (uint)count) > uint.MaxValue - ) + // We have a streaming archive, so we should add a post-data-descriptor, + // but we cannot as it does not hold the zip64 values + // Throwing an exception until the zip specification is clarified + + // Ideally, we should not throw from Dispose() + // We should not get here as the Write call checks the limits + if (zip64) { throw new NotSupportedException( - "Attempted to write a stream that is larger than 4GiB without setting the zip64 option" + "Streams larger than 4GiB are not supported for non-seekable streams" ); } - } - decompressed += (uint)count; - crc.SlurpBlock(buffer, offset, count); - writeStream.Write(buffer, offset, count); - - if (entry.Zip64HeaderOffset == 0) - { - // Post-check, this is accurate - if ((decompressed > uint.MaxValue) || counting!.Count > uint.MaxValue) - { - // We have written the data, so the archive is now broken - // Throwing the exception here, allows us to avoid - // throwing an exception in Dispose() which is discouraged - // as it can mask other errors - limitsExceeded = true; - throw new NotSupportedException( - "Attempted to write a stream that is larger than 4GiB without setting the zip64 option" - ); - } + Span intBuf = stackalloc byte[4]; + BinaryPrimitives.WriteUInt32LittleEndian( + intBuf, + ZipHeaderFactory.POST_DATA_DESCRIPTOR + ); + originalStream.Write(intBuf); + writer.WriteFooter(entry.Crc, compressedvalue, decompressedvalue); + writer.streamPosition += (long)entry.Compressed + 16; } + writer.entries.Add(entry); } } - #endregion Nested type: ZipWritingStream + public override void Flush() + { + writeStream.Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + // We check the limits first, because we can keep the archive consistent + // if we can prevent the writes from happening + if (entry.Zip64HeaderOffset == 0) + { + // Pre-check, the counting.Count is not exact, as we do not know the size before having actually compressed it + if ( + limitsExceeded + || ((decompressed + (uint)count) > uint.MaxValue) + || (counting!.Count + (uint)count) > uint.MaxValue + ) + { + throw new NotSupportedException( + "Attempted to write a stream that is larger than 4GiB without setting the zip64 option" + ); + } + } + + decompressed += (uint)count; + crc.SlurpBlock(buffer, offset, count); + writeStream.Write(buffer, offset, count); + + if (entry.Zip64HeaderOffset == 0) + { + // Post-check, this is accurate + if ((decompressed > uint.MaxValue) || counting!.Count > uint.MaxValue) + { + // We have written the data, so the archive is now broken + // Throwing the exception here, allows us to avoid + // throwing an exception in Dispose() which is discouraged + // as it can mask other errors + limitsExceeded = true; + throw new NotSupportedException( + "Attempted to write a stream that is larger than 4GiB without setting the zip64 option" + ); + } + } + } } + + #endregion Nested type: ZipWritingStream } diff --git a/src/SharpCompress/Writers/Zip/ZipWriterEntryOptions.cs b/src/SharpCompress/Writers/Zip/ZipWriterEntryOptions.cs index 0f4e8886..a718f3b5 100644 --- a/src/SharpCompress/Writers/Zip/ZipWriterEntryOptions.cs +++ b/src/SharpCompress/Writers/Zip/ZipWriterEntryOptions.cs @@ -1,28 +1,27 @@ -using System; +using System; using SharpCompress.Common; using SharpCompress.Compressors.Deflate; -namespace SharpCompress.Writers.Zip +namespace SharpCompress.Writers.Zip; + +public class ZipWriterEntryOptions { - public class ZipWriterEntryOptions - { - public CompressionType? CompressionType { get; set; } + public CompressionType? CompressionType { get; set; } - /// - /// When CompressionType.Deflate is used, this property is referenced. Defaults to CompressionLevel.Default. - /// - public CompressionLevel? DeflateCompressionLevel { get; set; } + /// + /// When CompressionType.Deflate is used, this property is referenced. Defaults to CompressionLevel.Default. + /// + public CompressionLevel? DeflateCompressionLevel { get; set; } - public string? EntryComment { get; set; } + public string? EntryComment { get; set; } - public DateTime? ModificationDateTime { get; set; } + public DateTime? ModificationDateTime { get; set; } - /// - /// Allocate an extra 20 bytes for this entry to store, - /// 64 bit length values, thus enabling streams - /// larger than 4GiB. - /// This option is not supported with non-seekable streams. - /// - public bool? EnableZip64 { get; set; } - } + /// + /// Allocate an extra 20 bytes for this entry to store, + /// 64 bit length values, thus enabling streams + /// larger than 4GiB. + /// This option is not supported with non-seekable streams. + /// + public bool? EnableZip64 { get; set; } } diff --git a/src/SharpCompress/Writers/Zip/ZipWriterOptions.cs b/src/SharpCompress/Writers/Zip/ZipWriterOptions.cs index e1758896..fc11ffbf 100644 --- a/src/SharpCompress/Writers/Zip/ZipWriterOptions.cs +++ b/src/SharpCompress/Writers/Zip/ZipWriterOptions.cs @@ -1,40 +1,38 @@ -using SharpCompress.Common; +using SharpCompress.Common; using SharpCompress.Compressors.Deflate; -namespace SharpCompress.Writers.Zip +namespace SharpCompress.Writers.Zip; + +public class ZipWriterOptions : WriterOptions { - public class ZipWriterOptions : WriterOptions + public ZipWriterOptions(CompressionType compressionType) : base(compressionType) { } + + internal ZipWriterOptions(WriterOptions options) : base(options.CompressionType) { - public ZipWriterOptions(CompressionType compressionType) : base(compressionType) { } + LeaveStreamOpen = options.LeaveStreamOpen; + ArchiveEncoding = options.ArchiveEncoding; - internal ZipWriterOptions(WriterOptions options) : base(options.CompressionType) + if (options is ZipWriterOptions writerOptions) { - LeaveStreamOpen = options.LeaveStreamOpen; - ArchiveEncoding = options.ArchiveEncoding; - - var writerOptions = options as ZipWriterOptions; - if (writerOptions != null) - { - UseZip64 = writerOptions.UseZip64; - DeflateCompressionLevel = writerOptions.DeflateCompressionLevel; - ArchiveComment = writerOptions.ArchiveComment; - } + UseZip64 = writerOptions.UseZip64; + DeflateCompressionLevel = writerOptions.DeflateCompressionLevel; + ArchiveComment = writerOptions.ArchiveComment; } - - /// - /// When CompressionType.Deflate is used, this property is referenced. Defaults to CompressionLevel.Default. - /// - public CompressionLevel DeflateCompressionLevel { get; set; } = CompressionLevel.Default; - - public string? ArchiveComment { get; set; } - - /// - /// Sets a value indicating if zip64 support is enabled. - /// If this is not set, individual stream lengths cannot exceed 4 GiB. - /// This option is not supported for non-seekable streams. - /// Archives larger than 4GiB are supported as long as all streams - /// are less than 4GiB in length. - /// - public bool UseZip64 { get; set; } } + + /// + /// When CompressionType.Deflate is used, this property is referenced. Defaults to CompressionLevel.Default. + /// + public CompressionLevel DeflateCompressionLevel { get; set; } = CompressionLevel.Default; + + public string? ArchiveComment { get; set; } + + /// + /// Sets a value indicating if zip64 support is enabled. + /// If this is not set, individual stream lengths cannot exceed 4 GiB. + /// This option is not supported for non-seekable streams. + /// Archives larger than 4GiB are supported as long as all streams + /// are less than 4GiB in length. + /// + public bool UseZip64 { get; set; } } diff --git a/tests/SharpCompress.Test/ADCTest.cs b/tests/SharpCompress.Test/ADCTest.cs index 4be158d1..ae131ceb 100644 --- a/tests/SharpCompress.Test/ADCTest.cs +++ b/tests/SharpCompress.Test/ADCTest.cs @@ -1,4 +1,4 @@ -// +// // ADC.cs // // Author: @@ -30,155 +30,112 @@ using SharpCompress.Compressors.Deflate; using SharpCompress.Crypto; using Xunit; -namespace SharpCompress.Test +namespace SharpCompress.Test; + +public class ADCTest : TestBase { - public class ADCTest : TestBase + [Fact] + public void TestBuffer() { - [Fact] - public void TestBuffer() + using var decFs = File.OpenRead( + Path.Combine(TEST_ARCHIVES_PATH, "adc_decompressed.bin") + ); + var decompressed = new byte[decFs.Length]; + decFs.Read(decompressed, 0, decompressed.Length); + + using var cmpFs = File.OpenRead( + Path.Combine(TEST_ARCHIVES_PATH, "adc_compressed.bin") + ); + var compressed = new byte[cmpFs.Length]; + cmpFs.Read(compressed, 0, compressed.Length); + + ADCBase.Decompress(compressed, out var test); + + Assert.Equal(decompressed, test); + } + + [Fact] + public void TestBaseStream() + { + using var decFs = File.OpenRead( + Path.Combine(TEST_ARCHIVES_PATH, "adc_decompressed.bin") + ); + var decompressed = new byte[decFs.Length]; + decFs.Read(decompressed, 0, decompressed.Length); + + using var cmpFs = File.OpenRead( + Path.Combine(TEST_ARCHIVES_PATH, "adc_compressed.bin") + ); + + ADCBase.Decompress(cmpFs, out var test); + + Assert.Equal(decompressed, test); + } + + [Fact] + public void TestADCStreamWholeChunk() + { + using var decFs = File.OpenRead( + Path.Combine(TEST_ARCHIVES_PATH, "adc_decompressed.bin") + ); + var decompressed = new byte[decFs.Length]; + decFs.Read(decompressed, 0, decompressed.Length); + + using var cmpFs = File.OpenRead( + Path.Combine(TEST_ARCHIVES_PATH, "adc_compressed.bin") + ); + using var decStream = new ADCStream(cmpFs, CompressionMode.Decompress); + var test = new byte[262144]; + + decStream.Read(test, 0, test.Length); + + Assert.Equal(decompressed, test); + } + + [Fact] + public void TestADCStream() + { + using var decFs = File.OpenRead( + Path.Combine(TEST_ARCHIVES_PATH, "adc_decompressed.bin") + ); + var decompressed = new byte[decFs.Length]; + decFs.Read(decompressed, 0, decompressed.Length); + + using var cmpFs = File.OpenRead( + Path.Combine(TEST_ARCHIVES_PATH, "adc_compressed.bin") + ); + using var decStream = new ADCStream(cmpFs, CompressionMode.Decompress); + using var decMs = new MemoryStream(); + var test = new byte[512]; + var count = 0; + + do { - using ( - FileStream decFs = File.OpenRead( - Path.Combine(TEST_ARCHIVES_PATH, "adc_decompressed.bin") - ) - ) - { - byte[] decompressed = new byte[decFs.Length]; - decFs.Read(decompressed, 0, decompressed.Length); + count = decStream.Read(test, 0, test.Length); + decMs.Write(test, 0, count); + } while (count > 0); - using ( - FileStream cmpFs = File.OpenRead( - Path.Combine(TEST_ARCHIVES_PATH, "adc_compressed.bin") - ) - ) - { - byte[] compressed = new byte[cmpFs.Length]; - cmpFs.Read(compressed, 0, compressed.Length); - byte[]? test; + Assert.Equal(decompressed, decMs.ToArray()); + } - ADCBase.Decompress(compressed, out test); + [Fact] + public void TestCrc32Stream() + { + using var decFs = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")); + var crc32 = new CRC32().GetCrc32(decFs); + decFs.Seek(0, SeekOrigin.Begin); - Assert.Equal(decompressed, test); - } - } - } + var memory = new MemoryStream(); + var crcStream = new Crc32Stream(memory, 0xEDB88320, 0xFFFFFFFF); + decFs.CopyTo(crcStream); - [Fact] - public void TestBaseStream() - { - using ( - FileStream decFs = File.OpenRead( - Path.Combine(TEST_ARCHIVES_PATH, "adc_decompressed.bin") - ) - ) - { - byte[] decompressed = new byte[decFs.Length]; - decFs.Read(decompressed, 0, decompressed.Length); + decFs.Seek(0, SeekOrigin.Begin); - using ( - FileStream cmpFs = File.OpenRead( - Path.Combine(TEST_ARCHIVES_PATH, "adc_compressed.bin") - ) - ) - { - byte[]? test; + var crc32a = crcStream.Crc; - ADCBase.Decompress(cmpFs, out test); + var crc32b = Crc32Stream.Compute(memory.ToArray()); - Assert.Equal(decompressed, test); - } - } - } - - [Fact] - public void TestADCStreamWholeChunk() - { - using ( - FileStream decFs = File.OpenRead( - Path.Combine(TEST_ARCHIVES_PATH, "adc_decompressed.bin") - ) - ) - { - byte[] decompressed = new byte[decFs.Length]; - decFs.Read(decompressed, 0, decompressed.Length); - - using ( - FileStream cmpFs = File.OpenRead( - Path.Combine(TEST_ARCHIVES_PATH, "adc_compressed.bin") - ) - ) - { - using (ADCStream decStream = new ADCStream(cmpFs, CompressionMode.Decompress)) - { - byte[] test = new byte[262144]; - - decStream.Read(test, 0, test.Length); - - Assert.Equal(decompressed, test); - } - } - } - } - - [Fact] - public void TestADCStream() - { - using ( - FileStream decFs = File.OpenRead( - Path.Combine(TEST_ARCHIVES_PATH, "adc_decompressed.bin") - ) - ) - { - byte[] decompressed = new byte[decFs.Length]; - decFs.Read(decompressed, 0, decompressed.Length); - - using ( - FileStream cmpFs = File.OpenRead( - Path.Combine(TEST_ARCHIVES_PATH, "adc_compressed.bin") - ) - ) - { - using (ADCStream decStream = new ADCStream(cmpFs, CompressionMode.Decompress)) - { - using (MemoryStream decMs = new MemoryStream()) - { - byte[] test = new byte[512]; - int count = 0; - - do - { - count = decStream.Read(test, 0, test.Length); - decMs.Write(test, 0, count); - } while (count > 0); - - Assert.Equal(decompressed, decMs.ToArray()); - } - } - } - } - } - - [Fact] - public void TestCrc32Stream() - { - using (FileStream decFs = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"))) - { - var crc32 = new CRC32().GetCrc32(decFs); - decFs.Seek(0, SeekOrigin.Begin); - - var memory = new MemoryStream(); - var crcStream = new Crc32Stream(memory, 0xEDB88320, 0xFFFFFFFF); - decFs.CopyTo(crcStream); - - decFs.Seek(0, SeekOrigin.Begin); - - var crc32a = crcStream.Crc; - - var crc32b = Crc32Stream.Compute(memory.ToArray()); - - Assert.Equal(crc32, crc32a); - Assert.Equal(crc32, crc32b); - } - } + Assert.Equal(crc32, crc32a); + Assert.Equal(crc32, crc32b); } } diff --git a/tests/SharpCompress.Test/ArchiveTests.cs b/tests/SharpCompress.Test/ArchiveTests.cs index dcf033ca..8b9a4c2d 100644 --- a/tests/SharpCompress.Test/ArchiveTests.cs +++ b/tests/SharpCompress.Test/ArchiveTests.cs @@ -8,148 +8,103 @@ using SharpCompress.IO; using SharpCompress.Readers; using Xunit; -namespace SharpCompress.Test +namespace SharpCompress.Test; + +public class ArchiveTests : ReaderTests { - public class ArchiveTests : ReaderTests + protected void ArchiveGetParts(IEnumerable testArchives) { - protected void ArchiveGetParts(IEnumerable testArchives) + var arcs = testArchives.Select(a => Path.Combine(TEST_ARCHIVES_PATH, a)).ToArray(); + var found = ArchiveFactory.GetFileParts(arcs[0]).ToArray(); + Assert.Equal(arcs.Length, found.Length); + for (var i = 0; i < arcs.Length; i++) { - string[] arcs = testArchives.Select(a => Path.Combine(TEST_ARCHIVES_PATH, a)).ToArray(); - string[] found = ArchiveFactory.GetFileParts(arcs[0]).ToArray(); - Assert.Equal(arcs.Length, found.Length); - for (int i = 0; i < arcs.Length; i++) - Assert.Equal(arcs[i], found[i]); + Assert.Equal(arcs[i], found[i]); } + } - protected void ArchiveStreamReadExtractAll(string testArchive, CompressionType compression) - { - testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); - ArchiveStreamReadExtractAll(new[] { testArchive }, compression); - } + protected void ArchiveStreamReadExtractAll(string testArchive, CompressionType compression) + { + testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); + ArchiveStreamReadExtractAll(new[] { testArchive }, compression); + } - protected void ArchiveStreamReadExtractAll( - IEnumerable testArchives, - CompressionType compression - ) + protected void ArchiveStreamReadExtractAll( + IEnumerable testArchives, + CompressionType compression + ) + { + foreach (var path in testArchives) { - foreach (var path in testArchives) + using (var stream = NonDisposingStream.Create(File.OpenRead(path), true)) { - using (var stream = NonDisposingStream.Create(File.OpenRead(path), true)) + try { - try + using var archive = ArchiveFactory.Open(stream); + Assert.True(archive.IsSolid); + using (var reader = archive.ExtractAllEntries()) { - using (var archive = ArchiveFactory.Open(stream)) - { - Assert.True(archive.IsSolid); - using (var reader = archive.ExtractAllEntries()) - { - UseReader(reader, compression); - } - VerifyFiles(); - - if (archive.Entries.First().CompressionType == CompressionType.Rar) - { - stream.ThrowOnDispose = false; - return; - } - foreach ( - var entry in archive.Entries.Where(entry => !entry.IsDirectory) - ) - { - entry.WriteToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions - { - ExtractFullPath = true, - Overwrite = true - } - ); - } - stream.ThrowOnDispose = false; - } + UseReader(reader, compression); } - catch (Exception) + VerifyFiles(); + + if (archive.Entries.First().CompressionType == CompressionType.Rar) { - // Otherwise this will hide the original exception. stream.ThrowOnDispose = false; - throw; + return; } - } - VerifyFiles(); - } - } - - protected void ArchiveStreamRead(string testArchive, ReaderOptions? readerOptions = null) - { - testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); - ArchiveStreamRead(readerOptions, testArchive); - } - - protected void ArchiveStreamRead( - ReaderOptions? readerOptions = null, - params string[] testArchives - ) - { - ArchiveStreamRead( - readerOptions, - testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x)) - ); - } - - protected void ArchiveStreamRead( - ReaderOptions? readerOptions, - IEnumerable testArchives - ) - { - foreach (var path in testArchives) - { - using (var stream = NonDisposingStream.Create(File.OpenRead(path), true)) - using (var archive = ArchiveFactory.Open(stream, readerOptions)) - { - try + foreach ( + var entry in archive.Entries.Where(entry => !entry.IsDirectory) + ) { - foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) - { - entry.WriteToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - catch (IndexOutOfRangeException) - { - //SevenZipArchive_BZip2_Split test needs this - stream.ThrowOnDispose = false; - throw; + entry.WriteToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions + { + ExtractFullPath = true, + Overwrite = true + } + ); } stream.ThrowOnDispose = false; } - VerifyFiles(); + catch (Exception) + { + // Otherwise this will hide the original exception. + stream.ThrowOnDispose = false; + throw; + } } + VerifyFiles(); } + } - protected void ArchiveStreamMultiRead( - ReaderOptions? readerOptions = null, - params string[] testArchives - ) - { - ArchiveStreamMultiRead( - readerOptions, - testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x)) - ); - } + protected void ArchiveStreamRead(string testArchive, ReaderOptions? readerOptions = null) + { + testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); + ArchiveStreamRead(readerOptions, testArchive); + } - protected void ArchiveStreamMultiRead( - ReaderOptions? readerOptions, - IEnumerable testArchives - ) + protected void ArchiveStreamRead( + ReaderOptions? readerOptions = null, + params string[] testArchives + ) + { + ArchiveStreamRead( + readerOptions, + testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x)) + ); + } + + protected void ArchiveStreamRead( + ReaderOptions? readerOptions, + IEnumerable testArchives + ) + { + foreach (var path in testArchives) { - using ( - var archive = ArchiveFactory.Open( - testArchives.Select(a => new FileInfo(a)), - readerOptions - ) - ) + using (var stream = NonDisposingStream.Create(File.OpenRead(path), true)) + using (var archive = ArchiveFactory.Open(stream, readerOptions)) { try { @@ -163,110 +118,40 @@ namespace SharpCompress.Test } catch (IndexOutOfRangeException) { + //SevenZipArchive_BZip2_Split test needs this + stream.ThrowOnDispose = false; throw; } + stream.ThrowOnDispose = false; } VerifyFiles(); } + } - protected void ArchiveOpenStreamRead( - ReaderOptions? readerOptions = null, - params string[] testArchives - ) - { - ArchiveOpenStreamRead( - readerOptions, - testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x)) - ); - } + protected void ArchiveStreamMultiRead( + ReaderOptions? readerOptions = null, + params string[] testArchives + ) + { + ArchiveStreamMultiRead( + readerOptions, + testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x)) + ); + } - protected void ArchiveOpenStreamRead( - ReaderOptions? readerOptions, - IEnumerable testArchives - ) - { - using ( - var archive = ArchiveFactory.Open( - testArchives.Select(f => new FileInfo(f)), - readerOptions - ) + protected void ArchiveStreamMultiRead( + ReaderOptions? readerOptions, + IEnumerable testArchives + ) + { + using ( + var archive = ArchiveFactory.Open( + testArchives.Select(a => new FileInfo(a)), + readerOptions ) - { - try - { - foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) - { - entry.WriteToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - catch (IndexOutOfRangeException) - { - throw; - } - } - VerifyFiles(); - } - - protected void ArchiveOpenEntryVolumeIndexTest( - int[][] results, - ReaderOptions? readerOptions = null, - params string[] testArchives ) { - ArchiveOpenEntryVolumeIndexTest( - results, - readerOptions, - testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x)) - ); - } - - protected void ArchiveOpenEntryVolumeIndexTest( - int[][] results, - ReaderOptions? readerOptions, - IEnumerable testArchives - ) - { - string[] src = testArchives.ToArray(); - using ( - var archive = ArchiveFactory.Open( - testArchives.Select(f => new FileInfo(f)), - readerOptions - ) - ) - { - try - { - int idx = 0; - foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) - { - Assert.Equal(entry.VolumeIndexFirst, results[idx][0]); - Assert.Equal(entry.VolumeIndexLast, results[idx][1]); - Assert.Equal( - src[entry.VolumeIndexFirst], - archive.Volumes.First(a => a.Index == entry.VolumeIndexFirst).FileName - ); - Assert.Equal( - src[entry.VolumeIndexLast], - archive.Volumes.First(a => a.Index == entry.VolumeIndexLast).FileName - ); - - idx++; - } - } - catch (IndexOutOfRangeException) - { - throw; - } - } - } - - protected void ArchiveFileRead(string testArchive, ReaderOptions? readerOptions = null) - { - testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); - using (var archive = ArchiveFactory.Open(testArchive, readerOptions)) + try { foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { @@ -276,32 +161,142 @@ namespace SharpCompress.Test ); } } - VerifyFiles(); + catch (IndexOutOfRangeException) + { + throw; + } } + VerifyFiles(); + } - /// - /// Demonstrate the ExtractionOptions.PreserveFileTime and ExtractionOptions.PreserveAttributes extract options - /// - protected void ArchiveFileReadEx(string testArchive) + protected void ArchiveOpenStreamRead( + ReaderOptions? readerOptions = null, + params string[] testArchives + ) + { + ArchiveOpenStreamRead( + readerOptions, + testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x)) + ); + } + + protected void ArchiveOpenStreamRead( + ReaderOptions? readerOptions, + IEnumerable testArchives + ) + { + using ( + var archive = ArchiveFactory.Open( + testArchives.Select(f => new FileInfo(f)), + readerOptions + ) + ) { - testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); - using (var archive = ArchiveFactory.Open(testArchive)) + try { foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { entry.WriteToDirectory( SCRATCH_FILES_PATH, - new ExtractionOptions() - { - ExtractFullPath = true, - Overwrite = true, - PreserveAttributes = true, - PreserveFileTime = true - } + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } ); } } - VerifyFilesEx(); + catch (IndexOutOfRangeException) + { + throw; + } + } + VerifyFiles(); + } + + protected void ArchiveOpenEntryVolumeIndexTest( + int[][] results, + ReaderOptions? readerOptions = null, + params string[] testArchives + ) + { + ArchiveOpenEntryVolumeIndexTest( + results, + readerOptions, + testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x)) + ); + } + + protected void ArchiveOpenEntryVolumeIndexTest( + int[][] results, + ReaderOptions? readerOptions, + IEnumerable testArchives + ) + { + var src = testArchives.ToArray(); + using var archive = ArchiveFactory.Open( + testArchives.Select(f => new FileInfo(f)), + readerOptions + ); + try + { + var idx = 0; + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) + { + Assert.Equal(entry.VolumeIndexFirst, results[idx][0]); + Assert.Equal(entry.VolumeIndexLast, results[idx][1]); + Assert.Equal( + src[entry.VolumeIndexFirst], + archive.Volumes.First(a => a.Index == entry.VolumeIndexFirst).FileName + ); + Assert.Equal( + src[entry.VolumeIndexLast], + archive.Volumes.First(a => a.Index == entry.VolumeIndexLast).FileName + ); + + idx++; + } + } + catch (IndexOutOfRangeException) + { + throw; } } + + protected void ArchiveFileRead(string testArchive, ReaderOptions? readerOptions = null) + { + testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); + using (var archive = ArchiveFactory.Open(testArchive, readerOptions)) + { + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) + { + entry.WriteToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); + } + } + VerifyFiles(); + } + + /// + /// Demonstrate the ExtractionOptions.PreserveFileTime and ExtractionOptions.PreserveAttributes extract options + /// + protected void ArchiveFileReadEx(string testArchive) + { + testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); + using (var archive = ArchiveFactory.Open(testArchive)) + { + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) + { + entry.WriteToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() + { + ExtractFullPath = true, + Overwrite = true, + PreserveAttributes = true, + PreserveFileTime = true + } + ); + } + } + VerifyFilesEx(); + } } diff --git a/tests/SharpCompress.Test/Filters/BranchExecTests.cs b/tests/SharpCompress.Test/Filters/BranchExecTests.cs index 138012c0..e979d537 100644 --- a/tests/SharpCompress.Test/Filters/BranchExecTests.cs +++ b/tests/SharpCompress.Test/Filters/BranchExecTests.cs @@ -8,1240 +8,1239 @@ using SharpCompress.Compressors.Filters; using Xunit; -namespace SharpCompress.Test.Filters +namespace SharpCompress.Test.Filters; + +public class BranchExecTests { - public class BranchExecTests + private static byte[] x86resultData { get; } = + new byte[] + { + 0x12, + 0x00, + 0x00, + 0x00, + 0x02, + 0x0B, + 0x00, + 0x00, + 0xE8, + 0xBD, + 0x00, + 0x00, + 0x00, + 0x07, + 0x00, + 0x00, + 0x12, + 0x00, + 0x00, + 0x00, + 0x6D, + 0x01, + 0x00, + 0x00, + 0xF0, + 0xCA, + 0x00, + 0x00, + 0x1C, + 0x00, + 0x00, + 0x00, + 0x12, + 0x00, + 0x00, + 0x00, + 0xBC, + 0x09, + 0x00, + 0x00, + 0x14, + 0xC2, + 0x00, + 0x00, + 0xE0, + 0x01, + 0x00, + 0x00, + 0x12, + 0x00, + 0x00, + 0x00, + 0x98, + 0x0B, + 0x00, + 0x00, + 0x60, + 0x75, + 0x0A, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x10, + 0x00, + 0xF1, + 0xFF, + 0x42, + 0x01, + 0x00, + 0x00, + 0x08, + 0xC8, + 0x00, + 0x00, + 0x1C, + 0x00, + }; + + private static byte[] x86Data { get; } = + new byte[] + { + 0x12, + 0x00, + 0x00, + 0x00, + 0x02, + 0x0B, + 0x00, + 0x00, + 0xE8, + 0xCA, + 0x20, + 0x00, + 0x00, + 0x07, + 0x00, + 0x00, + 0x12, + 0x00, + 0x00, + 0x00, + 0x6D, + 0x01, + 0x00, + 0x00, + 0xF0, + 0xCA, + 0x00, + 0x00, + 0x1C, + 0x00, + 0x00, + 0x00, + 0x12, + 0x00, + 0x00, + 0x00, + 0xBC, + 0x09, + 0x00, + 0x00, + 0x14, + 0xC2, + 0x00, + 0x00, + 0xE0, + 0x01, + 0x00, + 0x00, + 0x12, + 0x00, + 0x00, + 0x00, + 0x98, + 0x0B, + 0x00, + 0x00, + 0x60, + 0x75, + 0x0A, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x10, + 0x00, + 0xF1, + 0xFF, + 0x42, + 0x01, + 0x00, + 0x00, + 0x08, + 0xC8, + 0x00, + 0x00, + 0x1C, + 0x00, + }; + + private static byte[] ppcResultData { get; } = + new byte[] + { + 0xF8, + 0x6B, + 0x2E, + 0x8C, + 0x95, + 0xC5, + 0x4B, + 0x1B, + 0x94, + 0x78, + 0x9E, + 0x7C, + 0xBD, + 0x8B, + 0xA8, + 0xAF, + 0x31, + 0x20, + 0xFE, + 0x0F, + 0xB3, + 0x15, + 0x9A, + 0x7C, + 0xD5, + 0x5C, + 0xC2, + 0xC0, + 0xEC, + 0xE9, + 0x43, + 0x2B, + 0xD0, + 0x9F, + 0x2C, + 0xFC, + 0xB8, + 0x2B, + 0x6B, + 0x15, + 0xCD, + 0x3F, + 0x0C, + 0xAF, + 0x8F, + 0x68, + 0xB0, + 0x6E, + 0x6B, + 0x30, + 0x2E, + 0x8C, + 0x3F, + 0x7E, + 0x96, + 0x7C, + 0x93, + 0xB2, + 0xA4, + 0x0E, + 0x43, + 0xEA, + 0x20, + 0x10, + 0x38, + 0x6D, + 0x37, + 0xF8, + 0x87, + 0xFE, + 0xA9, + 0x63, + 0x75, + 0xF5, + 0x56, + 0x34, + 0x4A, + 0xE3, + 0xCF, + 0x89, + 0x18, + 0x08, + 0xC2, + 0x76, + 0x74, + 0x12, + 0xEC, + 0xA7, + 0x6D, + 0xC2, + 0xB7, + 0x1B, + 0x7A, + 0xB2, + 0xD4, + 0xED + }; + + private static byte[] ppcData { get; } = + new byte[] + { + 0xF8, + 0x6B, + 0x2E, + 0x8C, + 0x95, + 0xC5, + 0x4B, + 0x1B, + 0x94, + 0x78, + 0x9E, + 0x7C, + 0xBD, + 0x8B, + 0xA8, + 0xAF, + 0x31, + 0x20, + 0xFE, + 0x0F, + 0xB3, + 0x15, + 0x9A, + 0x7C, + 0xD5, + 0x5C, + 0xC2, + 0xC0, + 0xEC, + 0xE9, + 0x43, + 0x2B, + 0xD0, + 0x9F, + 0x2C, + 0xFC, + 0xB8, + 0x2B, + 0x6B, + 0x15, + 0xCD, + 0x3F, + 0x0C, + 0xAF, + 0x8F, + 0x68, + 0xB0, + 0x6E, + 0x6B, + 0x30, + 0x2E, + 0x8C, + 0x3F, + 0x7E, + 0x96, + 0x7C, + 0x93, + 0xB2, + 0xA4, + 0x0E, + 0x43, + 0xEA, + 0x20, + 0x10, + 0x38, + 0x6D, + 0x37, + 0xF8, + 0x87, + 0xFE, + 0xA9, + 0x63, + 0x75, + 0xF5, + 0x56, + 0x34, + 0x4A, + 0xE3, + 0xD6, + 0x75, + 0x18, + 0x08, + 0xC2, + 0x76, + 0x74, + 0x12, + 0xEC, + 0xA7, + 0x6D, + 0xC2, + 0xB7, + 0x1B, + 0x7A, + 0xB2, + 0xD4, + 0xED + }; + + private static byte[] armResultData { get; } = + new byte[] + { + 0x7C, + 0xFC, + 0x0A, + 0x00, + 0x16, + 0x42, + 0x01, + 0x00, + 0x80, + 0xFC, + 0x0A, + 0x00, + 0x16, + 0xB8, + 0x00, + 0x00, + 0x84, + 0xFC, + 0x0A, + 0x00, + 0x16, + 0x3A, + 0x00, + 0x00, + 0x04, + 0xE0, + 0x2D, + 0xE5, + 0x04, + 0xD0, + 0x4D, + 0xE2, + 0x0E, + 0x04, + 0x00, + 0xEB, + 0x04, + 0xD0, + 0x8D, + 0xE2, + 0x04, + 0xE0, + 0x9D, + 0xE4, + 0x1E, + 0xFF, + 0x2F, + 0xE1, + 0x04, + 0xE0, + 0x2D, + 0xE5, + 0x04, + 0xE0, + 0x9F, + 0xE5, + 0x0E, + 0xE0, + 0x8F, + 0xE0, + 0x08, + 0xF0, + 0xBE, + 0xE5, + 0xF0, + 0x3A, + 0x0A, + 0x00, + 0x00, + 0xC6, + 0x8F, + 0xE2, + 0xA3, + 0xCA, + 0x8C, + 0xE2, + 0xF0, + 0xFA, + 0xBC, + 0xE5, + 0x00, + 0xC6, + 0x8F, + 0xE2, + 0xA3, + 0xCA, + 0x8C, + 0xE2, + 0xE8, + 0xFA, + 0xBC, + 0xE5, + 0x00, + 0xC6, + 0x8F, + 0xE2 + }; + + private static byte[] armData { get; } = + new byte[] + { + 0x7C, + 0xFC, + 0x0A, + 0x00, + 0x16, + 0x42, + 0x01, + 0x00, + 0x80, + 0xFC, + 0x0A, + 0x00, + 0x16, + 0xB8, + 0x00, + 0x00, + 0x84, + 0xFC, + 0x0A, + 0x00, + 0x16, + 0x3A, + 0x00, + 0x00, + 0x04, + 0xE0, + 0x2D, + 0xE5, + 0x04, + 0xD0, + 0x4D, + 0xE2, + 0x18, + 0x13, + 0x00, + 0xEB, + 0x04, + 0xD0, + 0x8D, + 0xE2, + 0x04, + 0xE0, + 0x9D, + 0xE4, + 0x1E, + 0xFF, + 0x2F, + 0xE1, + 0x04, + 0xE0, + 0x2D, + 0xE5, + 0x04, + 0xE0, + 0x9F, + 0xE5, + 0x0E, + 0xE0, + 0x8F, + 0xE0, + 0x08, + 0xF0, + 0xBE, + 0xE5, + 0xF0, + 0x3A, + 0x0A, + 0x00, + 0x00, + 0xC6, + 0x8F, + 0xE2, + 0xA3, + 0xCA, + 0x8C, + 0xE2, + 0xF0, + 0xFA, + 0xBC, + 0xE5, + 0x00, + 0xC6, + 0x8F, + 0xE2, + 0xA3, + 0xCA, + 0x8C, + 0xE2, + 0xE8, + 0xFA, + 0xBC, + 0xE5, + 0x00, + 0xC6, + 0x8F, + 0xE2 + }; + + private static byte[] armtResultData { get; } = + new byte[] + { + 0x95, + 0x23, + 0xB6, + 0xB1, + 0xBE, + 0x60, + 0x79, + 0xF0, + 0xF6, + 0x01, + 0xD9, + 0x7F, + 0x2E, + 0x03, + 0x31, + 0x1C, + 0xFD, + 0xD3, + 0x40, + 0x0F, + 0x21, + 0x3C, + 0x06, + 0x97, + 0xE5, + 0xC3, + 0x57, + 0x11, + 0x76, + 0x6F, + 0xE3, + 0x70, + 0xED, + 0x49, + 0xCB, + 0xB5, + 0xC9, + 0x42, + 0x59, + 0x10, + 0x2F, + 0xBD, + 0xAE, + 0xB1, + 0x40, + 0x4D, + 0x9D, + 0x7C, + 0xE9, + 0xFC, + 0x48, + 0x3E, + 0xBC, + 0x7F, + 0x0B, + 0x23, + 0xB0, + 0x8A, + 0x4D, + 0x02, + 0x39, + 0xC4, + 0xFB, + 0x66, + 0x83, + 0x7F, + 0xA7, + 0xBD, + 0x12, + 0xAC, + 0xED, + 0x31, + 0x34, + 0x93, + 0x4D, + 0x8D, + 0xD7, + 0x94, + 0x93, + 0x1C, + 0x0A, + 0x50, + 0x54, + 0x4B, + 0x03, + 0x55, + 0x27, + 0xFE, + 0xCE, + 0x29, + 0x66, + 0x52, + 0x81, + 0xAE, + 0x69, + 0xA0, + 0x69, + 0xF2, + 0x3D, + 0xFF, + 0xA1, + 0x8A, + 0x5D, + 0x61, + 0x7D, + 0xC5, + 0x94, + 0x0A, + 0x7D, + 0xED, + 0x11, + 0x0F + }; + + private static byte[] armtData { get; } = + new byte[] + { + 0x95, + 0x23, + 0xB6, + 0xB1, + 0xBE, + 0x60, + 0x79, + 0xF0, + 0xF6, + 0x01, + 0xD9, + 0x7F, + 0x2E, + 0x03, + 0x31, + 0x1C, + 0xFD, + 0xD3, + 0x40, + 0x0F, + 0x21, + 0x3C, + 0x06, + 0x97, + 0xE5, + 0xC3, + 0x57, + 0x11, + 0x76, + 0x6F, + 0xE3, + 0x70, + 0xED, + 0x49, + 0xCB, + 0xB5, + 0xC9, + 0x42, + 0x59, + 0x10, + 0x2F, + 0xBD, + 0xAE, + 0xB1, + 0x40, + 0x4D, + 0x9D, + 0x7C, + 0xE9, + 0xFC, + 0x48, + 0x3E, + 0xBC, + 0x7F, + 0x0B, + 0x23, + 0xB0, + 0x8A, + 0x4D, + 0x02, + 0x39, + 0xC4, + 0xFB, + 0x66, + 0x83, + 0x7F, + 0xA7, + 0xBD, + 0x12, + 0xAC, + 0xED, + 0x31, + 0x34, + 0x93, + 0x4D, + 0x8D, + 0xD7, + 0x94, + 0x93, + 0x1C, + 0x0A, + 0x50, + 0x54, + 0x4B, + 0x03, + 0x55, + 0x27, + 0xFE, + 0xCE, + 0x29, + 0x66, + 0x52, + 0x81, + 0xAE, + 0x69, + 0xA0, + 0x6A, + 0xF2, + 0x6F, + 0xFC, + 0xA1, + 0x8A, + 0x5D, + 0x61, + 0x7D, + 0xC5, + 0x94, + 0x0A, + 0x7D, + 0xED, + 0x11, + 0x0F + }; + + private static byte[] ia64ResultData { get; } = + new byte[] + { + 0x4D, + 0xF8, + 0xF2, + 0x0D, + 0x06, + 0x2F, + 0x74, + 0x0F, + 0xF0, + 0x91, + 0x06, + 0x0B, + 0x19, + 0x22, + 0x91, + 0x5A, + 0x66, + 0x56, + 0xA7, + 0x15, + 0x77, + 0x1E, + 0x2F, + 0xA3, + 0xE4, + 0xDE, + 0x93, + 0x1C, + 0xD5, + 0xCE, + 0x6E, + 0x45, + 0x36, + 0x15, + 0x15, + 0x65, + 0x4E, + 0xC5, + 0xA3, + 0x8C, + 0x5A, + 0x8B, + 0x8A, + 0x1C, + 0x12, + 0x5B, + 0x39, + 0x1F, + 0xA0, + 0xF2, + 0x93, + 0x7C, + 0x7F, + 0x5D, + 0xD9, + 0x30, + 0x1F, + 0xF6, + 0x5C, + 0x10, + 0x62, + 0x3E, + 0xB4, + 0x64, + 0x56, + 0x48, + 0xB2, + 0x20, + 0x39, + 0xE8, + 0x44, + 0x10, + 0x87, + 0x9E, + 0x2C, + 0xFC, + 0x29, + 0x0E, + 0x20, + 0x76, + 0xCE, + 0xDA, + 0x93, + 0x1C, + 0xED, + 0x54, + 0x0D, + 0xAF, + 0xEC, + 0xDE, + 0x93, + 0x1C, + 0x2B, + 0x72, + 0xD5, + 0x0D + }; + + private static byte[] ia64Data { get; } = + new byte[] + { + 0x4D, + 0xF8, + 0xF2, + 0x0D, + 0x06, + 0x2F, + 0x74, + 0x0F, + 0xF0, + 0x91, + 0x06, + 0x0B, + 0x19, + 0x22, + 0x91, + 0x5A, + 0x66, + 0x56, + 0xA7, + 0x15, + 0x77, + 0x1E, + 0x2F, + 0xA3, + 0xE4, + 0xDE, + 0x93, + 0x1C, + 0xD5, + 0xCE, + 0x6E, + 0x45, + 0x36, + 0x15, + 0x15, + 0x65, + 0x4E, + 0xC5, + 0xA3, + 0x8C, + 0x5A, + 0x8B, + 0x8A, + 0x1C, + 0x12, + 0x5B, + 0x39, + 0x1F, + 0xA0, + 0xF2, + 0x93, + 0x7C, + 0x7F, + 0x5D, + 0xD9, + 0x30, + 0x1F, + 0xF6, + 0x5C, + 0x10, + 0x62, + 0x3E, + 0xB4, + 0x64, + 0x56, + 0x48, + 0xB2, + 0x20, + 0x39, + 0xE8, + 0x44, + 0x80, + 0x8C, + 0x9E, + 0x2C, + 0xFC, + 0x29, + 0x0E, + 0x20, + 0x76, + 0xCE, + 0xDA, + 0x93, + 0x1C, + 0xED, + 0x54, + 0x0D, + 0xAF, + 0xEC, + 0xDE, + 0x93, + 0x1C, + 0x2B, + 0x72, + 0xD5, + 0x0D + }; + + private static byte[] sparcResultData { get; } = + new byte[] + { + 0x78, + 0x2E, + 0x73, + 0x6F, + 0x2E, + 0x33, + 0x00, + 0x00, + 0x07, + 0x01, + 0x00, + 0x00, + 0x09, + 0x00, + 0x00, + 0x00, + 0x40, + 0x00, + 0x00, + 0x00, + 0x0B, + 0x00, + 0x00, + 0x00, + 0xA2, + 0x0D, + 0x10, + 0x12, + 0x30, + 0x03, + 0xC9, + 0x37, + 0x40, + 0x1A, + 0x85, + 0xD9, + 0x44, + 0x34, + 0x40, + 0x32, + 0x85, + 0xA0, + 0x30, + 0x40, + 0x00, + 0x70, + 0x00, + 0x40, + 0x84, + 0x80, + 0x04, + 0x00, + 0xE4, + 0xAC, + 0x07, + 0x04, + 0x21, + 0x44, + 0x02, + 0x20, + 0x10, + 0x00, + 0x40, + 0xC2, + 0x89, + 0x98, + 0x85, + 0x00, + 0x58, + 0x6A, + 0x41, + 0x8E, + 0x18, + 0xA1, + 0x91, + 0x00, + 0x10, + 0x00, + }; + + private static byte[] sparcData { get; } = + new byte[] + { + 0x78, + 0x2E, + 0x73, + 0x6F, + 0x2E, + 0x33, + 0x00, + 0x00, + 0x07, + 0x01, + 0x00, + 0x00, + 0x09, + 0x00, + 0x00, + 0x00, + 0x40, + 0x00, + 0x00, + 0x44, + 0x0B, + 0x00, + 0x00, + 0x00, + 0xA2, + 0x0D, + 0x10, + 0x12, + 0x30, + 0x03, + 0xC9, + 0x37, + 0x40, + 0x1A, + 0x86, + 0x21, + 0x44, + 0x34, + 0x40, + 0x32, + 0x85, + 0xA0, + 0x30, + 0x40, + 0x00, + 0x70, + 0x00, + 0x40, + 0x84, + 0x80, + 0x04, + 0x00, + 0xE4, + 0xAC, + 0x07, + 0x04, + 0x21, + 0x44, + 0x02, + 0x20, + 0x10, + 0x00, + 0x40, + 0xC2, + 0x89, + 0x98, + 0x85, + 0x00, + 0x58, + 0x6A, + 0x41, + 0x8E, + 0x18, + 0xA1, + 0x91, + 0x00, + 0x10, + 0x00, + }; + + private void CompareBuffer(byte[] testBuffer, byte[] targetBuffer) { - private static byte[] x86resultData { get; } = - new byte[] - { - 0x12, - 0x00, - 0x00, - 0x00, - 0x02, - 0x0B, - 0x00, - 0x00, - 0xE8, - 0xBD, - 0x00, - 0x00, - 0x00, - 0x07, - 0x00, - 0x00, - 0x12, - 0x00, - 0x00, - 0x00, - 0x6D, - 0x01, - 0x00, - 0x00, - 0xF0, - 0xCA, - 0x00, - 0x00, - 0x1C, - 0x00, - 0x00, - 0x00, - 0x12, - 0x00, - 0x00, - 0x00, - 0xBC, - 0x09, - 0x00, - 0x00, - 0x14, - 0xC2, - 0x00, - 0x00, - 0xE0, - 0x01, - 0x00, - 0x00, - 0x12, - 0x00, - 0x00, - 0x00, - 0x98, - 0x0B, - 0x00, - 0x00, - 0x60, - 0x75, - 0x0A, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x10, - 0x00, - 0xF1, - 0xFF, - 0x42, - 0x01, - 0x00, - 0x00, - 0x08, - 0xC8, - 0x00, - 0x00, - 0x1C, - 0x00, - }; + Assert.Equal(testBuffer, targetBuffer); + } - private static byte[] x86Data { get; } = - new byte[] - { - 0x12, - 0x00, - 0x00, - 0x00, - 0x02, - 0x0B, - 0x00, - 0x00, - 0xE8, - 0xCA, - 0x20, - 0x00, - 0x00, - 0x07, - 0x00, - 0x00, - 0x12, - 0x00, - 0x00, - 0x00, - 0x6D, - 0x01, - 0x00, - 0x00, - 0xF0, - 0xCA, - 0x00, - 0x00, - 0x1C, - 0x00, - 0x00, - 0x00, - 0x12, - 0x00, - 0x00, - 0x00, - 0xBC, - 0x09, - 0x00, - 0x00, - 0x14, - 0xC2, - 0x00, - 0x00, - 0xE0, - 0x01, - 0x00, - 0x00, - 0x12, - 0x00, - 0x00, - 0x00, - 0x98, - 0x0B, - 0x00, - 0x00, - 0x60, - 0x75, - 0x0A, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x10, - 0x00, - 0xF1, - 0xFF, - 0x42, - 0x01, - 0x00, - 0x00, - 0x08, - 0xC8, - 0x00, - 0x00, - 0x1C, - 0x00, - }; + [Fact] + public void X86ConverterDecodeTest() + { + uint state = 0; + uint ip = 0x2000; + var testData = x86Data; + BranchExecFilter.X86Converter(testData, ip, ref state); + CompareBuffer(testData, x86resultData); + } - private static byte[] ppcResultData { get; } = - new byte[] - { - 0xF8, - 0x6B, - 0x2E, - 0x8C, - 0x95, - 0xC5, - 0x4B, - 0x1B, - 0x94, - 0x78, - 0x9E, - 0x7C, - 0xBD, - 0x8B, - 0xA8, - 0xAF, - 0x31, - 0x20, - 0xFE, - 0x0F, - 0xB3, - 0x15, - 0x9A, - 0x7C, - 0xD5, - 0x5C, - 0xC2, - 0xC0, - 0xEC, - 0xE9, - 0x43, - 0x2B, - 0xD0, - 0x9F, - 0x2C, - 0xFC, - 0xB8, - 0x2B, - 0x6B, - 0x15, - 0xCD, - 0x3F, - 0x0C, - 0xAF, - 0x8F, - 0x68, - 0xB0, - 0x6E, - 0x6B, - 0x30, - 0x2E, - 0x8C, - 0x3F, - 0x7E, - 0x96, - 0x7C, - 0x93, - 0xB2, - 0xA4, - 0x0E, - 0x43, - 0xEA, - 0x20, - 0x10, - 0x38, - 0x6D, - 0x37, - 0xF8, - 0x87, - 0xFE, - 0xA9, - 0x63, - 0x75, - 0xF5, - 0x56, - 0x34, - 0x4A, - 0xE3, - 0xCF, - 0x89, - 0x18, - 0x08, - 0xC2, - 0x76, - 0x74, - 0x12, - 0xEC, - 0xA7, - 0x6D, - 0xC2, - 0xB7, - 0x1B, - 0x7A, - 0xB2, - 0xD4, - 0xED - }; + [Fact] + public void PowerPCConverterDecodeTest() + { + uint ip = 0x6A0; + var testData = ppcData; + BranchExecFilter.PowerPCConverter(testData, ip); + CompareBuffer(testData, ppcResultData); + } - private static byte[] ppcData { get; } = - new byte[] - { - 0xF8, - 0x6B, - 0x2E, - 0x8C, - 0x95, - 0xC5, - 0x4B, - 0x1B, - 0x94, - 0x78, - 0x9E, - 0x7C, - 0xBD, - 0x8B, - 0xA8, - 0xAF, - 0x31, - 0x20, - 0xFE, - 0x0F, - 0xB3, - 0x15, - 0x9A, - 0x7C, - 0xD5, - 0x5C, - 0xC2, - 0xC0, - 0xEC, - 0xE9, - 0x43, - 0x2B, - 0xD0, - 0x9F, - 0x2C, - 0xFC, - 0xB8, - 0x2B, - 0x6B, - 0x15, - 0xCD, - 0x3F, - 0x0C, - 0xAF, - 0x8F, - 0x68, - 0xB0, - 0x6E, - 0x6B, - 0x30, - 0x2E, - 0x8C, - 0x3F, - 0x7E, - 0x96, - 0x7C, - 0x93, - 0xB2, - 0xA4, - 0x0E, - 0x43, - 0xEA, - 0x20, - 0x10, - 0x38, - 0x6D, - 0x37, - 0xF8, - 0x87, - 0xFE, - 0xA9, - 0x63, - 0x75, - 0xF5, - 0x56, - 0x34, - 0x4A, - 0xE3, - 0xD6, - 0x75, - 0x18, - 0x08, - 0xC2, - 0x76, - 0x74, - 0x12, - 0xEC, - 0xA7, - 0x6D, - 0xC2, - 0xB7, - 0x1B, - 0x7A, - 0xB2, - 0xD4, - 0xED - }; + [Fact] + public void ARMConverteDecoderTest() + { + uint ip = 0x3C00; + var testData = armData; + BranchExecFilter.ARMConverter(testData, ip); + CompareBuffer(testData, armResultData); + } - private static byte[] armResultData { get; } = - new byte[] - { - 0x7C, - 0xFC, - 0x0A, - 0x00, - 0x16, - 0x42, - 0x01, - 0x00, - 0x80, - 0xFC, - 0x0A, - 0x00, - 0x16, - 0xB8, - 0x00, - 0x00, - 0x84, - 0xFC, - 0x0A, - 0x00, - 0x16, - 0x3A, - 0x00, - 0x00, - 0x04, - 0xE0, - 0x2D, - 0xE5, - 0x04, - 0xD0, - 0x4D, - 0xE2, - 0x0E, - 0x04, - 0x00, - 0xEB, - 0x04, - 0xD0, - 0x8D, - 0xE2, - 0x04, - 0xE0, - 0x9D, - 0xE4, - 0x1E, - 0xFF, - 0x2F, - 0xE1, - 0x04, - 0xE0, - 0x2D, - 0xE5, - 0x04, - 0xE0, - 0x9F, - 0xE5, - 0x0E, - 0xE0, - 0x8F, - 0xE0, - 0x08, - 0xF0, - 0xBE, - 0xE5, - 0xF0, - 0x3A, - 0x0A, - 0x00, - 0x00, - 0xC6, - 0x8F, - 0xE2, - 0xA3, - 0xCA, - 0x8C, - 0xE2, - 0xF0, - 0xFA, - 0xBC, - 0xE5, - 0x00, - 0xC6, - 0x8F, - 0xE2, - 0xA3, - 0xCA, - 0x8C, - 0xE2, - 0xE8, - 0xFA, - 0xBC, - 0xE5, - 0x00, - 0xC6, - 0x8F, - 0xE2 - }; + [Fact] + public void ARMTConverterDecodeTest() + { + uint ip = 0xA00; + var testData = armtData; + BranchExecFilter.ARMTConverter(testData, ip); + CompareBuffer(testData, armtResultData); + } - private static byte[] armData { get; } = - new byte[] - { - 0x7C, - 0xFC, - 0x0A, - 0x00, - 0x16, - 0x42, - 0x01, - 0x00, - 0x80, - 0xFC, - 0x0A, - 0x00, - 0x16, - 0xB8, - 0x00, - 0x00, - 0x84, - 0xFC, - 0x0A, - 0x00, - 0x16, - 0x3A, - 0x00, - 0x00, - 0x04, - 0xE0, - 0x2D, - 0xE5, - 0x04, - 0xD0, - 0x4D, - 0xE2, - 0x18, - 0x13, - 0x00, - 0xEB, - 0x04, - 0xD0, - 0x8D, - 0xE2, - 0x04, - 0xE0, - 0x9D, - 0xE4, - 0x1E, - 0xFF, - 0x2F, - 0xE1, - 0x04, - 0xE0, - 0x2D, - 0xE5, - 0x04, - 0xE0, - 0x9F, - 0xE5, - 0x0E, - 0xE0, - 0x8F, - 0xE0, - 0x08, - 0xF0, - 0xBE, - 0xE5, - 0xF0, - 0x3A, - 0x0A, - 0x00, - 0x00, - 0xC6, - 0x8F, - 0xE2, - 0xA3, - 0xCA, - 0x8C, - 0xE2, - 0xF0, - 0xFA, - 0xBC, - 0xE5, - 0x00, - 0xC6, - 0x8F, - 0xE2, - 0xA3, - 0xCA, - 0x8C, - 0xE2, - 0xE8, - 0xFA, - 0xBC, - 0xE5, - 0x00, - 0xC6, - 0x8F, - 0xE2 - }; + [Fact] + public void IA64ConverterDecodeTest() + { + uint ip = 0xAA0; + var testData = ia64Data; + BranchExecFilter.IA64Converter(testData, ip); + CompareBuffer(testData, ia64ResultData); + } - private static byte[] armtResultData { get; } = - new byte[] - { - 0x95, - 0x23, - 0xB6, - 0xB1, - 0xBE, - 0x60, - 0x79, - 0xF0, - 0xF6, - 0x01, - 0xD9, - 0x7F, - 0x2E, - 0x03, - 0x31, - 0x1C, - 0xFD, - 0xD3, - 0x40, - 0x0F, - 0x21, - 0x3C, - 0x06, - 0x97, - 0xE5, - 0xC3, - 0x57, - 0x11, - 0x76, - 0x6F, - 0xE3, - 0x70, - 0xED, - 0x49, - 0xCB, - 0xB5, - 0xC9, - 0x42, - 0x59, - 0x10, - 0x2F, - 0xBD, - 0xAE, - 0xB1, - 0x40, - 0x4D, - 0x9D, - 0x7C, - 0xE9, - 0xFC, - 0x48, - 0x3E, - 0xBC, - 0x7F, - 0x0B, - 0x23, - 0xB0, - 0x8A, - 0x4D, - 0x02, - 0x39, - 0xC4, - 0xFB, - 0x66, - 0x83, - 0x7F, - 0xA7, - 0xBD, - 0x12, - 0xAC, - 0xED, - 0x31, - 0x34, - 0x93, - 0x4D, - 0x8D, - 0xD7, - 0x94, - 0x93, - 0x1C, - 0x0A, - 0x50, - 0x54, - 0x4B, - 0x03, - 0x55, - 0x27, - 0xFE, - 0xCE, - 0x29, - 0x66, - 0x52, - 0x81, - 0xAE, - 0x69, - 0xA0, - 0x69, - 0xF2, - 0x3D, - 0xFF, - 0xA1, - 0x8A, - 0x5D, - 0x61, - 0x7D, - 0xC5, - 0x94, - 0x0A, - 0x7D, - 0xED, - 0x11, - 0x0F - }; - - private static byte[] armtData { get; } = - new byte[] - { - 0x95, - 0x23, - 0xB6, - 0xB1, - 0xBE, - 0x60, - 0x79, - 0xF0, - 0xF6, - 0x01, - 0xD9, - 0x7F, - 0x2E, - 0x03, - 0x31, - 0x1C, - 0xFD, - 0xD3, - 0x40, - 0x0F, - 0x21, - 0x3C, - 0x06, - 0x97, - 0xE5, - 0xC3, - 0x57, - 0x11, - 0x76, - 0x6F, - 0xE3, - 0x70, - 0xED, - 0x49, - 0xCB, - 0xB5, - 0xC9, - 0x42, - 0x59, - 0x10, - 0x2F, - 0xBD, - 0xAE, - 0xB1, - 0x40, - 0x4D, - 0x9D, - 0x7C, - 0xE9, - 0xFC, - 0x48, - 0x3E, - 0xBC, - 0x7F, - 0x0B, - 0x23, - 0xB0, - 0x8A, - 0x4D, - 0x02, - 0x39, - 0xC4, - 0xFB, - 0x66, - 0x83, - 0x7F, - 0xA7, - 0xBD, - 0x12, - 0xAC, - 0xED, - 0x31, - 0x34, - 0x93, - 0x4D, - 0x8D, - 0xD7, - 0x94, - 0x93, - 0x1C, - 0x0A, - 0x50, - 0x54, - 0x4B, - 0x03, - 0x55, - 0x27, - 0xFE, - 0xCE, - 0x29, - 0x66, - 0x52, - 0x81, - 0xAE, - 0x69, - 0xA0, - 0x6A, - 0xF2, - 0x6F, - 0xFC, - 0xA1, - 0x8A, - 0x5D, - 0x61, - 0x7D, - 0xC5, - 0x94, - 0x0A, - 0x7D, - 0xED, - 0x11, - 0x0F - }; - - private static byte[] ia64ResultData { get; } = - new byte[] - { - 0x4D, - 0xF8, - 0xF2, - 0x0D, - 0x06, - 0x2F, - 0x74, - 0x0F, - 0xF0, - 0x91, - 0x06, - 0x0B, - 0x19, - 0x22, - 0x91, - 0x5A, - 0x66, - 0x56, - 0xA7, - 0x15, - 0x77, - 0x1E, - 0x2F, - 0xA3, - 0xE4, - 0xDE, - 0x93, - 0x1C, - 0xD5, - 0xCE, - 0x6E, - 0x45, - 0x36, - 0x15, - 0x15, - 0x65, - 0x4E, - 0xC5, - 0xA3, - 0x8C, - 0x5A, - 0x8B, - 0x8A, - 0x1C, - 0x12, - 0x5B, - 0x39, - 0x1F, - 0xA0, - 0xF2, - 0x93, - 0x7C, - 0x7F, - 0x5D, - 0xD9, - 0x30, - 0x1F, - 0xF6, - 0x5C, - 0x10, - 0x62, - 0x3E, - 0xB4, - 0x64, - 0x56, - 0x48, - 0xB2, - 0x20, - 0x39, - 0xE8, - 0x44, - 0x10, - 0x87, - 0x9E, - 0x2C, - 0xFC, - 0x29, - 0x0E, - 0x20, - 0x76, - 0xCE, - 0xDA, - 0x93, - 0x1C, - 0xED, - 0x54, - 0x0D, - 0xAF, - 0xEC, - 0xDE, - 0x93, - 0x1C, - 0x2B, - 0x72, - 0xD5, - 0x0D - }; - - private static byte[] ia64Data { get; } = - new byte[] - { - 0x4D, - 0xF8, - 0xF2, - 0x0D, - 0x06, - 0x2F, - 0x74, - 0x0F, - 0xF0, - 0x91, - 0x06, - 0x0B, - 0x19, - 0x22, - 0x91, - 0x5A, - 0x66, - 0x56, - 0xA7, - 0x15, - 0x77, - 0x1E, - 0x2F, - 0xA3, - 0xE4, - 0xDE, - 0x93, - 0x1C, - 0xD5, - 0xCE, - 0x6E, - 0x45, - 0x36, - 0x15, - 0x15, - 0x65, - 0x4E, - 0xC5, - 0xA3, - 0x8C, - 0x5A, - 0x8B, - 0x8A, - 0x1C, - 0x12, - 0x5B, - 0x39, - 0x1F, - 0xA0, - 0xF2, - 0x93, - 0x7C, - 0x7F, - 0x5D, - 0xD9, - 0x30, - 0x1F, - 0xF6, - 0x5C, - 0x10, - 0x62, - 0x3E, - 0xB4, - 0x64, - 0x56, - 0x48, - 0xB2, - 0x20, - 0x39, - 0xE8, - 0x44, - 0x80, - 0x8C, - 0x9E, - 0x2C, - 0xFC, - 0x29, - 0x0E, - 0x20, - 0x76, - 0xCE, - 0xDA, - 0x93, - 0x1C, - 0xED, - 0x54, - 0x0D, - 0xAF, - 0xEC, - 0xDE, - 0x93, - 0x1C, - 0x2B, - 0x72, - 0xD5, - 0x0D - }; - - private static byte[] sparcResultData { get; } = - new byte[] - { - 0x78, - 0x2E, - 0x73, - 0x6F, - 0x2E, - 0x33, - 0x00, - 0x00, - 0x07, - 0x01, - 0x00, - 0x00, - 0x09, - 0x00, - 0x00, - 0x00, - 0x40, - 0x00, - 0x00, - 0x00, - 0x0B, - 0x00, - 0x00, - 0x00, - 0xA2, - 0x0D, - 0x10, - 0x12, - 0x30, - 0x03, - 0xC9, - 0x37, - 0x40, - 0x1A, - 0x85, - 0xD9, - 0x44, - 0x34, - 0x40, - 0x32, - 0x85, - 0xA0, - 0x30, - 0x40, - 0x00, - 0x70, - 0x00, - 0x40, - 0x84, - 0x80, - 0x04, - 0x00, - 0xE4, - 0xAC, - 0x07, - 0x04, - 0x21, - 0x44, - 0x02, - 0x20, - 0x10, - 0x00, - 0x40, - 0xC2, - 0x89, - 0x98, - 0x85, - 0x00, - 0x58, - 0x6A, - 0x41, - 0x8E, - 0x18, - 0xA1, - 0x91, - 0x00, - 0x10, - 0x00, - }; - - private static byte[] sparcData { get; } = - new byte[] - { - 0x78, - 0x2E, - 0x73, - 0x6F, - 0x2E, - 0x33, - 0x00, - 0x00, - 0x07, - 0x01, - 0x00, - 0x00, - 0x09, - 0x00, - 0x00, - 0x00, - 0x40, - 0x00, - 0x00, - 0x44, - 0x0B, - 0x00, - 0x00, - 0x00, - 0xA2, - 0x0D, - 0x10, - 0x12, - 0x30, - 0x03, - 0xC9, - 0x37, - 0x40, - 0x1A, - 0x86, - 0x21, - 0x44, - 0x34, - 0x40, - 0x32, - 0x85, - 0xA0, - 0x30, - 0x40, - 0x00, - 0x70, - 0x00, - 0x40, - 0x84, - 0x80, - 0x04, - 0x00, - 0xE4, - 0xAC, - 0x07, - 0x04, - 0x21, - 0x44, - 0x02, - 0x20, - 0x10, - 0x00, - 0x40, - 0xC2, - 0x89, - 0x98, - 0x85, - 0x00, - 0x58, - 0x6A, - 0x41, - 0x8E, - 0x18, - 0xA1, - 0x91, - 0x00, - 0x10, - 0x00, - }; - - private void CompareBuffer(byte[] testBuffer, byte[] targetBuffer) - { - Assert.Equal(testBuffer, targetBuffer); - } - - [Fact] - public void X86ConverterDecodeTest() - { - uint state = 0; - uint ip = 0x2000; - var testData = x86Data; - BranchExecFilter.X86Converter(testData, ip, ref state); - CompareBuffer(testData, x86resultData); - } - - [Fact] - public void PowerPCConverterDecodeTest() - { - uint ip = 0x6A0; - var testData = ppcData; - BranchExecFilter.PowerPCConverter(testData, ip); - CompareBuffer(testData, ppcResultData); - } - - [Fact] - public void ARMConverteDecoderTest() - { - uint ip = 0x3C00; - var testData = armData; - BranchExecFilter.ARMConverter(testData, ip); - CompareBuffer(testData, armResultData); - } - - [Fact] - public void ARMTConverterDecodeTest() - { - uint ip = 0xA00; - var testData = armtData; - BranchExecFilter.ARMTConverter(testData, ip); - CompareBuffer(testData, armtResultData); - } - - [Fact] - public void IA64ConverterDecodeTest() - { - uint ip = 0xAA0; - var testData = ia64Data; - BranchExecFilter.IA64Converter(testData, ip); - CompareBuffer(testData, ia64ResultData); - } - - [Fact] - public void SPARCConverterDecodeTest() - { - uint ip = 0x100; - var testData = sparcData; - BranchExecFilter.SPARCConverter(testData, ip); - CompareBuffer(testData, sparcResultData); - } + [Fact] + public void SPARCConverterDecodeTest() + { + uint ip = 0x100; + var testData = sparcData; + BranchExecFilter.SPARCConverter(testData, ip); + CompareBuffer(testData, sparcResultData); } } diff --git a/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs b/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs index cfe0bf51..51ffd019 100644 --- a/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs @@ -5,127 +5,118 @@ using SharpCompress.Archives; using SharpCompress.Archives.GZip; using Xunit; -namespace SharpCompress.Test.GZip +namespace SharpCompress.Test.GZip; + +public class GZipArchiveTests : ArchiveTests { - public class GZipArchiveTests : ArchiveTests + public GZipArchiveTests() { - public GZipArchiveTests() + UseExtensionInsteadOfNameToVerify = true; + } + + [Fact] + public void GZip_Archive_Generic() + { + using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"))) + using (var archive = ArchiveFactory.Open(stream)) { - UseExtensionInsteadOfNameToVerify = true; + var entry = archive.Entries.First(); + entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key)); + + var size = entry.Size; + var scratch = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar")); + var test = new FileInfo(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")); + + Assert.Equal(size, scratch.Length); + Assert.Equal(size, test.Length); } + CompareArchivesByPath( + Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"), + Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar") + ); + } - [Fact] - public void GZip_Archive_Generic() + [Fact] + public void GZip_Archive() + { + using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"))) + using (var archive = GZipArchive.Open(stream)) { - using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"))) - using (var archive = ArchiveFactory.Open(stream)) - { - var entry = archive.Entries.First(); - entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key)); + var entry = archive.Entries.First(); + entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key)); - long size = entry.Size; - var scratch = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar")); - var test = new FileInfo(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")); + var size = entry.Size; + var scratch = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar")); + var test = new FileInfo(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")); - Assert.Equal(size, scratch.Length); - Assert.Equal(size, test.Length); - } - CompareArchivesByPath( - Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"), - Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar") - ); + Assert.Equal(size, scratch.Length); + Assert.Equal(size, test.Length); } + CompareArchivesByPath( + Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"), + Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar") + ); + } - [Fact] - public void GZip_Archive() + [Fact] + public void GZip_Archive_NoAdd() + { + var jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg"); + using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")); + using var archive = GZipArchive.Open(stream); + Assert.Throws( + () => archive.AddEntry("jpg\\test.jpg", jpg) + ); + archive.SaveTo(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz")); + } + + [Fact] + public void GZip_Archive_Multiple_Reads() + { + var inputStream = new MemoryStream(); + using (var fileStream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"))) { - using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"))) - using (var archive = GZipArchive.Open(stream)) - { - var entry = archive.Entries.First(); - entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key)); - - long size = entry.Size; - var scratch = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar")); - var test = new FileInfo(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")); - - Assert.Equal(size, scratch.Length); - Assert.Equal(size, test.Length); - } - CompareArchivesByPath( - Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"), - Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar") - ); + fileStream.CopyTo(inputStream); + inputStream.Position = 0; } + using var archive = GZipArchive.Open(inputStream); + var archiveEntry = archive.Entries.First(); - [Fact] - public void GZip_Archive_NoAdd() + MemoryStream tarStream; + using (var entryStream = archiveEntry.OpenEntryStream()) { - string jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg"); - using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"))) - using (var archive = GZipArchive.Open(stream)) - { - Assert.Throws( - () => archive.AddEntry("jpg\\test.jpg", jpg) - ); - archive.SaveTo(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz")); - } + tarStream = new MemoryStream(); + entryStream.CopyTo(tarStream); } - - [Fact] - public void GZip_Archive_Multiple_Reads() + var size = tarStream.Length; + using (var entryStream = archiveEntry.OpenEntryStream()) { - var inputStream = new MemoryStream(); - using (var fileStream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"))) - { - fileStream.CopyTo(inputStream); - inputStream.Position = 0; - } - using (var archive = GZipArchive.Open(inputStream)) - { - var archiveEntry = archive.Entries.First(); - - MemoryStream tarStream; - using (var entryStream = archiveEntry.OpenEntryStream()) - { - tarStream = new MemoryStream(); - entryStream.CopyTo(tarStream); - } - var size = tarStream.Length; - using (var entryStream = archiveEntry.OpenEntryStream()) - { - tarStream = new MemoryStream(); - entryStream.CopyTo(tarStream); - } - Assert.Equal(size, tarStream.Length); - using (var entryStream = archiveEntry.OpenEntryStream()) - { - var result = Archives.Tar.TarArchive.IsTarFile(entryStream); - } - Assert.Equal(size, tarStream.Length); - using (var entryStream = archiveEntry.OpenEntryStream()) - { - tarStream = new MemoryStream(); - entryStream.CopyTo(tarStream); - } - Assert.Equal(size, tarStream.Length); - } + tarStream = new MemoryStream(); + entryStream.CopyTo(tarStream); } - - [Fact] - public void TestGzCrcWithMostSignificaltBitNotNegative() + Assert.Equal(size, tarStream.Length); + using (var entryStream = archiveEntry.OpenEntryStream()) { - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"))) - { - using (var archive = GZipArchive.Open(stream)) - { - //process all entries in solid archive until the one we want to test - foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) - { - Assert.InRange(entry.Crc, 0L, 0xFFFFFFFFL); - } - } - } + var result = Archives.Tar.TarArchive.IsTarFile(entryStream); + } + Assert.Equal(size, tarStream.Length); + using (var entryStream = archiveEntry.OpenEntryStream()) + { + tarStream = new MemoryStream(); + entryStream.CopyTo(tarStream); + } + Assert.Equal(size, tarStream.Length); + } + + [Fact] + public void TestGzCrcWithMostSignificaltBitNotNegative() + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")); + using var archive = GZipArchive.Open(stream); + //process all entries in solid archive until the one we want to test + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) + { + Assert.InRange(entry.Crc, 0L, 0xFFFFFFFFL); } } } diff --git a/tests/SharpCompress.Test/GZip/GZipReaderTests.cs b/tests/SharpCompress.Test/GZip/GZipReaderTests.cs index a2c8071d..ed65c2a7 100644 --- a/tests/SharpCompress.Test/GZip/GZipReaderTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipReaderTests.cs @@ -1,40 +1,35 @@ -using System.IO; +using System.IO; using SharpCompress.Common; using SharpCompress.IO; using Xunit; -namespace SharpCompress.Test.GZip +namespace SharpCompress.Test.GZip; + +public class GZipReaderTests : ReaderTests { - public class GZipReaderTests : ReaderTests + public GZipReaderTests() { - public GZipReaderTests() - { - UseExtensionInsteadOfNameToVerify = true; - } + UseExtensionInsteadOfNameToVerify = true; + } - [Fact] - public void GZip_Reader_Generic() - { - Read("Tar.tar.gz", CompressionType.GZip); - } + [Fact] + public void GZip_Reader_Generic() + { + Read("Tar.tar.gz", CompressionType.GZip); + } - [Fact] - public void GZip_Reader_Generic2() + [Fact] + public void GZip_Reader_Generic2() + { + //read only as GZip itme + using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")); + using var reader = Readers.GZip.GZipReader.Open( + new RewindableStream(stream) + ); + while (reader.MoveToNextEntry()) // Crash here { - //read only as GZip itme - using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"))) - using ( - var reader = SharpCompress.Readers.GZip.GZipReader.Open( - new RewindableStream(stream) - ) - ) - { - while (reader.MoveToNextEntry()) // Crash here - { - Assert.NotEqual(0, reader.Entry.Size); - Assert.NotEqual(0, reader.Entry.Crc); - } - } + Assert.NotEqual(0, reader.Entry.Size); + Assert.NotEqual(0, reader.Entry.Crc); } } } diff --git a/tests/SharpCompress.Test/GZip/GZipWriterTests.cs b/tests/SharpCompress.Test/GZip/GZipWriterTests.cs index 212e275e..360c5b65 100644 --- a/tests/SharpCompress.Test/GZip/GZipWriterTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipWriterTests.cs @@ -1,91 +1,86 @@ -using System.IO; +using System.IO; using SharpCompress.Common; using SharpCompress.Writers; using SharpCompress.Writers.GZip; using Xunit; -namespace SharpCompress.Test.GZip +namespace SharpCompress.Test.GZip; + +public class GZipWriterTests : WriterTests { - public class GZipWriterTests : WriterTests + public GZipWriterTests() : base(ArchiveType.GZip) { - public GZipWriterTests() : base(ArchiveType.GZip) - { - UseExtensionInsteadOfNameToVerify = true; - } + UseExtensionInsteadOfNameToVerify = true; + } - [Fact] - public void GZip_Writer_Generic() - { - using ( - Stream stream = File.Open( - Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), - FileMode.OpenOrCreate, - FileAccess.Write - ) - ) - using (var writer = WriterFactory.Open(stream, ArchiveType.GZip, CompressionType.GZip)) - { - writer.Write("Tar.tar", Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")); - } - CompareArchivesByPath( + [Fact] + public void GZip_Writer_Generic() + { + using ( + Stream stream = File.Open( Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), - Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz") - ); - } - - [Fact] - public void GZip_Writer() - { - using ( - Stream stream = File.Open( - Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), - FileMode.OpenOrCreate, - FileAccess.Write - ) + FileMode.OpenOrCreate, + FileAccess.Write ) - using (var writer = new GZipWriter(stream)) - { - writer.Write("Tar.tar", Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")); - } - CompareArchivesByPath( + ) + using (var writer = WriterFactory.Open(stream, ArchiveType.GZip, CompressionType.GZip)) + { + writer.Write("Tar.tar", Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")); + } + CompareArchivesByPath( + Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), + Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz") + ); + } + + [Fact] + public void GZip_Writer() + { + using ( + Stream stream = File.Open( Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), - Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz") - ); - } - - [Fact] - public void GZip_Writer_Generic_Bad_Compression() - { - Assert.Throws(() => - { - using ( - Stream stream = File.OpenWrite(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz")) - ) - using ( - var writer = WriterFactory.Open(stream, ArchiveType.GZip, CompressionType.BZip2) - ) { } - }); - } - - [Fact] - public void GZip_Writer_Entry_Path_With_Dir() - { - using ( - Stream stream = File.Open( - Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), - FileMode.OpenOrCreate, - FileAccess.Write - ) + FileMode.OpenOrCreate, + FileAccess.Write ) - using (var writer = new GZipWriter(stream)) - { - var path = Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"); - writer.Write(path, path); //covers issue #532 - } - CompareArchivesByPath( - Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), - Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz") - ); + ) + using (var writer = new GZipWriter(stream)) + { + writer.Write("Tar.tar", Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")); } + CompareArchivesByPath( + Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), + Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz") + ); + } + + [Fact] + public void GZip_Writer_Generic_Bad_Compression() + { + Assert.Throws(() => + { + using Stream stream = File.OpenWrite(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz")); + using var writer = WriterFactory.Open(stream, ArchiveType.GZip, CompressionType.BZip2); + }); + } + + [Fact] + public void GZip_Writer_Entry_Path_With_Dir() + { + using ( + Stream stream = File.Open( + Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), + FileMode.OpenOrCreate, + FileAccess.Write + ) + ) + using (var writer = new GZipWriter(stream)) + { + var path = Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"); + writer.Write(path, path); //covers issue #532 + } + CompareArchivesByPath( + Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"), + Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz") + ); } } diff --git a/tests/SharpCompress.Test/Mocks/FlushOnDisposeStream.cs b/tests/SharpCompress.Test/Mocks/FlushOnDisposeStream.cs index fb1106d5..5e2c4db2 100644 --- a/tests/SharpCompress.Test/Mocks/FlushOnDisposeStream.cs +++ b/tests/SharpCompress.Test/Mocks/FlushOnDisposeStream.cs @@ -1,69 +1,68 @@ -using System; +using System; using System.IO; -namespace SharpCompress.Test.Mocks +namespace SharpCompress.Test.Mocks; + +// This is a simplified version of CryptoStream that always flushes the inner stream on Dispose to trigger an error in EntryStream +// CryptoStream doesn't always trigger the Flush, so this class is used instead +// See https://referencesource.microsoft.com/#mscorlib/system/security/cryptography/cryptostream.cs,141 + +public class FlushOnDisposeStream : Stream, IDisposable { - // This is a simplified version of CryptoStream that always flushes the inner stream on Dispose to trigger an error in EntryStream - // CryptoStream doesn't always trigger the Flush, so this class is used instead - // See https://referencesource.microsoft.com/#mscorlib/system/security/cryptography/cryptostream.cs,141 + private Stream inner; - public class FlushOnDisposeStream : Stream, IDisposable + public FlushOnDisposeStream(Stream innerStream) { - private Stream inner; + inner = innerStream; + } - public FlushOnDisposeStream(Stream innerStream) + public override bool CanRead => inner.CanRead; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override long Length => inner.Length; + + public override long Position + { + get => inner.Position; + set => inner.Position = value; + } + + public override void Flush() + { + throw new NotImplementedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + return inner.Read(buffer, offset, count); + } + + 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(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) { - this.inner = innerStream; + inner.Flush(); + inner.Close(); } - public override bool CanRead => this.inner.CanRead; - - public override bool CanSeek => false; - - public override bool CanWrite => false; - - public override long Length => this.inner.Length; - - public override long Position - { - get => this.inner.Position; - set => this.inner.Position = value; - } - - public override void Flush() - { - throw new NotImplementedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - return this.inner.Read(buffer, offset, count); - } - - 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(); - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - this.inner.Flush(); - this.inner.Close(); - } - - base.Dispose(disposing); - } + base.Dispose(disposing); } } diff --git a/tests/SharpCompress.Test/Mocks/ForwardOnlyStream.cs b/tests/SharpCompress.Test/Mocks/ForwardOnlyStream.cs index 9c10941f..68301db1 100644 --- a/tests/SharpCompress.Test/Mocks/ForwardOnlyStream.cs +++ b/tests/SharpCompress.Test/Mocks/ForwardOnlyStream.cs @@ -1,68 +1,67 @@ -using System; +using System; using System.IO; -namespace SharpCompress.Test.Mocks +namespace SharpCompress.Test.Mocks; + +public class ForwardOnlyStream : Stream { - public class ForwardOnlyStream : Stream + private readonly Stream stream; + + public bool IsDisposed { get; private set; } + + public ForwardOnlyStream(Stream stream) { - private readonly Stream stream; + this.stream = stream; + } - public bool IsDisposed { get; private set; } - - public ForwardOnlyStream(Stream stream) + protected override void Dispose(bool disposing) + { + if (!IsDisposed) { - this.stream = stream; - } - - protected override void Dispose(bool disposing) - { - if (!IsDisposed) + if (disposing) { - if (disposing) - { - stream.Dispose(); - IsDisposed = true; - base.Dispose(disposing); - } + stream.Dispose(); + IsDisposed = true; + base.Dispose(disposing); } } + } - public override bool CanRead => true; + public override bool CanRead => true; - public override bool CanSeek => false; - public override bool CanWrite => false; + public override bool CanSeek => false; + public override bool CanWrite => false; - public override void Flush() - { - throw new NotSupportedException(); - } + public override void Flush() + { + throw new NotSupportedException(); + } - public override long Length => throw new NotSupportedException(); + public override long Length => throw new NotSupportedException(); - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } - public override int Read(byte[] buffer, int offset, int count) - { - return stream.Read(buffer, offset, count); - } + public override int Read(byte[] buffer, int offset, int count) + { + return stream.Read(buffer, offset, count); + } - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } - public override void SetLength(long value) - { - throw new NotSupportedException(); - } + public override void SetLength(long value) + { + throw new NotSupportedException(); + } - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); } } diff --git a/tests/SharpCompress.Test/Mocks/TestStream.cs b/tests/SharpCompress.Test/Mocks/TestStream.cs index 000cf408..3af53901 100644 --- a/tests/SharpCompress.Test/Mocks/TestStream.cs +++ b/tests/SharpCompress.Test/Mocks/TestStream.cs @@ -1,68 +1,67 @@ -using System.IO; +using System.IO; -namespace SharpCompress.Test.Mocks +namespace SharpCompress.Test.Mocks; + +public class TestStream : Stream { - public class TestStream : Stream + private readonly Stream stream; + + public TestStream(Stream stream) + : this(stream, stream.CanRead, stream.CanWrite, stream.CanSeek) { } + + public bool IsDisposed { get; private set; } + + public TestStream(Stream stream, bool read, bool write, bool seek) { - private readonly Stream stream; + this.stream = stream; + CanRead = read; + CanWrite = write; + CanSeek = seek; + } - public TestStream(Stream stream) - : this(stream, stream.CanRead, stream.CanWrite, stream.CanSeek) { } + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + stream.Dispose(); + IsDisposed = true; + } - public bool IsDisposed { get; private set; } + public override bool CanRead { get; } - public TestStream(Stream stream, bool read, bool write, bool seek) - { - this.stream = stream; - CanRead = read; - CanWrite = write; - CanSeek = seek; - } + public override bool CanSeek { get; } - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - stream.Dispose(); - IsDisposed = true; - } + public override bool CanWrite { get; } - public override bool CanRead { get; } + public override void Flush() + { + stream.Flush(); + } - public override bool CanSeek { get; } + public override long Length => stream.Length; - public override bool CanWrite { get; } + public override long Position + { + get => stream.Position; + set => stream.Position = value; + } - public override void Flush() - { - stream.Flush(); - } + public override int Read(byte[] buffer, int offset, int count) + { + return stream.Read(buffer, offset, count); + } - public override long Length => stream.Length; + public override long Seek(long offset, SeekOrigin origin) + { + return stream.Seek(offset, origin); + } - public override long Position - { - get => stream.Position; - set => stream.Position = value; - } + public override void SetLength(long value) + { + stream.SetLength(value); + } - public override int Read(byte[] buffer, int offset, int count) - { - return stream.Read(buffer, offset, count); - } - - public override long Seek(long offset, SeekOrigin origin) - { - return stream.Seek(offset, origin); - } - - public override void SetLength(long value) - { - stream.SetLength(value); - } - - public override void Write(byte[] buffer, int offset, int count) - { - stream.Write(buffer, offset, count); - } + public override void Write(byte[] buffer, int offset, int count) + { + stream.Write(buffer, offset, count); } } diff --git a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs index 6c61215c..af184c77 100644 --- a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs +++ b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs @@ -1,329 +1,88 @@ +using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using SharpCompress.Archives; using SharpCompress.Archives.Rar; using SharpCompress.Common; using SharpCompress.Compressors.LZMA.Utilites; +using SharpCompress.Factories; using SharpCompress.Readers; using Xunit; -namespace SharpCompress.Test.Rar +namespace SharpCompress.Test.Rar; + +public class RarArchiveTests : ArchiveTests { - public class RarArchiveTests : ArchiveTests + [Fact] + public void Rar_EncryptedFileAndHeader_Archive() { - [Fact] - public void Rar_EncryptedFileAndHeader_Archive() - { - ReadRarPassword("Rar.encrypted_filesAndHeader.rar", "test"); - } + ReadRarPassword("Rar.encrypted_filesAndHeader.rar", "test"); + } - [Fact] - public void Rar_EncryptedFileAndHeader_NoPasswordExceptionTest() - { - Assert.Throws( - typeof(CryptographicException), - () => ReadRarPassword("Rar.encrypted_filesAndHeader.rar", null) - ); - } + [Fact] + public void Rar_EncryptedFileAndHeader_NoPasswordExceptionTest() + { + Assert.Throws( + typeof(CryptographicException), + () => ReadRarPassword("Rar.encrypted_filesAndHeader.rar", null) + ); + } - /*[Fact] - public void Rar5_EncryptedFileAndHeader_Archive() - { - ReadRarPassword("Rar5.encrypted_filesAndHeader.rar", "test"); - }*/ + /*[Fact] + public void Rar5_EncryptedFileAndHeader_Archive() + { + ReadRarPassword("Rar5.encrypted_filesAndHeader.rar", "test"); + }*/ - [Fact] - public void Rar5_EncryptedFileAndHeader_NoPasswordExceptionTest() - { - Assert.Throws( - typeof(CryptographicException), - () => ReadRarPassword("Rar5.encrypted_filesAndHeader.rar", null) - ); - } + [Fact] + public void Rar5_EncryptedFileAndHeader_NoPasswordExceptionTest() + { + Assert.Throws( + typeof(CryptographicException), + () => ReadRarPassword("Rar5.encrypted_filesAndHeader.rar", null) + ); + } - [Fact] - public void Rar_EncryptedFileOnly_Archive() - { - ReadRarPassword("Rar.encrypted_filesOnly.rar", "test"); - } + [Fact] + public void Rar_EncryptedFileOnly_Archive() + { + ReadRarPassword("Rar.encrypted_filesOnly.rar", "test"); + } - /*[Fact] - public void Rar5_EncryptedFileOnly_Archive() - { - ReadRarPassword("Rar5.encrypted_filesOnly.rar", "test"); - }*/ + /*[Fact] + public void Rar5_EncryptedFileOnly_Archive() + { + ReadRarPassword("Rar5.encrypted_filesOnly.rar", "test"); + }*/ - [Fact] - public void Rar_Encrypted_Archive() - { - ReadRarPassword("Rar.Encrypted.rar", "test"); - } + [Fact] + public void Rar_Encrypted_Archive() + { + ReadRarPassword("Rar.Encrypted.rar", "test"); + } - /*[Fact] - public void Rar5_Encrypted_Archive() - { - ReadRarPassword("Rar5.encrypted_filesAndHeader.rar", "test"); - }*/ + /*[Fact] + public void Rar5_Encrypted_Archive() + { + ReadRarPassword("Rar5.encrypted_filesAndHeader.rar", "test"); + }*/ - private void ReadRarPassword(string testArchive, string? password) - { - using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, testArchive))) - using ( - var archive = RarArchive.Open( - stream, - new ReaderOptions() { Password = password, LeaveStreamOpen = true } - ) + private void ReadRarPassword(string testArchive, string? password) + { + using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, testArchive))) + using ( + var archive = RarArchive.Open( + stream, + new ReaderOptions() { Password = password, LeaveStreamOpen = true } ) + ) + { + foreach (var entry in archive.Entries) { - foreach (var entry in archive.Entries) - { - if (!entry.IsDirectory) - { - Assert.Equal(CompressionType.Rar, entry.CompressionType); - entry.WriteToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - } - VerifyFiles(); - } - - [Fact] - public void Rar_Multi_Archive_Encrypted() - { - Assert.Throws( - () => ArchiveFileReadPassword("Rar.EncryptedParts.part01.rar", "test") - ); - } - - protected void ArchiveFileReadPassword(string archiveName, string password) - { - using ( - var archive = RarArchive.Open( - Path.Combine(TEST_ARCHIVES_PATH, archiveName), - new ReaderOptions() { Password = password, LeaveStreamOpen = true } - ) - ) - { - foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) - { - entry.WriteToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - VerifyFiles(); - } - - [Fact] - public void Rar_None_ArchiveStreamRead() - { - ArchiveStreamRead("Rar.none.rar"); - } - - [Fact] - public void Rar5_None_ArchiveStreamRead() - { - ArchiveStreamRead("Rar5.none.rar"); - } - - [Fact] - public void Rar_ArchiveStreamRead() - { - ArchiveStreamRead("Rar.rar"); - } - - [Fact] - public void Rar5_ArchiveStreamRead() - { - ArchiveStreamRead("Rar5.rar"); - } - - [Fact] - public void Rar_test_invalid_exttime_ArchiveStreamRead() - { - DoRar_test_invalid_exttime_ArchiveStreamRead("Rar.test_invalid_exttime.rar"); - } - - private void DoRar_test_invalid_exttime_ArchiveStreamRead(string filename) - { - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename))) - { - using (var archive = ArchiveFactory.Open(stream)) - { - foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) - { - entry.WriteToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - } - } - - [Fact] - public void Rar_Jpg_ArchiveStreamRead() - { - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg"))) - { - using ( - var archive = RarArchive.Open( - stream, - new ReaderOptions() { LookForHeader = true } - ) - ) - { - foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) - { - entry.WriteToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - VerifyFiles(); - } - } - - [Fact] - public void Rar_IsSolidArchiveCheck() - { - DoRar_IsSolidArchiveCheck("Rar.rar"); - } - - [Fact] - public void Rar5_IsSolidArchiveCheck() - { - DoRar_IsSolidArchiveCheck("Rar5.rar"); - } - - private void DoRar_IsSolidArchiveCheck(string filename) - { - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename))) - { - using (var archive = RarArchive.Open(stream)) - { - Assert.False(archive.IsSolid); - foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) - { - entry.WriteToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - } - VerifyFiles(); - } - - [Fact] - public void Rar_IsSolidEntryStreamCheck() - { - DoRar_IsSolidEntryStreamCheck("Rar.solid.rar"); - } - - //Extract the 2nd file in a solid archive to check that the first file is skipped properly - private void DoRar_IsSolidEntryStreamCheck(string filename) - { - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename))) - { - using (var archive = RarArchive.Open(stream)) - { - Assert.True(archive.IsSolid); - IArchiveEntry[] entries = archive.Entries.Where(a => !a.IsDirectory).ToArray(); - Assert.NotInRange(entries.Length, 0, 1); - Assert.False(entries[0].IsSolid); //first item in a solid archive is not marked solid and is seekable - IArchiveEntry testEntry = entries[1]; - Assert.True(testEntry.IsSolid); //the target. The non seekable entry - - //process all entries in solid archive until the one we want to test - foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) - { - using (CrcCheckStream crcStream = new CrcCheckStream((uint)entry.Crc)) //use the 7zip CRC stream for convenience (required a bug fix) - { - using (Stream eStream = entry.OpenEntryStream()) //bug fix in RarStream to report the correct Position - eStream.CopyTo(crcStream); - } //throws if not valid - if (entry == testEntry) - break; - } - } - } - } - - [Fact] - public void Rar_Solid_ArchiveStreamRead() - { - ArchiveStreamRead("Rar.solid.rar"); - } - - [Fact] - public void Rar5_Solid_ArchiveStreamRead() - { - ArchiveStreamRead("Rar5.solid.rar"); - } - - [Fact] - public void Rar_Solid_StreamRead_Extract_All() - { - ArchiveStreamReadExtractAll("Rar.solid.rar", CompressionType.Rar); - } - - [Fact] - public void Rar5_Solid_StreamRead_Extract_All() - { - ArchiveStreamReadExtractAll("Rar5.solid.rar", CompressionType.Rar); - } - - [Fact] - public void Rar_Multi_ArchiveStreamRead() - { - DoRar_Multi_ArchiveStreamRead( - new string[] - { - "Rar.multi.part01.rar", - "Rar.multi.part02.rar", - "Rar.multi.part03.rar", - "Rar.multi.part04.rar", - "Rar.multi.part05.rar", - "Rar.multi.part06.rar" - }, - false - ); - } - - [Fact] - public void Rar5_Multi_ArchiveStreamRead() - { - DoRar_Multi_ArchiveStreamRead( - new string[] - { - "Rar5.multi.part01.rar", - "Rar5.multi.part02.rar", - "Rar5.multi.part03.rar", - "Rar5.multi.part04.rar", - "Rar5.multi.part05.rar", - "Rar5.multi.part06.rar" - }, - false - ); - } - - private void DoRar_Multi_ArchiveStreamRead(string[] archives, bool isSolid) - { - using ( - var archive = RarArchive.Open( - archives.Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)).Select(File.OpenRead) - ) - ) - { - Assert.Equal(archive.IsSolid, isSolid); - foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) + if (!entry.IsDirectory) { + Assert.Equal(CompressionType.Rar, entry.CompressionType); entry.WriteToDirectory( SCRATCH_FILES_PATH, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } @@ -331,336 +90,480 @@ namespace SharpCompress.Test.Rar } } } + VerifyFiles(); + } - [Fact] - public void Rar5_MultiSolid_ArchiveStreamRead() - { - DoRar_Multi_ArchiveStreamRead( - new string[] - { - "Rar.multi.solid.part01.rar", - "Rar.multi.solid.part02.rar", - "Rar.multi.solid.part03.rar", - "Rar.multi.solid.part04.rar", - "Rar.multi.solid.part05.rar", - "Rar.multi.solid.part06.rar" - }, - true - ); - } + [Fact] + public void Rar_Multi_Archive_Encrypted() + { + Assert.Throws( + () => ArchiveFileReadPassword("Rar.EncryptedParts.part01.rar", "test") + ); + } - [Fact] - public void RarNoneArchiveFileRead() - { - ArchiveFileRead("Rar.none.rar"); - } - - [Fact] - public void Rar5NoneArchiveFileRead() - { - ArchiveFileRead("Rar5.none.rar"); - } - - [Fact] - public void Rar_ArchiveFileRead() - { - ArchiveFileRead("Rar.rar"); - } - - [Fact] - public void Rar5_ArchiveFileRead() - { - ArchiveFileRead("Rar5.rar"); - } - - [Fact] - public void Rar_ArchiveFileRead_HasDirectories() - { - DoRar_ArchiveFileRead_HasDirectories("Rar.rar"); - } - - [Fact] - public void Rar5_ArchiveFileRead_HasDirectories() - { - DoRar_ArchiveFileRead_HasDirectories("Rar5.rar"); - } - - private void DoRar_ArchiveFileRead_HasDirectories(string filename) - { - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename))) - { - using (var archive = RarArchive.Open(stream)) - { - Assert.False(archive.IsSolid); - Assert.Contains(true, archive.Entries.Select(entry => entry.IsDirectory)); - } - } - } - - [Fact] - public void Rar_Jpg_ArchiveFileRead() - { - using ( - var archive = RarArchive.Open( - Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg"), - new ReaderOptions() { LookForHeader = true } - ) + protected void ArchiveFileReadPassword(string archiveName, string password) + { + using ( + var archive = RarArchive.Open( + Path.Combine(TEST_ARCHIVES_PATH, archiveName), + new ReaderOptions() { Password = password, LeaveStreamOpen = true } ) + ) + { + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { - foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) - { - entry.WriteToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - VerifyFiles(); - } - - [Fact] - public void Rar_Solid_ArchiveFileRead() - { - ArchiveFileRead("Rar.solid.rar"); - } - - [Fact] - public void Rar5_Solid_ArchiveFileRead() - { - ArchiveFileRead("Rar5.solid.rar"); - } - - [Fact] - public void Rar2_Multi_ArchiveStreamRead() - { - DoRar_Multi_ArchiveStreamRead( - new string[] - { - "Rar2.multi.rar", - "Rar2.multi.r00", - "Rar2.multi.r01", - "Rar2.multi.r02", - "Rar2.multi.r03", - "Rar2.multi.r04", - "Rar2.multi.r05" - }, - false - ); - } - - [Fact] - public void Rar2_Multi_ArchiveFileRead() - { - ArchiveFileRead("Rar2.multi.rar"); //r00, r01... - } - - [Fact] - public void Rar2_ArchiveFileRead() - { - ArchiveFileRead("Rar2.rar"); - } - - [Fact] - public void Rar2_ArchiveVersionTest() - { - string testArchive = Path.Combine(TEST_ARCHIVES_PATH, "Rar2.rar"); - - using (var archive = RarArchive.Open(testArchive)) - { - Assert.Equal(2, archive.MinVersion); - Assert.Equal(2, archive.MaxVersion); + entry.WriteToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); } } + VerifyFiles(); + } - [Fact] - public void Rar4_ArchiveVersionTest() + [Fact] + public void Rar_None_ArchiveStreamRead() + { + ArchiveStreamRead("Rar.none.rar"); + } + + [Fact] + public void Rar5_None_ArchiveStreamRead() + { + ArchiveStreamRead("Rar5.none.rar"); + } + + [Fact] + public void Rar_ArchiveStreamRead() + { + ArchiveStreamRead("Rar.rar"); + } + + [Fact] + public void Rar5_ArchiveStreamRead() + { + ArchiveStreamRead("Rar5.rar"); + } + + [Fact] + public void Rar_test_invalid_exttime_ArchiveStreamRead() + { + DoRar_test_invalid_exttime_ArchiveStreamRead("Rar.test_invalid_exttime.rar"); + } + + private void DoRar_test_invalid_exttime_ArchiveStreamRead(string filename) + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename)); + using var archive = ArchiveFactory.Open(stream); + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { - string testArchive = Path.Combine(TEST_ARCHIVES_PATH, "Rar4.multi.part01.rar"); + entry.WriteToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); + } + } - using (var archive = RarArchive.Open(testArchive)) + [Fact] + public void Rar_Jpg_ArchiveStreamRead() + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg")); + using ( + var archive = RarArchive.Open( + stream, + new ReaderOptions() { LookForHeader = true } + ) + ) + { + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { - Assert.Equal(3, archive.MinVersion); - Assert.Equal(4, archive.MaxVersion); + entry.WriteToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); } } + VerifyFiles(); + } - [Fact] - public void Rar5_ArchiveVersionTest() + [Fact] + public void Rar_IsSolidArchiveCheck() + { + DoRar_IsSolidArchiveCheck("Rar.rar"); + } + + [Fact] + public void Rar5_IsSolidArchiveCheck() + { + DoRar_IsSolidArchiveCheck("Rar5.rar"); + } + + private void DoRar_IsSolidArchiveCheck(string filename) + { + using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename))) { - string testArchive = Path.Combine(TEST_ARCHIVES_PATH, "Rar5.solid.rar"); - - using (var archive = RarArchive.Open(testArchive)) + using var archive = RarArchive.Open(stream); + Assert.False(archive.IsSolid); + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { - Assert.Equal(5, archive.MinVersion); - Assert.Equal(6, archive.MaxVersion); + entry.WriteToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); } } + VerifyFiles(); + } - [Fact] - public void Rar4_Multi_ArchiveFileRead() + [Fact] + public void QuickTest() + { + var textItems = new List(); + using var reader = new RarFactory().OpenReader(new FileInfo("/Users/adam/Downloads/Kuuki_Aether_chinesevcv_1.0.rar").OpenRead(), null); + textItems.Clear(); + while (reader.MoveToNextEntry()) { - ArchiveFileRead("Rar4.multi.part01.rar"); - } - - [Fact] - public void Rar4_ArchiveFileRead() - { - ArchiveFileRead("Rar4.rar"); - } - - [Fact] - public void Rar_GetPartsSplit() - { - //uses first part to search for all parts and compares against this array - ArchiveGetParts( - new string[] + var entry = reader.Entry; + if (!(entry.Key.EndsWith("character.txt") || entry.Key.EndsWith("oto.ini"))) + { + continue; + } + using var stream = reader.OpenEntryStream(); + // Crashes here + using var streamReader = new StreamReader(stream, Encoding.UTF8); + textItems.Add($"------ {entry.Key} ------"); + var count = 0; + while (count < 256 && !streamReader.EndOfStream) + { + var line = streamReader.ReadLine(); + if (!string.IsNullOrWhiteSpace(line)) { - "Rar4.split.001", - "Rar4.split.002", - "Rar4.split.003", - "Rar4.split.004", - "Rar4.split.005", - "Rar4.split.006" + textItems.Add(line); + count++; } + } + + if (!streamReader.EndOfStream) + { + textItems.Add($"..."); + } + } + } + + [Fact] + public void Rar_IsSolidEntryStreamCheck() + { + DoRar_IsSolidEntryStreamCheck("Rar.solid.rar"); + } + + //Extract the 2nd file in a solid archive to check that the first file is skipped properly + private void DoRar_IsSolidEntryStreamCheck(string filename) + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename)); + using var archive = RarArchive.Open(stream); + Assert.True(archive.IsSolid); + IArchiveEntry[] entries = archive.Entries.Where(a => !a.IsDirectory).ToArray(); + Assert.NotInRange(entries.Length, 0, 1); + Assert.False(entries[0].IsSolid); //first item in a solid archive is not marked solid and is seekable + var testEntry = entries[1]; + Assert.True(testEntry.IsSolid); //the target. The non seekable entry + + //process all entries in solid archive until the one we want to test + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) + { + using (var crcStream = new CrcCheckStream((uint)entry.Crc)) //use the 7zip CRC stream for convenience (required a bug fix) + { + using var eStream = entry.OpenEntryStream(); //bug fix in RarStream to report the correct Position + eStream.CopyTo(crcStream); + } //throws if not valid + if (entry == testEntry) + { + break; + } + } + } + + [Fact] + public void Rar_Solid_ArchiveStreamRead() + { + ArchiveStreamRead("Rar.solid.rar"); + } + + [Fact] + public void Rar5_Solid_ArchiveStreamRead() + { + ArchiveStreamRead("Rar5.solid.rar"); + } + + [Fact] + public void Rar_Solid_StreamRead_Extract_All() + { + ArchiveStreamReadExtractAll("Rar.solid.rar", CompressionType.Rar); + } + + [Fact] + public void Rar5_Solid_StreamRead_Extract_All() + { + ArchiveStreamReadExtractAll("Rar5.solid.rar", CompressionType.Rar); + } + + [Fact] + public void Rar_Multi_ArchiveStreamRead() + { + DoRar_Multi_ArchiveStreamRead( + new string[] + { + "Rar.multi.part01.rar", + "Rar.multi.part02.rar", + "Rar.multi.part03.rar", + "Rar.multi.part04.rar", + "Rar.multi.part05.rar", + "Rar.multi.part06.rar" + }, + false + ); + } + + [Fact] + public void Rar5_Multi_ArchiveStreamRead() + { + DoRar_Multi_ArchiveStreamRead( + new string[] + { + "Rar5.multi.part01.rar", + "Rar5.multi.part02.rar", + "Rar5.multi.part03.rar", + "Rar5.multi.part04.rar", + "Rar5.multi.part05.rar", + "Rar5.multi.part06.rar" + }, + false + ); + } + + private void DoRar_Multi_ArchiveStreamRead(string[] archives, bool isSolid) + { + using var archive = RarArchive.Open( + archives.Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)).Select(File.OpenRead) + ); + Assert.Equal(archive.IsSolid, isSolid); + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) + { + entry.WriteToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } ); } + } - [Fact] - public void Rar_GetPartsOld() + [Fact] + public void Rar5_MultiSolid_ArchiveStreamRead() + { + DoRar_Multi_ArchiveStreamRead( + new string[] + { + "Rar.multi.solid.part01.rar", + "Rar.multi.solid.part02.rar", + "Rar.multi.solid.part03.rar", + "Rar.multi.solid.part04.rar", + "Rar.multi.solid.part05.rar", + "Rar.multi.solid.part06.rar" + }, + true + ); + } + + [Fact] + public void RarNoneArchiveFileRead() + { + ArchiveFileRead("Rar.none.rar"); + } + + [Fact] + public void Rar5NoneArchiveFileRead() + { + ArchiveFileRead("Rar5.none.rar"); + } + + [Fact] + public void Rar_ArchiveFileRead() + { + ArchiveFileRead("Rar.rar"); + } + + [Fact] + public void Rar5_ArchiveFileRead() + { + ArchiveFileRead("Rar5.rar"); + } + + [Fact] + public void Rar_ArchiveFileRead_HasDirectories() + { + DoRar_ArchiveFileRead_HasDirectories("Rar.rar"); + } + + [Fact] + public void Rar5_ArchiveFileRead_HasDirectories() + { + DoRar_ArchiveFileRead_HasDirectories("Rar5.rar"); + } + + private void DoRar_ArchiveFileRead_HasDirectories(string filename) + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename)); + using var archive = RarArchive.Open(stream); + Assert.False(archive.IsSolid); + Assert.Contains(true, archive.Entries.Select(entry => entry.IsDirectory)); + } + + [Fact] + public void Rar_Jpg_ArchiveFileRead() + { + using ( + var archive = RarArchive.Open( + Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg"), + new ReaderOptions() { LookForHeader = true } + ) + ) { - //uses first part to search for all parts and compares against this array - ArchiveGetParts( - new string[] - { - "Rar2.multi.rar", - "Rar2.multi.r00", - "Rar2.multi.r01", - "Rar2.multi.r02", - "Rar2.multi.r03", - "Rar2.multi.r04", - "Rar2.multi.r05" - } - ); + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) + { + entry.WriteToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); + } } + VerifyFiles(); + } - [Fact] - public void Rar_GetPartsNew() - { - //uses first part to search for all parts and compares against this array - ArchiveGetParts( - new string[] - { - "Rar4.multi.part01.rar", - "Rar4.multi.part02.rar", - "Rar4.multi.part03.rar", - "Rar4.multi.part04.rar", - "Rar4.multi.part05.rar", - "Rar4.multi.part06.rar", - "Rar4.multi.part07.rar" - } - ); - } + [Fact] + public void Rar_Solid_ArchiveFileRead() + { + ArchiveFileRead("Rar.solid.rar"); + } - [Fact] - public void Rar4_Multi_ArchiveStreamRead() - { - DoRar_Multi_ArchiveStreamRead( - new string[] - { - "Rar4.multi.part01.rar", - "Rar4.multi.part02.rar", - "Rar4.multi.part03.rar", - "Rar4.multi.part04.rar", - "Rar4.multi.part05.rar", - "Rar4.multi.part06.rar", - "Rar4.multi.part07.rar" - }, - false - ); - } + [Fact] + public void Rar5_Solid_ArchiveFileRead() + { + ArchiveFileRead("Rar5.solid.rar"); + } - //no extension to test the lib identifies the archive by content not ext - [Fact] - public void Rar4_Split_ArchiveStreamRead() - { - ArchiveStreamMultiRead( - null, - new string[] - { - "Rar4.split.001", - "Rar4.split.002", - "Rar4.split.003", - "Rar4.split.004", - "Rar4.split.005", - "Rar4.split.006" - } - ); - } + [Fact] + public void Rar2_Multi_ArchiveStreamRead() + { + DoRar_Multi_ArchiveStreamRead( + new string[] + { + "Rar2.multi.rar", + "Rar2.multi.r00", + "Rar2.multi.r01", + "Rar2.multi.r02", + "Rar2.multi.r03", + "Rar2.multi.r04", + "Rar2.multi.r05" + }, + false + ); + } - //will detect and load other files - [Fact] - public void Rar4_Multi_ArchiveFirstFileRead() - { - ArchiveFileRead("Rar4.multi.part01.rar"); - //"Rar4.multi.part02.rar", - //"Rar4.multi.part03.rar", - //"Rar4.multi.part04.rar", - //"Rar4.multi.part05.rar", - //"Rar4.multi.part06.rar", - //"Rar4.multi.part07.rar" - } + [Fact] + public void Rar2_Multi_ArchiveFileRead() + { + ArchiveFileRead("Rar2.multi.rar"); //r00, r01... + } - //will detect and load other files - [Fact] - public void Rar4_Split_ArchiveFirstFileRead() - { - ArchiveFileRead("Rar4.split.001"); - //"Rar4.split.002", - //"Rar4.split.003", - //"Rar4.split.004", - //"Rar4.split.005", - //"Rar4.split.006" - } + [Fact] + public void Rar2_ArchiveFileRead() + { + ArchiveFileRead("Rar2.rar"); + } - //will detect and load other files - [Fact] - public void Rar4_Split_ArchiveStreamFirstFileRead() - { - ArchiveStreamMultiRead( - null, - new string[] - { - "Rar4.split.001", - //"Rar4.split.002", - //"Rar4.split.003", - //"Rar4.split.004", - //"Rar4.split.005", - //"Rar4.split.006" - } - ); - } + [Fact] + public void Rar2_ArchiveVersionTest() + { + var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "Rar2.rar"); - //open with ArchiveFactory.Open and stream - [Fact] - public void Rar4_Split_ArchiveOpen() - { - ArchiveOpenStreamRead( - null, + using var archive = RarArchive.Open(testArchive); + Assert.Equal(2, archive.MinVersion); + Assert.Equal(2, archive.MaxVersion); + } + + [Fact] + public void Rar4_ArchiveVersionTest() + { + var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "Rar4.multi.part01.rar"); + + using var archive = RarArchive.Open(testArchive); + Assert.Equal(3, archive.MinVersion); + Assert.Equal(4, archive.MaxVersion); + } + + [Fact] + public void Rar5_ArchiveVersionTest() + { + var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "Rar5.solid.rar"); + + using var archive = RarArchive.Open(testArchive); + Assert.Equal(5, archive.MinVersion); + Assert.Equal(6, archive.MaxVersion); + } + + [Fact] + public void Rar4_Multi_ArchiveFileRead() + { + ArchiveFileRead("Rar4.multi.part01.rar"); + } + + [Fact] + public void Rar4_ArchiveFileRead() + { + ArchiveFileRead("Rar4.rar"); + } + + [Fact] + public void Rar_GetPartsSplit() + { + //uses first part to search for all parts and compares against this array + ArchiveGetParts( + new string[] + { "Rar4.split.001", "Rar4.split.002", "Rar4.split.003", "Rar4.split.004", "Rar4.split.005", "Rar4.split.006" - ); - } + } + ); + } - //open with ArchiveFactory.Open and stream - [Fact] - public void Rar4_Multi_ArchiveOpen() - { - ArchiveOpenStreamRead( - null, + [Fact] + public void Rar_GetPartsOld() + { + //uses first part to search for all parts and compares against this array + ArchiveGetParts( + new string[] + { + "Rar2.multi.rar", + "Rar2.multi.r00", + "Rar2.multi.r01", + "Rar2.multi.r02", + "Rar2.multi.r03", + "Rar2.multi.r04", + "Rar2.multi.r05" + } + ); + } + + [Fact] + public void Rar_GetPartsNew() + { + //uses first part to search for all parts and compares against this array + ArchiveGetParts( + new string[] + { "Rar4.multi.part01.rar", "Rar4.multi.part02.rar", "Rar4.multi.part03.rar", @@ -668,20 +571,16 @@ namespace SharpCompress.Test.Rar "Rar4.multi.part05.rar", "Rar4.multi.part06.rar", "Rar4.multi.part07.rar" - ); - } + } + ); + } - [Fact] - public void Rar4_Multi_ArchiveOpenEntryVolumeIndexTest() - { - ArchiveOpenEntryVolumeIndexTest( - new[] - { - new[] { 0, 1 }, //exe - Rar4.multi.part01.rar to Rar4.multi.part02.rar - new[] { 1, 5 }, //jpg - Rar4.multi.part02.rar to Rar4.multi.part06.rar - new[] { 5, 6 } //txt - Rar4.multi.part06.rar to Rar4.multi.part07.rar - }, - null, + [Fact] + public void Rar4_Multi_ArchiveStreamRead() + { + DoRar_Multi_ArchiveStreamRead( + new string[] + { "Rar4.multi.part01.rar", "Rar4.multi.part02.rar", "Rar4.multi.part03.rar", @@ -689,63 +588,171 @@ namespace SharpCompress.Test.Rar "Rar4.multi.part05.rar", "Rar4.multi.part06.rar", "Rar4.multi.part07.rar" - ); - } + }, + false + ); + } - [Fact] - public void Rar_Multi_ArchiveFileRead() - { - ArchiveFileRead("Rar.multi.part01.rar"); - } - - [Fact] - public void Rar5_Multi_ArchiveFileRead() - { - ArchiveFileRead("Rar5.multi.part01.rar"); - } - - [Fact] - public void Rar_IsFirstVolume_True() - { - DoRar_IsFirstVolume_True("Rar.multi.part01.rar"); - } - - [Fact] - public void Rar5_IsFirstVolume_True() - { - DoRar_IsFirstVolume_True("Rar5.multi.part01.rar"); - } - - private void DoRar_IsFirstVolume_True(string firstFilename) - { - using (var archive = RarArchive.Open(Path.Combine(TEST_ARCHIVES_PATH, firstFilename))) + //no extension to test the lib identifies the archive by content not ext + [Fact] + public void Rar4_Split_ArchiveStreamRead() + { + ArchiveStreamMultiRead( + null, + new string[] { - Assert.True(archive.IsMultipartVolume()); - Assert.True(archive.IsFirstVolume()); + "Rar4.split.001", + "Rar4.split.002", + "Rar4.split.003", + "Rar4.split.004", + "Rar4.split.005", + "Rar4.split.006" } - } + ); + } - [Fact] - public void Rar_IsFirstVolume_False() - { - DoRar_IsFirstVolume_False("Rar.multi.part03.rar"); - } + //will detect and load other files + [Fact] + public void Rar4_Multi_ArchiveFirstFileRead() + { + ArchiveFileRead("Rar4.multi.part01.rar"); + //"Rar4.multi.part02.rar", + //"Rar4.multi.part03.rar", + //"Rar4.multi.part04.rar", + //"Rar4.multi.part05.rar", + //"Rar4.multi.part06.rar", + //"Rar4.multi.part07.rar" + } - [Fact] - public void Rar5_IsFirstVolume_False() - { - DoRar_IsFirstVolume_False("Rar5.multi.part03.rar"); - } + //will detect and load other files + [Fact] + public void Rar4_Split_ArchiveFirstFileRead() + { + ArchiveFileRead("Rar4.split.001"); + //"Rar4.split.002", + //"Rar4.split.003", + //"Rar4.split.004", + //"Rar4.split.005", + //"Rar4.split.006" + } - private void DoRar_IsFirstVolume_False(string notFirstFilename) - { - using ( - var archive = RarArchive.Open(Path.Combine(TEST_ARCHIVES_PATH, notFirstFilename)) - ) + //will detect and load other files + [Fact] + public void Rar4_Split_ArchiveStreamFirstFileRead() + { + ArchiveStreamMultiRead( + null, + new string[] { - Assert.True(archive.IsMultipartVolume()); - Assert.False(archive.IsFirstVolume()); + "Rar4.split.001", + //"Rar4.split.002", + //"Rar4.split.003", + //"Rar4.split.004", + //"Rar4.split.005", + //"Rar4.split.006" } - } + ); + } + + //open with ArchiveFactory.Open and stream + [Fact] + public void Rar4_Split_ArchiveOpen() + { + ArchiveOpenStreamRead( + null, + "Rar4.split.001", + "Rar4.split.002", + "Rar4.split.003", + "Rar4.split.004", + "Rar4.split.005", + "Rar4.split.006" + ); + } + + //open with ArchiveFactory.Open and stream + [Fact] + public void Rar4_Multi_ArchiveOpen() + { + ArchiveOpenStreamRead( + null, + "Rar4.multi.part01.rar", + "Rar4.multi.part02.rar", + "Rar4.multi.part03.rar", + "Rar4.multi.part04.rar", + "Rar4.multi.part05.rar", + "Rar4.multi.part06.rar", + "Rar4.multi.part07.rar" + ); + } + + [Fact] + public void Rar4_Multi_ArchiveOpenEntryVolumeIndexTest() + { + ArchiveOpenEntryVolumeIndexTest( + new[] + { + new[] { 0, 1 }, //exe - Rar4.multi.part01.rar to Rar4.multi.part02.rar + new[] { 1, 5 }, //jpg - Rar4.multi.part02.rar to Rar4.multi.part06.rar + new[] { 5, 6 } //txt - Rar4.multi.part06.rar to Rar4.multi.part07.rar + }, + null, + "Rar4.multi.part01.rar", + "Rar4.multi.part02.rar", + "Rar4.multi.part03.rar", + "Rar4.multi.part04.rar", + "Rar4.multi.part05.rar", + "Rar4.multi.part06.rar", + "Rar4.multi.part07.rar" + ); + } + + [Fact] + public void Rar_Multi_ArchiveFileRead() + { + ArchiveFileRead("Rar.multi.part01.rar"); + } + + [Fact] + public void Rar5_Multi_ArchiveFileRead() + { + ArchiveFileRead("Rar5.multi.part01.rar"); + } + + [Fact] + public void Rar_IsFirstVolume_True() + { + DoRar_IsFirstVolume_True("Rar.multi.part01.rar"); + } + + [Fact] + public void Rar5_IsFirstVolume_True() + { + DoRar_IsFirstVolume_True("Rar5.multi.part01.rar"); + } + + private void DoRar_IsFirstVolume_True(string firstFilename) + { + using var archive = RarArchive.Open(Path.Combine(TEST_ARCHIVES_PATH, firstFilename)); + Assert.True(archive.IsMultipartVolume()); + Assert.True(archive.IsFirstVolume()); + } + + [Fact] + public void Rar_IsFirstVolume_False() + { + DoRar_IsFirstVolume_False("Rar.multi.part03.rar"); + } + + [Fact] + public void Rar5_IsFirstVolume_False() + { + DoRar_IsFirstVolume_False("Rar5.multi.part03.rar"); + } + + private void DoRar_IsFirstVolume_False(string notFirstFilename) + { + using var archive = RarArchive.Open(Path.Combine(TEST_ARCHIVES_PATH, notFirstFilename)); + Assert.True(archive.IsMultipartVolume()); + Assert.False(archive.IsFirstVolume()); } } diff --git a/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs b/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs index 745935d2..7e4f013a 100644 --- a/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs +++ b/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs @@ -1,71 +1,66 @@ -using System.IO; +using System.IO; using SharpCompress.Common.Rar.Headers; using SharpCompress.IO; using SharpCompress.Readers; using Xunit; -namespace SharpCompress.Test.Rar +namespace SharpCompress.Test.Rar; + +/// +/// Summary description for RarFactoryReaderTest +/// +public class RarHeaderFactoryTest : TestBase { - /// - /// Summary description for RarFactoryReaderTest - /// - public class RarHeaderFactoryTest : TestBase + private readonly RarHeaderFactory rarHeaderFactory; + + public RarHeaderFactoryTest() { - private readonly RarHeaderFactory rarHeaderFactory; + rarHeaderFactory = new RarHeaderFactory( + StreamingMode.Seekable, + new ReaderOptions { LeaveStreamOpen = true } + ); + } - public RarHeaderFactoryTest() - { - rarHeaderFactory = new RarHeaderFactory( - StreamingMode.Seekable, - new ReaderOptions { LeaveStreamOpen = true } + [Fact] + public void Rar_ReadHeaders_RecognizeEncryptedFlag() + { + ReadEncryptedFlag("Rar.encrypted_filesAndHeader.rar", true); + } + + [Fact] + public void Rar5_ReadHeaders_RecognizeEncryptedFlag() + { + ReadEncryptedFlag("Rar5.encrypted_filesAndHeader.rar", true); + } + + [Fact] + public void Rar_ReadHeaders_RecognizeNoEncryptedFlag() + { + ReadEncryptedFlag("Rar.rar", false); + } + + [Fact] + public void Rar5_ReadHeaders_RecognizeNoEncryptedFlag() + { + ReadEncryptedFlag("Rar5.rar", false); + } + + private void ReadEncryptedFlag(string testArchive, bool isEncrypted) + { + using var stream = new FileStream( + Path.Combine(TEST_ARCHIVES_PATH, testArchive), + FileMode.Open, + FileAccess.Read ); - } - - [Fact] - public void Rar_ReadHeaders_RecognizeEncryptedFlag() + foreach (var header in rarHeaderFactory.ReadHeaders(stream)) { - ReadEncryptedFlag("Rar.encrypted_filesAndHeader.rar", true); - } - - [Fact] - public void Rar5_ReadHeaders_RecognizeEncryptedFlag() - { - ReadEncryptedFlag("Rar5.encrypted_filesAndHeader.rar", true); - } - - [Fact] - public void Rar_ReadHeaders_RecognizeNoEncryptedFlag() - { - ReadEncryptedFlag("Rar.rar", false); - } - - [Fact] - public void Rar5_ReadHeaders_RecognizeNoEncryptedFlag() - { - ReadEncryptedFlag("Rar5.rar", false); - } - - private void ReadEncryptedFlag(string testArchive, bool isEncrypted) - { - using ( - var stream = new FileStream( - Path.Combine(TEST_ARCHIVES_PATH, testArchive), - FileMode.Open, - FileAccess.Read - ) + if ( + header.HeaderType == HeaderType.Archive + || header.HeaderType == HeaderType.Crypt ) { - foreach (var header in rarHeaderFactory.ReadHeaders(stream)) - { - if ( - header.HeaderType == HeaderType.Archive - || header.HeaderType == HeaderType.Crypt - ) - { - Assert.Equal(isEncrypted, rarHeaderFactory.IsEncrypted); - break; - } - } + Assert.Equal(isEncrypted, rarHeaderFactory.IsEncrypted); + break; } } } diff --git a/tests/SharpCompress.Test/Rar/RarReaderTests.cs b/tests/SharpCompress.Test/Rar/RarReaderTests.cs index bb841475..15266d6c 100644 --- a/tests/SharpCompress.Test/Rar/RarReaderTests.cs +++ b/tests/SharpCompress.Test/Rar/RarReaderTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Linq; using SharpCompress.Common; @@ -6,49 +6,89 @@ using SharpCompress.Readers; using SharpCompress.Readers.Rar; using Xunit; -namespace SharpCompress.Test.Rar +namespace SharpCompress.Test.Rar; + +public class RarReaderTests : ReaderTests { - public class RarReaderTests : ReaderTests + [Fact] + public void Rar_Multi_Reader() { - [Fact] - public void Rar_Multi_Reader() - { - DoRar_Multi_Reader( - new string[] - { - "Rar.multi.part01.rar", - "Rar.multi.part02.rar", - "Rar.multi.part03.rar", - "Rar.multi.part04.rar", - "Rar.multi.part05.rar", - "Rar.multi.part06.rar" - } - ); - } + DoRar_Multi_Reader( + new string[] + { + "Rar.multi.part01.rar", + "Rar.multi.part02.rar", + "Rar.multi.part03.rar", + "Rar.multi.part04.rar", + "Rar.multi.part05.rar", + "Rar.multi.part06.rar" + } + ); + } - [Fact] - public void Rar5_Multi_Reader() - { - DoRar_Multi_Reader( - new string[] - { - "Rar5.multi.part01.rar", - "Rar5.multi.part02.rar", - "Rar5.multi.part03.rar", - "Rar5.multi.part04.rar", - "Rar5.multi.part05.rar", - "Rar5.multi.part06.rar" - } - ); - } + [Fact] + public void Rar5_Multi_Reader() + { + DoRar_Multi_Reader( + new string[] + { + "Rar5.multi.part01.rar", + "Rar5.multi.part02.rar", + "Rar5.multi.part03.rar", + "Rar5.multi.part04.rar", + "Rar5.multi.part05.rar", + "Rar5.multi.part06.rar" + } + ); + } - private void DoRar_Multi_Reader(string[] archives) + private void DoRar_Multi_Reader(string[] archives) + { + using ( + var reader = RarReader.Open( + archives + .Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) + .Select(p => File.OpenRead(p)) + ) + ) + { + while (reader.MoveToNextEntry()) + { + reader.WriteEntryToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); + } + } + VerifyFiles(); + } + + [Fact] + public void Rar_Multi_Reader_Encrypted() + { + DoRar_Multi_Reader_Encrypted( + new string[] + { + "Rar.EncryptedParts.part01.rar", + "Rar.EncryptedParts.part02.rar", + "Rar.EncryptedParts.part03.rar", + "Rar.EncryptedParts.part04.rar", + "Rar.EncryptedParts.part05.rar", + "Rar.EncryptedParts.part06.rar" + } + ); + } + + private void DoRar_Multi_Reader_Encrypted(string[] archives) + { + Assert.Throws(() => { using ( var reader = RarReader.Open( archives .Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) - .Select(p => File.OpenRead(p)) + .Select(p => File.OpenRead(p)), + new ReaderOptions() { Password = "test" } ) ) { @@ -61,358 +101,305 @@ namespace SharpCompress.Test.Rar } } VerifyFiles(); - } + }); + } - [Fact] - public void Rar_Multi_Reader_Encrypted() - { - DoRar_Multi_Reader_Encrypted( - new string[] - { - "Rar.EncryptedParts.part01.rar", - "Rar.EncryptedParts.part02.rar", - "Rar.EncryptedParts.part03.rar", - "Rar.EncryptedParts.part04.rar", - "Rar.EncryptedParts.part05.rar", - "Rar.EncryptedParts.part06.rar" - } - ); - } - - private void DoRar_Multi_Reader_Encrypted(string[] archives) - { - Assert.Throws(() => + [Fact] + public void Rar_Multi_Reader_Delete_Files() + { + DoRar_Multi_Reader_Delete_Files( + new string[] { - using ( - var reader = RarReader.Open( - archives - .Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) - .Select(p => File.OpenRead(p)), - new ReaderOptions() { Password = "test" } - ) - ) - { - while (reader.MoveToNextEntry()) - { - reader.WriteEntryToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - VerifyFiles(); - }); - } + "Rar.multi.part01.rar", + "Rar.multi.part02.rar", + "Rar.multi.part03.rar", + "Rar.multi.part04.rar", + "Rar.multi.part05.rar", + "Rar.multi.part06.rar" + } + ); + } - [Fact] - public void Rar_Multi_Reader_Delete_Files() - { - DoRar_Multi_Reader_Delete_Files( - new string[] - { - "Rar.multi.part01.rar", - "Rar.multi.part02.rar", - "Rar.multi.part03.rar", - "Rar.multi.part04.rar", - "Rar.multi.part05.rar", - "Rar.multi.part06.rar" - } - ); - } - - [Fact] - public void Rar5_Multi_Reader_Delete_Files() - { - DoRar_Multi_Reader_Delete_Files( - new string[] - { - "Rar5.multi.part01.rar", - "Rar5.multi.part02.rar", - "Rar5.multi.part03.rar", - "Rar5.multi.part04.rar", - "Rar5.multi.part05.rar", - "Rar5.multi.part06.rar" - } - ); - } - - private void DoRar_Multi_Reader_Delete_Files(string[] archives) - { - foreach (var file in archives) + [Fact] + public void Rar5_Multi_Reader_Delete_Files() + { + DoRar_Multi_Reader_Delete_Files( + new string[] { - File.Copy( - Path.Combine(TEST_ARCHIVES_PATH, file), - Path.Combine(SCRATCH2_FILES_PATH, file) + "Rar5.multi.part01.rar", + "Rar5.multi.part02.rar", + "Rar5.multi.part03.rar", + "Rar5.multi.part04.rar", + "Rar5.multi.part05.rar", + "Rar5.multi.part06.rar" + } + ); + } + + private void DoRar_Multi_Reader_Delete_Files(string[] archives) + { + foreach (var file in archives) + { + File.Copy( + Path.Combine(TEST_ARCHIVES_PATH, file), + Path.Combine(SCRATCH2_FILES_PATH, file) + ); + } + var streams = archives + .Select(s => Path.Combine(SCRATCH2_FILES_PATH, s)) + .Select(File.OpenRead) + .ToList(); + using (var reader = RarReader.Open(streams)) + { + while (reader.MoveToNextEntry()) + { + reader.WriteEntryToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } ); } - var streams = archives - .Select(s => Path.Combine(SCRATCH2_FILES_PATH, s)) - .Select(File.OpenRead) - .ToList(); - using (var reader = RarReader.Open(streams)) + } + foreach (var stream in streams) + { + stream.Dispose(); + } + VerifyFiles(); + + foreach (var file in archives.Select(s => Path.Combine(SCRATCH2_FILES_PATH, s))) + { + File.Delete(file); + } + } + + [Fact] + public void Rar_None_Reader() + { + Read("Rar.none.rar", CompressionType.Rar); + } + + [Fact] + public void Rar5_None_Reader() + { + Read("Rar5.none.rar", CompressionType.Rar); + } + + [Fact] + public void Rar_Reader() + { + Read("Rar.rar", CompressionType.Rar); + } + + [Fact] + public void Rar5_Reader() + { + Read("Rar5.rar", CompressionType.Rar); + } + + [Fact] + public void Rar_EncryptedFileAndHeader_Reader() + { + ReadRar("Rar.encrypted_filesAndHeader.rar", "test"); + } + + /*[Fact] + public void Rar5_EncryptedFileAndHeader_Reader() + { + ReadRar("Rar5.encrypted_filesAndHeader.rar", "test"); + }*/ + + [Fact] + public void Rar_EncryptedFileOnly_Reader() + { + ReadRar("Rar.encrypted_filesOnly.rar", "test"); + } + + /*[Fact] + public void Rar5_EncryptedFileOnly_Reader() + { + ReadRar("Rar5.encrypted_filesOnly.rar", "test"); + }*/ + + [Fact] + public void Rar_Encrypted_Reader() + { + ReadRar("Rar.Encrypted.rar", "test"); + } + + /*[Fact] + public void Rar5_Encrypted_Reader() + { + ReadRar("Rar5.encrypted_filesOnly.rar", "test"); + }*/ + + private void ReadRar(string testArchive, string password) + { + Read(testArchive, CompressionType.Rar, new ReaderOptions { Password = password }); + } + + [Fact] + public void Rar_Entry_Stream() + { + DoRar_Entry_Stream("Rar.rar"); + } + + [Fact] + public void Rar5_Entry_Stream() + { + DoRar_Entry_Stream("Rar5.rar"); + } + + private void DoRar_Entry_Stream(string filename) + { + using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename))) + using (var reader = ReaderFactory.Open(stream)) + { + while (reader.MoveToNextEntry()) { - while (reader.MoveToNextEntry()) - { - reader.WriteEntryToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - foreach (var stream in streams) - { - stream.Dispose(); - } - VerifyFiles(); - - foreach (var file in archives.Select(s => Path.Combine(SCRATCH2_FILES_PATH, s))) - { - File.Delete(file); - } - } - - [Fact] - public void Rar_None_Reader() - { - Read("Rar.none.rar", CompressionType.Rar); - } - - [Fact] - public void Rar5_None_Reader() - { - Read("Rar5.none.rar", CompressionType.Rar); - } - - [Fact] - public void Rar_Reader() - { - Read("Rar.rar", CompressionType.Rar); - } - - [Fact] - public void Rar5_Reader() - { - Read("Rar5.rar", CompressionType.Rar); - } - - [Fact] - public void Rar_EncryptedFileAndHeader_Reader() - { - ReadRar("Rar.encrypted_filesAndHeader.rar", "test"); - } - - /*[Fact] - public void Rar5_EncryptedFileAndHeader_Reader() - { - ReadRar("Rar5.encrypted_filesAndHeader.rar", "test"); - }*/ - - [Fact] - public void Rar_EncryptedFileOnly_Reader() - { - ReadRar("Rar.encrypted_filesOnly.rar", "test"); - } - - /*[Fact] - public void Rar5_EncryptedFileOnly_Reader() - { - ReadRar("Rar5.encrypted_filesOnly.rar", "test"); - }*/ - - [Fact] - public void Rar_Encrypted_Reader() - { - ReadRar("Rar.Encrypted.rar", "test"); - } - - /*[Fact] - public void Rar5_Encrypted_Reader() - { - ReadRar("Rar5.encrypted_filesOnly.rar", "test"); - }*/ - - private void ReadRar(string testArchive, string password) - { - Read(testArchive, CompressionType.Rar, new ReaderOptions { Password = password }); - } - - [Fact] - public void Rar_Entry_Stream() - { - DoRar_Entry_Stream("Rar.rar"); - } - - [Fact] - public void Rar5_Entry_Stream() - { - DoRar_Entry_Stream("Rar5.rar"); - } - - private void DoRar_Entry_Stream(string filename) - { - using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename))) - using (var reader = ReaderFactory.Open(stream)) - { - while (reader.MoveToNextEntry()) - { - if (!reader.Entry.IsDirectory) - { - Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); - using (var entryStream = reader.OpenEntryStream()) - { - string file = Path.GetFileName(reader.Entry.Key); - string folder = - Path.GetDirectoryName(reader.Entry.Key) - ?? throw new ArgumentNullException(); - string destdir = Path.Combine(SCRATCH_FILES_PATH, folder); - if (!Directory.Exists(destdir)) - { - Directory.CreateDirectory(destdir); - } - string destinationFileName = Path.Combine(destdir, file); - - using (FileStream fs = File.OpenWrite(destinationFileName)) - { - entryStream.TransferTo(fs); - } - } - } - } - } - VerifyFiles(); - } - - [Fact] - public void Rar_Reader_Audio_program() - { - using ( - var stream = File.OpenRead( - Path.Combine(TEST_ARCHIVES_PATH, "Rar.Audio_program.rar") - ) - ) - using ( - var reader = ReaderFactory.Open( - stream, - new ReaderOptions() { LookForHeader = true } - ) - ) - { - while (reader.MoveToNextEntry()) + if (!reader.Entry.IsDirectory) { Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); - reader.WriteEntryToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); + using var entryStream = reader.OpenEntryStream(); + var file = Path.GetFileName(reader.Entry.Key); + var folder = + Path.GetDirectoryName(reader.Entry.Key) + ?? throw new ArgumentNullException(); + var destdir = Path.Combine(SCRATCH_FILES_PATH, folder); + if (!Directory.Exists(destdir)) + { + Directory.CreateDirectory(destdir); + } + var destinationFileName = Path.Combine(destdir, file); + + using var fs = File.OpenWrite(destinationFileName); + entryStream.TransferTo(fs); } } - CompareFilesByPath( - Path.Combine(SCRATCH_FILES_PATH, "test.dat"), - Path.Combine(MISC_TEST_FILES_PATH, "test.dat") + } + VerifyFiles(); + } + + [Fact] + public void Rar_Reader_Audio_program() + { + using ( + var stream = File.OpenRead( + Path.Combine(TEST_ARCHIVES_PATH, "Rar.Audio_program.rar") + ) + ) + using ( + var reader = ReaderFactory.Open( + stream, + new ReaderOptions() { LookForHeader = true } + ) + ) + { + while (reader.MoveToNextEntry()) + { + Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); + reader.WriteEntryToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); + } + } + CompareFilesByPath( + Path.Combine(SCRATCH_FILES_PATH, "test.dat"), + Path.Combine(MISC_TEST_FILES_PATH, "test.dat") + ); + } + + [Fact] + public void Rar_Jpg_Reader() + { + using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg"))) + using ( + var reader = RarReader.Open(stream, new ReaderOptions() { LookForHeader = true }) + ) + { + while (reader.MoveToNextEntry()) + { + Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); + reader.WriteEntryToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); + } + } + VerifyFiles(); + } + + [Fact] + public void Rar_Solid_Reader() + { + Read("Rar.solid.rar", CompressionType.Rar); + } + + [Fact] + public void Rar5_Solid_Reader() + { + Read("Rar5.solid.rar", CompressionType.Rar); + } + + [Fact] + public void Rar_Solid_Skip_Reader() + { + DoRar_Solid_Skip_Reader("Rar.solid.rar"); + } + + [Fact] + public void Rar5_Solid_Skip_Reader() + { + DoRar_Solid_Skip_Reader("Rar5.solid.rar"); + } + + private void DoRar_Solid_Skip_Reader(string filename) + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename)); + using var reader = ReaderFactory.Open( + stream, + new ReaderOptions() { LookForHeader = true } ); - } - - [Fact] - public void Rar_Jpg_Reader() + while (reader.MoveToNextEntry()) { - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg"))) - using ( - var reader = RarReader.Open(stream, new ReaderOptions() { LookForHeader = true }) - ) + if (reader.Entry.Key.Contains("jpg")) { - while (reader.MoveToNextEntry()) - { - Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); - reader.WriteEntryToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - VerifyFiles(); - } - - [Fact] - public void Rar_Solid_Reader() - { - Read("Rar.solid.rar", CompressionType.Rar); - } - - [Fact] - public void Rar5_Solid_Reader() - { - Read("Rar5.solid.rar", CompressionType.Rar); - } - - [Fact] - public void Rar_Solid_Skip_Reader() - { - DoRar_Solid_Skip_Reader("Rar.solid.rar"); - } - - [Fact] - public void Rar5_Solid_Skip_Reader() - { - DoRar_Solid_Skip_Reader("Rar5.solid.rar"); - } - - private void DoRar_Solid_Skip_Reader(string filename) - { - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename))) - using ( - var reader = ReaderFactory.Open( - stream, - new ReaderOptions() { LookForHeader = true } - ) - ) - { - while (reader.MoveToNextEntry()) - { - if (reader.Entry.Key.Contains("jpg")) - { - Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); - reader.WriteEntryToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } + Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); + reader.WriteEntryToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); } } + } - [Fact] - public void Rar_Reader_Skip() - { - DoRar_Reader_Skip("Rar.rar"); - } + [Fact] + public void Rar_Reader_Skip() + { + DoRar_Reader_Skip("Rar.rar"); + } - [Fact] - public void Rar5_Reader_Skip() - { - DoRar_Reader_Skip("Rar5.rar"); - } + [Fact] + public void Rar5_Reader_Skip() + { + DoRar_Reader_Skip("Rar5.rar"); + } - private void DoRar_Reader_Skip(string filename) + private void DoRar_Reader_Skip(string filename) + { + using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename)); + using var reader = ReaderFactory.Open( + stream, + new ReaderOptions() { LookForHeader = true } + ); + while (reader.MoveToNextEntry()) { - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename))) - using ( - var reader = ReaderFactory.Open( - stream, - new ReaderOptions() { LookForHeader = true } - ) - ) + if (reader.Entry.Key.Contains("jpg")) { - while (reader.MoveToNextEntry()) - { - if (reader.Entry.Key.Contains("jpg")) - { - Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); - reader.WriteEntryToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } + Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); + reader.WriteEntryToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); } } } diff --git a/tests/SharpCompress.Test/ReaderTests.cs b/tests/SharpCompress.Test/ReaderTests.cs index 8d02d1e6..eb0de103 100644 --- a/tests/SharpCompress.Test/ReaderTests.cs +++ b/tests/SharpCompress.Test/ReaderTests.cs @@ -5,77 +5,68 @@ using SharpCompress.Readers; using SharpCompress.Test.Mocks; using Xunit; -namespace SharpCompress.Test +namespace SharpCompress.Test; + +public abstract class ReaderTests : TestBase { - public abstract class ReaderTests : TestBase + protected void Read( + string testArchive, + CompressionType expectedCompression, + ReaderOptions? options = null + ) { - protected void Read( - string testArchive, - CompressionType expectedCompression, - ReaderOptions? options = null - ) + testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); + + options ??= new ReaderOptions(); + + options.LeaveStreamOpen = true; + ReadImpl(testArchive, expectedCompression, options); + + options.LeaveStreamOpen = false; + ReadImpl(testArchive, expectedCompression, options); + VerifyFiles(); + } + + private void ReadImpl( + string testArchive, + CompressionType expectedCompression, + ReaderOptions options + ) + { + using var file = File.OpenRead(testArchive); + using var protectedStream = NonDisposingStream.Create( + new ForwardOnlyStream(file), + throwOnDispose: true + ); + using var testStream = new TestStream(protectedStream); + using (var reader = ReaderFactory.Open(testStream, options)) { - testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); - - options = options ?? new ReaderOptions(); - - options.LeaveStreamOpen = true; - ReadImpl(testArchive, expectedCompression, options); - - options.LeaveStreamOpen = false; - ReadImpl(testArchive, expectedCompression, options); - VerifyFiles(); + UseReader(reader, expectedCompression); + protectedStream.ThrowOnDispose = false; + Assert.False( + testStream.IsDisposed, + "{nameof(testStream)} prematurely closed" + ); } - private void ReadImpl( - string testArchive, - CompressionType expectedCompression, - ReaderOptions options - ) - { - using (var file = File.OpenRead(testArchive)) - { - using ( - var protectedStream = NonDisposingStream.Create( - new ForwardOnlyStream(file), - throwOnDispose: true - ) - ) - { - using (var testStream = new TestStream(protectedStream)) - { - using (var reader = ReaderFactory.Open(testStream, options)) - { - UseReader(reader, expectedCompression); - protectedStream.ThrowOnDispose = false; - Assert.False( - testStream.IsDisposed, - "{nameof(testStream)} prematurely closed" - ); - } + // Boolean XOR -- If the stream should be left open (true), then the stream should not be diposed (false) + // and if the stream should be closed (false), then the stream should be disposed (true) + var message = + $"{nameof(options.LeaveStreamOpen)} is set to '{options.LeaveStreamOpen}', so {nameof(testStream.IsDisposed)} should be set to '{!testStream.IsDisposed}', but is set to {testStream.IsDisposed}"; + Assert.True(options.LeaveStreamOpen != testStream.IsDisposed, message); + } - // Boolean XOR -- If the stream should be left open (true), then the stream should not be diposed (false) - // and if the stream should be closed (false), then the stream should be disposed (true) - var message = - $"{nameof(options.LeaveStreamOpen)} is set to '{options.LeaveStreamOpen}', so {nameof(testStream.IsDisposed)} should be set to '{!testStream.IsDisposed}', but is set to {testStream.IsDisposed}"; - Assert.True(options.LeaveStreamOpen != testStream.IsDisposed, message); - } - } - } - } - - public void UseReader(IReader reader, CompressionType expectedCompression) + public void UseReader(IReader reader, CompressionType expectedCompression) + { + while (reader.MoveToNextEntry()) { - while (reader.MoveToNextEntry()) + if (!reader.Entry.IsDirectory) { - if (!reader.Entry.IsDirectory) - { - Assert.Equal(expectedCompression, reader.Entry.CompressionType); - reader.WriteEntryToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } + Assert.Equal(expectedCompression, reader.Entry.CompressionType); + reader.WriteEntryToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); } } } diff --git a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs index 4532a613..c988d1c2 100644 --- a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs +++ b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs @@ -3,163 +3,162 @@ using SharpCompress.Common; using SharpCompress.Readers; using Xunit; -namespace SharpCompress.Test.SevenZip +namespace SharpCompress.Test.SevenZip; + +public class SevenZipArchiveTests : ArchiveTests { - public class SevenZipArchiveTests : ArchiveTests + [Fact] + public void SevenZipArchive_Solid_StreamRead() { - [Fact] - public void SevenZipArchive_Solid_StreamRead() - { - ArchiveStreamRead("7Zip.solid.7z"); - } + ArchiveStreamRead("7Zip.solid.7z"); + } - [Fact] - public void SevenZipArchive_NonSolid_StreamRead() - { - ArchiveStreamRead("7Zip.nonsolid.7z"); - } + [Fact] + public void SevenZipArchive_NonSolid_StreamRead() + { + ArchiveStreamRead("7Zip.nonsolid.7z"); + } - [Fact] - public void SevenZipArchive_LZMA_StreamRead() - { - ArchiveStreamRead("7Zip.LZMA.7z"); - } + [Fact] + public void SevenZipArchive_LZMA_StreamRead() + { + ArchiveStreamRead("7Zip.LZMA.7z"); + } - [Fact] - public void SevenZipArchive_LZMA_PathRead() - { - ArchiveFileRead("7Zip.LZMA.7z"); - } + [Fact] + public void SevenZipArchive_LZMA_PathRead() + { + ArchiveFileRead("7Zip.LZMA.7z"); + } - [Fact] - public void SevenZipArchive_LZMAAES_StreamRead() - { - ArchiveStreamRead( - "7Zip.LZMA.Aes.7z", - new ReaderOptions() { Password = "testpassword" } - ); - } + [Fact] + public void SevenZipArchive_LZMAAES_StreamRead() + { + ArchiveStreamRead( + "7Zip.LZMA.Aes.7z", + new ReaderOptions() { Password = "testpassword" } + ); + } - [Fact] - public void SevenZipArchive_LZMAAES_PathRead() - { - ArchiveFileRead("7Zip.LZMA.Aes.7z", new ReaderOptions() { Password = "testpassword" }); - } + [Fact] + public void SevenZipArchive_LZMAAES_PathRead() + { + ArchiveFileRead("7Zip.LZMA.Aes.7z", new ReaderOptions() { Password = "testpassword" }); + } - [Fact] - public void SevenZipArchive_LZMAAES_NoPasswordExceptionTest() - { - Assert.Throws( - typeof(CryptographicException), - () => ArchiveFileRead("7Zip.LZMA.Aes.7z", new ReaderOptions() { Password = null }) - ); //was failing with ArgumentNullException not CryptographicException like rar - } + [Fact] + public void SevenZipArchive_LZMAAES_NoPasswordExceptionTest() + { + Assert.Throws( + typeof(CryptographicException), + () => ArchiveFileRead("7Zip.LZMA.Aes.7z", new ReaderOptions() { Password = null }) + ); //was failing with ArgumentNullException not CryptographicException like rar + } - [Fact] - public void SevenZipArchive_PPMd_StreamRead() - { - ArchiveStreamRead("7Zip.PPMd.7z"); - } + [Fact] + public void SevenZipArchive_PPMd_StreamRead() + { + ArchiveStreamRead("7Zip.PPMd.7z"); + } - [Fact] - public void SevenZipArchive_PPMd_StreamRead_Extract_All() - { - ArchiveStreamReadExtractAll("7Zip.PPMd.7z", CompressionType.PPMd); - } + [Fact] + public void SevenZipArchive_PPMd_StreamRead_Extract_All() + { + ArchiveStreamReadExtractAll("7Zip.PPMd.7z", CompressionType.PPMd); + } - [Fact] - public void SevenZipArchive_PPMd_PathRead() - { - ArchiveFileRead("7Zip.PPMd.7z"); - } + [Fact] + public void SevenZipArchive_PPMd_PathRead() + { + ArchiveFileRead("7Zip.PPMd.7z"); + } - [Fact] - public void SevenZipArchive_LZMA2_StreamRead() - { - ArchiveStreamRead("7Zip.LZMA2.7z"); - } + [Fact] + public void SevenZipArchive_LZMA2_StreamRead() + { + ArchiveStreamRead("7Zip.LZMA2.7z"); + } - [Fact] - public void SevenZipArchive_LZMA2_PathRead() - { - ArchiveFileRead("7Zip.LZMA2.7z"); - } + [Fact] + public void SevenZipArchive_LZMA2_PathRead() + { + ArchiveFileRead("7Zip.LZMA2.7z"); + } - [Fact] - public void SevenZipArchive_LZMA2AES_StreamRead() - { - ArchiveStreamRead("7Zip.LZMA2.Aes.7z", new ReaderOptions { Password = "testpassword" }); - } + [Fact] + public void SevenZipArchive_LZMA2AES_StreamRead() + { + ArchiveStreamRead("7Zip.LZMA2.Aes.7z", new ReaderOptions { Password = "testpassword" }); + } - [Fact] - public void SevenZipArchive_LZMA2AES_PathRead() - { - ArchiveFileRead("7Zip.LZMA2.Aes.7z", new ReaderOptions { Password = "testpassword" }); - } + [Fact] + public void SevenZipArchive_LZMA2AES_PathRead() + { + ArchiveFileRead("7Zip.LZMA2.Aes.7z", new ReaderOptions { Password = "testpassword" }); + } - [Fact] - public void SevenZipArchive_BZip2_StreamRead() - { - ArchiveStreamRead("7Zip.BZip2.7z"); - } + [Fact] + public void SevenZipArchive_BZip2_StreamRead() + { + ArchiveStreamRead("7Zip.BZip2.7z"); + } - [Fact] - public void SevenZipArchive_BZip2_PathRead() - { - ArchiveFileRead("7Zip.BZip2.7z"); - } + [Fact] + public void SevenZipArchive_BZip2_PathRead() + { + ArchiveFileRead("7Zip.BZip2.7z"); + } - [Fact] - public void SevenZipArchive_LZMA_Time_Attributes_PathRead() - { - ArchiveFileReadEx("7Zip.LZMA.7z"); - } + [Fact] + public void SevenZipArchive_LZMA_Time_Attributes_PathRead() + { + ArchiveFileReadEx("7Zip.LZMA.7z"); + } - [Fact] - public void SevenZipArchive_BZip2_Split() - { - Assert.Throws( - () => - ArchiveStreamRead( - null, - "Original.7z.001", - "Original.7z.002", - "Original.7z.003", - "Original.7z.004", - "Original.7z.005", - "Original.7z.006", - "Original.7z.007" - ) - ); - } + [Fact] + public void SevenZipArchive_BZip2_Split() + { + Assert.Throws( + () => + ArchiveStreamRead( + null, + "Original.7z.001", + "Original.7z.002", + "Original.7z.003", + "Original.7z.004", + "Original.7z.005", + "Original.7z.006", + "Original.7z.007" + ) + ); + } - //Same as archive as Original.7z.001 ... 007 files without the root directory 'Original\' in the archive - this caused the verify to fail - [Fact] - public void SevenZipArchive_BZip2_Split_Working() - { - ArchiveStreamMultiRead( - null, - "7Zip.BZip2.split.001", - "7Zip.BZip2.split.002", - "7Zip.BZip2.split.003", - "7Zip.BZip2.split.004", - "7Zip.BZip2.split.005", - "7Zip.BZip2.split.006", - "7Zip.BZip2.split.007" - ); - } + //Same as archive as Original.7z.001 ... 007 files without the root directory 'Original\' in the archive - this caused the verify to fail + [Fact] + public void SevenZipArchive_BZip2_Split_Working() + { + ArchiveStreamMultiRead( + null, + "7Zip.BZip2.split.001", + "7Zip.BZip2.split.002", + "7Zip.BZip2.split.003", + "7Zip.BZip2.split.004", + "7Zip.BZip2.split.005", + "7Zip.BZip2.split.006", + "7Zip.BZip2.split.007" + ); + } - //will detect and load other files - [Fact] - public void SevenZipArchive_BZip2_Split_FirstFileRead() - { - ArchiveFileRead("7Zip.BZip2.split.001"); - //"7Zip.BZip2.split.002", - //"7Zip.BZip2.split.003", - //"7Zip.BZip2.split.004", - //"7Zip.BZip2.split.005", - //"7Zip.BZip2.split.006", - //"7Zip.BZip2.split.007" - } + //will detect and load other files + [Fact] + public void SevenZipArchive_BZip2_Split_FirstFileRead() + { + ArchiveFileRead("7Zip.BZip2.split.001"); + //"7Zip.BZip2.split.002", + //"7Zip.BZip2.split.003", + //"7Zip.BZip2.split.004", + //"7Zip.BZip2.split.005", + //"7Zip.BZip2.split.006", + //"7Zip.BZip2.split.007" } } diff --git a/tests/SharpCompress.Test/Streams/LzmaStreamTests.cs b/tests/SharpCompress.Test/Streams/LzmaStreamTests.cs index 54ac757b..ad97cdd4 100644 --- a/tests/SharpCompress.Test/Streams/LzmaStreamTests.cs +++ b/tests/SharpCompress.Test/Streams/LzmaStreamTests.cs @@ -3,532 +3,529 @@ using System.IO; using SharpCompress.Compressors.LZMA; using Xunit; -namespace SharpCompress.Test.Streams +namespace SharpCompress.Test.Streams; + +public class LzmaStreamTests { - public class LzmaStreamTests + [Fact] + public void TestLzma2Decompress1Byte() { - [Fact] - public void TestLzma2Decompress1Byte() + var properties = new byte[] { 0x01 }; + var compressedData = new byte[] { 0x01, 0x00, 0x00, 0x58, 0x00 }; + var lzma2Stream = new MemoryStream(compressedData); + + var decompressor = new LzmaStream(properties, lzma2Stream, 5, 1); + Assert.Equal('X', decompressor.ReadByte()); + } + + private static byte[] lzmaData { get; } = + new byte[] { - var properties = new byte[] { 0x01 }; - var compressedData = new byte[] { 0x01, 0x00, 0x00, 0x58, 0x00 }; - var lzma2Stream = new MemoryStream(compressedData); + 0x5D, + 0x00, + 0x20, + 0x00, + 0x00, + 0x48, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x80, + 0x24, + 0x18, + 0x2F, + 0xEB, + 0x20, + 0x78, + 0xBA, + 0x78, + 0x70, + 0xDC, + 0x43, + 0x2C, + 0x32, + 0xC9, + 0xC3, + 0x97, + 0x4D, + 0x10, + 0x74, + 0xE2, + 0x20, + 0xBF, + 0x5A, + 0xB4, + 0xB3, + 0xC4, + 0x31, + 0x80, + 0x26, + 0x3E, + 0x6A, + 0xEA, + 0x51, + 0xFC, + 0xE4, + 0x8D, + 0x54, + 0x96, + 0x05, + 0xCC, + 0x78, + 0x59, + 0xAC, + 0xD4, + 0x21, + 0x65, + 0x8F, + 0xA9, + 0xC8, + 0x0D, + 0x9B, + 0xE2, + 0xC2, + 0xF9, + 0x7C, + 0x3C, + 0xDD, + 0x4D, + 0x38, + 0x04, + 0x0B, + 0xF8, + 0x0B, + 0x68, + 0xA5, + 0x93, + 0x6C, + 0x64, + 0xAC, + 0xCF, + 0x71, + 0x68, + 0xE8, + 0x69, + 0x25, + 0xC6, + 0x17, + 0x28, + 0xF1, + 0x7C, + 0xF1, + 0xDC, + 0x47, + 0x51, + 0x4D, + 0x1E, + 0x0E, + 0x0B, + 0x80, + 0x37, + 0x24, + 0x58, + 0x80, + 0xF7, + 0xB4, + 0xAC, + 0x54, + 0xF1, + 0x0F, + 0x7F, + 0x0F, + 0x0F, + 0xF5, + 0x9C, + 0xDE, + 0x54, + 0x4F, + 0xA3, + 0x7B, + 0x20, + 0xC5, + 0xA8, + 0x18, + 0x3B, + 0xED, + 0xDC, + 0x04, + 0xF6, + 0xFB, + 0x86, + 0xE0, + 0xAB, + 0xB6, + 0x87, + 0x99, + 0x92, + 0x43, + 0x7B, + 0x2C, + 0xCC, + 0x31, + 0x83, + 0x90, + 0xFF, + 0xF1, + 0x76, + 0x03, + 0x90 + }; - var decompressor = new LzmaStream(properties, lzma2Stream, 5, 1); - Assert.Equal('X', decompressor.ReadByte()); - } - - private static byte[] lzmaData { get; } = - new byte[] - { - 0x5D, - 0x00, - 0x20, - 0x00, - 0x00, - 0x48, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x80, - 0x24, - 0x18, - 0x2F, - 0xEB, - 0x20, - 0x78, - 0xBA, - 0x78, - 0x70, - 0xDC, - 0x43, - 0x2C, - 0x32, - 0xC9, - 0xC3, - 0x97, - 0x4D, - 0x10, - 0x74, - 0xE2, - 0x20, - 0xBF, - 0x5A, - 0xB4, - 0xB3, - 0xC4, - 0x31, - 0x80, - 0x26, - 0x3E, - 0x6A, - 0xEA, - 0x51, - 0xFC, - 0xE4, - 0x8D, - 0x54, - 0x96, - 0x05, - 0xCC, - 0x78, - 0x59, - 0xAC, - 0xD4, - 0x21, - 0x65, - 0x8F, - 0xA9, - 0xC8, - 0x0D, - 0x9B, - 0xE2, - 0xC2, - 0xF9, - 0x7C, - 0x3C, - 0xDD, - 0x4D, - 0x38, - 0x04, - 0x0B, - 0xF8, - 0x0B, - 0x68, - 0xA5, - 0x93, - 0x6C, - 0x64, - 0xAC, - 0xCF, - 0x71, - 0x68, - 0xE8, - 0x69, - 0x25, - 0xC6, - 0x17, - 0x28, - 0xF1, - 0x7C, - 0xF1, - 0xDC, - 0x47, - 0x51, - 0x4D, - 0x1E, - 0x0E, - 0x0B, - 0x80, - 0x37, - 0x24, - 0x58, - 0x80, - 0xF7, - 0xB4, - 0xAC, - 0x54, - 0xF1, - 0x0F, - 0x7F, - 0x0F, - 0x0F, - 0xF5, - 0x9C, - 0xDE, - 0x54, - 0x4F, - 0xA3, - 0x7B, - 0x20, - 0xC5, - 0xA8, - 0x18, - 0x3B, - 0xED, - 0xDC, - 0x04, - 0xF6, - 0xFB, - 0x86, - 0xE0, - 0xAB, - 0xB6, - 0x87, - 0x99, - 0x92, - 0x43, - 0x7B, - 0x2C, - 0xCC, - 0x31, - 0x83, - 0x90, - 0xFF, - 0xF1, - 0x76, - 0x03, - 0x90 - }; - - private static byte[] lzmaResultData { get; } = - new byte[] - { - 0x01, - 0x00, - 0xFD, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0xFA, - 0x61, - 0x18, - 0x5F, - 0x02, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x02, - 0x00, - 0x00, - 0x00, - 0x03, - 0x00, - 0x00, - 0x00, - 0x01, - 0x00, - 0x00, - 0x00, - 0x02, - 0x00, - 0xB4, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x3D, - 0x61, - 0xE5, - 0x5E, - 0x03, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x12, - 0x00, - 0x00, - 0x00, - 0x02, - 0x00, - 0xB4, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0xE2, - 0x61, - 0x18, - 0x5F, - 0x04, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x12, - 0x00, - 0x00, - 0x00, - 0x29, - 0x00, - 0x00, - 0x00, - 0x01, - 0x00, - 0xFD, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x14, - 0x62, - 0x18, - 0x5F, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x03, - 0x00, - 0x00, - 0x00, - 0x40, - 0x00, - 0x00, - 0x00, - 0x09, - 0x00, - 0x00, - 0x00, - 0x02, - 0x00, - 0xB4, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x7F, - 0x61, - 0xE5, - 0x5E, - 0x05, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x3B, - 0x00, - 0x00, - 0x00, - 0xCB, - 0x15, - 0x00, - 0x00, - 0x02, - 0x00, - 0xB4, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x7F, - 0x61, - 0xE5, - 0x5E, - 0x06, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x3B, - 0x00, - 0x00, - 0x00, - 0xCB, - 0x15, - 0x00, - 0x00, - 0x02, - 0x00, - 0xB4, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x3D, - 0x61, - 0xE5, - 0x5E, - 0x07, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x12, - 0x00, - 0x00, - 0x00, - 0x02, - 0x00, - 0xB4, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0xFC, - 0x96, - 0x40, - 0x5C, - 0x08, - 0x00, - 0x00, - 0x00, - 0x60, - 0x00, - 0x00, - 0x00, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0x00, - 0x00, - 0x00, - 0x00, - 0xF8, - 0x83, - 0x12, - 0x00, - 0xD4, - 0x99, - 0x00, - 0x00, - 0x43, - 0x95, - 0x00, - 0x00, - 0xEB, - 0x7A, - 0x00, - 0x00, - 0x40, - 0x6F, - 0x00, - 0x00, - 0xD2, - 0x6F, - 0x00, - 0x00, - 0x67, - 0x74, - 0x00, - 0x00, - 0x02, - 0x69, - 0x00, - 0x00, - 0x76, - 0x79, - 0x00, - 0x00, - 0x98, - 0x66, - 0x00, - 0x00, - 0x23, - 0x25, - 0x00, - 0x00, - 0x01, - 0x00, - 0xFD, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x3B, - 0x2F, - 0xC0, - 0x5F, - 0x09, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x03, - 0x00, - 0x00, - 0x00, - 0x69, - 0x00, - 0x3D, - 0x00, - 0x0A, - 0x00, - 0x00, - 0x00 - }; - - [Fact] - public void TestLzmaBuffer() + private static byte[] lzmaResultData { get; } = + new byte[] { - var input = new MemoryStream(lzmaData); - using (var output = new MemoryStream()) - { - var properties = new byte[5]; - input.Read(properties, 0, 5); + 0x01, + 0x00, + 0xFD, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0xFA, + 0x61, + 0x18, + 0x5F, + 0x02, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x02, + 0x00, + 0x00, + 0x00, + 0x03, + 0x00, + 0x00, + 0x00, + 0x01, + 0x00, + 0x00, + 0x00, + 0x02, + 0x00, + 0xB4, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3D, + 0x61, + 0xE5, + 0x5E, + 0x03, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x12, + 0x00, + 0x00, + 0x00, + 0x02, + 0x00, + 0xB4, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0xE2, + 0x61, + 0x18, + 0x5F, + 0x04, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x12, + 0x00, + 0x00, + 0x00, + 0x29, + 0x00, + 0x00, + 0x00, + 0x01, + 0x00, + 0xFD, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x14, + 0x62, + 0x18, + 0x5F, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x03, + 0x00, + 0x00, + 0x00, + 0x40, + 0x00, + 0x00, + 0x00, + 0x09, + 0x00, + 0x00, + 0x00, + 0x02, + 0x00, + 0xB4, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x7F, + 0x61, + 0xE5, + 0x5E, + 0x05, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3B, + 0x00, + 0x00, + 0x00, + 0xCB, + 0x15, + 0x00, + 0x00, + 0x02, + 0x00, + 0xB4, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x7F, + 0x61, + 0xE5, + 0x5E, + 0x06, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3B, + 0x00, + 0x00, + 0x00, + 0xCB, + 0x15, + 0x00, + 0x00, + 0x02, + 0x00, + 0xB4, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3D, + 0x61, + 0xE5, + 0x5E, + 0x07, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x12, + 0x00, + 0x00, + 0x00, + 0x02, + 0x00, + 0xB4, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0xFC, + 0x96, + 0x40, + 0x5C, + 0x08, + 0x00, + 0x00, + 0x00, + 0x60, + 0x00, + 0x00, + 0x00, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x00, + 0x00, + 0x00, + 0x00, + 0xF8, + 0x83, + 0x12, + 0x00, + 0xD4, + 0x99, + 0x00, + 0x00, + 0x43, + 0x95, + 0x00, + 0x00, + 0xEB, + 0x7A, + 0x00, + 0x00, + 0x40, + 0x6F, + 0x00, + 0x00, + 0xD2, + 0x6F, + 0x00, + 0x00, + 0x67, + 0x74, + 0x00, + 0x00, + 0x02, + 0x69, + 0x00, + 0x00, + 0x76, + 0x79, + 0x00, + 0x00, + 0x98, + 0x66, + 0x00, + 0x00, + 0x23, + 0x25, + 0x00, + 0x00, + 0x01, + 0x00, + 0xFD, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x3B, + 0x2F, + 0xC0, + 0x5F, + 0x09, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x03, + 0x00, + 0x00, + 0x00, + 0x69, + 0x00, + 0x3D, + 0x00, + 0x0A, + 0x00, + 0x00, + 0x00 + }; - var fileLengthBytes = new byte[8]; - input.Read(fileLengthBytes, 0, 8); - var fileLength = BitConverter.ToInt64(fileLengthBytes, 0); + [Fact] + public void TestLzmaBuffer() + { + var input = new MemoryStream(lzmaData); + using var output = new MemoryStream(); + var properties = new byte[5]; + input.Read(properties, 0, 5); - var coder = new Decoder(); - coder.SetDecoderProperties(properties); - coder.Code(input, output, input.Length, fileLength, null); + var fileLengthBytes = new byte[8]; + input.Read(fileLengthBytes, 0, 8); + var fileLength = BitConverter.ToInt64(fileLengthBytes, 0); - Assert.Equal(output.ToArray(), lzmaResultData); - } - } + var coder = new Decoder(); + coder.SetDecoderProperties(properties); + coder.Code(input, output, input.Length, fileLength, null); + + Assert.Equal(output.ToArray(), lzmaResultData); } } diff --git a/tests/SharpCompress.Test/Streams/RewindableStreamTest.cs b/tests/SharpCompress.Test/Streams/RewindableStreamTest.cs index 14beab4f..ab18da45 100644 --- a/tests/SharpCompress.Test/Streams/RewindableStreamTest.cs +++ b/tests/SharpCompress.Test/Streams/RewindableStreamTest.cs @@ -1,83 +1,82 @@ -using System.IO; +using System.IO; using SharpCompress.IO; using Xunit; -namespace SharpCompress.Test.Streams -{ - public class RewindableStreamTest - { - [Fact] - public void TestRewind() - { - MemoryStream ms = new MemoryStream(); - BinaryWriter bw = new BinaryWriter(ms); - bw.Write(1); - bw.Write(2); - bw.Write(3); - bw.Write(4); - bw.Write(5); - bw.Write(6); - bw.Write(7); - bw.Flush(); - ms.Position = 0; - RewindableStream stream = new RewindableStream(ms); - stream.StartRecording(); - BinaryReader br = new BinaryReader(stream); - Assert.Equal(1, br.ReadInt32()); - Assert.Equal(2, br.ReadInt32()); - Assert.Equal(3, br.ReadInt32()); - Assert.Equal(4, br.ReadInt32()); - stream.Rewind(true); - stream.StartRecording(); - Assert.Equal(1, br.ReadInt32()); - Assert.Equal(2, br.ReadInt32()); - Assert.Equal(3, br.ReadInt32()); - Assert.Equal(4, br.ReadInt32()); - Assert.Equal(5, br.ReadInt32()); - Assert.Equal(6, br.ReadInt32()); - Assert.Equal(7, br.ReadInt32()); - stream.Rewind(true); - stream.StartRecording(); - Assert.Equal(1, br.ReadInt32()); - Assert.Equal(2, br.ReadInt32()); - Assert.Equal(3, br.ReadInt32()); - Assert.Equal(4, br.ReadInt32()); - } +namespace SharpCompress.Test.Streams; - [Fact] - public void TestIncompleteRewind() - { - MemoryStream ms = new MemoryStream(); - BinaryWriter bw = new BinaryWriter(ms); - bw.Write(1); - bw.Write(2); - bw.Write(3); - bw.Write(4); - bw.Write(5); - bw.Write(6); - bw.Write(7); - bw.Flush(); - ms.Position = 0; - RewindableStream stream = new RewindableStream(ms); - stream.StartRecording(); - BinaryReader br = new BinaryReader(stream); - Assert.Equal(1, br.ReadInt32()); - Assert.Equal(2, br.ReadInt32()); - Assert.Equal(3, br.ReadInt32()); - Assert.Equal(4, br.ReadInt32()); - stream.Rewind(true); - Assert.Equal(1, br.ReadInt32()); - Assert.Equal(2, br.ReadInt32()); - stream.StartRecording(); - Assert.Equal(3, br.ReadInt32()); - Assert.Equal(4, br.ReadInt32()); - Assert.Equal(5, br.ReadInt32()); - stream.Rewind(true); - Assert.Equal(3, br.ReadInt32()); - Assert.Equal(4, br.ReadInt32()); - Assert.Equal(5, br.ReadInt32()); - Assert.Equal(6, br.ReadInt32()); - Assert.Equal(7, br.ReadInt32()); - } +public class RewindableStreamTest +{ + [Fact] + public void TestRewind() + { + var ms = new MemoryStream(); + var bw = new BinaryWriter(ms); + bw.Write(1); + bw.Write(2); + bw.Write(3); + bw.Write(4); + bw.Write(5); + bw.Write(6); + bw.Write(7); + bw.Flush(); + ms.Position = 0; + var stream = new RewindableStream(ms); + stream.StartRecording(); + var br = new BinaryReader(stream); + Assert.Equal(1, br.ReadInt32()); + Assert.Equal(2, br.ReadInt32()); + Assert.Equal(3, br.ReadInt32()); + Assert.Equal(4, br.ReadInt32()); + stream.Rewind(true); + stream.StartRecording(); + Assert.Equal(1, br.ReadInt32()); + Assert.Equal(2, br.ReadInt32()); + Assert.Equal(3, br.ReadInt32()); + Assert.Equal(4, br.ReadInt32()); + Assert.Equal(5, br.ReadInt32()); + Assert.Equal(6, br.ReadInt32()); + Assert.Equal(7, br.ReadInt32()); + stream.Rewind(true); + stream.StartRecording(); + Assert.Equal(1, br.ReadInt32()); + Assert.Equal(2, br.ReadInt32()); + Assert.Equal(3, br.ReadInt32()); + Assert.Equal(4, br.ReadInt32()); + } + + [Fact] + public void TestIncompleteRewind() + { + var ms = new MemoryStream(); + var bw = new BinaryWriter(ms); + bw.Write(1); + bw.Write(2); + bw.Write(3); + bw.Write(4); + bw.Write(5); + bw.Write(6); + bw.Write(7); + bw.Flush(); + ms.Position = 0; + var stream = new RewindableStream(ms); + stream.StartRecording(); + var br = new BinaryReader(stream); + Assert.Equal(1, br.ReadInt32()); + Assert.Equal(2, br.ReadInt32()); + Assert.Equal(3, br.ReadInt32()); + Assert.Equal(4, br.ReadInt32()); + stream.Rewind(true); + Assert.Equal(1, br.ReadInt32()); + Assert.Equal(2, br.ReadInt32()); + stream.StartRecording(); + Assert.Equal(3, br.ReadInt32()); + Assert.Equal(4, br.ReadInt32()); + Assert.Equal(5, br.ReadInt32()); + stream.Rewind(true); + Assert.Equal(3, br.ReadInt32()); + Assert.Equal(4, br.ReadInt32()); + Assert.Equal(5, br.ReadInt32()); + Assert.Equal(6, br.ReadInt32()); + Assert.Equal(7, br.ReadInt32()); } } diff --git a/tests/SharpCompress.Test/Streams/ZlibBaseStreamTests.cs b/tests/SharpCompress.Test/Streams/ZlibBaseStreamTests.cs index 99bdeb38..44f6843c 100644 --- a/tests/SharpCompress.Test/Streams/ZlibBaseStreamTests.cs +++ b/tests/SharpCompress.Test/Streams/ZlibBaseStreamTests.cs @@ -6,116 +6,101 @@ using SharpCompress.Compressors.Deflate; using SharpCompress.IO; using Xunit; -namespace SharpCompress.Test.Streams +namespace SharpCompress.Test.Streams; + +public class ZLibBaseStreamTests { - public class ZLibBaseStreamTests + [Fact] + public void TestChunkedZlibCompressesEverything() { - [Fact] - public void TestChunkedZlibCompressesEverything() + var plainData = new byte[] { - byte[] plainData = new byte[] - { - 0xf7, - 0x1b, - 0xda, - 0x0f, - 0xb6, - 0x2b, - 0x3d, - 0x91, - 0xd7, - 0xe1, - 0xb5, - 0x11, - 0x34, - 0x5a, - 0x51, - 0x3f, - 0x8b, - 0xce, - 0x49, - 0xd2 - }; - byte[] buf = new byte[plainData.Length * 2]; + 0xf7, + 0x1b, + 0xda, + 0x0f, + 0xb6, + 0x2b, + 0x3d, + 0x91, + 0xd7, + 0xe1, + 0xb5, + 0x11, + 0x34, + 0x5a, + 0x51, + 0x3f, + 0x8b, + 0xce, + 0x49, + 0xd2 + }; + var buf = new byte[plainData.Length * 2]; - MemoryStream plainStream1 = new MemoryStream(plainData); - DeflateStream compressor1 = new DeflateStream(plainStream1, CompressionMode.Compress); - // This is enough to read the entire data - int realCompressedSize = compressor1.Read(buf, 0, plainData.Length * 2); + var plainStream1 = new MemoryStream(plainData); + var compressor1 = new DeflateStream(plainStream1, CompressionMode.Compress); + // This is enough to read the entire data + var realCompressedSize = compressor1.Read(buf, 0, plainData.Length * 2); - MemoryStream plainStream2 = new MemoryStream(plainData); - DeflateStream compressor2 = new DeflateStream(plainStream2, CompressionMode.Compress); - int total = 0; - int r = -1; // Jumpstart - while (r != 0) - { - // Reading in chunks - r = compressor2.Read(buf, 0, plainData.Length); - total += r; - } - - Assert.Equal(total, realCompressedSize); + var plainStream2 = new MemoryStream(plainData); + var compressor2 = new DeflateStream(plainStream2, CompressionMode.Compress); + var total = 0; + var r = -1; // Jumpstart + while (r != 0) + { + // Reading in chunks + r = compressor2.Read(buf, 0, plainData.Length); + total += r; } - [Fact] - public void Zlib_should_read_the_previously_written_message() - { - var message = new string('a', 131073); // 131073 causes the failure, but 131072 (-1) doesn't - var bytes = Encoding.ASCII.GetBytes(message); + Assert.Equal(total, realCompressedSize); + } - using (var inputStream = new MemoryStream(bytes)) - { - using (var compressedStream = new MemoryStream()) - using (var byteBufferStream = new BufferedStream(inputStream)) // System.IO - { - Compress(byteBufferStream, compressedStream, compressionLevel: 1); - compressedStream.Position = 0; + [Fact] + public void Zlib_should_read_the_previously_written_message() + { + var message = new string('a', 131073); // 131073 causes the failure, but 131072 (-1) doesn't + var bytes = Encoding.ASCII.GetBytes(message); - using (var decompressedStream = new MemoryStream()) - { - Decompress(compressedStream, decompressedStream); + using var inputStream = new MemoryStream(bytes); + using var compressedStream = new MemoryStream(); + using var byteBufferStream = new BufferedStream(inputStream); // System.IO + Compress(byteBufferStream, compressedStream, compressionLevel: 1); + compressedStream.Position = 0; - byteBufferStream.Position = 0; - var result = Encoding.ASCII.GetString(GetBytes(byteBufferStream)); - result.Should().Be(message); - } - } - } - } + using var decompressedStream = new MemoryStream(); + Decompress(compressedStream, decompressedStream); - private void Compress(Stream input, Stream output, int compressionLevel) - { - using ( - var zlibStream = new ZlibStream( - NonDisposingStream.Create(output), - CompressionMode.Compress, - (CompressionLevel)compressionLevel - ) - ) - { - zlibStream.FlushMode = FlushType.Sync; - input.CopyTo(zlibStream); - } - } + byteBufferStream.Position = 0; + var result = Encoding.ASCII.GetString(GetBytes(byteBufferStream)); + result.Should().Be(message); + } - private void Decompress(Stream input, Stream output) - { - using ( - var zlibStream = new ZlibStream( - NonDisposingStream.Create(input), - CompressionMode.Decompress - ) - ) - { - zlibStream.CopyTo(output); - } - } + private void Compress(Stream input, Stream output, int compressionLevel) + { + using var zlibStream = new ZlibStream( + NonDisposingStream.Create(output), + CompressionMode.Compress, + (CompressionLevel)compressionLevel + ); + zlibStream.FlushMode = FlushType.Sync; + input.CopyTo(zlibStream); + } - byte[] GetBytes(BufferedStream stream) - { - byte[] bytes = new byte[stream.Length]; - stream.Read(bytes, 0, (int)stream.Length); - return bytes; - } + private void Decompress(Stream input, Stream output) + { + using var zlibStream = new ZlibStream( + NonDisposingStream.Create(input), + CompressionMode.Decompress + ); + zlibStream.CopyTo(output); + } + + private byte[] GetBytes(BufferedStream stream) + { + var bytes = new byte[stream.Length]; + stream.Read(bytes, 0, (int)stream.Length); + return bytes; } } diff --git a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs index 45b32694..37dc7650 100644 --- a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs +++ b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Linq; using SharpCompress.Archives; diff --git a/tests/SharpCompress.Test/Tar/TarReaderTests.cs b/tests/SharpCompress.Test/Tar/TarReaderTests.cs index 966b82bf..52ad2b33 100644 --- a/tests/SharpCompress.Test/Tar/TarReaderTests.cs +++ b/tests/SharpCompress.Test/Tar/TarReaderTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using SharpCompress.Common; diff --git a/tests/SharpCompress.Test/Tar/TarWriterTests.cs b/tests/SharpCompress.Test/Tar/TarWriterTests.cs index d9bff3d8..197e7ba3 100644 --- a/tests/SharpCompress.Test/Tar/TarWriterTests.cs +++ b/tests/SharpCompress.Test/Tar/TarWriterTests.cs @@ -1,90 +1,85 @@ -using System.IO; +using System.IO; using System.Text; using SharpCompress.Common; using SharpCompress.Writers.Tar; using Xunit; -namespace SharpCompress.Test.Tar +namespace SharpCompress.Test.Tar; + +public class TarWriterTests : WriterTests { - public class TarWriterTests : WriterTests + public TarWriterTests() : base(ArchiveType.Tar) { - public TarWriterTests() : base(ArchiveType.Tar) - { - UseExtensionInsteadOfNameToVerify = true; - } + UseExtensionInsteadOfNameToVerify = true; + } - [Fact] - public void Tar_Writer() - { - Write( - CompressionType.None, - "Tar.noEmptyDirs.tar", - "Tar.noEmptyDirs.tar", - Encoding.GetEncoding(866) - ); - } + [Fact] + public void Tar_Writer() + { + Write( + CompressionType.None, + "Tar.noEmptyDirs.tar", + "Tar.noEmptyDirs.tar", + Encoding.GetEncoding(866) + ); + } - [Fact] - public void Tar_BZip2_Writer() - { - Write( - CompressionType.BZip2, - "Tar.noEmptyDirs.tar.bz2", - "Tar.noEmptyDirs.tar.bz2", - Encoding.GetEncoding(866) - ); - } + [Fact] + public void Tar_BZip2_Writer() + { + Write( + CompressionType.BZip2, + "Tar.noEmptyDirs.tar.bz2", + "Tar.noEmptyDirs.tar.bz2", + Encoding.GetEncoding(866) + ); + } - [Fact] - public void Tar_LZip_Writer() - { - Write( - CompressionType.LZip, - "Tar.noEmptyDirs.tar.lz", - "Tar.noEmptyDirs.tar.lz", - Encoding.GetEncoding(866) - ); - } + [Fact] + public void Tar_LZip_Writer() + { + Write( + CompressionType.LZip, + "Tar.noEmptyDirs.tar.lz", + "Tar.noEmptyDirs.tar.lz", + Encoding.GetEncoding(866) + ); + } - [Fact] - public void Tar_Rar_Write() - { - Assert.Throws( - () => - Write( - CompressionType.Rar, - "Zip.ppmd.noEmptyDirs.zip", - "Zip.ppmd.noEmptyDirs.zip" - ) - ); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Tar_Finalize_Archive(bool finalizeArchive) - { - using (MemoryStream stream = new MemoryStream()) - using ( - Stream content = File.OpenRead(Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg")) - ) - { - using ( - TarWriter writer = new TarWriter( - stream, - new TarWriterOptions(CompressionType.None, finalizeArchive) - ) + [Fact] + public void Tar_Rar_Write() + { + Assert.Throws( + () => + Write( + CompressionType.Rar, + "Zip.ppmd.noEmptyDirs.zip", + "Zip.ppmd.noEmptyDirs.zip" ) - { - writer.Write("doesn't matter", content, null); - } + ); + } - var paddedContentWithHeader = content.Length / 512 * 512 + 512 + 512; - var expectedStreamLength = finalizeArchive - ? paddedContentWithHeader + 512 * 2 - : paddedContentWithHeader; - Assert.Equal(expectedStreamLength, stream.Length); - } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void Tar_Finalize_Archive(bool finalizeArchive) + { + using var stream = new MemoryStream(); + using Stream content = File.OpenRead(Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg")); + using ( + var writer = new TarWriter( + stream, + new TarWriterOptions(CompressionType.None, finalizeArchive) + ) + ) + { + writer.Write("doesn't matter", content, null); } + + var paddedContentWithHeader = (content.Length / 512 * 512) + 512 + 512; + var expectedStreamLength = finalizeArchive + ? paddedContentWithHeader + (512 * 2) + : paddedContentWithHeader; + Assert.Equal(expectedStreamLength, stream.Length); } } diff --git a/tests/SharpCompress.Test/TestBase.cs b/tests/SharpCompress.Test/TestBase.cs index 455d4845..51900b33 100644 --- a/tests/SharpCompress.Test/TestBase.cs +++ b/tests/SharpCompress.Test/TestBase.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -6,221 +6,218 @@ using System.Text; using SharpCompress.Readers; using Xunit; -namespace SharpCompress.Test +namespace SharpCompress.Test; + +public class TestBase : IDisposable { - public class TestBase : IDisposable + private string SOLUTION_BASE_PATH; + protected string TEST_ARCHIVES_PATH; + protected string ORIGINAL_FILES_PATH; + protected string MISC_TEST_FILES_PATH; + private string SCRATCH_BASE_PATH; + public string SCRATCH_FILES_PATH; + protected string SCRATCH2_FILES_PATH; + + public TestBase() { - private string SOLUTION_BASE_PATH; - protected string TEST_ARCHIVES_PATH; - protected string ORIGINAL_FILES_PATH; - protected string MISC_TEST_FILES_PATH; - private string SCRATCH_BASE_PATH; - public string SCRATCH_FILES_PATH; - protected string SCRATCH2_FILES_PATH; + var index = AppDomain.CurrentDomain.BaseDirectory.IndexOf( + "SharpCompress.Test", + StringComparison.OrdinalIgnoreCase + ); + var path = AppDomain.CurrentDomain.BaseDirectory.Substring(0, index); + SOLUTION_BASE_PATH = Path.GetDirectoryName(path) ?? throw new ArgumentNullException(); - public TestBase() + TEST_ARCHIVES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Archives"); + ORIGINAL_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Original"); + MISC_TEST_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "MiscTest"); + + SCRATCH_BASE_PATH = Path.Combine( + SOLUTION_BASE_PATH, + "TestArchives", + Guid.NewGuid().ToString() + ); + SCRATCH_FILES_PATH = Path.Combine(SCRATCH_BASE_PATH, "Scratch"); + SCRATCH2_FILES_PATH = Path.Combine(SCRATCH_BASE_PATH, "Scratch2"); + + Directory.CreateDirectory(SCRATCH_FILES_PATH); + Directory.CreateDirectory(SCRATCH2_FILES_PATH); + } + + public void Dispose() + { + Directory.Delete(SCRATCH_BASE_PATH, true); + } + + public void VerifyFiles() + { + if (UseExtensionInsteadOfNameToVerify) { - var index = AppDomain.CurrentDomain.BaseDirectory.IndexOf( - "SharpCompress.Test", - StringComparison.OrdinalIgnoreCase - ); - var path = AppDomain.CurrentDomain.BaseDirectory.Substring(0, index); - SOLUTION_BASE_PATH = Path.GetDirectoryName(path) ?? throw new ArgumentNullException(); + VerifyFilesByExtension(); + } + else + { + VerifyFilesByName(); + } + } - TEST_ARCHIVES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Archives"); - ORIGINAL_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Original"); - MISC_TEST_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "MiscTest"); + /// + /// Verifies the files also check modified time and attributes. + /// + public void VerifyFilesEx() + { + if (UseExtensionInsteadOfNameToVerify) + { + VerifyFilesByExtensionEx(); + } + else + { + VerifyFilesByNameEx(); + } + } - SCRATCH_BASE_PATH = Path.Combine( - SOLUTION_BASE_PATH, - "TestArchives", - Guid.NewGuid().ToString() - ); - SCRATCH_FILES_PATH = Path.Combine(SCRATCH_BASE_PATH, "Scratch"); - SCRATCH2_FILES_PATH = Path.Combine(SCRATCH_BASE_PATH, "Scratch2"); + protected void VerifyFilesByName() + { + var extracted = Directory + .EnumerateFiles(SCRATCH_FILES_PATH, "*.*", SearchOption.AllDirectories) + .ToLookup(path => path.Substring(SCRATCH_FILES_PATH.Length)); + var original = Directory + .EnumerateFiles(ORIGINAL_FILES_PATH, "*.*", SearchOption.AllDirectories) + .ToLookup(path => path.Substring(ORIGINAL_FILES_PATH.Length)); - Directory.CreateDirectory(SCRATCH_FILES_PATH); - Directory.CreateDirectory(SCRATCH2_FILES_PATH); + Assert.Equal(extracted.Count, original.Count); + + foreach (var orig in original) + { + Assert.True(extracted.Contains(orig.Key)); + + CompareFilesByPath(orig.Single(), extracted[orig.Key].Single()); + } + } + + /// + /// Verifies the files by name also check modified time and attributes. + /// + protected void VerifyFilesByNameEx() + { + var extracted = Directory + .EnumerateFiles(SCRATCH_FILES_PATH, "*.*", SearchOption.AllDirectories) + .ToLookup(path => path.Substring(SCRATCH_FILES_PATH.Length)); + var original = Directory + .EnumerateFiles(ORIGINAL_FILES_PATH, "*.*", SearchOption.AllDirectories) + .ToLookup(path => path.Substring(ORIGINAL_FILES_PATH.Length)); + + Assert.Equal(extracted.Count, original.Count); + + foreach (var orig in original) + { + Assert.True(extracted.Contains(orig.Key)); + + CompareFilesByPath(orig.Single(), extracted[orig.Key].Single()); + CompareFilesByTimeAndAttribut(orig.Single(), extracted[orig.Key].Single()); + } + } + + /// + /// Verifies the files by extension also check modified time and attributes. + /// + protected void VerifyFilesByExtensionEx() + { + var extracted = Directory + .EnumerateFiles(SCRATCH_FILES_PATH, "*.*", SearchOption.AllDirectories) + .ToLookup(path => Path.GetExtension(path)); + var original = Directory + .EnumerateFiles(ORIGINAL_FILES_PATH, "*.*", SearchOption.AllDirectories) + .ToLookup(path => Path.GetExtension(path)); + + Assert.Equal(extracted.Count, original.Count); + + foreach (var orig in original) + { + Assert.True(extracted.Contains(orig.Key)); + + CompareFilesByPath(orig.Single(), extracted[orig.Key].Single()); + CompareFilesByTimeAndAttribut(orig.Single(), extracted[orig.Key].Single()); + } + } + + protected bool UseExtensionInsteadOfNameToVerify { get; set; } + + protected void VerifyFilesByExtension() + { + var extracted = Directory + .EnumerateFiles(SCRATCH_FILES_PATH, "*.*", SearchOption.AllDirectories) + .ToLookup(path => Path.GetExtension(path)); + var original = Directory + .EnumerateFiles(ORIGINAL_FILES_PATH, "*.*", SearchOption.AllDirectories) + .ToLookup(path => Path.GetExtension(path)); + + Assert.Equal(extracted.Count, original.Count); + + foreach (var orig in original) + { + Assert.True(extracted.Contains(orig.Key)); + + CompareFilesByPath(orig.Single(), extracted[orig.Key].Single()); + } + } + + protected void CompareFilesByPath(string file1, string file2) + { + //TODO: fix line ending issues with the text file + if (file1.EndsWith("txt", StringComparison.OrdinalIgnoreCase)) + { + return; } - public void Dispose() + using var file1Stream = File.OpenRead(file1); + using var file2Stream = File.OpenRead(file2); + Assert.Equal(file1Stream.Length, file2Stream.Length); + var byte1 = 0; + var byte2 = 0; + for (var counter = 0; byte1 != -1; counter++) { - Directory.Delete(SCRATCH_BASE_PATH, true); - } - - public void VerifyFiles() - { - if (UseExtensionInsteadOfNameToVerify) + byte1 = file1Stream.ReadByte(); + byte2 = file2Stream.ReadByte(); + if (byte1 != byte2) { - VerifyFilesByExtension(); - } - else - { - VerifyFilesByName(); - } - } - - /// - /// Verifies the files also check modified time and attributes. - /// - public void VerifyFilesEx() - { - if (UseExtensionInsteadOfNameToVerify) - { - VerifyFilesByExtensionEx(); - } - else - { - VerifyFilesByNameEx(); - } - } - - protected void VerifyFilesByName() - { - var extracted = Directory - .EnumerateFiles(SCRATCH_FILES_PATH, "*.*", SearchOption.AllDirectories) - .ToLookup(path => path.Substring(SCRATCH_FILES_PATH.Length)); - var original = Directory - .EnumerateFiles(ORIGINAL_FILES_PATH, "*.*", SearchOption.AllDirectories) - .ToLookup(path => path.Substring(ORIGINAL_FILES_PATH.Length)); - - Assert.Equal(extracted.Count, original.Count); - - foreach (var orig in original) - { - Assert.True(extracted.Contains(orig.Key)); - - CompareFilesByPath(orig.Single(), extracted[orig.Key].Single()); - } - } - - /// - /// Verifies the files by name also check modified time and attributes. - /// - protected void VerifyFilesByNameEx() - { - var extracted = Directory - .EnumerateFiles(SCRATCH_FILES_PATH, "*.*", SearchOption.AllDirectories) - .ToLookup(path => path.Substring(SCRATCH_FILES_PATH.Length)); - var original = Directory - .EnumerateFiles(ORIGINAL_FILES_PATH, "*.*", SearchOption.AllDirectories) - .ToLookup(path => path.Substring(ORIGINAL_FILES_PATH.Length)); - - Assert.Equal(extracted.Count, original.Count); - - foreach (var orig in original) - { - Assert.True(extracted.Contains(orig.Key)); - - CompareFilesByPath(orig.Single(), extracted[orig.Key].Single()); - CompareFilesByTimeAndAttribut(orig.Single(), extracted[orig.Key].Single()); - } - } - - /// - /// Verifies the files by extension also check modified time and attributes. - /// - protected void VerifyFilesByExtensionEx() - { - var extracted = Directory - .EnumerateFiles(SCRATCH_FILES_PATH, "*.*", SearchOption.AllDirectories) - .ToLookup(path => Path.GetExtension(path)); - var original = Directory - .EnumerateFiles(ORIGINAL_FILES_PATH, "*.*", SearchOption.AllDirectories) - .ToLookup(path => Path.GetExtension(path)); - - Assert.Equal(extracted.Count, original.Count); - - foreach (var orig in original) - { - Assert.True(extracted.Contains(orig.Key)); - - CompareFilesByPath(orig.Single(), extracted[orig.Key].Single()); - CompareFilesByTimeAndAttribut(orig.Single(), extracted[orig.Key].Single()); - } - } - - protected bool UseExtensionInsteadOfNameToVerify { get; set; } - - protected void VerifyFilesByExtension() - { - var extracted = Directory - .EnumerateFiles(SCRATCH_FILES_PATH, "*.*", SearchOption.AllDirectories) - .ToLookup(path => Path.GetExtension(path)); - var original = Directory - .EnumerateFiles(ORIGINAL_FILES_PATH, "*.*", SearchOption.AllDirectories) - .ToLookup(path => Path.GetExtension(path)); - - Assert.Equal(extracted.Count, original.Count); - - foreach (var orig in original) - { - Assert.True(extracted.Contains(orig.Key)); - - CompareFilesByPath(orig.Single(), extracted[orig.Key].Single()); - } - } - - protected void CompareFilesByPath(string file1, string file2) - { - //TODO: fix line ending issues with the text file - if (file1.EndsWith("txt", StringComparison.OrdinalIgnoreCase)) - { - return; - } - - using (var file1Stream = File.OpenRead(file1)) - using (var file2Stream = File.OpenRead(file2)) - { - Assert.Equal(file1Stream.Length, file2Stream.Length); - int byte1 = 0; - int byte2 = 0; - for (int counter = 0; byte1 != -1; counter++) - { - byte1 = file1Stream.ReadByte(); - byte2 = file2Stream.ReadByte(); - if (byte1 != byte2) - { - //string.Format("Byte {0} differ between {1} and {2}", counter, file1, file2) - Assert.Equal(byte1, byte2); - } - } - } - } - - protected void CompareFilesByTimeAndAttribut(string file1, string file2) - { - FileInfo fi1 = new FileInfo(file1); - FileInfo fi2 = new FileInfo(file2); - Assert.NotEqual(fi1.LastWriteTime, fi2.LastWriteTime); - Assert.Equal(fi1.Attributes, fi2.Attributes); - } - - protected void CompareArchivesByPath(string file1, string file2, Encoding? encoding = null) - { - ReaderOptions readerOptions = new ReaderOptions { LeaveStreamOpen = false }; - readerOptions.ArchiveEncoding.Default = encoding ?? Encoding.Default; - - //don't compare the order. OS X reads files from the file system in a different order therefore makes the archive ordering different - var archive1Entries = new List(); - var archive2Entries = new List(); - using (var archive1 = ReaderFactory.Open(File.OpenRead(file1), readerOptions)) - using (var archive2 = ReaderFactory.Open(File.OpenRead(file2), readerOptions)) - { - while (archive1.MoveToNextEntry()) - { - Assert.True(archive2.MoveToNextEntry()); - archive1Entries.Add(archive1.Entry.Key); - archive2Entries.Add(archive2.Entry.Key); - } - Assert.False(archive2.MoveToNextEntry()); - } - archive1Entries.Sort(); - archive2Entries.Sort(); - for (int i = 0; i < archive1Entries.Count; i++) - { - Assert.Equal(archive1Entries[i], archive2Entries[i]); + //string.Format("Byte {0} differ between {1} and {2}", counter, file1, file2) + Assert.Equal(byte1, byte2); } } } + + protected void CompareFilesByTimeAndAttribut(string file1, string file2) + { + var fi1 = new FileInfo(file1); + var fi2 = new FileInfo(file2); + Assert.NotEqual(fi1.LastWriteTime, fi2.LastWriteTime); + Assert.Equal(fi1.Attributes, fi2.Attributes); + } + + protected void CompareArchivesByPath(string file1, string file2, Encoding? encoding = null) + { + var readerOptions = new ReaderOptions { LeaveStreamOpen = false }; + readerOptions.ArchiveEncoding.Default = encoding ?? Encoding.Default; + + //don't compare the order. OS X reads files from the file system in a different order therefore makes the archive ordering different + var archive1Entries = new List(); + var archive2Entries = new List(); + using (var archive1 = ReaderFactory.Open(File.OpenRead(file1), readerOptions)) + using (var archive2 = ReaderFactory.Open(File.OpenRead(file2), readerOptions)) + { + while (archive1.MoveToNextEntry()) + { + Assert.True(archive2.MoveToNextEntry()); + archive1Entries.Add(archive1.Entry.Key); + archive2Entries.Add(archive2.Entry.Key); + } + Assert.False(archive2.MoveToNextEntry()); + } + archive1Entries.Sort(); + archive2Entries.Sort(); + for (var i = 0; i < archive1Entries.Count; i++) + { + Assert.Equal(archive1Entries[i], archive2Entries[i]); + } + } } diff --git a/tests/SharpCompress.Test/WriterTests.cs b/tests/SharpCompress.Test/WriterTests.cs index ad322949..7134ea9e 100644 --- a/tests/SharpCompress.Test/WriterTests.cs +++ b/tests/SharpCompress.Test/WriterTests.cs @@ -5,63 +5,56 @@ using SharpCompress.IO; using SharpCompress.Readers; using SharpCompress.Writers; -namespace SharpCompress.Test +namespace SharpCompress.Test; + +public class WriterTests : TestBase { - public class WriterTests : TestBase + private readonly ArchiveType type; + + protected WriterTests(ArchiveType type) { - private readonly ArchiveType type; + this.type = type; + } - protected WriterTests(ArchiveType type) + protected void Write( + CompressionType compressionType, + string archive, + string archiveToVerifyAgainst, + Encoding? encoding = null + ) + { + using (Stream stream = File.OpenWrite(Path.Combine(SCRATCH2_FILES_PATH, archive))) { - this.type = type; - } - - protected void Write( - CompressionType compressionType, - string archive, - string archiveToVerifyAgainst, - Encoding? encoding = null - ) - { - using (Stream stream = File.OpenWrite(Path.Combine(SCRATCH2_FILES_PATH, archive))) + var writerOptions = new WriterOptions(compressionType) { - WriterOptions writerOptions = new WriterOptions(compressionType) - { - LeaveStreamOpen = true, - }; + LeaveStreamOpen = true, + }; - writerOptions.ArchiveEncoding.Default = encoding ?? Encoding.Default; + writerOptions.ArchiveEncoding.Default = encoding ?? Encoding.Default; - using (var writer = WriterFactory.Open(stream, type, writerOptions)) - { - writer.WriteAll(ORIGINAL_FILES_PATH, "*", SearchOption.AllDirectories); - } - } - CompareArchivesByPath( - Path.Combine(SCRATCH2_FILES_PATH, archive), - Path.Combine(TEST_ARCHIVES_PATH, archiveToVerifyAgainst) + using var writer = WriterFactory.Open(stream, type, writerOptions); + writer.WriteAll(ORIGINAL_FILES_PATH, "*", SearchOption.AllDirectories); + } + CompareArchivesByPath( + Path.Combine(SCRATCH2_FILES_PATH, archive), + Path.Combine(TEST_ARCHIVES_PATH, archiveToVerifyAgainst) + ); + + using (Stream stream = File.OpenRead(Path.Combine(SCRATCH2_FILES_PATH, archive))) + { + var readerOptions = new ReaderOptions(); + + readerOptions.ArchiveEncoding.Default = encoding ?? Encoding.Default; + + using var reader = ReaderFactory.Open( + NonDisposingStream.Create(stream), + readerOptions + ); + reader.WriteAllToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true } ); - - using (Stream stream = File.OpenRead(Path.Combine(SCRATCH2_FILES_PATH, archive))) - { - ReaderOptions readerOptions = new ReaderOptions(); - - readerOptions.ArchiveEncoding.Default = encoding ?? Encoding.Default; - - using ( - var reader = ReaderFactory.Open( - NonDisposingStream.Create(stream), - readerOptions - ) - ) - { - reader.WriteAllToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true } - ); - } - } - VerifyFiles(); } + VerifyFiles(); } } diff --git a/tests/SharpCompress.Test/Xz/Crc32Tests.cs b/tests/SharpCompress.Test/Xz/Crc32Tests.cs index 7afa27a1..7476b748 100644 --- a/tests/SharpCompress.Test/Xz/Crc32Tests.cs +++ b/tests/SharpCompress.Test/Xz/Crc32Tests.cs @@ -1,32 +1,31 @@ -using SharpCompress.Compressors.Xz; using System; using System.Text; +using SharpCompress.Compressors.Xz; using Xunit; -namespace SharpCompress.Test.Xz +namespace SharpCompress.Test.Xz; + +public class Crc32Tests { - public class Crc32Tests + private const string SimpleString = @"The quick brown fox jumps over the lazy dog."; + private readonly byte[] SimpleBytes = Encoding.ASCII.GetBytes(SimpleString); + private const string SimpleString2 = + @"Life moves pretty fast. If you don't stop and look around once in a while, you could miss it."; + private readonly byte[] SimpleBytes2 = Encoding.ASCII.GetBytes(SimpleString2); + + [Fact] + public void ShortAsciiString() { - private const string SimpleString = @"The quick brown fox jumps over the lazy dog."; - private readonly byte[] SimpleBytes = Encoding.ASCII.GetBytes(SimpleString); - private const string SimpleString2 = - @"Life moves pretty fast. If you don't stop and look around once in a while, you could miss it."; - private readonly byte[] SimpleBytes2 = Encoding.ASCII.GetBytes(SimpleString2); + var actual = Crc32.Compute(SimpleBytes); - [Fact] - public void ShortAsciiString() - { - var actual = Crc32.Compute(SimpleBytes); + Assert.Equal((uint)0x519025e9, actual); + } - Assert.Equal((UInt32)0x519025e9, actual); - } + [Fact] + public void ShortAsciiString2() + { + var actual = Crc32.Compute(SimpleBytes2); - [Fact] - public void ShortAsciiString2() - { - var actual = Crc32.Compute(SimpleBytes2); - - Assert.Equal((UInt32)0x6ee3ad88, actual); - } + Assert.Equal((uint)0x6ee3ad88, actual); } } diff --git a/tests/SharpCompress.Test/Xz/Crc64Tests.cs b/tests/SharpCompress.Test/Xz/Crc64Tests.cs index f4ef9493..6b9cb31e 100644 --- a/tests/SharpCompress.Test/Xz/Crc64Tests.cs +++ b/tests/SharpCompress.Test/Xz/Crc64Tests.cs @@ -1,32 +1,31 @@ -using SharpCompress.Compressors.Xz; using System; using System.Text; +using SharpCompress.Compressors.Xz; using Xunit; -namespace SharpCompress.Test.Xz +namespace SharpCompress.Test.Xz; + +public class Crc64Tests { - public class Crc64Tests + private const string SimpleString = @"The quick brown fox jumps over the lazy dog."; + private readonly byte[] SimpleBytes = Encoding.ASCII.GetBytes(SimpleString); + private const string SimpleString2 = + @"Life moves pretty fast. If you don't stop and look around once in a while, you could miss it."; + private readonly byte[] SimpleBytes2 = Encoding.ASCII.GetBytes(SimpleString2); + + [Fact] + public void ShortAsciiString() { - private const string SimpleString = @"The quick brown fox jumps over the lazy dog."; - private readonly byte[] SimpleBytes = Encoding.ASCII.GetBytes(SimpleString); - private const string SimpleString2 = - @"Life moves pretty fast. If you don't stop and look around once in a while, you could miss it."; - private readonly byte[] SimpleBytes2 = Encoding.ASCII.GetBytes(SimpleString2); + var actual = Crc64.Compute(SimpleBytes); - [Fact] - public void ShortAsciiString() - { - var actual = Crc64.Compute(SimpleBytes); + Assert.Equal((ulong)0x7E210EB1B03E5A1D, actual); + } - Assert.Equal((UInt64)0x7E210EB1B03E5A1D, actual); - } + [Fact] + public void ShortAsciiString2() + { + var actual = Crc64.Compute(SimpleBytes2); - [Fact] - public void ShortAsciiString2() - { - var actual = Crc64.Compute(SimpleBytes2); - - Assert.Equal((UInt64)0x416B4150508661EE, actual); - } + Assert.Equal((ulong)0x416B4150508661EE, actual); } } diff --git a/tests/SharpCompress.Test/Xz/Filters/BCJTests.cs b/tests/SharpCompress.Test/Xz/Filters/BCJTests.cs index a18005dd..427a7b98 100644 --- a/tests/SharpCompress.Test/Xz/Filters/BCJTests.cs +++ b/tests/SharpCompress.Test/Xz/Filters/BCJTests.cs @@ -7,83 +7,82 @@ using System.IO; using SharpCompress.Compressors.Xz.Filters; using Xunit; -namespace SharpCompress.Test.Xz.Filters +namespace SharpCompress.Test.Xz.Filters; + +public class BCJTests : XZTestsBase { - public class BCJTests : XZTestsBase + private readonly ArmFilter armFilter; + private readonly ArmThumbFilter armtFilter; + private readonly IA64Filter ia64Filter; + private readonly PowerPCFilter ppcFilter; + private readonly SparcFilter sparcFilter; + private readonly X86Filter x86Filter; + + public BCJTests() { - private readonly ArmFilter armFilter; - private readonly ArmThumbFilter armtFilter; - private readonly IA64Filter ia64Filter; - private readonly PowerPCFilter ppcFilter; - private readonly SparcFilter sparcFilter; - private readonly X86Filter x86Filter; + armFilter = new ArmFilter(); + armtFilter = new ArmThumbFilter(); + ia64Filter = new IA64Filter(); + ppcFilter = new PowerPCFilter(); + sparcFilter = new SparcFilter(); + x86Filter = new X86Filter(); + } - public BCJTests() - { - armFilter = new ArmFilter(); - armtFilter = new ArmThumbFilter(); - ia64Filter = new IA64Filter(); - ppcFilter = new PowerPCFilter(); - sparcFilter = new SparcFilter(); - x86Filter = new X86Filter(); - } + [Fact] + public void IsOnlyAllowedLast() + { + Assert.False(armFilter.AllowAsLast); + Assert.True(armFilter.AllowAsNonLast); - [Fact] - public void IsOnlyAllowedLast() - { - Assert.False(armFilter.AllowAsLast); - Assert.True(armFilter.AllowAsNonLast); + Assert.False(armtFilter.AllowAsLast); + Assert.True(armtFilter.AllowAsNonLast); - Assert.False(armtFilter.AllowAsLast); - Assert.True(armtFilter.AllowAsNonLast); + Assert.False(ia64Filter.AllowAsLast); + Assert.True(ia64Filter.AllowAsNonLast); - Assert.False(ia64Filter.AllowAsLast); - Assert.True(ia64Filter.AllowAsNonLast); + Assert.False(ppcFilter.AllowAsLast); + Assert.True(ppcFilter.AllowAsNonLast); - Assert.False(ppcFilter.AllowAsLast); - Assert.True(ppcFilter.AllowAsNonLast); + Assert.False(sparcFilter.AllowAsLast); + Assert.True(sparcFilter.AllowAsNonLast); - Assert.False(sparcFilter.AllowAsLast); - Assert.True(sparcFilter.AllowAsNonLast); + Assert.False(x86Filter.AllowAsLast); + Assert.True(x86Filter.AllowAsNonLast); + } - Assert.False(x86Filter.AllowAsLast); - Assert.True(x86Filter.AllowAsNonLast); - } + [Fact] + public void ChangesStreamSize() + { + Assert.False(armFilter.ChangesDataSize); + Assert.False(armtFilter.ChangesDataSize); + Assert.False(ia64Filter.ChangesDataSize); + Assert.False(ppcFilter.ChangesDataSize); + Assert.False(sparcFilter.ChangesDataSize); + Assert.False(x86Filter.ChangesDataSize); + } - [Fact] - public void ChangesStreamSize() - { - Assert.False(armFilter.ChangesDataSize); - Assert.False(armtFilter.ChangesDataSize); - Assert.False(ia64Filter.ChangesDataSize); - Assert.False(ppcFilter.ChangesDataSize); - Assert.False(sparcFilter.ChangesDataSize); - Assert.False(x86Filter.ChangesDataSize); - } + [Theory] + [InlineData(new byte[] { 0 })] + [InlineData(new byte[] { 0, 0, 0, 0, 0 })] + public void OnlyAcceptsOneByte(byte[] bytes) + { + InvalidDataException ex; + ex = Assert.Throws(() => armFilter.Init(bytes)); + Assert.Equal("ARM properties unexpected length", ex.Message); - [Theory] - [InlineData(new byte[] { 0 })] - [InlineData(new byte[] { 0, 0, 0, 0, 0 })] - public void OnlyAcceptsOneByte(byte[] bytes) - { - InvalidDataException ex; - ex = Assert.Throws(() => armFilter.Init(bytes)); - Assert.Equal("ARM properties unexpected length", ex.Message); + ex = Assert.Throws(() => armtFilter.Init(bytes)); + Assert.Equal("ARM Thumb properties unexpected length", ex.Message); - ex = Assert.Throws(() => armtFilter.Init(bytes)); - Assert.Equal("ARM Thumb properties unexpected length", ex.Message); + ex = Assert.Throws(() => ia64Filter.Init(bytes)); + Assert.Equal("IA64 properties unexpected length", ex.Message); - ex = Assert.Throws(() => ia64Filter.Init(bytes)); - Assert.Equal("IA64 properties unexpected length", ex.Message); + ex = Assert.Throws(() => ppcFilter.Init(bytes)); + Assert.Equal("PPC properties unexpected length", ex.Message); - ex = Assert.Throws(() => ppcFilter.Init(bytes)); - Assert.Equal("PPC properties unexpected length", ex.Message); + ex = Assert.Throws(() => sparcFilter.Init(bytes)); + Assert.Equal("SPARC properties unexpected length", ex.Message); - ex = Assert.Throws(() => sparcFilter.Init(bytes)); - Assert.Equal("SPARC properties unexpected length", ex.Message); - - ex = Assert.Throws(() => x86Filter.Init(bytes)); - Assert.Equal("X86 properties unexpected length", ex.Message); - } + ex = Assert.Throws(() => x86Filter.Init(bytes)); + Assert.Equal("X86 properties unexpected length", ex.Message); } } diff --git a/tests/SharpCompress.Test/Xz/Filters/Lzma2Tests.cs b/tests/SharpCompress.Test/Xz/Filters/Lzma2Tests.cs index 1f527452..fd6caf61 100644 --- a/tests/SharpCompress.Test/Xz/Filters/Lzma2Tests.cs +++ b/tests/SharpCompress.Test/Xz/Filters/Lzma2Tests.cs @@ -1,4 +1,4 @@ -using System; +using System; using Xunit; using System.IO; using SharpCompress.Compressors.Xz.Filters; diff --git a/tests/SharpCompress.Test/Xz/XZBlockTests.cs b/tests/SharpCompress.Test/Xz/XZBlockTests.cs index 0e070c67..4218fa36 100644 --- a/tests/SharpCompress.Test/Xz/XZBlockTests.cs +++ b/tests/SharpCompress.Test/Xz/XZBlockTests.cs @@ -1,107 +1,102 @@ -using System.Text; using System.IO; +using System.Text; using SharpCompress.Compressors.Xz; using Xunit; -namespace SharpCompress.Test.Xz +namespace SharpCompress.Test.Xz; + +public class XZBlockTests : XZTestsBase { - public class XZBlockTests : XZTestsBase + protected override void Rewind(Stream stream) { - protected override void Rewind(Stream stream) + stream.Position = 12; + } + + protected override void RewindIndexed(Stream stream) + { + stream.Position = 12; + } + + private byte[] ReadBytes(XZBlock block, int bytesToRead) + { + var buffer = new byte[bytesToRead]; + var read = block.Read(buffer, 0, bytesToRead); + if (read != bytesToRead) { - stream.Position = 12; + throw new EndOfStreamException(); } - protected override void RewindIndexed(Stream stream) - { - stream.Position = 12; - } + return buffer; + } - private byte[] ReadBytes(XZBlock block, int bytesToRead) + [Fact] + public void OnFindIndexBlockThrow() + { + var bytes = new byte[] { 0 }; + using Stream indexBlockStream = new MemoryStream(bytes); + var XZBlock = new XZBlock(indexBlockStream, CheckType.CRC64, 8); + Assert.Throws(() => { - byte[] buffer = new byte[bytesToRead]; - var read = block.Read(buffer, 0, bytesToRead); - if (read != bytesToRead) - { - throw new EndOfStreamException(); - } + ReadBytes(XZBlock, 1); + }); + } - return buffer; - } - - [Fact] - public void OnFindIndexBlockThrow() + [Fact] + public void CrcIncorrectThrows() + { + var bytes = (byte[])Compressed.Clone(); + bytes[20]++; + using Stream badCrcStream = new MemoryStream(bytes); + Rewind(badCrcStream); + var XZBlock = new XZBlock(badCrcStream, CheckType.CRC64, 8); + var ex = Assert.Throws(() => { - var bytes = new byte[] { 0 }; - using (Stream indexBlockStream = new MemoryStream(bytes)) - { - var XZBlock = new XZBlock(indexBlockStream, CheckType.CRC64, 8); - Assert.Throws(() => - { - ReadBytes(XZBlock, 1); - }); - } - } + ReadBytes(XZBlock, 1); + }); + Assert.Equal("Block header corrupt", ex.Message); + } - [Fact] - public void CrcIncorrectThrows() - { - var bytes = (byte[])Compressed.Clone(); - bytes[20]++; - using (Stream badCrcStream = new MemoryStream(bytes)) - { - Rewind(badCrcStream); - var XZBlock = new XZBlock(badCrcStream, CheckType.CRC64, 8); - var ex = Assert.Throws(() => - { - ReadBytes(XZBlock, 1); - }); - Assert.Equal("Block header corrupt", ex.Message); - } - } + [Fact] + public void CanReadM() + { + var XZBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); + Assert.Equal(Encoding.ASCII.GetBytes("M"), ReadBytes(XZBlock, 1)); + } - [Fact] - public void CanReadM() - { - var XZBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); - Assert.Equal(Encoding.ASCII.GetBytes("M"), ReadBytes(XZBlock, 1)); - } + [Fact] + public void CanReadMary() + { + var XZBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); + Assert.Equal(Encoding.ASCII.GetBytes("M"), ReadBytes(XZBlock, 1)); + Assert.Equal(Encoding.ASCII.GetBytes("a"), ReadBytes(XZBlock, 1)); + Assert.Equal(Encoding.ASCII.GetBytes("ry"), ReadBytes(XZBlock, 2)); + } - [Fact] - public void CanReadMary() - { - var XZBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); - Assert.Equal(Encoding.ASCII.GetBytes("M"), ReadBytes(XZBlock, 1)); - Assert.Equal(Encoding.ASCII.GetBytes("a"), ReadBytes(XZBlock, 1)); - Assert.Equal(Encoding.ASCII.GetBytes("ry"), ReadBytes(XZBlock, 2)); - } + [Fact] + public void CanReadPoemWithStreamReader() + { + var XZBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); + var sr = new StreamReader(XZBlock); + Assert.Equal(sr.ReadToEnd(), Original); + } - [Fact] - public void CanReadPoemWithStreamReader() - { - var XZBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); - var sr = new StreamReader(XZBlock); - Assert.Equal(sr.ReadToEnd(), Original); - } + [Fact] + public void NoopWhenNoPadding() + { + // CompressedStream's only block has no padding. + var XZBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); + var sr = new StreamReader(XZBlock); + sr.ReadToEnd(); + Assert.Equal(0L, CompressedStream.Position % 4L); + } - [Fact] - public void NoopWhenNoPadding() - { - // CompressedStream's only block has no padding. - var XZBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); - var sr = new StreamReader(XZBlock); - sr.ReadToEnd(); - Assert.Equal(0L, CompressedStream.Position % 4L); - } - - [Fact] - public void SkipsPaddingWhenPresent() - { - // CompressedIndexedStream's first block has 1-byte padding. - var XZBlock = new XZBlock(CompressedIndexedStream, CheckType.CRC64, 8); - var sr = new StreamReader(XZBlock); - sr.ReadToEnd(); - Assert.Equal(0L, CompressedIndexedStream.Position % 4L); - } + [Fact] + public void SkipsPaddingWhenPresent() + { + // CompressedIndexedStream's first block has 1-byte padding. + var XZBlock = new XZBlock(CompressedIndexedStream, CheckType.CRC64, 8); + var sr = new StreamReader(XZBlock); + sr.ReadToEnd(); + Assert.Equal(0L, CompressedIndexedStream.Position % 4L); } } diff --git a/tests/SharpCompress.Test/Xz/XZHeaderTests.cs b/tests/SharpCompress.Test/Xz/XZHeaderTests.cs index 30893fbc..db1f876d 100644 --- a/tests/SharpCompress.Test/Xz/XZHeaderTests.cs +++ b/tests/SharpCompress.Test/Xz/XZHeaderTests.cs @@ -1,4 +1,4 @@ -using SharpCompress.Compressors.Xz; +using SharpCompress.Compressors.Xz; using System.IO; using Xunit; diff --git a/tests/SharpCompress.Test/Xz/XZIndexTests.cs b/tests/SharpCompress.Test/Xz/XZIndexTests.cs index 898d71f4..ecc249e0 100644 --- a/tests/SharpCompress.Test/Xz/XZIndexTests.cs +++ b/tests/SharpCompress.Test/Xz/XZIndexTests.cs @@ -1,4 +1,4 @@ -using SharpCompress.Compressors.Xz; +using SharpCompress.Compressors.Xz; using System.IO; using Xunit; diff --git a/tests/SharpCompress.Test/Xz/XZStreamTests.cs b/tests/SharpCompress.Test/Xz/XZStreamTests.cs index 4b6bbaf6..fea155de 100644 --- a/tests/SharpCompress.Test/Xz/XZStreamTests.cs +++ b/tests/SharpCompress.Test/Xz/XZStreamTests.cs @@ -1,4 +1,4 @@ -using SharpCompress.Compressors.Xz; +using SharpCompress.Compressors.Xz; using System.IO; using Xunit; diff --git a/tests/SharpCompress.Test/Xz/XZTestsBase.cs b/tests/SharpCompress.Test/Xz/XZTestsBase.cs index e5a3b53c..2e931820 100644 --- a/tests/SharpCompress.Test/Xz/XZTestsBase.cs +++ b/tests/SharpCompress.Test/Xz/XZTestsBase.cs @@ -1,1150 +1,1149 @@ -using System; -using System.Text; +using System; using System.IO; +using System.Text; -namespace SharpCompress.Test.Xz +namespace SharpCompress.Test.Xz; + +public abstract class XZTestsBase : IDisposable { - public abstract class XZTestsBase : IDisposable + public XZTestsBase() { - public XZTestsBase() - { - RewindEmpty(CompressedEmptyStream); - Rewind(CompressedStream); - RewindIndexed(CompressedIndexedStream); - } - - public void Dispose() - { - CompressedEmptyStream?.Dispose(); - CompressedStream?.Dispose(); - CompressedIndexedStream?.Dispose(); - } - - protected virtual void RewindEmpty(Stream stream) - { - stream.Position = 0; - } - - protected virtual void Rewind(Stream stream) - { - stream.Position = 0; - } - - protected virtual void RewindIndexed(Stream stream) - { - stream.Position = 0; - } - - protected Stream CompressedEmptyStream { get; } = new MemoryStream(CompressedEmpty); - - protected static byte[] CompressedEmpty { get; } = - new byte[] - { - 0xfd, - 0x37, - 0x7a, - 0x58, - 0x5a, - 0x00, - 0x00, - 0x01, - 0x69, - 0x22, - 0xde, - 0x36, - 0x00, - 0x00, - 0x00, - 0x00, - 0x1c, - 0xdf, - 0x44, - 0x21, - 0x90, - 0x42, - 0x99, - 0x0d, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x01, - 0x59, - 0x5a - }; - - protected static byte[] OriginalEmptyBytes => Encoding.ASCII.GetBytes(OriginalEmpty); - - protected static string OriginalEmpty { get; } = string.Empty; - - protected Stream CompressedStream { get; } = new MemoryStream(Compressed); - - protected static byte[] Compressed { get; } = - new byte[] - { - 0xfd, - 0x37, - 0x7a, - 0x58, - 0x5a, - 0x00, - 0x00, - 0x04, - 0xe6, - 0xd6, - 0xb4, - 0x46, - 0x02, - 0x00, - 0x21, - 0x01, - 0x16, - 0x00, - 0x00, - 0x00, - 0x74, - 0x2f, - 0xe5, - 0xa3, - 0xe0, - 0x01, - 0xe4, - 0x01, - 0x3c, - 0x5d, - 0x00, - 0x26, - 0x98, - 0x4a, - 0x47, - 0xc6, - 0x6a, - 0x27, - 0xd7, - 0x36, - 0x7a, - 0x05, - 0xb9, - 0x4f, - 0xd7, - 0xde, - 0x52, - 0x4c, - 0xca, - 0x26, - 0x4f, - 0x23, - 0x60, - 0x4d, - 0xf3, - 0x1f, - 0xa3, - 0x67, - 0x49, - 0x53, - 0xd0, - 0xf5, - 0xc7, - 0xa9, - 0x3e, - 0xd6, - 0xb5, - 0x3d, - 0x2b, - 0x02, - 0xbe, - 0x83, - 0x27, - 0xe2, - 0xa6, - 0xc3, - 0x13, - 0x4a, - 0x31, - 0x14, - 0x33, - 0xed, - 0x9a, - 0x85, - 0x1d, - 0x05, - 0x6e, - 0x7e, - 0xa4, - 0x91, - 0xbf, - 0x46, - 0x71, - 0x7d, - 0xa7, - 0xfb, - 0x12, - 0x10, - 0xdf, - 0x21, - 0x73, - 0x75, - 0xd8, - 0xd9, - 0xab, - 0x8f, - 0x1f, - 0x8b, - 0xb0, - 0xb9, - 0x3f, - 0x9a, - 0xa5, - 0x1e, - 0xd4, - 0x2f, - 0xdf, - 0x09, - 0xb3, - 0xfe, - 0x45, - 0xef, - 0x16, - 0xec, - 0x95, - 0x68, - 0x64, - 0xbb, - 0x42, - 0x0c, - 0x8b, - 0x96, - 0x27, - 0x30, - 0x62, - 0x42, - 0x91, - 0x7c, - 0xf3, - 0x6e, - 0x4d, - 0x03, - 0xc5, - 0x00, - 0x04, - 0x73, - 0xdd, - 0xee, - 0xb0, - 0xaa, - 0xd6, - 0x0b, - 0x11, - 0x90, - 0x81, - 0xd4, - 0xaa, - 0x69, - 0x63, - 0xfa, - 0x2f, - 0xb4, - 0x25, - 0x0a, - 0x7f, - 0xf9, - 0x47, - 0x77, - 0xb1, - 0x1f, - 0xc3, - 0xb4, - 0x4d, - 0x51, - 0xf8, - 0x23, - 0x3a, - 0x7c, - 0x44, - 0xc8, - 0xcc, - 0xca, - 0x72, - 0x09, - 0xae, - 0xc9, - 0x7b, - 0x7e, - 0x91, - 0x5d, - 0xff, - 0xc4, - 0xeb, - 0xfd, - 0xa1, - 0x9b, - 0xd4, - 0x8d, - 0xd7, - 0xd3, - 0x57, - 0xac, - 0x7e, - 0x3b, - 0x97, - 0x2e, - 0xe4, - 0xc2, - 0x2e, - 0x93, - 0x3d, - 0xb0, - 0x16, - 0x64, - 0x78, - 0x45, - 0xb1, - 0xc9, - 0x40, - 0x96, - 0xcf, - 0x5b, - 0xc2, - 0x2f, - 0xaa, - 0xba, - 0xcf, - 0x98, - 0x38, - 0x21, - 0x3d, - 0x1a, - 0x13, - 0xe8, - 0xa6, - 0xa6, - 0xdf, - 0xf4, - 0x3d, - 0x01, - 0xa1, - 0x9d, - 0xc1, - 0x3e, - 0x37, - 0xac, - 0x20, - 0xc4, - 0xef, - 0x18, - 0xb1, - 0xeb, - 0x35, - 0xf4, - 0x66, - 0x9a, - 0x47, - 0x3c, - 0xce, - 0x7c, - 0xad, - 0xdb, - 0x2e, - 0x39, - 0xf5, - 0x8d, - 0x4a, - 0x1d, - 0x65, - 0xc2, - 0x0f, - 0xa4, - 0x40, - 0x7e, - 0xe6, - 0xa7, - 0x17, - 0xce, - 0x75, - 0x7f, - 0xd9, - 0xa3, - 0xf9, - 0x27, - 0x42, - 0xd7, - 0x98, - 0x54, - 0x17, - 0xa7, - 0x7a, - 0x7c, - 0x82, - 0xdf, - 0xeb, - 0x08, - 0x28, - 0x86, - 0xdd, - 0x57, - 0x77, - 0x92, - 0x80, - 0x5f, - 0x7b, - 0x3b, - 0xce, - 0x77, - 0x72, - 0xff, - 0xa3, - 0x85, - 0xd8, - 0x5c, - 0x8a, - 0xb7, - 0x83, - 0x58, - 0xfa, - 0xbd, - 0x72, - 0xe3, - 0x66, - 0x9d, - 0x3b, - 0xff, - 0x13, - 0x5b, - 0x0b, - 0xf1, - 0x6c, - 0xa6, - 0xb1, - 0x3b, - 0x85, - 0x3b, - 0x47, - 0x91, - 0xc8, - 0x7c, - 0x38, - 0xe2, - 0xe5, - 0x54, - 0xf8, - 0x27, - 0xee, - 0x00, - 0xff, - 0xd3, - 0x68, - 0xf1, - 0xc6, - 0xc7, - 0xd7, - 0x24, - 0x00, - 0x01, - 0xd8, - 0x02, - 0xe5, - 0x03, - 0x00, - 0x00, - 0xac, - 0x16, - 0x1f, - 0xa4, - 0xb1, - 0xc4, - 0x67, - 0xfb, - 0x02, - 0x00, - 0x00, - 0x00, - 0x00, - 0x04, - 0x59, - 0x5a - }; - protected static byte[] OriginalBytes => Encoding.ASCII.GetBytes(Original); - - protected static string Original { get; } = - "Mary had a little lamb,\r\n" - + "His fleece was white as snow,\r\n" - + "And everywhere that Mary went,\r\n" - + "The lamb was sure to go.\r\n" - + "\r\n" - + "He followed her to school one day,\r\n" - + "Which was against the rule,\r\n" - + "It made the children laugh and play\r\n" - + "To see a lamb at school.\r\n" - + "\r\n" - + "And so the teacher turned it out,\r\n" - + "But still it lingered near,\r\n" - + "And waited patiently about,\r\n" - + "Till Mary did appear.\r\n" - + "\r\n" - + "\"Why does the lamb love Mary so?\"\r\n" - + "The eager children cry.\r\n" - + "\"Why, Mary loves the lamb, you know.\"\r\n" - + "The teacher did reply."; - - protected Stream CompressedIndexedStream { get; } = new MemoryStream(CompressedIndexed); - - protected static byte[] CompressedIndexed { get; } = - new byte[] - { - 0xfd, - 0x37, - 0x7a, - 0x58, - 0x5a, - 0x00, - 0x00, - 0x01, - 0x69, - 0x22, - 0xde, - 0x36, - 0x03, - 0xc0, - 0xe3, - 0x02, - 0x80, - 0x20, - 0x21, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0x7e, - 0xe5, - 0xd7, - 0x32, - 0xe0, - 0x0f, - 0xff, - 0x01, - 0x5b, - 0x5d, - 0x00, - 0x26, - 0x98, - 0x4a, - 0x47, - 0xc6, - 0x6a, - 0x27, - 0xd7, - 0x36, - 0x7a, - 0x05, - 0xb9, - 0x4f, - 0xd7, - 0xde, - 0x3a, - 0x0e, - 0xee, - 0x1b, - 0xd7, - 0x81, - 0xe9, - 0xf5, - 0x90, - 0x1e, - 0xd5, - 0x9e, - 0x88, - 0x32, - 0x1c, - 0x7b, - 0x43, - 0x84, - 0x02, - 0x58, - 0x92, - 0xcf, - 0x97, - 0xfc, - 0xae, - 0x01, - 0x83, - 0x23, - 0x48, - 0x93, - 0xc6, - 0x56, - 0xcc, - 0x6d, - 0xb1, - 0x23, - 0x10, - 0x24, - 0x3b, - 0x9e, - 0x06, - 0xaa, - 0xc0, - 0xce, - 0x86, - 0x0a, - 0xb7, - 0x9f, - 0x99, - 0x61, - 0xbe, - 0x3b, - 0x6d, - 0xfe, - 0x60, - 0xef, - 0x14, - 0x35, - 0x7f, - 0x21, - 0xe8, - 0x96, - 0x0e, - 0xbd, - 0x41, - 0x7c, - 0x65, - 0x89, - 0x96, - 0x28, - 0x5e, - 0x85, - 0xa6, - 0x4b, - 0xf3, - 0xf9, - 0xf8, - 0x25, - 0x31, - 0x4a, - 0xbb, - 0x72, - 0xce, - 0xcf, - 0x53, - 0xdf, - 0x13, - 0x42, - 0x2d, - 0xbc, - 0x95, - 0xa5, - 0x6d, - 0xc4, - 0x8c, - 0x72, - 0x99, - 0xe8, - 0x9a, - 0xcf, - 0x80, - 0xd4, - 0xc4, - 0x3f, - 0x55, - 0xc3, - 0x9b, - 0x00, - 0xce, - 0x65, - 0x27, - 0x6e, - 0xbf, - 0xb2, - 0x88, - 0xab, - 0xc0, - 0x5f, - 0xf9, - 0xd0, - 0xc8, - 0xbb, - 0xd7, - 0x48, - 0xd7, - 0x2e, - 0x5e, - 0xbb, - 0x23, - 0x35, - 0x6e, - 0x62, - 0xb6, - 0x13, - 0xd4, - 0x06, - 0xd1, - 0x5b, - 0x97, - 0xee, - 0x5b, - 0x89, - 0x78, - 0x07, - 0x24, - 0x74, - 0x59, - 0x06, - 0x1e, - 0x7f, - 0x8c, - 0xb0, - 0x48, - 0xff, - 0x0a, - 0x76, - 0xb2, - 0x07, - 0xa0, - 0x99, - 0xf5, - 0x4b, - 0x68, - 0xd4, - 0x55, - 0x32, - 0xb3, - 0x17, - 0x7b, - 0xb6, - 0x26, - 0xdb, - 0x1c, - 0xc3, - 0x0b, - 0xda, - 0x3e, - 0x46, - 0xba, - 0x1a, - 0x67, - 0x23, - 0xb7, - 0x2a, - 0x40, - 0xdc, - 0xc9, - 0xa2, - 0xe4, - 0xb5, - 0x68, - 0x5c, - 0x81, - 0x60, - 0xa7, - 0xad, - 0xe6, - 0xba, - 0xbb, - 0x0d, - 0x82, - 0x8a, - 0xe2, - 0x03, - 0xa9, - 0x22, - 0x09, - 0x5e, - 0xd8, - 0x69, - 0xfa, - 0x29, - 0xd1, - 0x32, - 0xa1, - 0xf0, - 0x9b, - 0x3c, - 0xc3, - 0x0b, - 0x9a, - 0x53, - 0xf0, - 0x3e, - 0xf3, - 0x1b, - 0x77, - 0xee, - 0x8f, - 0xa6, - 0x15, - 0x02, - 0x77, - 0x14, - 0x54, - 0x60, - 0xae, - 0xbe, - 0x91, - 0x9e, - 0xe6, - 0x8b, - 0x87, - 0x6e, - 0x46, - 0x44, - 0x64, - 0xc7, - 0x58, - 0x90, - 0x62, - 0x25, - 0x32, - 0xf9, - 0xcd, - 0xd2, - 0x73, - 0x2e, - 0x3f, - 0xd7, - 0x5d, - 0x3c, - 0x86, - 0x1c, - 0xa8, - 0x35, - 0xa9, - 0xc2, - 0xcb, - 0x59, - 0xcb, - 0xac, - 0xb3, - 0x03, - 0x12, - 0xd4, - 0x8a, - 0xde, - 0xd5, - 0xc1, - 0xd8, - 0x0c, - 0x32, - 0x49, - 0x87, - 0x97, - 0x62, - 0x4f, - 0x32, - 0x39, - 0x63, - 0x5b, - 0x8b, - 0xd1, - 0x6c, - 0x5c, - 0x90, - 0xd9, - 0x93, - 0x13, - 0xae, - 0x70, - 0xf5, - 0x2f, - 0x40, - 0xaf, - 0x01, - 0x95, - 0x01, - 0x0c, - 0xc5, - 0xfa, - 0x82, - 0xf8, - 0x71, - 0x9d, - 0x53, - 0xe6, - 0x47, - 0x6e, - 0x99, - 0x54, - 0x57, - 0x41, - 0x72, - 0xea, - 0xf5, - 0x78, - 0xdd, - 0x86, - 0xbd, - 0x00, - 0x00, - 0x00, - 0x72, - 0x6a, - 0xf2, - 0x47, - 0x03, - 0xc0, - 0xcb, - 0x01, - 0x8d, - 0x02, - 0x21, - 0x01, - 0x00, - 0x00, - 0x00, - 0x00, - 0xfb, - 0xa7, - 0xf7, - 0x94, - 0xe0, - 0x01, - 0x0c, - 0x00, - 0xc3, - 0x5d, - 0x00, - 0x06, - 0x82, - 0xca, - 0x9b, - 0x77, - 0x93, - 0x57, - 0xb3, - 0x76, - 0xbd, - 0x8b, - 0xcb, - 0xee, - 0xf4, - 0x2c, - 0xff, - 0x7f, - 0x95, - 0x33, - 0x15, - 0x10, - 0xa5, - 0xf9, - 0xfd, - 0xa6, - 0xbb, - 0x9e, - 0xf9, - 0x75, - 0x67, - 0xee, - 0xec, - 0x8b, - 0x40, - 0xea, - 0x32, - 0x47, - 0x3d, - 0x26, - 0xbe, - 0x11, - 0x9c, - 0xa6, - 0x40, - 0xbe, - 0x84, - 0x1f, - 0x1b, - 0x35, - 0x1a, - 0x66, - 0x10, - 0x9c, - 0xf4, - 0x12, - 0x1a, - 0x95, - 0x81, - 0xb5, - 0x55, - 0x6b, - 0xc5, - 0x42, - 0xfd, - 0x37, - 0x70, - 0xc5, - 0x08, - 0xa4, - 0x27, - 0x67, - 0x11, - 0x0b, - 0x1f, - 0xcc, - 0xdb, - 0x54, - 0x9b, - 0x5a, - 0x5f, - 0xee, - 0x21, - 0x63, - 0xdd, - 0x4b, - 0xbc, - 0x49, - 0x95, - 0x6d, - 0xf4, - 0xcb, - 0x9a, - 0x9a, - 0x5e, - 0xe4, - 0x7d, - 0x0f, - 0x02, - 0x22, - 0xa9, - 0x42, - 0x46, - 0x1a, - 0x04, - 0x87, - 0x43, - 0x72, - 0x59, - 0xa4, - 0xd6, - 0xeb, - 0x69, - 0x36, - 0xde, - 0xea, - 0x53, - 0x8c, - 0x89, - 0xd7, - 0x22, - 0xa6, - 0xf7, - 0xa8, - 0x4c, - 0x72, - 0x6c, - 0x80, - 0x69, - 0x01, - 0xb2, - 0xa7, - 0xe8, - 0x8b, - 0x94, - 0xaf, - 0x0e, - 0x47, - 0x58, - 0x1d, - 0x0e, - 0x5c, - 0x7c, - 0x33, - 0x9f, - 0x21, - 0x17, - 0x2c, - 0x4f, - 0x3d, - 0x72, - 0xff, - 0xcf, - 0x7a, - 0x4f, - 0x82, - 0x5b, - 0x85, - 0x28, - 0x70, - 0xf4, - 0x8c, - 0x81, - 0x41, - 0xb8, - 0x20, - 0x5c, - 0x3e, - 0x02, - 0x5e, - 0x5a, - 0x61, - 0xbb, - 0x2f, - 0x64, - 0xc5, - 0x4e, - 0x53, - 0xe4, - 0xca, - 0xe4, - 0xd9, - 0x75, - 0xaf, - 0x15, - 0x4d, - 0xff, - 0x01, - 0xec, - 0x13, - 0x4a, - 0x70, - 0x00, - 0x04, - 0xf9, - 0xfa, - 0x00, - 0x00, - 0x99, - 0x57, - 0xc4, - 0x96, - 0x00, - 0x02, - 0xf7, - 0x02, - 0x80, - 0x20, - 0xdf, - 0x01, - 0x8d, - 0x02, - 0x00, - 0x00, - 0x4c, - 0x41, - 0xe6, - 0xa1, - 0x9b, - 0xe3, - 0x51, - 0x40, - 0x03, - 0x00, - 0x00, - 0x00, - 0x00, - 0x01, - 0x59, - 0x5a - }; - - protected static byte[] OriginalIndexedBytes => Encoding.ASCII.GetBytes(OriginalIndexed); - - protected static string OriginalIndexed { get; } = - Original - + Original - + Original - + Original - + Original - + Original - + Original - + Original - + Original; + RewindEmpty(CompressedEmptyStream); + Rewind(CompressedStream); + RewindIndexed(CompressedIndexedStream); } + + public void Dispose() + { + CompressedEmptyStream?.Dispose(); + CompressedStream?.Dispose(); + CompressedIndexedStream?.Dispose(); + } + + protected virtual void RewindEmpty(Stream stream) + { + stream.Position = 0; + } + + protected virtual void Rewind(Stream stream) + { + stream.Position = 0; + } + + protected virtual void RewindIndexed(Stream stream) + { + stream.Position = 0; + } + + protected Stream CompressedEmptyStream { get; } = new MemoryStream(CompressedEmpty); + + protected static byte[] CompressedEmpty { get; } = + new byte[] + { + 0xfd, + 0x37, + 0x7a, + 0x58, + 0x5a, + 0x00, + 0x00, + 0x01, + 0x69, + 0x22, + 0xde, + 0x36, + 0x00, + 0x00, + 0x00, + 0x00, + 0x1c, + 0xdf, + 0x44, + 0x21, + 0x90, + 0x42, + 0x99, + 0x0d, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0x59, + 0x5a + }; + + protected static byte[] OriginalEmptyBytes => Encoding.ASCII.GetBytes(OriginalEmpty); + + protected static string OriginalEmpty { get; } = string.Empty; + + protected Stream CompressedStream { get; } = new MemoryStream(Compressed); + + protected static byte[] Compressed { get; } = + new byte[] + { + 0xfd, + 0x37, + 0x7a, + 0x58, + 0x5a, + 0x00, + 0x00, + 0x04, + 0xe6, + 0xd6, + 0xb4, + 0x46, + 0x02, + 0x00, + 0x21, + 0x01, + 0x16, + 0x00, + 0x00, + 0x00, + 0x74, + 0x2f, + 0xe5, + 0xa3, + 0xe0, + 0x01, + 0xe4, + 0x01, + 0x3c, + 0x5d, + 0x00, + 0x26, + 0x98, + 0x4a, + 0x47, + 0xc6, + 0x6a, + 0x27, + 0xd7, + 0x36, + 0x7a, + 0x05, + 0xb9, + 0x4f, + 0xd7, + 0xde, + 0x52, + 0x4c, + 0xca, + 0x26, + 0x4f, + 0x23, + 0x60, + 0x4d, + 0xf3, + 0x1f, + 0xa3, + 0x67, + 0x49, + 0x53, + 0xd0, + 0xf5, + 0xc7, + 0xa9, + 0x3e, + 0xd6, + 0xb5, + 0x3d, + 0x2b, + 0x02, + 0xbe, + 0x83, + 0x27, + 0xe2, + 0xa6, + 0xc3, + 0x13, + 0x4a, + 0x31, + 0x14, + 0x33, + 0xed, + 0x9a, + 0x85, + 0x1d, + 0x05, + 0x6e, + 0x7e, + 0xa4, + 0x91, + 0xbf, + 0x46, + 0x71, + 0x7d, + 0xa7, + 0xfb, + 0x12, + 0x10, + 0xdf, + 0x21, + 0x73, + 0x75, + 0xd8, + 0xd9, + 0xab, + 0x8f, + 0x1f, + 0x8b, + 0xb0, + 0xb9, + 0x3f, + 0x9a, + 0xa5, + 0x1e, + 0xd4, + 0x2f, + 0xdf, + 0x09, + 0xb3, + 0xfe, + 0x45, + 0xef, + 0x16, + 0xec, + 0x95, + 0x68, + 0x64, + 0xbb, + 0x42, + 0x0c, + 0x8b, + 0x96, + 0x27, + 0x30, + 0x62, + 0x42, + 0x91, + 0x7c, + 0xf3, + 0x6e, + 0x4d, + 0x03, + 0xc5, + 0x00, + 0x04, + 0x73, + 0xdd, + 0xee, + 0xb0, + 0xaa, + 0xd6, + 0x0b, + 0x11, + 0x90, + 0x81, + 0xd4, + 0xaa, + 0x69, + 0x63, + 0xfa, + 0x2f, + 0xb4, + 0x25, + 0x0a, + 0x7f, + 0xf9, + 0x47, + 0x77, + 0xb1, + 0x1f, + 0xc3, + 0xb4, + 0x4d, + 0x51, + 0xf8, + 0x23, + 0x3a, + 0x7c, + 0x44, + 0xc8, + 0xcc, + 0xca, + 0x72, + 0x09, + 0xae, + 0xc9, + 0x7b, + 0x7e, + 0x91, + 0x5d, + 0xff, + 0xc4, + 0xeb, + 0xfd, + 0xa1, + 0x9b, + 0xd4, + 0x8d, + 0xd7, + 0xd3, + 0x57, + 0xac, + 0x7e, + 0x3b, + 0x97, + 0x2e, + 0xe4, + 0xc2, + 0x2e, + 0x93, + 0x3d, + 0xb0, + 0x16, + 0x64, + 0x78, + 0x45, + 0xb1, + 0xc9, + 0x40, + 0x96, + 0xcf, + 0x5b, + 0xc2, + 0x2f, + 0xaa, + 0xba, + 0xcf, + 0x98, + 0x38, + 0x21, + 0x3d, + 0x1a, + 0x13, + 0xe8, + 0xa6, + 0xa6, + 0xdf, + 0xf4, + 0x3d, + 0x01, + 0xa1, + 0x9d, + 0xc1, + 0x3e, + 0x37, + 0xac, + 0x20, + 0xc4, + 0xef, + 0x18, + 0xb1, + 0xeb, + 0x35, + 0xf4, + 0x66, + 0x9a, + 0x47, + 0x3c, + 0xce, + 0x7c, + 0xad, + 0xdb, + 0x2e, + 0x39, + 0xf5, + 0x8d, + 0x4a, + 0x1d, + 0x65, + 0xc2, + 0x0f, + 0xa4, + 0x40, + 0x7e, + 0xe6, + 0xa7, + 0x17, + 0xce, + 0x75, + 0x7f, + 0xd9, + 0xa3, + 0xf9, + 0x27, + 0x42, + 0xd7, + 0x98, + 0x54, + 0x17, + 0xa7, + 0x7a, + 0x7c, + 0x82, + 0xdf, + 0xeb, + 0x08, + 0x28, + 0x86, + 0xdd, + 0x57, + 0x77, + 0x92, + 0x80, + 0x5f, + 0x7b, + 0x3b, + 0xce, + 0x77, + 0x72, + 0xff, + 0xa3, + 0x85, + 0xd8, + 0x5c, + 0x8a, + 0xb7, + 0x83, + 0x58, + 0xfa, + 0xbd, + 0x72, + 0xe3, + 0x66, + 0x9d, + 0x3b, + 0xff, + 0x13, + 0x5b, + 0x0b, + 0xf1, + 0x6c, + 0xa6, + 0xb1, + 0x3b, + 0x85, + 0x3b, + 0x47, + 0x91, + 0xc8, + 0x7c, + 0x38, + 0xe2, + 0xe5, + 0x54, + 0xf8, + 0x27, + 0xee, + 0x00, + 0xff, + 0xd3, + 0x68, + 0xf1, + 0xc6, + 0xc7, + 0xd7, + 0x24, + 0x00, + 0x01, + 0xd8, + 0x02, + 0xe5, + 0x03, + 0x00, + 0x00, + 0xac, + 0x16, + 0x1f, + 0xa4, + 0xb1, + 0xc4, + 0x67, + 0xfb, + 0x02, + 0x00, + 0x00, + 0x00, + 0x00, + 0x04, + 0x59, + 0x5a + }; + protected static byte[] OriginalBytes => Encoding.ASCII.GetBytes(Original); + + protected static string Original { get; } = + "Mary had a little lamb,\r\n" + + "His fleece was white as snow,\r\n" + + "And everywhere that Mary went,\r\n" + + "The lamb was sure to go.\r\n" + + "\r\n" + + "He followed her to school one day,\r\n" + + "Which was against the rule,\r\n" + + "It made the children laugh and play\r\n" + + "To see a lamb at school.\r\n" + + "\r\n" + + "And so the teacher turned it out,\r\n" + + "But still it lingered near,\r\n" + + "And waited patiently about,\r\n" + + "Till Mary did appear.\r\n" + + "\r\n" + + "\"Why does the lamb love Mary so?\"\r\n" + + "The eager children cry.\r\n" + + "\"Why, Mary loves the lamb, you know.\"\r\n" + + "The teacher did reply."; + + protected Stream CompressedIndexedStream { get; } = new MemoryStream(CompressedIndexed); + + protected static byte[] CompressedIndexed { get; } = + new byte[] + { + 0xfd, + 0x37, + 0x7a, + 0x58, + 0x5a, + 0x00, + 0x00, + 0x01, + 0x69, + 0x22, + 0xde, + 0x36, + 0x03, + 0xc0, + 0xe3, + 0x02, + 0x80, + 0x20, + 0x21, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0x7e, + 0xe5, + 0xd7, + 0x32, + 0xe0, + 0x0f, + 0xff, + 0x01, + 0x5b, + 0x5d, + 0x00, + 0x26, + 0x98, + 0x4a, + 0x47, + 0xc6, + 0x6a, + 0x27, + 0xd7, + 0x36, + 0x7a, + 0x05, + 0xb9, + 0x4f, + 0xd7, + 0xde, + 0x3a, + 0x0e, + 0xee, + 0x1b, + 0xd7, + 0x81, + 0xe9, + 0xf5, + 0x90, + 0x1e, + 0xd5, + 0x9e, + 0x88, + 0x32, + 0x1c, + 0x7b, + 0x43, + 0x84, + 0x02, + 0x58, + 0x92, + 0xcf, + 0x97, + 0xfc, + 0xae, + 0x01, + 0x83, + 0x23, + 0x48, + 0x93, + 0xc6, + 0x56, + 0xcc, + 0x6d, + 0xb1, + 0x23, + 0x10, + 0x24, + 0x3b, + 0x9e, + 0x06, + 0xaa, + 0xc0, + 0xce, + 0x86, + 0x0a, + 0xb7, + 0x9f, + 0x99, + 0x61, + 0xbe, + 0x3b, + 0x6d, + 0xfe, + 0x60, + 0xef, + 0x14, + 0x35, + 0x7f, + 0x21, + 0xe8, + 0x96, + 0x0e, + 0xbd, + 0x41, + 0x7c, + 0x65, + 0x89, + 0x96, + 0x28, + 0x5e, + 0x85, + 0xa6, + 0x4b, + 0xf3, + 0xf9, + 0xf8, + 0x25, + 0x31, + 0x4a, + 0xbb, + 0x72, + 0xce, + 0xcf, + 0x53, + 0xdf, + 0x13, + 0x42, + 0x2d, + 0xbc, + 0x95, + 0xa5, + 0x6d, + 0xc4, + 0x8c, + 0x72, + 0x99, + 0xe8, + 0x9a, + 0xcf, + 0x80, + 0xd4, + 0xc4, + 0x3f, + 0x55, + 0xc3, + 0x9b, + 0x00, + 0xce, + 0x65, + 0x27, + 0x6e, + 0xbf, + 0xb2, + 0x88, + 0xab, + 0xc0, + 0x5f, + 0xf9, + 0xd0, + 0xc8, + 0xbb, + 0xd7, + 0x48, + 0xd7, + 0x2e, + 0x5e, + 0xbb, + 0x23, + 0x35, + 0x6e, + 0x62, + 0xb6, + 0x13, + 0xd4, + 0x06, + 0xd1, + 0x5b, + 0x97, + 0xee, + 0x5b, + 0x89, + 0x78, + 0x07, + 0x24, + 0x74, + 0x59, + 0x06, + 0x1e, + 0x7f, + 0x8c, + 0xb0, + 0x48, + 0xff, + 0x0a, + 0x76, + 0xb2, + 0x07, + 0xa0, + 0x99, + 0xf5, + 0x4b, + 0x68, + 0xd4, + 0x55, + 0x32, + 0xb3, + 0x17, + 0x7b, + 0xb6, + 0x26, + 0xdb, + 0x1c, + 0xc3, + 0x0b, + 0xda, + 0x3e, + 0x46, + 0xba, + 0x1a, + 0x67, + 0x23, + 0xb7, + 0x2a, + 0x40, + 0xdc, + 0xc9, + 0xa2, + 0xe4, + 0xb5, + 0x68, + 0x5c, + 0x81, + 0x60, + 0xa7, + 0xad, + 0xe6, + 0xba, + 0xbb, + 0x0d, + 0x82, + 0x8a, + 0xe2, + 0x03, + 0xa9, + 0x22, + 0x09, + 0x5e, + 0xd8, + 0x69, + 0xfa, + 0x29, + 0xd1, + 0x32, + 0xa1, + 0xf0, + 0x9b, + 0x3c, + 0xc3, + 0x0b, + 0x9a, + 0x53, + 0xf0, + 0x3e, + 0xf3, + 0x1b, + 0x77, + 0xee, + 0x8f, + 0xa6, + 0x15, + 0x02, + 0x77, + 0x14, + 0x54, + 0x60, + 0xae, + 0xbe, + 0x91, + 0x9e, + 0xe6, + 0x8b, + 0x87, + 0x6e, + 0x46, + 0x44, + 0x64, + 0xc7, + 0x58, + 0x90, + 0x62, + 0x25, + 0x32, + 0xf9, + 0xcd, + 0xd2, + 0x73, + 0x2e, + 0x3f, + 0xd7, + 0x5d, + 0x3c, + 0x86, + 0x1c, + 0xa8, + 0x35, + 0xa9, + 0xc2, + 0xcb, + 0x59, + 0xcb, + 0xac, + 0xb3, + 0x03, + 0x12, + 0xd4, + 0x8a, + 0xde, + 0xd5, + 0xc1, + 0xd8, + 0x0c, + 0x32, + 0x49, + 0x87, + 0x97, + 0x62, + 0x4f, + 0x32, + 0x39, + 0x63, + 0x5b, + 0x8b, + 0xd1, + 0x6c, + 0x5c, + 0x90, + 0xd9, + 0x93, + 0x13, + 0xae, + 0x70, + 0xf5, + 0x2f, + 0x40, + 0xaf, + 0x01, + 0x95, + 0x01, + 0x0c, + 0xc5, + 0xfa, + 0x82, + 0xf8, + 0x71, + 0x9d, + 0x53, + 0xe6, + 0x47, + 0x6e, + 0x99, + 0x54, + 0x57, + 0x41, + 0x72, + 0xea, + 0xf5, + 0x78, + 0xdd, + 0x86, + 0xbd, + 0x00, + 0x00, + 0x00, + 0x72, + 0x6a, + 0xf2, + 0x47, + 0x03, + 0xc0, + 0xcb, + 0x01, + 0x8d, + 0x02, + 0x21, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + 0xfb, + 0xa7, + 0xf7, + 0x94, + 0xe0, + 0x01, + 0x0c, + 0x00, + 0xc3, + 0x5d, + 0x00, + 0x06, + 0x82, + 0xca, + 0x9b, + 0x77, + 0x93, + 0x57, + 0xb3, + 0x76, + 0xbd, + 0x8b, + 0xcb, + 0xee, + 0xf4, + 0x2c, + 0xff, + 0x7f, + 0x95, + 0x33, + 0x15, + 0x10, + 0xa5, + 0xf9, + 0xfd, + 0xa6, + 0xbb, + 0x9e, + 0xf9, + 0x75, + 0x67, + 0xee, + 0xec, + 0x8b, + 0x40, + 0xea, + 0x32, + 0x47, + 0x3d, + 0x26, + 0xbe, + 0x11, + 0x9c, + 0xa6, + 0x40, + 0xbe, + 0x84, + 0x1f, + 0x1b, + 0x35, + 0x1a, + 0x66, + 0x10, + 0x9c, + 0xf4, + 0x12, + 0x1a, + 0x95, + 0x81, + 0xb5, + 0x55, + 0x6b, + 0xc5, + 0x42, + 0xfd, + 0x37, + 0x70, + 0xc5, + 0x08, + 0xa4, + 0x27, + 0x67, + 0x11, + 0x0b, + 0x1f, + 0xcc, + 0xdb, + 0x54, + 0x9b, + 0x5a, + 0x5f, + 0xee, + 0x21, + 0x63, + 0xdd, + 0x4b, + 0xbc, + 0x49, + 0x95, + 0x6d, + 0xf4, + 0xcb, + 0x9a, + 0x9a, + 0x5e, + 0xe4, + 0x7d, + 0x0f, + 0x02, + 0x22, + 0xa9, + 0x42, + 0x46, + 0x1a, + 0x04, + 0x87, + 0x43, + 0x72, + 0x59, + 0xa4, + 0xd6, + 0xeb, + 0x69, + 0x36, + 0xde, + 0xea, + 0x53, + 0x8c, + 0x89, + 0xd7, + 0x22, + 0xa6, + 0xf7, + 0xa8, + 0x4c, + 0x72, + 0x6c, + 0x80, + 0x69, + 0x01, + 0xb2, + 0xa7, + 0xe8, + 0x8b, + 0x94, + 0xaf, + 0x0e, + 0x47, + 0x58, + 0x1d, + 0x0e, + 0x5c, + 0x7c, + 0x33, + 0x9f, + 0x21, + 0x17, + 0x2c, + 0x4f, + 0x3d, + 0x72, + 0xff, + 0xcf, + 0x7a, + 0x4f, + 0x82, + 0x5b, + 0x85, + 0x28, + 0x70, + 0xf4, + 0x8c, + 0x81, + 0x41, + 0xb8, + 0x20, + 0x5c, + 0x3e, + 0x02, + 0x5e, + 0x5a, + 0x61, + 0xbb, + 0x2f, + 0x64, + 0xc5, + 0x4e, + 0x53, + 0xe4, + 0xca, + 0xe4, + 0xd9, + 0x75, + 0xaf, + 0x15, + 0x4d, + 0xff, + 0x01, + 0xec, + 0x13, + 0x4a, + 0x70, + 0x00, + 0x04, + 0xf9, + 0xfa, + 0x00, + 0x00, + 0x99, + 0x57, + 0xc4, + 0x96, + 0x00, + 0x02, + 0xf7, + 0x02, + 0x80, + 0x20, + 0xdf, + 0x01, + 0x8d, + 0x02, + 0x00, + 0x00, + 0x4c, + 0x41, + 0xe6, + 0xa1, + 0x9b, + 0xe3, + 0x51, + 0x40, + 0x03, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0x59, + 0x5a + }; + + protected static byte[] OriginalIndexedBytes => Encoding.ASCII.GetBytes(OriginalIndexed); + + protected static string OriginalIndexed { get; } = + Original + + Original + + Original + + Original + + Original + + Original + + Original + + Original + + Original; } diff --git a/tests/SharpCompress.Test/Zip/Zip64Tests.cs b/tests/SharpCompress.Test/Zip/Zip64Tests.cs index c1f4453f..d19913e1 100644 --- a/tests/SharpCompress.Test/Zip/Zip64Tests.cs +++ b/tests/SharpCompress.Test/Zip/Zip64Tests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Linq; using SharpCompress.Archives; @@ -10,224 +10,218 @@ using SharpCompress.Writers; using SharpCompress.Writers.Zip; using Xunit; -namespace SharpCompress.Test.Zip +namespace SharpCompress.Test.Zip; + +public class Zip64Tests : WriterTests { - public class Zip64Tests : WriterTests + public Zip64Tests() : base(ArchiveType.Zip) { } + + // 4GiB + 1 + private const long FOUR_GB_LIMIT = ((long)uint.MaxValue) + 1; + + [Trait("format", "zip64")] + public void Zip64_Single_Large_File() { - public Zip64Tests() : base(ArchiveType.Zip) { } + // One single file, requires zip64 + RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: true, forward_only: false); + } - // 4GiB + 1 - private const long FOUR_GB_LIMIT = ((long)uint.MaxValue) + 1; + [Trait("format", "zip64")] + public void Zip64_Two_Large_Files() + { + // One single file, requires zip64 + RunSingleTest(2, FOUR_GB_LIMIT, set_zip64: true, forward_only: false); + } - [Trait("format", "zip64")] - public void Zip64_Single_Large_File() + [Trait("format", "zip64")] + public void Zip64_Two_Small_files() + { + // Multiple files, does not require zip64 + RunSingleTest(2, FOUR_GB_LIMIT / 2, set_zip64: false, forward_only: false); + } + + [Trait("format", "zip64")] + public void Zip64_Two_Small_files_stream() + { + // Multiple files, does not require zip64, and works with streams + RunSingleTest(2, FOUR_GB_LIMIT / 2, set_zip64: false, forward_only: true); + } + + [Trait("format", "zip64")] + public void Zip64_Two_Small_Files_Zip64() + { + // Multiple files, use zip64 even though it is not required + RunSingleTest(2, FOUR_GB_LIMIT / 2, set_zip64: true, forward_only: false); + } + + [Trait("format", "zip64")] + public void Zip64_Single_Large_File_Fail() + { + try { - // One single file, requires zip64 - RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: true, forward_only: false); + // One single file, should fail + RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: false, forward_only: false); + throw new InvalidOperationException("Test did not fail?"); + } + catch (NotSupportedException) { } + } + + [Trait("zip64", "true")] + public void Zip64_Single_Large_File_Zip64_Streaming_Fail() + { + try + { + // One single file, should fail (fast) with zip64 + RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: true, forward_only: true); + throw new InvalidOperationException("Test did not fail?"); + } + catch (NotSupportedException) { } + } + + [Trait("zip64", "true")] + public void Zip64_Single_Large_File_Streaming_Fail() + { + try + { + // One single file, should fail once the write discovers the problem + RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: false, forward_only: true); + throw new InvalidOperationException("Test did not fail?"); + } + catch (NotSupportedException) { } + } + + public void RunSingleTest( + long files, + long filesize, + bool set_zip64, + bool forward_only, + long write_chunk_size = 1024 * 1024, + string filename = "zip64-test.zip" + ) + { + filename = Path.Combine(SCRATCH2_FILES_PATH, filename); + + if (File.Exists(filename)) + { + File.Delete(filename); } - [Trait("format", "zip64")] - public void Zip64_Two_Large_Files() + if (!File.Exists(filename)) { - // One single file, requires zip64 - RunSingleTest(2, FOUR_GB_LIMIT, set_zip64: true, forward_only: false); + CreateZipArchive( + filename, + files, + filesize, + write_chunk_size, + set_zip64, + forward_only + ); } - [Trait("format", "zip64")] - public void Zip64_Two_Small_files() + var resForward = ReadForwardOnly(filename); + if (resForward.Item1 != files) { - // Multiple files, does not require zip64 - RunSingleTest(2, FOUR_GB_LIMIT / 2, set_zip64: false, forward_only: false); + throw new InvalidOperationException( + $"Incorrect number of items reported: {resForward.Item1}, should have been {files}" + ); } - [Trait("format", "zip64")] - public void Zip64_Two_Small_files_stream() + if (resForward.Item2 != files * filesize) { - // Multiple files, does not require zip64, and works with streams - RunSingleTest(2, FOUR_GB_LIMIT / 2, set_zip64: false, forward_only: true); + throw new InvalidOperationException( + $"Incorrect combined size reported: {resForward.Item2}, should have been {files * filesize}" + ); } - [Trait("format", "zip64")] - public void Zip64_Two_Small_Files_Zip64() + var resArchive = ReadArchive(filename); + if (resArchive.Item1 != files) { - // Multiple files, use zip64 even though it is not required - RunSingleTest(2, FOUR_GB_LIMIT / 2, set_zip64: true, forward_only: false); + throw new InvalidOperationException( + $"Incorrect number of items reported: {resArchive.Item1}, should have been {files}" + ); } - [Trait("format", "zip64")] - public void Zip64_Single_Large_File_Fail() + if (resArchive.Item2 != files * filesize) { - try - { - // One single file, should fail - RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: false, forward_only: false); - throw new InvalidOperationException("Test did not fail?"); - } - catch (NotSupportedException) { } + throw new InvalidOperationException( + $"Incorrect number of items reported: {resArchive.Item2}, should have been {files * filesize}" + ); } + } - [Trait("zip64", "true")] - public void Zip64_Single_Large_File_Zip64_Streaming_Fail() + public void CreateZipArchive( + string filename, + long files, + long filesize, + long chunksize, + bool set_zip64, + bool forward_only + ) + { + var data = new byte[chunksize]; + + // Use deflate for speed + var opts = new ZipWriterOptions(CompressionType.Deflate) { UseZip64 = set_zip64 }; + + // Use no compression to ensure we hit the limits (actually inflates a bit, but seems better than using method==Store) + var eo = new ZipWriterEntryOptions() { - try - { - // One single file, should fail (fast) with zip64 - RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: true, forward_only: true); - throw new InvalidOperationException("Test did not fail?"); - } - catch (NotSupportedException) { } - } + DeflateCompressionLevel = Compressors.Deflate.CompressionLevel.None + }; - [Trait("zip64", "true")] - public void Zip64_Single_Large_File_Streaming_Fail() + using var zip = File.OpenWrite(filename); + using var st = forward_only ? (Stream)new ForwardOnlyStream(zip) : zip; + using var zipWriter = (ZipWriter)WriterFactory.Open(st, ArchiveType.Zip, opts); + for (var i = 0; i < files; i++) { - try + using var str = zipWriter.WriteToStream(i.ToString(), eo); + var left = filesize; + while (left > 0) { - // One single file, should fail once the write discovers the problem - RunSingleTest(1, FOUR_GB_LIMIT, set_zip64: false, forward_only: true); - throw new InvalidOperationException("Test did not fail?"); - } - catch (NotSupportedException) { } - } - - public void RunSingleTest( - long files, - long filesize, - bool set_zip64, - bool forward_only, - long write_chunk_size = 1024 * 1024, - string filename = "zip64-test.zip" - ) - { - filename = Path.Combine(SCRATCH2_FILES_PATH, filename); - - if (File.Exists(filename)) - { - File.Delete(filename); - } - - if (!File.Exists(filename)) - { - CreateZipArchive( - filename, - files, - filesize, - write_chunk_size, - set_zip64, - forward_only - ); - } - - var resForward = ReadForwardOnly(filename); - if (resForward.Item1 != files) - { - throw new InvalidOperationException( - $"Incorrect number of items reported: {resForward.Item1}, should have been {files}" - ); - } - - if (resForward.Item2 != files * filesize) - { - throw new InvalidOperationException( - $"Incorrect combined size reported: {resForward.Item2}, should have been {files * filesize}" - ); - } - - var resArchive = ReadArchive(filename); - if (resArchive.Item1 != files) - { - throw new InvalidOperationException( - $"Incorrect number of items reported: {resArchive.Item1}, should have been {files}" - ); - } - - if (resArchive.Item2 != files * filesize) - { - throw new InvalidOperationException( - $"Incorrect number of items reported: {resArchive.Item2}, should have been {files * filesize}" - ); - } - } - - public void CreateZipArchive( - string filename, - long files, - long filesize, - long chunksize, - bool set_zip64, - bool forward_only - ) - { - var data = new byte[chunksize]; - - // Use deflate for speed - var opts = new ZipWriterOptions(CompressionType.Deflate) { UseZip64 = set_zip64 }; - - // Use no compression to ensure we hit the limits (actually inflates a bit, but seems better than using method==Store) - var eo = new ZipWriterEntryOptions() - { - DeflateCompressionLevel = Compressors.Deflate.CompressionLevel.None - }; - - using (var zip = File.OpenWrite(filename)) - using (var st = forward_only ? (Stream)new ForwardOnlyStream(zip) : zip) - using (var zipWriter = (ZipWriter)WriterFactory.Open(st, ArchiveType.Zip, opts)) - { - for (var i = 0; i < files; i++) - { - using (var str = zipWriter.WriteToStream(i.ToString(), eo)) - { - var left = filesize; - while (left > 0) - { - var b = (int)Math.Min(left, data.Length); - str.Write(data, 0, b); - left -= b; - } - } - } - } - } - - public Tuple ReadForwardOnly(string filename) - { - long count = 0; - long size = 0; - Common.Zip.ZipEntry? prev = null; - using (var fs = File.OpenRead(filename)) - using (var rd = ZipReader.Open(fs, new ReaderOptions() { LookForHeader = false })) - { - while (rd.MoveToNextEntry()) - { - using (rd.OpenEntryStream()) { } - - count++; - if (prev != null) - { - size += prev.Size; - } - - prev = rd.Entry; - } - } - - if (prev != null) - { - size += prev.Size; - } - - return new Tuple(count, size); - } - - public Tuple ReadArchive(string filename) - { - using (var archive = ArchiveFactory.Open(filename)) - { - return new Tuple( - archive.Entries.Count(), - archive.Entries.Select(x => x.Size).Sum() - ); + var b = (int)Math.Min(left, data.Length); + str.Write(data, 0, b); + left -= b; } } } + + public Tuple ReadForwardOnly(string filename) + { + long count = 0; + long size = 0; + Common.Zip.ZipEntry? prev = null; + using (var fs = File.OpenRead(filename)) + using (var rd = ZipReader.Open(fs, new ReaderOptions() { LookForHeader = false })) + { + while (rd.MoveToNextEntry()) + { + using (rd.OpenEntryStream()) + { } + + count++; + if (prev != null) + { + size += prev.Size; + } + + prev = rd.Entry; + } + } + + if (prev != null) + { + size += prev.Size; + } + + return new Tuple(count, size); + } + + public Tuple ReadArchive(string filename) + { + using var archive = ArchiveFactory.Open(filename); + return new Tuple( + archive.Entries.Count(), + archive.Entries.Select(x => x.Size).Sum() + ); + } } diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs index c1375801..738223a7 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Linq; using System.Text; diff --git a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs index 446c0e82..dab65cc0 100644 --- a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs @@ -8,263 +8,223 @@ using SharpCompress.Test.Mocks; using SharpCompress.Writers; using Xunit; -namespace SharpCompress.Test.Zip +namespace SharpCompress.Test.Zip; + +public class ZipReaderTests : ReaderTests { - public class ZipReaderTests : ReaderTests + public ZipReaderTests() { - public ZipReaderTests() - { - UseExtensionInsteadOfNameToVerify = true; - } + UseExtensionInsteadOfNameToVerify = true; + } - [Fact] - public void Issue_269_Double_Skip() + [Fact] + public void Issue_269_Double_Skip() + { + var path = Path.Combine(TEST_ARCHIVES_PATH, "PrePostHeaders.zip"); + using Stream stream = new ForwardOnlyStream(File.OpenRead(path)); + using var reader = ReaderFactory.Open(stream); + var count = 0; + while (reader.MoveToNextEntry()) { - var path = Path.Combine(TEST_ARCHIVES_PATH, "PrePostHeaders.zip"); - using (Stream stream = new ForwardOnlyStream(File.OpenRead(path))) - using (IReader reader = ReaderFactory.Open(stream)) + count++; + if (!reader.Entry.IsDirectory) { - int count = 0; - while (reader.MoveToNextEntry()) + if (count % 2 != 0) { - count++; - if (!reader.Entry.IsDirectory) - { - if (count % 2 != 0) - { - reader.WriteEntryTo(Stream.Null); - } - } + reader.WriteEntryTo(Stream.Null); } } } + } - [Fact] - public void Zip_Zip64_Streamed_Read() + [Fact] + public void Zip_Zip64_Streamed_Read() + { + Read("Zip.zip64.zip", CompressionType.Deflate); + } + + [Fact] + public void Zip_ZipX_Streamed_Read() + { + Read("Zip.zipx", CompressionType.LZMA); + } + + [Fact] + public void Zip_BZip2_Streamed_Read() + { + Read("Zip.bzip2.dd.zip", CompressionType.BZip2); + } + + [Fact] + public void Zip_BZip2_Read() + { + Read("Zip.bzip2.zip", CompressionType.BZip2); + } + + [Fact] + public void Zip_Deflate_Streamed2_Read() + { + Read("Zip.deflate.dd-.zip", CompressionType.Deflate); + } + + [Fact] + public void Zip_Deflate_Streamed_Read() + { + Read("Zip.deflate.dd.zip", CompressionType.Deflate); + } + + [Fact] + public void Zip_Deflate_Streamed_Skip() + { + using Stream stream = new ForwardOnlyStream( + File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.dd.zip")) + ); + using var reader = ReaderFactory.Open(stream); + var x = 0; + while (reader.MoveToNextEntry()) { - Read("Zip.zip64.zip", CompressionType.Deflate); + if (!reader.Entry.IsDirectory) + { + x++; + if (x % 2 == 0) + { + reader.WriteEntryToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); + } + } } + } - [Fact] - public void Zip_ZipX_Streamed_Read() - { - Read("Zip.zipx", CompressionType.LZMA); - } + [Fact] + public void Zip_Deflate_Read() + { + Read("Zip.deflate.zip", CompressionType.Deflate); + } - [Fact] - public void Zip_BZip2_Streamed_Read() - { - Read("Zip.bzip2.dd.zip", CompressionType.BZip2); - } + [Fact] + public void Zip_Deflate64_Read() + { + Read("Zip.deflate64.zip", CompressionType.Deflate64); + } - [Fact] - public void Zip_BZip2_Read() - { - Read("Zip.bzip2.zip", CompressionType.BZip2); - } + [Fact] + public void Zip_LZMA_Streamed_Read() + { + Read("Zip.lzma.dd.zip", CompressionType.LZMA); + } - [Fact] - public void Zip_Deflate_Streamed2_Read() - { - Read("Zip.deflate.dd-.zip", CompressionType.Deflate); - } + [Fact] + public void Zip_LZMA_Read() + { + Read("Zip.lzma.zip", CompressionType.LZMA); + } - [Fact] - public void Zip_Deflate_Streamed_Read() - { - Read("Zip.deflate.dd.zip", CompressionType.Deflate); - } + [Fact] + public void Zip_PPMd_Streamed_Read() + { + Read("Zip.ppmd.dd.zip", CompressionType.PPMd); + } - [Fact] - public void Zip_Deflate_Streamed_Skip() - { - using ( - Stream stream = new ForwardOnlyStream( - File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.dd.zip")) - ) + [Fact] + public void Zip_PPMd_Read() + { + Read("Zip.ppmd.zip", CompressionType.PPMd); + } + + [Fact] + public void Zip_None_Read() + { + Read("Zip.none.zip", CompressionType.None); + } + + [Fact] + public void Zip_Deflate_NoEmptyDirs_Read() + { + Read("Zip.deflate.noEmptyDirs.zip", CompressionType.Deflate); + } + + [Fact] + public void Zip_BZip2_PkwareEncryption_Read() + { + using ( + Stream stream = File.OpenRead( + Path.Combine(TEST_ARCHIVES_PATH, "Zip.bzip2.pkware.zip") ) - using (IReader reader = ReaderFactory.Open(stream)) + ) + using (var reader = ZipReader.Open(stream, new ReaderOptions() { Password = "test" })) + { + while (reader.MoveToNextEntry()) { - int x = 0; - while (reader.MoveToNextEntry()) + if (!reader.Entry.IsDirectory) { - if (!reader.Entry.IsDirectory) - { - x++; - if (x % 2 == 0) - { - reader.WriteEntryToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } + Assert.Equal(CompressionType.BZip2, reader.Entry.CompressionType); + reader.WriteEntryToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); } } } + VerifyFiles(); + } - [Fact] - public void Zip_Deflate_Read() + [Fact] + public void Zip_Reader_Disposal_Test() + { + using var stream = new TestStream( + File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.dd.zip")) + ); + using (var reader = ReaderFactory.Open(stream)) { - Read("Zip.deflate.zip", CompressionType.Deflate); + while (reader.MoveToNextEntry()) + { + if (!reader.Entry.IsDirectory) + { + reader.WriteEntryToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); + } + } } + Assert.True(stream.IsDisposed); + } - [Fact] - public void Zip_Deflate64_Read() + [Fact] + public void Zip_Reader_Disposal_Test2() + { + using var stream = new TestStream( + File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.dd.zip")) + ); + var reader = ReaderFactory.Open(stream); + while (reader.MoveToNextEntry()) { - Read("Zip.deflate64.zip", CompressionType.Deflate64); + if (!reader.Entry.IsDirectory) + { + reader.WriteEntryToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); + } } + Assert.False(stream.IsDisposed); + } - [Fact] - public void Zip_LZMA_Streamed_Read() - { - Read("Zip.lzma.dd.zip", CompressionType.LZMA); - } - - [Fact] - public void Zip_LZMA_Read() - { - Read("Zip.lzma.zip", CompressionType.LZMA); - } - - [Fact] - public void Zip_PPMd_Streamed_Read() - { - Read("Zip.ppmd.dd.zip", CompressionType.PPMd); - } - - [Fact] - public void Zip_PPMd_Read() - { - Read("Zip.ppmd.zip", CompressionType.PPMd); - } - - [Fact] - public void Zip_None_Read() - { - Read("Zip.none.zip", CompressionType.None); - } - - [Fact] - public void Zip_Deflate_NoEmptyDirs_Read() - { - Read("Zip.deflate.noEmptyDirs.zip", CompressionType.Deflate); - } - - [Fact] - public void Zip_BZip2_PkwareEncryption_Read() + [Fact] + public void Zip_LZMA_WinzipAES_Read() + { + Assert.Throws(() => { using ( Stream stream = File.OpenRead( - Path.Combine(TEST_ARCHIVES_PATH, "Zip.bzip2.pkware.zip") + Path.Combine(TEST_ARCHIVES_PATH, "Zip.lzma.WinzipAES.zip") ) ) - using (var reader = ZipReader.Open(stream, new ReaderOptions() { Password = "test" })) - { - while (reader.MoveToNextEntry()) - { - if (!reader.Entry.IsDirectory) - { - Assert.Equal(CompressionType.BZip2, reader.Entry.CompressionType); - reader.WriteEntryToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - } - VerifyFiles(); - } - - [Fact] - public void Zip_Reader_Disposal_Test() - { using ( - TestStream stream = new TestStream( - File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.dd.zip")) - ) + var reader = ZipReader.Open(stream, new ReaderOptions() { Password = "test" }) ) - { - using (var reader = ReaderFactory.Open(stream)) - { - while (reader.MoveToNextEntry()) - { - if (!reader.Entry.IsDirectory) - { - reader.WriteEntryToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - } - Assert.True(stream.IsDisposed); - } - } - - [Fact] - public void Zip_Reader_Disposal_Test2() - { - using ( - TestStream stream = new TestStream( - File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.dd.zip")) - ) - ) - { - var reader = ReaderFactory.Open(stream); - while (reader.MoveToNextEntry()) - { - if (!reader.Entry.IsDirectory) - { - reader.WriteEntryToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - Assert.False(stream.IsDisposed); - } - } - - [Fact] - public void Zip_LZMA_WinzipAES_Read() - { - Assert.Throws(() => - { - using ( - Stream stream = File.OpenRead( - Path.Combine(TEST_ARCHIVES_PATH, "Zip.lzma.WinzipAES.zip") - ) - ) - using ( - var reader = ZipReader.Open(stream, new ReaderOptions() { Password = "test" }) - ) - { - while (reader.MoveToNextEntry()) - { - if (!reader.Entry.IsDirectory) - { - Assert.Equal(CompressionType.Unknown, reader.Entry.CompressionType); - reader.WriteEntryToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - } - } - } - VerifyFiles(); - }); - } - - [Fact] - public void Zip_Deflate_WinzipAES_Read() - { - using ( - Stream stream = File.OpenRead( - Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.WinzipAES.zip") - ) - ) - using (var reader = ZipReader.Open(stream, new ReaderOptions() { Password = "test" })) { while (reader.MoveToNextEntry()) { @@ -279,187 +239,193 @@ namespace SharpCompress.Test.Zip } } VerifyFiles(); - } + }); + } - [Fact] - public void Zip_Deflate_ZipCrypto_Read() - { - int count = 0; - using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "zipcrypto.zip"))) - using (var reader = ZipReader.Open(stream, new ReaderOptions() { Password = "test" })) - { - while (reader.MoveToNextEntry()) - { - if (!reader.Entry.IsDirectory) - { - Assert.Equal(CompressionType.None, reader.Entry.CompressionType); - reader.WriteEntryToDirectory( - SCRATCH_FILES_PATH, - new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } - ); - count++; - } - } - } - Assert.Equal(8, count); - } - - [Fact] - public void TestSharpCompressWithEmptyStream() - { - var expected = new Tuple[] - { - new Tuple("foo.txt", Array.Empty()), - new Tuple("foo2.txt", new byte[10]) - }; - - using (var memory = new MemoryStream()) - { - Stream stream = new TestStream(memory, read: true, write: true, seek: false); - - using ( - IWriter zipWriter = WriterFactory.Open( - stream, - ArchiveType.Zip, - CompressionType.Deflate - ) - ) - { - zipWriter.Write(expected[0].Item1, new MemoryStream(expected[0].Item2)); - zipWriter.Write(expected[1].Item1, new MemoryStream(expected[1].Item2)); - } - - stream = new MemoryStream(memory.ToArray()); - File.WriteAllBytes(Path.Combine(SCRATCH_FILES_PATH, "foo.zip"), memory.ToArray()); - - using (IReader zipReader = ZipReader.Open(NonDisposingStream.Create(stream, true))) - { - var i = 0; - while (zipReader.MoveToNextEntry()) - { - using (EntryStream entry = zipReader.OpenEntryStream()) - { - MemoryStream tempStream = new MemoryStream(); - const int bufSize = 0x1000; - byte[] buf = new byte[bufSize]; - int bytesRead = 0; - while ((bytesRead = entry.Read(buf, 0, bufSize)) > 0) - { - tempStream.Write(buf, 0, bytesRead); - } - - Assert.Equal(expected[i].Item1, zipReader.Entry.Key); - Assert.Equal(expected[i].Item2, tempStream.ToArray()); - } - i++; - } - } - } - } - - [Fact] - public void Zip_None_Issue86_Streamed_Read() - { - var keys = new string[] - { - "Empty1", - "Empty2", - "Dir1/", - "Dir2/", - "Fake1", - "Fake2", - "Internal.zip" - }; - - using ( - Stream stream = File.OpenRead( - Path.Combine(TEST_ARCHIVES_PATH, "Zip.none.issue86.zip") - ) + [Fact] + public void Zip_Deflate_WinzipAES_Read() + { + using ( + Stream stream = File.OpenRead( + Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.WinzipAES.zip") ) - using (var reader = ZipReader.Open(stream)) - { - foreach (var key in keys) - { - reader.MoveToNextEntry(); - - Assert.Equal(reader.Entry.Key, key); - - if (!reader.Entry.IsDirectory) - { - Assert.Equal(CompressionType.None, reader.Entry.CompressionType); - } - } - - Assert.False(reader.MoveToNextEntry()); - } - } - - [Fact] - public void Zip_ReaderMoveToNextEntry() + ) + using (var reader = ZipReader.Open(stream, new ReaderOptions() { Password = "test" })) { - var keys = new string[] { "version", "sizehint", "data/0/metadata", "data/0/records" }; - - using (var fileStream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "test_477.zip"))) - using (var reader = ZipReader.Open(fileStream)) + while (reader.MoveToNextEntry()) { - foreach (var key in keys) + if (!reader.Entry.IsDirectory) { - reader.MoveToNextEntry(); - - Assert.Equal(reader.Entry.Key, key); + Assert.Equal(CompressionType.Unknown, reader.Entry.CompressionType); + reader.WriteEntryToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); } } } + VerifyFiles(); + } - [Fact] - public void Issue_685() + [Fact] + public void Zip_Deflate_ZipCrypto_Read() + { + var count = 0; + using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "zipcrypto.zip"))) + using (var reader = ZipReader.Open(stream, new ReaderOptions() { Password = "test" })) { - var count = 0; - using ( - var fileStream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Issue_685.zip")) - ) - using (var reader = ZipReader.Open(fileStream)) + while (reader.MoveToNextEntry()) { - while (reader.MoveToNextEntry()) + if (!reader.Entry.IsDirectory) { + Assert.Equal(CompressionType.None, reader.Entry.CompressionType); + reader.WriteEntryToDirectory( + SCRATCH_FILES_PATH, + new ExtractionOptions() { ExtractFullPath = true, Overwrite = true } + ); count++; - reader.OpenEntryStream().Dispose(); // Uncomment for workaround - } - Assert.Equal(4, count); - } - } - - [Fact] - public void Zip_Uncompressed_Skip_All() - { - var zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.uncompressed.zip"); - using (var stream = File.Open(zipPath, FileMode.Open, FileAccess.Read)) - { - using (var reader = ReaderFactory.Open(stream)) - { - while (reader.MoveToNextEntry()) { } } } } + Assert.Equal(8, count); + } - [Fact] - public void Zip_Uncompressed_64bit() + [Fact] + public void TestSharpCompressWithEmptyStream() + { + var expected = new Tuple[] { - var zipPath = Path.Combine(TEST_ARCHIVES_PATH, "64bitstream.zip.7z"); - using (var stream = File.Open(zipPath, FileMode.Open, FileAccess.Read)) + new Tuple("foo.txt", Array.Empty()), + new Tuple("foo2.txt", new byte[10]) + }; + + using var memory = new MemoryStream(); + Stream stream = new TestStream(memory, read: true, write: true, seek: false); + + using ( + var zipWriter = WriterFactory.Open( + stream, + ArchiveType.Zip, + CompressionType.Deflate + ) + ) + { + zipWriter.Write(expected[0].Item1, new MemoryStream(expected[0].Item2)); + zipWriter.Write(expected[1].Item1, new MemoryStream(expected[1].Item2)); + } + + stream = new MemoryStream(memory.ToArray()); + File.WriteAllBytes(Path.Combine(SCRATCH_FILES_PATH, "foo.zip"), memory.ToArray()); + + using IReader zipReader = ZipReader.Open(NonDisposingStream.Create(stream, true)); + var i = 0; + while (zipReader.MoveToNextEntry()) + { + using (var entry = zipReader.OpenEntryStream()) { - var archive = Archives.ArchiveFactory.Open(stream); - var reader = archive.ExtractAllEntries(); - reader.MoveToNextEntry(); - var zipReader = ZipReader.Open(reader.OpenEntryStream()); - var x = 0; - while (zipReader.MoveToNextEntry()) + var tempStream = new MemoryStream(); + const int bufSize = 0x1000; + var buf = new byte[bufSize]; + var bytesRead = 0; + while ((bytesRead = entry.Read(buf, 0, bufSize)) > 0) { - x++; + tempStream.Write(buf, 0, bytesRead); } - Assert.Equal(4, x); + Assert.Equal(expected[i].Item1, zipReader.Entry.Key); + Assert.Equal(expected[i].Item2, tempStream.ToArray()); } + i++; } } + + [Fact] + public void Zip_None_Issue86_Streamed_Read() + { + var keys = new string[] + { + "Empty1", + "Empty2", + "Dir1/", + "Dir2/", + "Fake1", + "Fake2", + "Internal.zip" + }; + + using Stream stream = File.OpenRead( + Path.Combine(TEST_ARCHIVES_PATH, "Zip.none.issue86.zip") + ); + using var reader = ZipReader.Open(stream); + foreach (var key in keys) + { + reader.MoveToNextEntry(); + + Assert.Equal(reader.Entry.Key, key); + + if (!reader.Entry.IsDirectory) + { + Assert.Equal(CompressionType.None, reader.Entry.CompressionType); + } + } + + Assert.False(reader.MoveToNextEntry()); + } + + [Fact] + public void Zip_ReaderMoveToNextEntry() + { + var keys = new string[] { "version", "sizehint", "data/0/metadata", "data/0/records" }; + + using var fileStream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "test_477.zip")); + using var reader = ZipReader.Open(fileStream); + foreach (var key in keys) + { + reader.MoveToNextEntry(); + + Assert.Equal(reader.Entry.Key, key); + } + } + + [Fact] + public void Issue_685() + { + var count = 0; + using var fileStream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Issue_685.zip")); + using var reader = ZipReader.Open(fileStream); + while (reader.MoveToNextEntry()) + { + count++; + reader.OpenEntryStream().Dispose(); // Uncomment for workaround + } + Assert.Equal(4, count); + } + + [Fact] + public void Zip_Uncompressed_Skip_All() + { + var zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.uncompressed.zip"); + using var stream = File.Open(zipPath, FileMode.Open, FileAccess.Read); + using var reader = ReaderFactory.Open(stream); + while (reader.MoveToNextEntry()) + { } + } + + [Fact] + public void Zip_Uncompressed_64bit() + { + var zipPath = Path.Combine(TEST_ARCHIVES_PATH, "64bitstream.zip.7z"); + using var stream = File.Open(zipPath, FileMode.Open, FileAccess.Read); + var archive = Archives.ArchiveFactory.Open(stream); + var reader = archive.ExtractAllEntries(); + reader.MoveToNextEntry(); + var zipReader = ZipReader.Open(reader.OpenEntryStream()); + var x = 0; + while (zipReader.MoveToNextEntry()) + { + x++; + } + + Assert.Equal(4, x); + } } diff --git a/tests/SharpCompress.Test/Zip/ZipWriterTests.cs b/tests/SharpCompress.Test/Zip/ZipWriterTests.cs index 28e90f51..c073a8a2 100644 --- a/tests/SharpCompress.Test/Zip/ZipWriterTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipWriterTests.cs @@ -3,78 +3,77 @@ using System.Text; using SharpCompress.Common; using Xunit; -namespace SharpCompress.Test.Zip +namespace SharpCompress.Test.Zip; + +public class ZipWriterTests : WriterTests { - public class ZipWriterTests : WriterTests + public ZipWriterTests() : base(ArchiveType.Zip) { } + + [Fact] + public void Zip_Deflate_Write() { - public ZipWriterTests() : base(ArchiveType.Zip) { } + Write( + CompressionType.Deflate, + "Zip.deflate.noEmptyDirs.zip", + "Zip.deflate.noEmptyDirs.zip", + Encoding.UTF8 + ); + } - [Fact] - public void Zip_Deflate_Write() - { - Write( - CompressionType.Deflate, - "Zip.deflate.noEmptyDirs.zip", - "Zip.deflate.noEmptyDirs.zip", - Encoding.UTF8 - ); - } + [Fact] + public void Zip_BZip2_Write() + { + Write( + CompressionType.BZip2, + "Zip.bzip2.noEmptyDirs.zip", + "Zip.bzip2.noEmptyDirs.zip", + Encoding.UTF8 + ); + } - [Fact] - public void Zip_BZip2_Write() - { - Write( - CompressionType.BZip2, - "Zip.bzip2.noEmptyDirs.zip", - "Zip.bzip2.noEmptyDirs.zip", - Encoding.UTF8 - ); - } + [Fact] + public void Zip_None_Write() + { + Write( + CompressionType.None, + "Zip.none.noEmptyDirs.zip", + "Zip.none.noEmptyDirs.zip", + Encoding.UTF8 + ); + } - [Fact] - public void Zip_None_Write() - { - Write( - CompressionType.None, - "Zip.none.noEmptyDirs.zip", - "Zip.none.noEmptyDirs.zip", - Encoding.UTF8 - ); - } + [Fact] + public void Zip_LZMA_Write() + { + Write( + CompressionType.LZMA, + "Zip.lzma.noEmptyDirs.zip", + "Zip.lzma.noEmptyDirs.zip", + Encoding.UTF8 + ); + } - [Fact] - public void Zip_LZMA_Write() - { - Write( - CompressionType.LZMA, - "Zip.lzma.noEmptyDirs.zip", - "Zip.lzma.noEmptyDirs.zip", - Encoding.UTF8 - ); - } + [Fact] + public void Zip_PPMd_Write() + { + Write( + CompressionType.PPMd, + "Zip.ppmd.noEmptyDirs.zip", + "Zip.ppmd.noEmptyDirs.zip", + Encoding.UTF8 + ); + } - [Fact] - public void Zip_PPMd_Write() - { - Write( - CompressionType.PPMd, - "Zip.ppmd.noEmptyDirs.zip", - "Zip.ppmd.noEmptyDirs.zip", - Encoding.UTF8 - ); - } - - [Fact] - public void Zip_Rar_Write() - { - Assert.Throws( - () => - Write( - CompressionType.Rar, - "Zip.ppmd.noEmptyDirs.zip", - "Zip.ppmd.noEmptyDirs.zip" - ) - ); - } + [Fact] + public void Zip_Rar_Write() + { + Assert.Throws( + () => + Write( + CompressionType.Rar, + "Zip.ppmd.noEmptyDirs.zip", + "Zip.ppmd.noEmptyDirs.zip" + ) + ); } }