#ifndef __CLZMA_H__ #define __CLZMA_H__ #include "compressor.h" #include "7zip/7zip/IStream.h" #include "7zip/7zip/Compress/LZMA/LZMAEncoder.h" #include "7zip/Common/MyCom.h" #include "7zip/Common/Defs.h" // implemented in build.cpp - simply calls CompressReal #ifdef _WIN32 DWORD WINAPI lzmaCompressThread(LPVOID lpParameter); #else void *lzmaCompressThread(void *arg); #endif #define LZMA_BAD_CALL -1 #define LZMA_INIT_ERROR -2 #define LZMA_THREAD_ERROR -3 #define LZMA_IO_ERROR -4 #define LZMA_MEM_ERROR -5 class CLZMA: public ICompressor, public ISequentialInStream, public ISequentialOutStream, public CMyUnknownImp { private: NCompress::NLZMA::CEncoder *_encoder; #ifdef _WIN32 HANDLE hCompressionThread; #else pthread_t hCompressionThread; #endif HANDLE hNeedIOEvent; HANDLE hIOReadyEvent; BYTE *next_in; /* next input byte */ UINT avail_in; /* number of bytes available at next_in */ BYTE *next_out; /* next output byte should be put there */ UINT avail_out; /* remaining free space at next_out */ int res; BOOL finish; BOOL compressor_finished; #ifndef _WIN32 struct evnet_t { pthread_cond_t cond; pthread_mutex_t mutex; bool signaled; }; HANDLE CreateEvent(void *, BOOL, BOOL, char *) { evnet_t *event = (evnet_t *) malloc(sizeof(evnet_t)); if (!event) return 0; if (pthread_cond_init(&event->cond, NULL)) { free(event); return 0; } if (pthread_mutex_init(&event->mutex, NULL)) { free(event); return 0; } event->signaled = false; return (HANDLE) event; } BOOL SetEvent(HANDLE _event) { evnet_t *event = (evnet_t *) _event; if (pthread_mutex_lock(&event->mutex)) return FALSE; event->signaled = true; pthread_cond_signal(&event->cond); if (pthread_mutex_unlock(&event->mutex)) return FALSE; return TRUE; } BOOL ResetEvent(HANDLE _event) { evnet_t *event = (evnet_t *) _event; event->signaled = false; return TRUE; } BOOL CloseHandle(HANDLE _event) { BOOL ret = TRUE; evnet_t *event = (evnet_t *) _event; if (event) return FALSE; if (pthread_cond_destroy(&event->cond)) ret = FALSE; if (pthread_mutex_destroy(&event->mutex)) ret = FALSE; free(event); return ret; } #define WAIT_OBJECT_0 0 #define INFINITE 0 DWORD WaitForSingleObject(HANDLE _event, DWORD) { DWORD ret = WAIT_OBJECT_0; evnet_t *event = (evnet_t *) _event; if (!event->signaled) { pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; if (pthread_mutex_lock(&m) || pthread_cond_wait(&event->cond, &m)) { ret = !WAIT_OBJECT_0; } pthread_mutex_unlock(&m); pthread_mutex_destroy(&m); } if (pthread_mutex_lock(&event->mutex)) return !WAIT_OBJECT_0; event->signaled = false; if (pthread_mutex_unlock(&event->mutex)) return !WAIT_OBJECT_0; return ret; } #define WaitForMultipleObjects(x, list, y, t) WaitForSingleObject(list[0], t) #endif int ConvertError(HRESULT result) { if (result != S_OK) { if (result == E_OUTOFMEMORY) return LZMA_MEM_ERROR; else return LZMA_IO_ERROR; } return C_OK; } public: MY_UNKNOWN_IMP CLZMA(): _encoder(NULL) { _encoder = new NCompress::NLZMA::CEncoder(); _encoder->SetWriteEndMarkerMode(true); #ifdef _WIN32 hCompressionThread = NULL; #else hCompressionThread = 0; #endif hNeedIOEvent = CreateEvent(NULL, FALSE, FALSE, NULL); hIOReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); finish = FALSE; compressor_finished = TRUE; hCompressionThread = 0; SetNextOut(NULL, 0); SetNextIn(NULL, 0); } virtual ~CLZMA() { End(); if (hNeedIOEvent) CloseHandle(hNeedIOEvent); if (hIOReadyEvent) CloseHandle(hIOReadyEvent); if (_encoder) { delete _encoder; _encoder = NULL; } } int Init(int level, UINT32 dicSize) { End(); compressor_finished = FALSE; finish = FALSE; res = C_OK; if (!hNeedIOEvent || !hIOReadyEvent) { return LZMA_INIT_ERROR; } ResetEvent(hNeedIOEvent); ResetEvent(hIOReadyEvent); res = C_OK; PROPID propdIDs [] = { NCoderPropID::kAlgorithm, NCoderPropID::kDictionarySize, NCoderPropID::kNumFastBytes }; const int kNumProps = sizeof(propdIDs) / sizeof(propdIDs[0]); PROPVARIANT props[kNumProps]; // NCoderPropID::kAlgorithm props[0].vt = VT_UI4; props[0].ulVal = 2; // NCoderPropID::kDictionarySize props[1].vt = VT_UI4; props[1].ulVal = dicSize; // NCoderPropID::kNumFastBytes props[2].vt = VT_UI4; props[2].ulVal = 64; if (_encoder->SetCoderProperties(propdIDs, props, kNumProps) != 0) return LZMA_INIT_ERROR; return _encoder->SetStreams(this, this, 0, 0) == S_OK ? C_OK : LZMA_INIT_ERROR; } int Init(int level) { // default dictionary size is 8MB return Init(level, 8 << 20); } int End() { // has compressor not finished? if (hCompressionThread && !compressor_finished) { // kill compression thread avail_in = 0; avail_out = 0; compressor_finished = TRUE; SetEvent(hIOReadyEvent); #ifdef _WIN32 WaitForSingleObject(hCompressionThread, INFINITE); #else pthread_join(hCompressionThread, NULL); #endif } #ifdef _WIN32 if (hCompressionThread) { CloseHandle(hCompressionThread); hCompressionThread = NULL; } #else hCompressionThread = 0; #endif SetNextOut(NULL, 0); SetNextIn(NULL, 0); return C_OK; } int CompressReal() { try { HRESULT hResult = _encoder->WriteCoderProperties(this); if (res == S_OK) { while (true) { UINT64 inSize, outSize; INT32 finished; res = ConvertError(_encoder->CodeOneBlock(&inSize, &outSize, &finished)); if (res != C_OK) break; if (finished) { res = C_OK; break; } } } else { res = ConvertError(hResult); } } catch (...) { res = LZMA_IO_ERROR; } compressor_finished = TRUE; SetEvent(hNeedIOEvent); return C_OK; } int Compress(BOOL flush) { if (compressor_finished) { // act like zlib when it comes to stream ending if (flush) return C_OK; else return LZMA_BAD_CALL; } finish = flush; if (!hCompressionThread) { #ifdef _WIN32 DWORD dwThreadId; hCompressionThread = CreateThread(0, 0, lzmaCompressThread, (LPVOID) this, 0, &dwThreadId); if (!hCompressionThread) #else if (pthread_create(&hCompressionThread, NULL, lzmaCompressThread, (LPVOID) this)) #endif return LZMA_INIT_ERROR; } else { SetEvent(hIOReadyEvent); } HANDLE waitList[2] = {hNeedIOEvent, (HANDLE) hCompressionThread}; if (WaitForMultipleObjects(2, waitList, FALSE, INFINITE) != WAIT_OBJECT_0) { // thread ended or WaitForMultipleObjects failed compressor_finished = TRUE; SetEvent(hIOReadyEvent); return LZMA_THREAD_ERROR; } if (compressor_finished) { return res; } return C_OK; } void GetMoreIO() { SetEvent(hNeedIOEvent); if (WaitForSingleObject(hIOReadyEvent, INFINITE) != WAIT_OBJECT_0) { compressor_finished = TRUE; res = LZMA_THREAD_ERROR; } } STDMETHOD(Read)(void *data, UINT32 size, UINT32 *processedSize) { return ReadPart(data, size, processedSize); } STDMETHOD(ReadPart)(void *data, UINT32 size, UINT32 *processedSize) { if (processedSize) *processedSize = 0; while (size) { if (!avail_in) { if (finish) { return S_OK; } GetMoreIO(); if (!avail_in) { if (finish) { return S_OK; } return E_ABORT; } if (compressor_finished) return E_ABORT; } UINT32 l = min(size, avail_in); memcpy(data, next_in, l); avail_in -= l; size -= l; next_in += l; data = LPBYTE(data) + l; if (processedSize) *processedSize += l; } return S_OK; } STDMETHOD(Write)(const void *data, UINT32 size, UINT32 *processedSize) { return WritePart(data, size, processedSize); } STDMETHOD(WritePart)(const void *data, UINT32 size, UINT32 *processedSize) { if (processedSize) *processedSize = 0; while (size) { if (!avail_out) { GetMoreIO(); if (!avail_out) return E_ABORT; } UINT32 l = min(size, avail_out); memcpy(next_out, data, l); avail_out -= l; size -= l; next_out += l; data = LPBYTE(data) + l; if (processedSize) *processedSize += l; } return S_OK; } void SetNextIn(char *in, unsigned int size) { next_in = (LPBYTE) in; avail_in = size; } void SetNextOut(char *out, unsigned int size) { next_out = (LPBYTE) out; avail_out = size; } virtual char *GetNextOut() { return (char *) next_out; } virtual unsigned int GetAvailIn() { return avail_in; } virtual unsigned int GetAvailOut() { return avail_out; } const char *GetName() { return "lzma"; } const char* GetErrStr(int err) { switch (err) { case LZMA_BAD_CALL: return "bad call"; case LZMA_INIT_ERROR: return "initialization failed"; case LZMA_THREAD_ERROR: return "thread synchronization error"; case LZMA_IO_ERROR: return "input/output error"; case LZMA_MEM_ERROR: return "not enough memory"; default: return "unknown error"; } } }; #endif