#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2013 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 "MultiDecoderContext.h" #include "StreamProcessor.h" namespace Nuclex { namespace Storage { namespace FileSystem { namespace SevenZip { // ------------------------------------------------------------------------------------------- // MultiDecoderContext::MultiDecoderContext( const std::shared_ptr &sevenZipArchive, std::size_t solidBlockIndex ) : StreamingCacheContext( solidBlockIndex, sevenZipArchive->GetArchiveBlob(), sevenZipArchive->GetStreamStartPosition(solidBlockIndex), sevenZipArchive->GetStreamSize(solidBlockIndex) ), // CHECK: Is the first stream always the main stream? sevenZipArchive(sevenZipArchive), inputBufferLocation(0) {} // ------------------------------------------------------------------------------------------- // void MultiDecoderContext::AddStreamProcessor( const std::shared_ptr &streamProcessor ) { this->streamProcessors.push_back(streamProcessor); } // ------------------------------------------------------------------------------------------- // void MultiDecoderContext::SkipTo(std::uint64_t location) { this->Location += this->UncompressedData.size(); this->UncompressedData.resize(this->UncompressedData.capacity()); for(;;) { std::size_t decompressedLength = this->UncompressedData.size(); // If the input buffer is running low, refill it std::size_t remainingInputLength = this->CompressedData.size() - this->inputBufferLocation; if(remainingInputLength <= BufferRefillSize) { RefillInputBuffer(remainingInputLength); this->inputBufferLocation = 0; remainingInputLength = this->CompressedData.size(); } if(this->streamProcessors.size() == 1) { this->streamProcessors.front()->Process( &this->CompressedData[this->inputBufferLocation], remainingInputLength, &this->UncompressedData[0], decompressedLength ); this->inputBufferLocation += remainingInputLength; } else { throw std::runtime_error( "Compression methods with multiple coders are not supported yet" ); } if(this->Location + decompressedLength > location) { this->UncompressedData.resize(decompressedLength); return; } this->Location += decompressedLength; } } // ------------------------------------------------------------------------------------------- // std::size_t MultiDecoderContext::ExtractDirectly( std::uint8_t *indexableBuffer, std::size_t count ) { this->Location += this->UncompressedData.size(); this->UncompressedData.resize(0); // When this method is called it is not known whether the input buffer is still // full or if it is running low. Only the output buffer is guaranteed to be empty. std::size_t inputLength = this->CompressedData.size() - this->inputBufferLocation; if(inputLength <= BufferRefillSize) { RefillInputBuffer(inputLength); this->inputBufferLocation = 0; inputLength = this->CompressedData.size(); } // Keep extracting the input buffer into the user-provided memory block until we // have extracted everything the caller was interested in. for(;;) { std::size_t decompressedLength = count; if(this->streamProcessors.size() == 1) { this->streamProcessors.front()->Process( &this->CompressedData[this->inputBufferLocation], inputLength, indexableBuffer, decompressedLength ); } else { throw std::runtime_error( "Compression methods with multiple coders are not supported yet" ); } this->Location += decompressedLength; // If everything has been extracted in this go we can stop right here if(decompressedLength >= count) { { using namespace std; assert( (decompressedLength == count) && "7-zip decoder should only extract the amount of data it was instructed to" ); } return 0; } indexableBuffer += decompressedLength; count -= decompressedLength; // There can only be one reason for reaching this point: the contents of the input buffer // have been completely consumed, so refill it. RefillInputBuffer(this->CompressedData.size() - inputLength); // If there is less than the low water mark of data remaining, extract the rest // through the buffer (we don't know if the decompressor supports point landings, // it might need to extract to the next multiple of something bytes). if(count < BufferRefillSize) { return count; } } } // ------------------------------------------------------------------------------------------- // void MultiDecoderContext::ExtractUsingBuffer( std::uint8_t *indexableBuffer, std::size_t count ) { { using namespace std; assert( (count <= DecompressionBufferSize) && "Buffered extraction should only be used if amount of data is less than buffer capacity" ); } this->Location += this->UncompressedData.size(); this->UncompressedData.resize(this->UncompressedData.capacity()); // When this method is called it is not known whether the input buffer is still // full or if it is running low. Only the output buffer is guaranteed to be empty. std::size_t remainingInputLength = this->CompressedData.size() - this->inputBufferLocation; if(remainingInputLength <= BufferRefillSize) { RefillInputBuffer(remainingInputLength); this->inputBufferLocation = 0; remainingInputLength = this->CompressedData.size(); } std::size_t decompressedOffset = 0; for(;;) { std::size_t decompressedLength = this->UncompressedData.size() - decompressedOffset; if(this->streamProcessors.size() == 1) { this->streamProcessors.front()->Process( &this->CompressedData[this->inputBufferLocation], remainingInputLength, &this->UncompressedData[decompressedOffset], decompressedLength ); this->inputBufferLocation += remainingInputLength; } else { throw std::runtime_error( "Compression methods with multiple coders are not supported yet" ); } // If everything has been extracted in this go we can stop right here if(decompressedLength >= count) { assert( (decompressedLength <= this->UncompressedData.size()) && "Stream processor should only extract the amount of data it was instructed to" ); this->UncompressedData.resize(decompressedLength); std::copy_n(&this->UncompressedData[0], count, indexableBuffer); return; } decompressedOffset += decompressedLength; // There can only be one reason for reaching this point: the contents of the input buffer // have been completely consumed, so refill it. RefillInputBuffer(this->CompressedData.size() - this->inputBufferLocation); } } // ------------------------------------------------------------------------------------------- // }}}} // namespace Nuclex::Storage::FileSystem::SevenZip