/*- * Copyright 2007 Guram Dukashvili * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ //--------------------------------------------------------------------------- #include #ifndef NDEBUG //--------------------------------------------------------------------------- #include #define _NO_EXCEPTIONS 1 #include #include #include #include #include #include #include #include #include #include //------------------------------------------------------------------------------ namespace ksys { //------------------------------------------------------------------------------ extern "C" const char IID_ICompressWriteCoderProperties[] = ""; extern "C" const char IID_ICompressSetCoderProperties[] = ""; extern "C" const char IID_ICompressSetOutStream[] = ""; extern "C" const char IID_ICompressGetInStreamProcessedSize[] = ""; extern "C" const char IID_ICompressSetDecoderProperties2[] = ""; //------------------------------------------------------------------------------ //////////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------ void LZMAFilter::Encoder::fiberExecute() { while( !terminated_ ){ HRESULT hr = WriteCoderProperties(this); if( SUCCEEDED(hr) ){ hr = Code(this,this,NULL,NULL,NULL); assert( flush_ ); } err_ = HRESULT_CODE(hr); if( guest_ == NULL ){ sem_.post(); } else { guest_->event_.type_ = etDispatch; guest_->thread()->postEvent(&guest_->event_); } switchFiber(mainFiber()); } } //--------------------------------------------------------------------------- HRESULT STDMETHODCALLTYPE LZMAFilter::Encoder::Read(void * data,UInt32 size,UInt32 * processedSize) { if( processedSize != NULL ) *processedSize = 0; for(;;){ UInt32 r = size > count_ ? UInt32(count_) : size; if( r > 0 ){ memcpy(data,data_,r); data = (uint8_t *) data + r; data_ = (const uint8_t *) data_ + r; count_ -= r; size -= r; if( processedSize != NULL ) *processedSize += r; } if( size == 0 || flush_ ) break; if( count_ == 0 ){ if( guest_ == NULL ){ sem_.post(); } else { guest_->event_.type_ = etDispatch; guest_->thread()->postEvent(&guest_->event_); } switchFiber(mainFiber()); } } return S_OK; } //--------------------------------------------------------------------------- HRESULT STDMETHODCALLTYPE LZMAFilter::Encoder::Write(const void *data,UInt32 size,UInt32 * processedSize) { HRESULT hr = S_OK; try { filter_->writeCompressedBuffer(data,size); if( processedSize != NULL ) *processedSize = size; } catch( ExceptionSP & e ){ hr = HRESULT_FROM_WIN32(e->code()); } return hr; } //------------------------------------------------------------------------------ //////////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------ void LZMAFilter::Decoder::fiberExecute() { while( !terminated_ ){ uint8_t coderProperties[5]; HRESULT hr = Read(coderProperties,sizeof(coderProperties),NULL); if( SUCCEEDED(hr) ){ hr = SetDecoderProperties2(coderProperties,sizeof(coderProperties)); if( SUCCEEDED(hr) ){ hr = Code(this,this,NULL,NULL,NULL); assert( flush_ ); } } err_ = HRESULT_CODE(hr); if( guest_ == NULL ){ sem_.post(); } else { guest_->event_.type_ = etDispatch; guest_->thread()->postEvent(&guest_->event_); } switchFiber(mainFiber()); } } //------------------------------------------------------------------------------ HRESULT STDMETHODCALLTYPE LZMAFilter::Decoder::Read(void * data,UInt32 size,UInt32 * processedSize) { if( processedSize != NULL ) *processedSize = 0; for(;;){ UInt32 r = size > count_ ? UInt32(count_) : size; if( r > 0 ){ memcpy(data,data_,r); data = (uint8_t *) data + r; data_ = (const uint8_t *) data_ + r; count_ -= r; size -= r; if( processedSize != NULL ) *processedSize += r; } if( size == 0 || flush_ ) break; if( count_ == 0 ){ if( guest_ == NULL ){ sem_.post(); } else { guest_->event_.type_ = etDispatch; guest_->thread()->postEvent(&guest_->event_); } switchFiber(mainFiber()); } } return S_OK; } //--------------------------------------------------------------------------- HRESULT STDMETHODCALLTYPE LZMAFilter::Decoder::Write(const void *data,UInt32 size,UInt32 * processedSize) { HRESULT hr = S_OK; try { filter_->writeCompressedBuffer(data,size); if( processedSize != NULL ) *processedSize = size; } catch( ExceptionSP & e ){ hr = HRESULT_FROM_WIN32(e->code()); } return hr; } //------------------------------------------------------------------------------ //////////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------ LZMAFilter::~LZMAFilter() { dropEncoder(); dropDecoder(); } //--------------------------------------------------------------------------- LZMAFilter::LZMAFilter() { howCloseServer(HowCloseServer() & ~csWait); } //--------------------------------------------------------------------------- LZMAFilter & LZMAFilter::dropEncoder() { if( encoder_ != NULL ){ encoder_->terminate(); encoder_->event_.type_ = etDispatch; encoder_->thread()->postEvent(&encoder_->event_); while( !encoder_->finished() ) ksleep1(); encoder_ = NULL; } return *this; } //--------------------------------------------------------------------------- LZMAFilter & LZMAFilter::dropDecoder() { if( decoder_ != NULL ){ decoder_->terminate(); decoder_->event_.type_ = etDispatch; decoder_->thread()->postEvent(&decoder_->event_); while( !decoder_->finished() ) ksleep1(); decoder_ = NULL; } return *this; } //--------------------------------------------------------------------------- StreamCompressionFilter & LZMAFilter::initializeCompression() { dropEncoder(); encoder_ = newObject(); encoder_->filter_ = this; static const PROPID propIDs[] = { NCoderPropID::kNumFastBytes, NCoderPropID::kMatchFinderCycles, NCoderPropID::kAlgorithm, NCoderPropID::kMatchFinder, NCoderPropID::kDictionarySize, NCoderPropID::kPosStateBits, NCoderPropID::kLitPosBits, NCoderPropID::kLitContextBits, NCoderPropID::kEndMarker }; PROPVARIANT properties[sizeof(propIDs) / sizeof(propIDs[0])] = { { VT_UI4, 0, 0, 0 /* 5 - 273 */ }, { VT_UI4, 0, 0, 0 }, { VT_UI4, 0, 0, 0 }, { VT_BSTR, 0, 0, 0 /* BT2, BT3, BT4, HC4 */}, { VT_UI4, 0, 0, 0 /* 1 - 1 << 30 */}, { VT_UI4, 0, 0, 0 /* 0 - 4 */}, { VT_UI4, 0, 0, 0 /* 0 - 4 */}, { VT_UI4, 0, 0, 0 /* 0 - 8 */}, { VT_BOOL, 0, 0, 0 /* VARIANT_FALSE, VARIANT_TRUE */} }; // this is default values V_UI4(properties + 0) = 32; // NumFastBytes V_UI4(properties + 1) = 0; // MatchFinderCycles V_UI4(properties + 2) = 1; // Algorithm V_BSTR(properties + 3) = L"BT4"; // MatchFinder V_UI4(properties + 4) = 4194304; // DictionarySize V_UI4(properties + 5) = 2; // PosStateBits V_UI4(properties + 6) = 0; // LitPosBits V_UI4(properties + 7) = 3; // LitContextBits V_BOOL(properties + 8) = VARIANT_FALSE; // EndMarker // test values for fast V_UI4(properties + 2) = 0; // Algorithm V_BSTR(properties + 3) = L"HC4"; // MatchFinder V_UI4(properties + 4) = 64 * 1024; // DictionarySize V_BOOL(properties + 8) = VARIANT_TRUE; // EndMarker if( FAILED(encoder_->SetCoderProperties(propIDs,properties,sizeof(propIDs) / sizeof(propIDs[0]))) ) newObjectV1C2(EINVAL,__PRETTY_FUNCTION__); return *this; } //--------------------------------------------------------------------------- StreamCompressionFilter & LZMAFilter::compress(const void * buf,uintptr_t count) { if( count > 0 ){ encoder_->guest_ = currentFiber(); encoder_->data_ = buf; encoder_->count_ = count; encoder_->flush_ = false; if( !encoder_->started() || encoder_->finished() ){ if( encoder_->guest_ != NULL ) encoder_->guest_->thread()->server()->attachFiber(encoder_); else attachFiber(encoder_); } else { encoder_->event_.type_ = etDispatch; encoder_->thread()->postEvent(&encoder_->event_); } if( encoder_->guest_ == NULL ) encoder_->sem_.wait(); else encoder_->guest_->switchFiber(encoder_->guest_->mainFiber()); if( encoder_->err_ != 0 ) newObjectV1C2(encoder_->err_,__PRETTY_FUNCTION__); } return *this; } //--------------------------------------------------------------------------- StreamCompressionFilter & LZMAFilter::finishCompression() { encoder_->guest_ = currentFiber(); encoder_->data_ = NULL; encoder_->count_ = 0; encoder_->flush_ = true; if( !encoder_->started() || encoder_->finished() ){ if( encoder_->guest_ != NULL ) encoder_->guest_->thread()->server()->attachFiber(encoder_); else attachFiber(encoder_); } else { encoder_->event_.type_ = etDispatch; encoder_->thread()->postEvent(&encoder_->event_); } if( encoder_->guest_ == NULL ) encoder_->sem_.wait(); else encoder_->guest_->switchFiber(encoder_->guest_->mainFiber()); if( encoder_->err_ != 0 ) newObjectV1C2(encoder_->err_,__PRETTY_FUNCTION__); dropEncoder(); return *this; } //--------------------------------------------------------------------------- StreamCompressionFilter & LZMAFilter::initializeDecompression() { dropDecoder(); decoder_ = newObject(); decoder_->filter_ = this; return *this; } //--------------------------------------------------------------------------- StreamCompressionFilter & LZMAFilter::decompress(void * buf,uintptr_t count) { if( count > 0 ){ decoder_->guest_ = currentFiber(); decoder_->data_ = buf; decoder_->count_ = count; decoder_->flush_ = false; if( !decoder_->started() || decoder_->finished() ){ if( decoder_->guest_ != NULL ) decoder_->guest_->thread()->server()->attachFiber(decoder_); else attachFiber(decoder_); } else { decoder_->event_.type_ = etDispatch; decoder_->thread()->postEvent(&decoder_->event_); } if( decoder_->guest_ == NULL ) decoder_->sem_.wait(); else decoder_->guest_->switchFiber(decoder_->guest_->mainFiber()); if( decoder_->err_ != 0 ) newObjectV1C2(encoder_->err_,__PRETTY_FUNCTION__); } return *this; } //--------------------------------------------------------------------------- StreamCompressionFilter & LZMAFilter::finishDecompression() { decoder_->guest_ = currentFiber(); decoder_->data_ = NULL; decoder_->count_ = 0; decoder_->flush_ = true; if( !decoder_->started() || decoder_->finished() ){ if( decoder_->guest_ != NULL ) decoder_->guest_->thread()->server()->attachFiber(decoder_); else attachFiber(decoder_); } else { decoder_->event_.type_ = etDispatch; decoder_->thread()->postEvent(&decoder_->event_); } if( decoder_->guest_ == NULL ) decoder_->sem_.wait(); else decoder_->guest_->switchFiber(decoder_->guest_->mainFiber()); if( decoder_->err_ != 0 ) newObjectV1C2(decoder_->err_,__PRETTY_FUNCTION__); dropDecoder(); return *this; } //------------------------------------------------------------------------------ //////////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------ StreamCompressionFilter & LZMAFileFilter::compressFile(const utf8::String & srcFileName,const utf8::String & dstFileName) { srcFile_.fileName(srcFileName).readOnly(true).open(); AutoFileClose autoSrcFileClose(srcFile_); dstFile_.fileName(dstFileName).createIfNotExist(true).open(); AutoFileClose autoDstFileClose(dstFile_); dstFile_.resize(0); AutoPtr buf; buf.alloc(getpagesize()); initializeCompression(); for(;;){ int64_t r = srcFile_.read(buf,getpagesize()); if( r <= 0 ) break; compress(buf,uintptr_t(r)); } finishCompression(); return *this; } //--------------------------------------------------------------------------- StreamCompressionFilter & LZMAFileFilter::decompressFile(const utf8::String & srcFileName,const utf8::String & dstFileName) { srcFile_.fileName(srcFileName).readOnly(true).open(); AutoFileClose autoSrcFileClose(srcFile_); dstFile_.fileName(dstFileName).createIfNotExist(true).open(); AutoFileClose autoDstFileClose(dstFile_); dstFile_.resize(0); AutoPtr buf; buf.alloc(getpagesize()); initializeDecompression(); for(;;){ int64_t r = srcFile_.read(buf,getpagesize()); if( r <= 0 ) break; decompress(buf,uintptr_t(r)); } finishDecompression(); return *this; } //--------------------------------------------------------------------------- StreamCompressionFilter & LZMAFileFilter::writeCompressedBuffer(const void * buf,uintptr_t count) { dstFile_.writeBuffer(buf,count); return *this; } //--------------------------------------------------------------------------- StreamCompressionFilter & LZMAFileFilter::writeDecompressedBuffer(void * buf,uintptr_t count) { dstFile_.writeBuffer(buf,count); return *this; } //--------------------------------------------------------------------------- } // namespace ksys //------------------------------------------------------------------------------ #endif //------------------------------------------------------------------------------