#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2021 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License // If the library is compiled as a DLL, this ensures symbols are exported #define NUCLEX_STORAGE_SOURCE 1 #include "ZPaqDecompressor.h" #if defined(NUCLEX_STORAGE_HAVE_ZPAQ) #include "Nuclex/Storage/Binary/InputStream.h" #include "Nuclex/Storage/Binary/OutputStream.h" #include #include namespace Nuclex { namespace Storage { namespace Compression { namespace ZPaq { // ------------------------------------------------------------------------------------------- // ZPaqDecompressor::ZPaqDecompressor() : hasFoundBlock(false), hasFoundFilename(false), isFinishing(false) { this->decompressor.setInput(&this->reader); this->decompressor.setOutput(&this->writer); } // ------------------------------------------------------------------------------------------- // ZPaqDecompressor::~ZPaqDecompressor() { } // ------------------------------------------------------------------------------------------- // StopReason ZPaqDecompressor::Process( const std::uint8_t *compressedBuffer, std::size_t &compressedByteCount, std::uint8_t *outputBuffer, std::size_t &outputByteCount ) { // Because we cannot stop the ZPaq compressor until it has processed all input // bytes, it may generate more output than we want. This will have been saved by // our special buffer writer. If there's still output waiting in the buffer writer, // it'll be written to the output buffer here std::size_t overflowedByteCount = this->writer.TargetNewOutputBuffer( outputBuffer, outputByteCount ); if(overflowedByteCount >= outputByteCount) { return StopReason::OutputBufferFull; // outputByteCount can remain as-is } // Select the new input buffer and let libzpaq do the compression this->reader.SetInputBuffer(compressedBuffer, compressedByteCount); // ZPaq comes with a whole container format for backups and archiving. We could either // work around the container (requiring this implementation to deal with internal ZPaq // classes and maintain it when ZPaq updates) or just go with the smallest container // possible and keep things simple at the cost of a few extra bytes. // // We took the latter choice, so now need to skip some container format data here... if(!this->hasFoundBlock) { this->hasFoundBlock = this->decompressor.findBlock(); if(!this->hasFoundBlock) { throw std::runtime_error(u8"libzpaq could not find a compression block in the data"); } } if(!this->hasFoundFilename) { this->hasFoundFilename = this->decompressor.findFilename(); if(!this->hasFoundFilename) { throw std::runtime_error(u8"libzpaq could not find a file header in the data"); } this->decompressor.readComment(); } // Decompress the assert(compressedByteCount < std::numeric_limits::max()); bool moreInputFollowing = this->decompressor.decompress( static_cast(compressedByteCount) ); (void)moreInputFollowing; // Some compilers like to complain even with the assert... assert((!moreInputFollowing) && u8"libzpaq accessed more bytes than told to"); std::size_t remainingOutputByteCount = this->writer.GetRemainingByteCount(); if(remainingOutputByteCount == 0) { return StopReason::OutputBufferFull; } else { outputByteCount -= remainingOutputByteCount; return StopReason::InputBufferExhausted; } } // ------------------------------------------------------------------------------------------- // StopReason ZPaqDecompressor::Finish( std::uint8_t *outputBuffer, std::size_t &outputByteCount ) { // Because we cannot stop the ZPaq decompressor until it has processed all input // bytes, it may generate more output than we want. This will have been saved by // our special buffer writer. If there's still output waiting in the buffer writer, // it'll be written to the output buffer here std::size_t overflowedByteCount = this->writer.TargetNewOutputBuffer( outputBuffer, outputByteCount ); if(overflowedByteCount >= outputByteCount) { return StopReason::OutputBufferFull; // outputByteCount can remain as-is } if(!this->isFinishing) { this->decompressor.readSegmentEnd(); this->isFinishing = true; } if(this->writer.HasOverflowBytes()) { return StopReason::OutputBufferFull; } else { outputByteCount -= this->writer.GetRemainingByteCount(); return StopReason::Finished; } } // ------------------------------------------------------------------------------------------- // }}}} // namespace Nuclex::Storage::Compression::ZPaq #endif // defined(NUCLEX_STORAGE_HAVE_ZPAQ)