/* This file is part of libdjinterop. libdjinterop is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. libdjinterop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with libdjinterop. If not, see . */ #include #include #include #include #include #include namespace djinterop { namespace enginelibrary { // Uncompress a zlib'ed BLOB std::vector zlib_uncompress( const std::vector& compressed, std::vector uncompressed) { if (compressed.size() > 0 && compressed.size() < 4) throw std::length_error( "Compressed data is less than the minimum size of 4 bytes"); uncompressed.clear(); auto apparent_size = compressed.size() == 0 ? 0 : decode_int32_be(compressed.data()).first; if (apparent_size == 0) { // No data, which is a valid situation return uncompressed; // Named RVO } uncompressed.reserve(apparent_size); const char* ptr = &compressed[4]; const char* end = ptr + compressed.size(); const int chunk_size = 16384; int ret; unsigned int have; unsigned char out[chunk_size]; z_stream strm; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = 0; strm.next_in = Z_NULL; ret = inflateInit(&strm); if (ret != Z_OK) throw std::system_error{ret, std::system_category(), "Error calling inflateInit from zlib"}; // Take chunks from the input vector do { strm.next_in = (Bytef*)ptr; strm.avail_in = (ptr + chunk_size) < end ? chunk_size : end - ptr; ptr += strm.avail_in; // Run inflate() on until we are no longer filling the output buffer do { strm.next_out = out; strm.avail_out = chunk_size; ret = inflate(&strm, Z_NO_FLUSH); switch (ret) { case Z_NEED_DICT: ret = Z_DATA_ERROR; // Fall through case Z_DATA_ERROR: case Z_MEM_ERROR: inflateEnd(&strm); throw std::system_error{ret, std::system_category(), "Error calling inflate from zlib"}; } have = chunk_size - strm.avail_out; uncompressed.insert(uncompressed.end(), out, out + have); } while (strm.avail_out == 0); // done when inflate() says it's done } while (ret != Z_STREAM_END); // Clean up inflateEnd(&strm); if (ret != Z_STREAM_END) { throw std::system_error{Z_DATA_ERROR, std::system_category(), "Error at end of inflation"}; } return uncompressed; // Named RVO } // Compress a byte array using zlib std::vector zlib_compress( const std::vector& uncompressed, std::vector compressed) { // Put a placeholder four bytes to hold the uncompressed buffer size compressed.resize(4); encode_int32_be(uncompressed.size(), compressed.data()); // Compress const char* ptr = &uncompressed[0]; const char* end = ptr + uncompressed.size(); const int chunk_size = 16384; int ret, flush; unsigned int have; z_stream strm; unsigned char out[chunk_size]; // Allocate deflate state strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION); if (ret != Z_OK) throw std::system_error{ret, std::system_category(), "Error calling deflateInit from zlib"}; // Compress until end of input do { strm.next_in = (Bytef*)ptr; if (ptr + chunk_size < end) { strm.avail_in = chunk_size; flush = Z_NO_FLUSH; } else { strm.avail_in = end - ptr; flush = Z_FINISH; } ptr += strm.avail_in; // Run deflate() on input until output buffer not full, finish // compression if all of source has been read in do { strm.next_out = out; strm.avail_out = chunk_size; ret = deflate(&strm, flush); /* no bad return value */ have = chunk_size - strm.avail_out; compressed.insert(compressed.end(), out, out + have); } while (strm.avail_out == 0); // Done when last data in input processed } while (flush != Z_FINISH); // Clean up and return deflateEnd(&strm); return compressed; } } // namespace enginelibrary } // namespace djinterop