|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "sparse_matmul/zlib_wrapper/zlibwrapper.h" |
|
|
|
#include <assert.h> |
|
#include <stdio.h> |
|
|
|
#include <algorithm> |
|
#include <memory> |
|
#include <string> |
|
|
|
#include "glog/logging.h" |
|
#include "sparse_matmul/zlib_wrapper/gzipheader.h" |
|
#include "zconf.h" |
|
#include "zlib.h" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define GZIP_HEADER "\037\213\010\000\000\000\000\000\002\377" |
|
|
|
namespace csrblocksparse { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool ZLib::should_be_flexible_with_gzip_footer_ = false; |
|
|
|
|
|
ZLib::ZLib() |
|
: comp_init_(false), uncomp_init_(false), gzip_header_(new GZipHeader) { |
|
Reinit(); |
|
init_settings_ = settings_; |
|
} |
|
|
|
ZLib::~ZLib() { |
|
if (comp_init_) { |
|
deflateEnd(&comp_stream_); |
|
} |
|
if (uncomp_init_) { |
|
inflateEnd(&uncomp_stream_); |
|
} |
|
delete gzip_header_; |
|
} |
|
|
|
void ZLib::Reinit() { |
|
settings_.dictionary_ = nullptr; |
|
settings_.dict_len_ = 0; |
|
settings_.compression_level_ = Z_DEFAULT_COMPRESSION; |
|
settings_.window_bits_ = MAX_WBITS; |
|
settings_.mem_level_ = 8; |
|
settings_.no_header_mode_ = false; |
|
settings_.gzip_header_mode_ = false; |
|
settings_.dont_hide_zstream_end_ = false; |
|
|
|
if (comp_init_) { |
|
int err = deflateReset(&comp_stream_); |
|
if (err != Z_OK) { |
|
deflateEnd(&comp_stream_); |
|
comp_init_ = false; |
|
} |
|
} |
|
if (uncomp_init_) { |
|
|
|
int wbits = (settings_.no_header_mode_ ? -MAX_WBITS : MAX_WBITS); |
|
int err = inflateReset2(&uncomp_stream_, wbits); |
|
if (err == Z_OK) { |
|
init_settings_.no_header_mode_ = settings_.no_header_mode_; |
|
} else { |
|
inflateEnd(&uncomp_stream_); |
|
uncomp_init_ = false; |
|
} |
|
} |
|
crc_ = 0; |
|
uncompressed_size_ = 0; |
|
gzip_header_->Reset(); |
|
gzip_footer_bytes_ = -1; |
|
first_chunk_ = true; |
|
} |
|
|
|
void ZLib::Reset() { |
|
first_chunk_ = true; |
|
gzip_header_->Reset(); |
|
} |
|
|
|
void ZLib::CheckValidParams() { |
|
if (settings_.dictionary_ != nullptr && |
|
(settings_.no_header_mode_ || settings_.gzip_header_mode_)) { |
|
LOG(FATAL) |
|
<< "Incompatible params: require zlib headers with preset dictionary"; |
|
} |
|
} |
|
|
|
void ZLib::SetNoHeaderMode(bool no_header_mode) { |
|
settings_.no_header_mode_ = no_header_mode; |
|
if (init_settings_.no_header_mode_ != settings_.no_header_mode_) { |
|
|
|
if (comp_init_) { |
|
deflateEnd(&comp_stream_); |
|
comp_init_ = false; |
|
} |
|
if (uncomp_init_) { |
|
inflateEnd(&uncomp_stream_); |
|
uncomp_init_ = false; |
|
} |
|
} else { |
|
|
|
Reset(); |
|
} |
|
CheckValidParams(); |
|
} |
|
|
|
void ZLib::SetGzipHeaderMode() { |
|
settings_.gzip_header_mode_ = true; |
|
SetNoHeaderMode(true); |
|
CheckValidParams(); |
|
} |
|
|
|
void ZLib::SetDictionary(const char* initial_dict, unsigned int dict_len) { |
|
settings_.dictionary_ = (Bytef*)initial_dict; |
|
settings_.dict_len_ = dict_len; |
|
CheckValidParams(); |
|
} |
|
|
|
void ZLib::SetDontHideStreamEnd() { settings_.dont_hide_zstream_end_ = true; } |
|
|
|
int ZLib::MinFooterSize() const { |
|
int min_footer_size = 2; |
|
if (settings_.gzip_header_mode_) { |
|
min_footer_size += 8; |
|
} |
|
return min_footer_size; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
void ZLib::CompressErrorInit() { |
|
if (comp_init_) { |
|
deflateEnd(&comp_stream_); |
|
comp_init_ = false; |
|
} |
|
Reset(); |
|
} |
|
|
|
|
|
int ZLib::WriteGzipHeader() { |
|
if (comp_stream_.avail_out < sizeof(GZIP_HEADER)) return Z_BUF_ERROR; |
|
memcpy(comp_stream_.next_out, GZIP_HEADER, sizeof(GZIP_HEADER) - 1); |
|
comp_stream_.next_out += sizeof(GZIP_HEADER) - 1; |
|
comp_stream_.avail_out -= sizeof(GZIP_HEADER) - 1; |
|
return Z_OK; |
|
} |
|
|
|
int ZLib::WriteGzipFooter(Bytef* dest, uLongf destLen) { |
|
if (destLen < 8) |
|
return Z_BUF_ERROR; |
|
*dest++ = (crc_ >> 0) & 255; |
|
*dest++ = (crc_ >> 8) & 255; |
|
*dest++ = (crc_ >> 16) & 255; |
|
*dest++ = (crc_ >> 24) & 255; |
|
*dest++ = (uncompressed_size_ >> 0) & 255; |
|
*dest++ = (uncompressed_size_ >> 8) & 255; |
|
*dest++ = (uncompressed_size_ >> 16) & 255; |
|
*dest++ = (uncompressed_size_ >> 24) & 255; |
|
return Z_OK; |
|
} |
|
|
|
int ZLib::DeflateInit() { |
|
int err = |
|
deflateInit2(&comp_stream_, settings_.compression_level_, Z_DEFLATED, |
|
(settings_.no_header_mode_ ? -settings_.window_bits_ |
|
: settings_.window_bits_), |
|
settings_.mem_level_, Z_DEFAULT_STRATEGY); |
|
if (err == Z_OK) { |
|
|
|
init_settings_.compression_level_ = settings_.compression_level_; |
|
init_settings_.window_bits_ = settings_.window_bits_; |
|
init_settings_.mem_level_ = settings_.mem_level_; |
|
init_settings_.no_header_mode_ = settings_.no_header_mode_; |
|
} |
|
return err; |
|
} |
|
|
|
int ZLib::CompressInit(Bytef* dest, uLongf* destLen, const Bytef* source, |
|
uLong* sourceLen) { |
|
int err; |
|
|
|
comp_stream_.next_in = (Bytef*)source; |
|
comp_stream_.avail_in = (uInt)*sourceLen; |
|
|
|
if ((uLong)comp_stream_.avail_in != *sourceLen) return Z_BUF_ERROR; |
|
comp_stream_.next_out = dest; |
|
comp_stream_.avail_out = (uInt)*destLen; |
|
|
|
if ((uLong)comp_stream_.avail_out != *destLen) return Z_BUF_ERROR; |
|
|
|
if (!first_chunk_) |
|
return Z_OK; |
|
|
|
|
|
if (comp_init_ && |
|
(init_settings_.dictionary_ != settings_.dictionary_ || |
|
init_settings_.dict_len_ != settings_.dict_len_ || |
|
init_settings_.window_bits_ != settings_.window_bits_ || |
|
init_settings_.mem_level_ != settings_.mem_level_ || |
|
init_settings_.no_header_mode_ != settings_.no_header_mode_)) { |
|
deflateEnd(&comp_stream_); |
|
comp_init_ = false; |
|
} |
|
|
|
|
|
if (comp_init_) { |
|
err = deflateReset(&comp_stream_); |
|
if (err != Z_OK) { |
|
deflateEnd(&comp_stream_); |
|
comp_init_ = false; |
|
} |
|
} |
|
|
|
|
|
if (comp_init_ && |
|
init_settings_.compression_level_ != settings_.compression_level_) { |
|
err = deflateParams(&comp_stream_, settings_.compression_level_, |
|
Z_DEFAULT_STRATEGY); |
|
if (err == Z_OK) { |
|
init_settings_.compression_level_ = settings_.compression_level_; |
|
} else { |
|
deflateEnd(&comp_stream_); |
|
comp_init_ = false; |
|
} |
|
} |
|
|
|
|
|
if (!comp_init_) { |
|
comp_stream_.zalloc = (alloc_func)0; |
|
comp_stream_.zfree = (free_func)0; |
|
comp_stream_.opaque = (voidpf)0; |
|
err = DeflateInit(); |
|
if (err != Z_OK) return err; |
|
comp_init_ = true; |
|
} |
|
return Z_OK; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int ZLib::CompressAtMostOrAll(Bytef* dest, uLongf* destLen, const Bytef* source, |
|
uLong* sourceLen, |
|
int flush_mode) { |
|
int err; |
|
|
|
if ((err = CompressInit(dest, destLen, source, sourceLen)) != Z_OK) |
|
return err; |
|
|
|
|
|
int compressed_size = comp_stream_.total_out; |
|
|
|
|
|
if (first_chunk_) { |
|
|
|
if (settings_.gzip_header_mode_) { |
|
if ((err = WriteGzipHeader()) != Z_OK) return err; |
|
compressed_size -= sizeof(GZIP_HEADER) - 1; |
|
crc_ = crc32(0, nullptr, 0); |
|
} |
|
|
|
|
|
if (settings_.dictionary_) { |
|
err = deflateSetDictionary(&comp_stream_, settings_.dictionary_, |
|
settings_.dict_len_); |
|
if (err != Z_OK) return err; |
|
init_settings_.dictionary_ = settings_.dictionary_; |
|
init_settings_.dict_len_ = settings_.dict_len_; |
|
} |
|
|
|
uncompressed_size_ = 0; |
|
first_chunk_ = false; |
|
} |
|
|
|
|
|
|
|
err = deflate(&comp_stream_, flush_mode); |
|
|
|
const uLong source_bytes_consumed = *sourceLen - comp_stream_.avail_in; |
|
*sourceLen = comp_stream_.avail_in; |
|
|
|
if ((err == Z_STREAM_END || err == Z_OK) && comp_stream_.avail_in == 0 && |
|
comp_stream_.avail_out != 0) { |
|
|
|
{} |
|
} else if (err == Z_STREAM_END && comp_stream_.avail_in > 0) { |
|
return Z_BUF_ERROR; |
|
} else if (err != Z_OK && err != Z_STREAM_END && err != Z_BUF_ERROR) { |
|
|
|
CompressErrorInit(); |
|
return err; |
|
} else if (comp_stream_.avail_out == 0) { |
|
err = Z_BUF_ERROR; |
|
} |
|
|
|
assert(err == Z_OK || err == Z_STREAM_END || err == Z_BUF_ERROR); |
|
if (err == Z_STREAM_END) err = Z_OK; |
|
|
|
|
|
uncompressed_size_ += source_bytes_consumed; |
|
compressed_size = comp_stream_.total_out - compressed_size; |
|
*destLen = compressed_size; |
|
if (settings_.gzip_header_mode_) |
|
crc_ = crc32(crc_, source, source_bytes_consumed); |
|
|
|
return err; |
|
} |
|
|
|
int ZLib::CompressChunkOrAll(Bytef* dest, uLongf* destLen, const Bytef* source, |
|
uLong sourceLen, |
|
int flush_mode) { |
|
const int ret = |
|
CompressAtMostOrAll(dest, destLen, source, &sourceLen, flush_mode); |
|
if (ret == Z_BUF_ERROR) CompressErrorInit(); |
|
return ret; |
|
} |
|
|
|
int ZLib::CompressChunk(Bytef* dest, uLongf* destLen, const Bytef* source, |
|
uLong sourceLen) { |
|
return CompressChunkOrAll(dest, destLen, source, sourceLen, Z_SYNC_FLUSH); |
|
} |
|
|
|
int ZLib::CompressAtMost(Bytef* dest, uLongf* destLen, const Bytef* source, |
|
uLong* sourceLen) { |
|
return CompressAtMostOrAll(dest, destLen, source, sourceLen, Z_SYNC_FLUSH); |
|
} |
|
|
|
|
|
|
|
int ZLib::CompressChunkDone(Bytef* dest, uLongf* destLen) { |
|
|
|
if (*destLen < MinFooterSize()) { |
|
*destLen = 0; |
|
return Z_BUF_ERROR; |
|
} |
|
|
|
|
|
|
|
|
|
char dummy = '\0'; |
|
int err; |
|
|
|
assert(!first_chunk_ && comp_init_); |
|
|
|
const uLongf orig_destLen = *destLen; |
|
|
|
if ((err = CompressChunkOrAll(dest, destLen, (const Bytef*)&dummy, 0, |
|
Z_FINISH)) != Z_OK) { |
|
Reset(); |
|
return err; |
|
} |
|
|
|
|
|
|
|
Reset(); |
|
|
|
|
|
if (settings_.gzip_header_mode_) { |
|
if ((err = WriteGzipFooter(dest + *destLen, orig_destLen - *destLen)) != |
|
Z_OK) |
|
return err; |
|
*destLen += 8; |
|
} |
|
return Z_OK; |
|
} |
|
|
|
|
|
|
|
int ZLib::Compress(Bytef* dest, uLongf* destLen, const Bytef* source, |
|
uLong sourceLen) { |
|
int err; |
|
const uLongf orig_destLen = *destLen; |
|
if ((err = CompressChunkOrAll(dest, destLen, source, sourceLen, Z_FINISH)) != |
|
Z_OK) |
|
return err; |
|
Reset(); |
|
|
|
if (settings_.gzip_header_mode_) { |
|
if ((err = WriteGzipFooter(dest + *destLen, orig_destLen - *destLen)) != |
|
Z_OK) |
|
return err; |
|
*destLen += 8; |
|
} |
|
|
|
return Z_OK; |
|
} |
|
|
|
|
|
|
|
int ZLib::InflateInit() { |
|
|
|
int wbits = (settings_.no_header_mode_ ? -MAX_WBITS : MAX_WBITS); |
|
int err = inflateInit2(&uncomp_stream_, wbits); |
|
if (err == Z_OK) { |
|
init_settings_.no_header_mode_ = settings_.no_header_mode_; |
|
} |
|
return err; |
|
} |
|
|
|
|
|
|
|
|
|
void ZLib::UncompressErrorInit() { |
|
if (uncomp_init_) { |
|
inflateEnd(&uncomp_stream_); |
|
uncomp_init_ = false; |
|
} |
|
Reset(); |
|
} |
|
|
|
int ZLib::UncompressInit(Bytef* dest, uLongf* destLen, const Bytef* source, |
|
uLong* sourceLen) { |
|
int err; |
|
|
|
uncomp_stream_.next_in = (Bytef*)source; |
|
uncomp_stream_.avail_in = (uInt)*sourceLen; |
|
|
|
if ((uLong)uncomp_stream_.avail_in != *sourceLen) return Z_BUF_ERROR; |
|
|
|
uncomp_stream_.next_out = dest; |
|
uncomp_stream_.avail_out = (uInt)*destLen; |
|
|
|
if ((uLong)uncomp_stream_.avail_out != *destLen) return Z_BUF_ERROR; |
|
|
|
if (!first_chunk_) |
|
return Z_OK; |
|
|
|
|
|
if (uncomp_init_ && (init_settings_.dictionary_ != settings_.dictionary_ || |
|
init_settings_.dict_len_ != settings_.dict_len_)) { |
|
inflateEnd(&uncomp_stream_); |
|
uncomp_init_ = false; |
|
} |
|
|
|
|
|
if (uncomp_init_) { |
|
|
|
int wbits = (settings_.no_header_mode_ ? -MAX_WBITS : MAX_WBITS); |
|
err = inflateReset2(&uncomp_stream_, wbits); |
|
if (err == Z_OK) { |
|
init_settings_.no_header_mode_ = settings_.no_header_mode_; |
|
} else { |
|
UncompressErrorInit(); |
|
} |
|
} |
|
|
|
|
|
if (!uncomp_init_) { |
|
uncomp_stream_.zalloc = (alloc_func)0; |
|
uncomp_stream_.zfree = (free_func)0; |
|
uncomp_stream_.opaque = (voidpf)0; |
|
err = InflateInit(); |
|
if (err != Z_OK) return err; |
|
uncomp_init_ = true; |
|
} |
|
return Z_OK; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
int ZLib::UncompressAtMostOrAll(Bytef* dest, uLongf* destLen, |
|
const Bytef* source, uLong* sourceLen, |
|
int flush_mode) { |
|
int err = Z_OK; |
|
|
|
if (first_chunk_) { |
|
gzip_footer_bytes_ = -1; |
|
if (settings_.gzip_header_mode_) { |
|
|
|
|
|
|
|
const Bytef* bodyBegin = nullptr; |
|
GZipHeader::Status status = gzip_header_->ReadMore( |
|
reinterpret_cast<const char*>(source), *sourceLen, |
|
reinterpret_cast<const char**>(&bodyBegin)); |
|
switch (status) { |
|
case GZipHeader::INCOMPLETE_HEADER: |
|
*destLen = 0; |
|
*sourceLen = 0; |
|
return Z_OK; |
|
case GZipHeader::INVALID_HEADER: |
|
Reset(); |
|
return Z_DATA_ERROR; |
|
case GZipHeader::COMPLETE_HEADER: |
|
*sourceLen -= (bodyBegin - source); |
|
source = bodyBegin; |
|
crc_ = crc32(0, nullptr, 0); |
|
break; |
|
default: |
|
LOG(FATAL) << "Unexpected gzip header parsing result: " << status; |
|
} |
|
} |
|
} else if (gzip_footer_bytes_ >= 0) { |
|
|
|
if (gzip_footer_bytes_ + *sourceLen > sizeof(gzip_footer_) && |
|
|
|
|
|
!should_be_flexible_with_gzip_footer_) { |
|
VLOG(1) << "UncompressChunkOrAll: Received " |
|
<< (gzip_footer_bytes_ + *sourceLen - sizeof(gzip_footer_)) |
|
<< " extra bytes after gzip footer: " |
|
<< std::string(reinterpret_cast<const char*>(source), |
|
std::min(*sourceLen, 20UL)); |
|
Reset(); |
|
return Z_DATA_ERROR; |
|
} |
|
uLong len = sizeof(gzip_footer_) - gzip_footer_bytes_; |
|
if (len > *sourceLen) len = *sourceLen; |
|
if (len > 0) { |
|
memcpy(gzip_footer_ + gzip_footer_bytes_, source, len); |
|
gzip_footer_bytes_ += len; |
|
} |
|
*sourceLen -= len; |
|
*destLen = 0; |
|
return Z_OK; |
|
} |
|
|
|
if ((err = UncompressInit(dest, destLen, source, sourceLen)) != Z_OK) { |
|
LOG(WARNING) << "ZLib: UncompressInit: Error: " << err |
|
<< "SourceLen: " << *sourceLen; |
|
return err; |
|
} |
|
|
|
|
|
const uLong old_total_out = uncomp_stream_.total_out; |
|
|
|
|
|
const uLong old_total_in = uncomp_stream_.total_in; |
|
|
|
|
|
if (first_chunk_) { |
|
|
|
if (settings_.gzip_header_mode_ || settings_.no_header_mode_) { |
|
|
|
|
|
|
|
|
|
if (settings_.dictionary_) { |
|
err = inflateSetDictionary(&uncomp_stream_, settings_.dictionary_, |
|
settings_.dict_len_); |
|
if (err != Z_OK) { |
|
LOG(WARNING) << "inflateSetDictionary: Error: " << err |
|
<< " dict_len: " << settings_.dict_len_; |
|
UncompressErrorInit(); |
|
return err; |
|
} |
|
init_settings_.dictionary_ = settings_.dictionary_; |
|
init_settings_.dict_len_ = settings_.dict_len_; |
|
} |
|
} |
|
|
|
first_chunk_ = false; |
|
|
|
|
|
|
|
|
|
if (*sourceLen == 0) { |
|
*destLen = 0; |
|
return Z_OK; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
err = inflate(&uncomp_stream_, flush_mode); |
|
if (settings_.dictionary_ && err == Z_NEED_DICT) { |
|
err = inflateSetDictionary(&uncomp_stream_, settings_.dictionary_, |
|
settings_.dict_len_); |
|
if (err != Z_OK) { |
|
LOG(WARNING) << "UncompressChunkOrAll: failed in inflateSetDictionary : " |
|
<< err; |
|
UncompressErrorInit(); |
|
return err; |
|
} |
|
init_settings_.dictionary_ = settings_.dictionary_; |
|
init_settings_.dict_len_ = settings_.dict_len_; |
|
err = inflate(&uncomp_stream_, flush_mode); |
|
} |
|
|
|
|
|
const uLong bytes_read = uncomp_stream_.total_in - old_total_in; |
|
CHECK_LE(source + bytes_read, source + *sourceLen); |
|
*sourceLen = uncomp_stream_.avail_in; |
|
|
|
|
|
|
|
|
|
|
|
if ((err == Z_STREAM_END) && |
|
((gzip_footer_bytes_ == -1) || |
|
(gzip_footer_bytes_ < sizeof(gzip_footer_))) && |
|
(uncomp_stream_.avail_in <= sizeof(gzip_footer_) || |
|
|
|
|
|
should_be_flexible_with_gzip_footer_)) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gzip_footer_bytes_ = std::min(static_cast<size_t>(uncomp_stream_.avail_in), |
|
sizeof(gzip_footer_)); |
|
memcpy(gzip_footer_, source + bytes_read, gzip_footer_bytes_); |
|
*sourceLen -= gzip_footer_bytes_; |
|
} else if ((err == Z_STREAM_END || err == Z_OK) |
|
&& uncomp_stream_.avail_in == 0) { |
|
{} |
|
} else if (err == Z_STREAM_END && uncomp_stream_.avail_in > 0) { |
|
VLOG(1) << "UncompressChunkOrAll: Received some extra data, bytes total: " |
|
<< uncomp_stream_.avail_in << " bytes: " |
|
<< std::string( |
|
reinterpret_cast<const char*>(uncomp_stream_.next_in), |
|
std::min(static_cast<int>(uncomp_stream_.avail_in), 20)); |
|
UncompressErrorInit(); |
|
return Z_DATA_ERROR; |
|
} else if (err != Z_OK && err != Z_STREAM_END && err != Z_BUF_ERROR) { |
|
|
|
VLOG(1) << "UncompressChunkOrAll: Error: " << err |
|
<< " avail_out: " << uncomp_stream_.avail_out; |
|
UncompressErrorInit(); |
|
return err; |
|
} else if (uncomp_stream_.avail_out == 0) { |
|
err = Z_BUF_ERROR; |
|
} |
|
|
|
assert(err == Z_OK || err == Z_BUF_ERROR || err == Z_STREAM_END); |
|
if (err == Z_STREAM_END && !settings_.dont_hide_zstream_end_) err = Z_OK; |
|
|
|
|
|
uncompressed_size_ = uncomp_stream_.total_out; |
|
*destLen = uncomp_stream_.total_out - old_total_out; |
|
if (settings_.gzip_header_mode_) crc_ = crc32(crc_, dest, *destLen); |
|
|
|
return err; |
|
} |
|
|
|
int ZLib::UncompressChunkOrAll(Bytef* dest, uLongf* destLen, |
|
const Bytef* source, uLong sourceLen, |
|
int flush_mode) { |
|
const int ret = |
|
UncompressAtMostOrAll(dest, destLen, source, &sourceLen, flush_mode); |
|
if (ret == Z_BUF_ERROR) UncompressErrorInit(); |
|
return ret; |
|
} |
|
|
|
int ZLib::UncompressAtMost(Bytef* dest, uLongf* destLen, const Bytef* source, |
|
uLong* sourceLen) { |
|
return UncompressAtMostOrAll(dest, destLen, source, sourceLen, Z_SYNC_FLUSH); |
|
} |
|
|
|
int ZLib::UncompressChunk(Bytef* dest, uLongf* destLen, const Bytef* source, |
|
uLong sourceLen) { |
|
return UncompressChunkOrAll(dest, destLen, source, sourceLen, Z_SYNC_FLUSH); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool ZLib::UncompressChunkDone() { |
|
if (first_chunk_ || !uncomp_init_) { |
|
return false; |
|
} |
|
|
|
|
|
|
|
Bytef dummyin, dummyout; |
|
uLongf dummylen = 0; |
|
if (UncompressChunkOrAll(&dummyout, &dummylen, &dummyin, 0, Z_FINISH) != |
|
Z_OK) { |
|
return false; |
|
} |
|
|
|
|
|
Reset(); |
|
|
|
|
|
if (should_be_flexible_with_gzip_footer_) { |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ((gzip_footer_bytes_ != -1) && (gzip_footer_bytes_ != 0) && |
|
(gzip_footer_bytes_ != sizeof(gzip_footer_))) |
|
return false; |
|
|
|
if (!settings_.gzip_header_mode_) return true; |
|
|
|
return IsGzipFooterValid(); |
|
} |
|
|
|
bool ZLib::IsGzipFooterValid() const { |
|
|
|
|
|
if (gzip_footer_bytes_ == -1 || gzip_footer_bytes_ < sizeof(gzip_footer_)) |
|
return false; |
|
|
|
|
|
uLong uncompressed_size = 0; |
|
uncompressed_size += static_cast<uLong>(gzip_footer_[7]) << 24; |
|
uncompressed_size += gzip_footer_[6] << 16; |
|
uncompressed_size += gzip_footer_[5] << 8; |
|
uncompressed_size += gzip_footer_[4] << 0; |
|
if (uncompressed_size != (uncompressed_size_ & 0xffffffff)) { |
|
return false; |
|
} |
|
|
|
uLong checksum = 0; |
|
checksum += static_cast<uLong>(gzip_footer_[3]) << 24; |
|
checksum += gzip_footer_[2] << 16; |
|
checksum += gzip_footer_[1] << 8; |
|
checksum += gzip_footer_[0] << 0; |
|
if (crc_ != checksum) return false; |
|
|
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int ZLib::Uncompress(Bytef* dest, uLongf* destLen, const Bytef* source, |
|
uLong sourceLen) { |
|
int err; |
|
if ((err = UncompressChunkOrAll(dest, destLen, source, sourceLen, |
|
Z_FINISH)) != Z_OK) { |
|
Reset(); |
|
return err; |
|
} |
|
if (!UncompressChunkDone()) |
|
return Z_DATA_ERROR; |
|
return Z_OK; |
|
} |
|
|
|
|
|
uLongf ZLib::GzipUncompressedLength(const Bytef* source, uLong len) { |
|
if (len <= 4) return 0; |
|
|
|
return (static_cast<uLongf>(source[len - 1]) << 24) + |
|
(static_cast<uLongf>(source[len - 2]) << 16) + |
|
(static_cast<uLongf>(source[len - 3]) << 8) + |
|
(static_cast<uLongf>(source[len - 4]) << 0); |
|
} |
|
|
|
int ZLib::UncompressGzipAndAllocate(Bytef** dest, uLongf* destLen, |
|
const Bytef* source, uLong sourceLen) { |
|
*dest = nullptr; |
|
if (!settings_.gzip_header_mode_) return Z_VERSION_ERROR; |
|
|
|
uLongf uncompress_length = GzipUncompressedLength(source, sourceLen); |
|
|
|
|
|
if (uncompress_length > *destLen) { |
|
if (!HasGzipHeader(reinterpret_cast<const char*>(source), sourceLen)) { |
|
VLOG(1) << "Attempted to un-gzip data that is not gzipped."; |
|
return Z_DATA_ERROR; |
|
} |
|
VLOG(1) << "Uncompressed size " << uncompress_length |
|
<< " exceeds maximum expected size " << *destLen; |
|
return Z_MEM_ERROR; |
|
} |
|
|
|
*destLen = uncompress_length; |
|
|
|
*dest = (Bytef*)malloc(*destLen); |
|
if (*dest == nullptr) |
|
return Z_MEM_ERROR; |
|
|
|
const int retval = Uncompress(*dest, destLen, source, sourceLen); |
|
if (retval != Z_OK) { |
|
free(*dest); |
|
*dest = nullptr; |
|
} |
|
return retval; |
|
} |
|
|
|
|
|
|
|
bool ZLib::HasGzipHeader(const char* source, int sourceLen) { |
|
GZipHeader gzh; |
|
const char* ptr = nullptr; |
|
return gzh.ReadMore(source, sourceLen, &ptr) == GZipHeader::COMPLETE_HEADER; |
|
} |
|
|
|
} |
|
|