/* * clzma.cpp * * This file is a part of NSIS. * * Copyright (C) 1999-2007 Nullsoft and Contributors * * Licensed under the zlib/libpng license (the "License"); * you may not use this file except in compliance with the License. * * Licence details can be found in the file COPYING. * * This software is provided 'as-is', without any express or implied * warranty. */ #include // for std::min #include "clzma.h" using namespace std; #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)) { pthread_cond_destroy(&event->cond); 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; if (pthread_mutex_lock(&event->mutex)) return FALSE; event->signaled = false; if (pthread_mutex_unlock(&event->mutex)) return 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 (pthread_mutex_lock(&event->mutex)) return !WAIT_OBJECT_0; if (!event->signaled) { if (pthread_cond_wait(&event->cond, &event->mutex)) { ret = !WAIT_OBJECT_0; } } event->signaled = false; pthread_mutex_unlock(&event->mutex); return ret; } #define WaitForMultipleObjects(x, list, y, t) WaitForSingleObject(list[0], t) #endif #ifdef _WIN32 DWORD CLZMA::lzmaCompressThread(LPVOID lpParameter) #else void* CLZMA::lzmaCompressThread(void *lpParameter) #endif { CLZMA *Compressor = (CLZMA *) lpParameter; if (!Compressor) return 0; Compressor->CompressReal(); return 0; } int CLZMA::ConvertError(HRESULT result) { if (result != S_OK) { if (result == E_OUTOFMEMORY) return LZMA_MEM_ERROR; else return LZMA_IO_ERROR; } return C_OK; } CLZMA::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); AddRef(); // will be manually deleted, not released } CLZMA::~CLZMA() { End(); if (hNeedIOEvent) { CloseHandle(hNeedIOEvent); hNeedIOEvent = NULL; } if (hIOReadyEvent) { CloseHandle(hIOReadyEvent); hIOReadyEvent = NULL; } if (_encoder) { delete _encoder; _encoder = NULL; } } int CLZMA::Init(int level, unsigned int 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 CLZMA::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 } if (hCompressionThread) { #ifdef _WIN32 CloseHandle(hCompressionThread); hCompressionThread = NULL; #else pthread_detach(hCompressionThread); hCompressionThread = 0; #endif } SetNextOut(NULL, 0); SetNextIn(NULL, 0); return C_OK; } int CLZMA::CompressReal() { try { HRESULT hResult = _encoder->WriteCoderProperties(this); if (hResult == S_OK) { while (true) { UINT64 inSize, outSize; INT32 finished; hResult = _encoder->CodeOneBlock(&inSize, &outSize, &finished); if (hResult != S_OK && res == C_OK) res = ConvertError(hResult); if (res != C_OK) break; if (finished) { res = C_FINISHED; break; } } } else { if (res == C_OK) res = ConvertError(hResult); } } catch (...) { if (res == C_OK) res = LZMA_IO_ERROR; } compressor_finished = TRUE; SetEvent(hNeedIOEvent); return C_OK; } int CLZMA::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 CLZMA::GetMoreIO() { SetEvent(hNeedIOEvent); if (WaitForSingleObject(hIOReadyEvent, INFINITE) != WAIT_OBJECT_0) { compressor_finished = TRUE; res = LZMA_THREAD_ERROR; } } HRESULT CLZMA::Read(void *data, UINT32 size, UINT32 *processedSize) { return ReadPart(data, size, processedSize); } HRESULT CLZMA::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; } HRESULT CLZMA::Write(const void *data, UINT32 size, UINT32 *processedSize) { return WritePart(data, size, processedSize); } HRESULT CLZMA::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 CLZMA::SetNextIn(char *in, unsigned int size) { next_in = (LPBYTE) in; avail_in = size; } void CLZMA::SetNextOut(char *out, unsigned int size) { next_out = (LPBYTE) out; avail_out = size; } char* CLZMA::GetNextOut() { return (char *) next_out; } unsigned int CLZMA::GetAvailIn() { return avail_in; } unsigned int CLZMA::GetAvailOut() { return avail_out; } const char* CLZMA::GetName() { return "lzma"; } const char* CLZMA::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"; } }