// File: comp_stream.cpp // LZHAM is in the Public Domain. Please see the Public Domain declaration at the end of include/lzham.h #include "comp_stream.h" namespace lzham_ex { compression_stream::compression_stream() : data_stream(), m_pComp_state(NULL), m_total_uncomp_size(0), m_total_comp_size(0), m_total_promised_uncomp_size(DATA_STREAM_SIZE_UNKNOWN), m_header_stream_ofs(0), m_pOutput_stream(NULL), m_opened(false) { m_hdr.clear(); } compression_stream::compression_stream(data_stream *pOutput_stream, const lzham_compress_params *pComp_params, const char* pName, uint window_size, lzham_compress_level level, bool multithreading, uint64 source_stream_size) : data_stream(), m_pComp_state(NULL), m_total_uncomp_size(0), m_total_comp_size(0), m_total_promised_uncomp_size(DATA_STREAM_SIZE_UNKNOWN), m_header_stream_ofs(0), m_pOutput_stream(NULL), m_opened(false) { m_hdr.clear(); open(pOutput_stream, pComp_params, pName, window_size, level, multithreading, source_stream_size); } compression_stream::~compression_stream() { close(); } bool compression_stream::open(data_stream *pOutput_stream, const lzham_compress_params *pComp_params, const char* pName, uint window_size, lzham_compress_level level, bool multithreading, uint64 source_stream_size) { if (m_opened) return false; if (!pOutput_stream) return false; m_name = pName ? pName : "comp_stream"; m_attribs = cDataStreamWritable; m_pOutput_stream = pOutput_stream; m_total_promised_uncomp_size = source_stream_size; lzham_compress_params lzham_params; zero_object(lzham_params); if (!pComp_params) { lzham_params.m_struct_size = sizeof(lzham_params); lzham_params.m_max_helper_threads = multithreading ? -1 : 0; lzham_params.m_dict_size_log2 = window_size; lzham_params.m_level = level; pComp_params = &lzham_params; } m_pComp_state = lzham_compress_init(pComp_params); if (!m_pComp_state) { clear(); return false; } m_buf.resize(cBufSize); m_header_stream_ofs = m_pOutput_stream->get_ofs(); m_hdr.clear(); m_hdr.m_comp_size = DATA_STREAM_SIZE_UNKNOWN; m_hdr.m_uncomp_size = source_stream_size; m_hdr.m_method = compressed_stream_header::cCompMethodLZHAM; m_hdr.m_window_size = static_cast(pComp_params->m_dict_size_log2); m_hdr.compute_crc(); if (pOutput_stream->write(&m_hdr, sizeof(m_hdr)) != sizeof(m_hdr)) { clear(); return false; } m_opened = true; return true; } bool compression_stream::flush_output_buf(uint out_buf_size) { if (!out_buf_size) return true; assert(out_buf_size <= LZHAM_UINT16_MAX); assert(cBufSize <= LZHAM_UINT16_MAX); if (!m_pOutput_stream->is_seekable()) { if (!m_pOutput_stream->write_byte(static_cast(out_buf_size))) return false; if (!m_pOutput_stream->write_byte(static_cast(out_buf_size >> 8))) return false; m_total_comp_size += 2; } uint64 bytes_written = m_pOutput_stream->write(&m_buf[0], static_cast(out_buf_size)); m_total_comp_size += bytes_written; return bytes_written == out_buf_size; } bool compression_stream::close() { if (!m_opened) return true; if ( (m_error) || ((m_total_promised_uncomp_size != DATA_STREAM_SIZE_UNKNOWN) && (m_total_uncomp_size != m_total_promised_uncomp_size)) ) { clear(); data_stream::close(); return false; } bool success = true; lzham_compress_status_t status; do { size_t in_buf_size = 0; size_t out_buf_size = cBufSize; status = lzham_compress(m_pComp_state, NULL, &in_buf_size, &m_buf[0], &out_buf_size, true); if (out_buf_size) { if (!flush_output_buf(static_cast(out_buf_size))) { success = false; break; } } } while (status < LZHAM_COMP_STATUS_FIRST_SUCCESS_OR_FAILURE_CODE); if ((success) && (status == LZHAM_COMP_STATUS_SUCCESS)) { if (!m_pOutput_stream->is_seekable()) { if (!m_pOutput_stream->write_byte(0)) success = false; if (!m_pOutput_stream->write_byte(0)) success = false; m_total_comp_size += 2; } else { uint64 cur_ofs = m_pOutput_stream->get_ofs(); if (!m_pOutput_stream->seek(m_header_stream_ofs, false)) { success = false; } else { m_hdr.m_comp_size = m_total_comp_size; m_hdr.m_uncomp_size = m_total_uncomp_size; m_hdr.compute_crc(); assert(m_hdr.validate()); if (m_pOutput_stream->write(&m_hdr, sizeof(m_hdr)) != sizeof(m_hdr)) { success = false; } m_pOutput_stream->seek(cur_ofs, false); } } } else { success = false; } clear(); data_stream::close(); return success; } uint compression_stream::read(void* pBuf, uint len) { pBuf, len; return 0; } uint compression_stream::write(const void* pBuf, uint len) { if ((!m_opened) || (!len) || (m_error)) return 0; if (m_total_promised_uncomp_size != DATA_STREAM_SIZE_UNKNOWN) { if ((m_total_uncomp_size + len) > m_total_promised_uncomp_size) { m_error = true; return 0; } } m_total_uncomp_size += len; lzham_compress_status_t status; uint buf_ofs = 0; do { size_t in_buf_size = len - buf_ofs; size_t out_buf_size = cBufSize; status = lzham_compress(m_pComp_state, static_cast(pBuf) + buf_ofs, &in_buf_size, &m_buf[0], &out_buf_size, false); if (out_buf_size) { if (!flush_output_buf(static_cast(out_buf_size))) { m_error = true; return 0; } } buf_ofs += static_cast(in_buf_size); } while ((buf_ofs < len) && (status < LZHAM_COMP_STATUS_FIRST_SUCCESS_OR_FAILURE_CODE)); if (status >= LZHAM_COMP_STATUS_FIRST_SUCCESS_OR_FAILURE_CODE) { m_error = true; return 0; } return len; } bool compression_stream::flush() { if ((!m_opened) || (m_error)) return false; return true; } uint64 compression_stream::get_size() { if (!m_opened) return 0; return m_pOutput_stream->get_size(); } uint64 compression_stream::get_remaining() { return 0; } // Should this return the number of original/source bytes? Or the current output stream's size? uint64 compression_stream::get_ofs() { if (!m_opened) return 0; //return m_pOutput_stream->get_ofs(); return m_total_uncomp_size; } bool compression_stream::seek(int64 ofs, bool relative) { ofs, relative; return false; } void compression_stream::clear() { if (m_pComp_state) { lzham_compress_deinit(m_pComp_state); m_pComp_state = NULL; } m_buf.clear(); m_total_uncomp_size = 0; m_total_comp_size = 0; m_total_promised_uncomp_size = DATA_STREAM_SIZE_UNKNOWN; m_header_stream_ofs = 0; m_pOutput_stream = NULL; m_hdr.clear(); m_opened = false; } //------------------------------------------------------------------------------------------------------------------ decompression_stream::decompression_stream() : m_pInput_stream(NULL), m_pDecomp_state(NULL), m_buf_ofs(0), m_chunk_size(0), m_no_more_input_bytes(0), m_total_bytes_read(0), m_total_bytes_unpacked(0), m_decomp_status(LZHAM_DECOMP_STATUS_NOT_FINISHED), m_decomp_completed(false), m_comp_size_known(false), m_opened(false) { m_hdr.clear(); } decompression_stream::decompression_stream(data_stream *pInput_stream, const char* pName) : m_pInput_stream(NULL), m_pDecomp_state(NULL), m_buf_ofs(0), m_chunk_size(0), m_no_more_input_bytes(0), m_total_bytes_read(0), m_total_bytes_unpacked(0), m_decomp_status(LZHAM_DECOMP_STATUS_NOT_FINISHED), m_decomp_completed(false), m_comp_size_known(false), m_opened(false) { open(pInput_stream, pName); } decompression_stream::~decompression_stream() { close(); } bool decompression_stream::open(data_stream *pInput_stream, const char* pName) { if (m_opened) return false; if (!pInput_stream) return false; m_name = pName ? pName : "comp_stream"; m_attribs = cDataStreamWritable; m_pInput_stream = pInput_stream; if (m_pInput_stream->get_remaining() < sizeof(compressed_stream_header)) { clear(); return false; } if (m_pInput_stream->read(&m_hdr, sizeof(m_hdr)) != sizeof(m_hdr)) { clear(); return false; } if (!m_hdr.validate()) { clear(); return false; } if ((!m_hdr.m_comp_size) || (m_hdr.m_method != compressed_stream_header::cCompMethodLZHAM)) { clear(); return false; } if ((m_hdr.m_window_size < LZHAM_MIN_DICT_SIZE_LOG2) || (m_hdr.m_window_size > LZHAM_MAX_DICT_SIZE_LOG2_X64)) { clear(); return false; } m_comp_size_known = (m_hdr.m_comp_size != DATA_STREAM_SIZE_UNKNOWN); if (m_comp_size_known) { if (m_pInput_stream->get_remaining() < m_hdr.m_comp_size) { clear(); return false; } } m_buf.resize(LZHAM_UINT16_MAX); m_opened = true; return true; } bool decompression_stream::close() { if (!m_opened) return true; bool status = !m_error; clear(); data_stream::close(); return status; } bool decompression_stream::refill_input_buffer() { m_buf_ofs = 0; m_chunk_size = 0; if (m_no_more_input_bytes) return true; if (m_comp_size_known) { uint64 comp_bytes_remaining = m_hdr.m_comp_size - m_total_bytes_read; m_chunk_size = static_cast(LZHAM_EX_MIN(comp_bytes_remaining, m_buf.size())); if (m_hdr.m_comp_size == (m_total_bytes_read + m_chunk_size)) { m_no_more_input_bytes = true; } } else { if (m_pInput_stream->get_remaining() < sizeof(uint16)) { return false; } m_chunk_size = m_pInput_stream->read_byte(); m_chunk_size |= (m_pInput_stream->read_byte() << 8); if (!m_chunk_size) { m_no_more_input_bytes = true; } } if (m_chunk_size) { if (m_pInput_stream->get_remaining() < m_chunk_size) { return false; } assert(m_buf.size() >= m_chunk_size); if (m_pInput_stream->read(&m_buf[0], m_chunk_size) != m_chunk_size) { return false; } m_total_bytes_read += m_chunk_size; } return true; } bool decompression_stream::term_stream() { if (m_decomp_status != LZHAM_DECOMP_STATUS_SUCCESS) { // Decompression failed. return false; } if (get_remaining()) { // Decompressor says we're done, but there are still bytes left to decompress. return false; } if (m_buf_ofs < m_chunk_size) { // We haven't consumed the entire expected input stream. return false; } if (!m_no_more_input_bytes) { // We haven't consumed the entire input stream. if (m_comp_size_known) { if (m_total_bytes_read != m_hdr.m_comp_size) { return false; } } else { // Read last chunk's size, which should be 0. if (m_pInput_stream->get_remaining() < sizeof(uint16)) { return false; } m_chunk_size = m_pInput_stream->read_byte(); m_chunk_size |= (m_pInput_stream->read_byte() << 8); if (m_chunk_size) { // We should be at the end of the compressed stream, but there's more bytes following - this is an error. return false; } } } return true; } uint decompression_stream::read(void* pBuf, uint len) { if ((!m_opened) || (m_error) || (!len)) return 0; len = static_cast(LZHAM_EX_MIN(len, get_remaining())); if (!len) return 0; if (!m_pDecomp_state) { lzham_decompress_params lzham_params; zero_object(lzham_params); lzham_params.m_struct_size = sizeof(lzham_params); lzham_params.m_dict_size_log2 = m_hdr.m_window_size; lzham_params.m_decompress_flags = LZHAM_DECOMP_FLAG_COMPUTE_ADLER32; if ((m_hdr.m_uncomp_size != DATA_STREAM_SIZE_UNKNOWN) && (len >= m_hdr.m_uncomp_size)) { lzham_params.m_decompress_flags |= LZHAM_DECOMP_FLAG_OUTPUT_UNBUFFERED; } m_pDecomp_state = lzham_decompress_init(&lzham_params); if (!m_pDecomp_state) { m_error = true; return 0; } } uint out_buf_ofs = 0; while (out_buf_ofs < len) { if ((m_buf_ofs >= m_chunk_size) && (!m_no_more_input_bytes)) { if (!refill_input_buffer()) { m_error = true; return out_buf_ofs; } } size_t in_buf_size = m_chunk_size - m_buf_ofs; size_t out_buf_size = len - out_buf_ofs; m_decomp_status = lzham_decompress(m_pDecomp_state, &m_buf[0] + m_buf_ofs, &in_buf_size, static_cast(pBuf) + out_buf_ofs, &out_buf_size, m_no_more_input_bytes); m_buf_ofs += static_cast(in_buf_size); out_buf_ofs += static_cast(out_buf_size); m_total_bytes_unpacked += out_buf_size; if ((m_hdr.m_uncomp_size != DATA_STREAM_SIZE_UNKNOWN) && (m_total_bytes_unpacked > m_hdr.m_uncomp_size)) { // Decompressor's output was too large. m_error = true; return out_buf_ofs; } if (m_decomp_status >= LZHAM_DECOMP_STATUS_FIRST_SUCCESS_OR_FAILURE_CODE) { if (!term_stream()) { m_error = true; return out_buf_ofs; } break; } } return out_buf_ofs; } uint decompression_stream::write(const void* pBuf, uint len) { pBuf, len; return 0; } bool decompression_stream::flush() { if ((!m_opened) || (m_error)) return false; return true; } uint64 decompression_stream::get_size() { if ((!m_opened) || (m_error)) return false; uint64 uncomp_size = m_hdr.m_uncomp_size; if ((uncomp_size == DATA_STREAM_SIZE_UNKNOWN) && (m_decomp_status == LZHAM_DECOMP_STATUS_SUCCESS)) { return m_total_bytes_unpacked; } return uncomp_size; } uint64 decompression_stream::get_remaining() { if ((!m_opened) || (m_error)) return 0; uint64 uncomp_size = m_hdr.m_uncomp_size; if ((uncomp_size == DATA_STREAM_SIZE_UNKNOWN) && (m_decomp_status == LZHAM_DECOMP_STATUS_SUCCESS)) { return 0; } return uncomp_size - m_total_bytes_unpacked; } uint64 decompression_stream::get_ofs() { if ((!m_opened) || (m_error)) return false; return m_total_bytes_unpacked; } bool decompression_stream::seek(int64 ofs, bool relative) { ofs, relative; return false; } void decompression_stream::clear() { m_pInput_stream = NULL; m_hdr.clear(); if ( m_pDecomp_state ) { lzham_decompress_deinit( m_pDecomp_state ); m_pDecomp_state = NULL; } m_buf.clear(); m_buf_ofs = 0; m_chunk_size = 0; m_no_more_input_bytes = false; m_total_bytes_read = 0; m_total_bytes_unpacked = 0; m_decomp_status = LZHAM_DECOMP_STATUS_NOT_FINISHED; m_comp_size_known = false; m_opened = false; } } // namespace lzham_ex