1/* compression_utils_portable.cc 2 * 3 * Copyright 2019 The Chromium Authors 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the Chromium source repository LICENSE file. 6 */ 7 8#include "compression_utils_portable.h" 9 10#include <stddef.h> 11#include <stdlib.h> 12#include <string.h> 13 14namespace zlib_internal { 15 16// The difference in bytes between a zlib header and a gzip header. 17const size_t kGzipZlibHeaderDifferenceBytes = 16; 18 19// Pass an integer greater than the following get a gzip header instead of a 20// zlib header when calling deflateInit2() and inflateInit2(). 21const int kWindowBitsToGetGzipHeader = 16; 22 23// This describes the amount of memory zlib uses to compress data. It can go 24// from 1 to 9, with 8 being the default. For details, see: 25// http://www.zlib.net/manual.html (search for memLevel). 26const int kZlibMemoryLevel = 8; 27 28// The expected compressed size is based on the input size factored by 29// internal Zlib constants (e.g. window size, etc) plus the wrapper 30// header size. 31uLongf GzipExpectedCompressedSize(uLongf input_size) { 32 return kGzipZlibHeaderDifferenceBytes + compressBound(input_size); 33} 34 35// The expected decompressed size is stored in the last 36// 4 bytes of |input| in LE. See https://tools.ietf.org/html/rfc1952#page-5 37uint32_t GetGzipUncompressedSize(const Bytef* compressed_data, size_t length) { 38 uint32_t size; 39 if (length < sizeof(size)) 40 return 0; 41 42 memcpy(&size, &compressed_data[length - sizeof(size)], sizeof(size)); 43#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 44 return size; 45#else 46 return __builtin_bswap32(size); 47#endif 48} 49 50// The number of window bits determines the type of wrapper to use - see 51// https://cs.chromium.org/chromium/src/third_party/zlib/zlib.h?l=566 52inline int ZlibStreamWrapperType(WrapperType type) { 53 if (type == ZLIB) // zlib DEFLATE stream wrapper 54 return MAX_WBITS; 55 if (type == GZIP) // gzip DEFLATE stream wrapper 56 return MAX_WBITS + kWindowBitsToGetGzipHeader; 57 if (type == ZRAW) // no wrapper, use raw DEFLATE 58 return -MAX_WBITS; 59 return 0; 60} 61 62int GzipCompressHelper(Bytef* dest, 63 uLongf* dest_length, 64 const Bytef* source, 65 uLong source_length, 66 void* (*malloc_fn)(size_t), 67 void (*free_fn)(void*)) { 68 return CompressHelper(GZIP, dest, dest_length, source, source_length, 69 Z_DEFAULT_COMPRESSION, malloc_fn, free_fn); 70} 71 72// This code is taken almost verbatim from third_party/zlib/compress.c. The only 73// difference is deflateInit2() is called which allows different window bits to 74// be set. > 16 causes a gzip header to be emitted rather than a zlib header, 75// and negative causes no header to emitted. 76// 77// Compression level can be a number from 1-9, with 1 being the fastest, 9 being 78// the best compression. The default, which the GZIP helper uses, is 6. 79int CompressHelper(WrapperType wrapper_type, 80 Bytef* dest, 81 uLongf* dest_length, 82 const Bytef* source, 83 uLong source_length, 84 int compression_level, 85 void* (*malloc_fn)(size_t), 86 void (*free_fn)(void*)) { 87 if (compression_level < 0 || compression_level > 9) { 88 compression_level = Z_DEFAULT_COMPRESSION; 89 } 90 91 z_stream stream; 92 93 // FIXME(cavalcantii): z_const is not defined as 'const'. 94 stream.next_in = static_cast<z_const Bytef*>(const_cast<Bytef*>(source)); 95 stream.avail_in = static_cast<uInt>(source_length); 96 stream.next_out = dest; 97 stream.avail_out = static_cast<uInt>(*dest_length); 98 if (static_cast<uLong>(stream.avail_out) != *dest_length) 99 return Z_BUF_ERROR; 100 101 // Cannot convert capturing lambdas to function pointers directly, hence the 102 // structure. 103 struct MallocFreeFunctions { 104 void* (*malloc_fn)(size_t); 105 void (*free_fn)(void*); 106 } malloc_free = {malloc_fn, free_fn}; 107 108 if (malloc_fn) { 109 if (!free_fn) 110 return Z_BUF_ERROR; 111 112 auto zalloc = [](void* opaque, uInt items, uInt size) { 113 return reinterpret_cast<MallocFreeFunctions*>(opaque)->malloc_fn(items * 114 size); 115 }; 116 auto zfree = [](void* opaque, void* address) { 117 return reinterpret_cast<MallocFreeFunctions*>(opaque)->free_fn(address); 118 }; 119 120 stream.zalloc = static_cast<alloc_func>(zalloc); 121 stream.zfree = static_cast<free_func>(zfree); 122 stream.opaque = static_cast<voidpf>(&malloc_free); 123 } else { 124 stream.zalloc = static_cast<alloc_func>(0); 125 stream.zfree = static_cast<free_func>(0); 126 stream.opaque = static_cast<voidpf>(0); 127 } 128 129 int err = deflateInit2(&stream, compression_level, Z_DEFLATED, 130 ZlibStreamWrapperType(wrapper_type), kZlibMemoryLevel, 131 Z_DEFAULT_STRATEGY); 132 if (err != Z_OK) 133 return err; 134 135 // This has to exist outside of the if statement to prevent it going off the 136 // stack before deflate(), which will use this object. 137 gz_header gzip_header; 138 if (wrapper_type == GZIP) { 139 memset(&gzip_header, 0, sizeof(gzip_header)); 140 err = deflateSetHeader(&stream, &gzip_header); 141 if (err != Z_OK) 142 return err; 143 } 144 145 err = deflate(&stream, Z_FINISH); 146 if (err != Z_STREAM_END) { 147 deflateEnd(&stream); 148 return err == Z_OK ? Z_BUF_ERROR : err; 149 } 150 *dest_length = stream.total_out; 151 152 err = deflateEnd(&stream); 153 return err; 154} 155 156int GzipUncompressHelper(Bytef* dest, 157 uLongf* dest_length, 158 const Bytef* source, 159 uLong source_length) { 160 return UncompressHelper(GZIP, dest, dest_length, source, source_length); 161} 162 163// This code is taken almost verbatim from third_party/zlib/uncompr.c. The only 164// difference is inflateInit2() is called which allows different window bits to 165// be set. > 16 causes a gzip header to be emitted rather than a zlib header, 166// and negative causes no header to emitted. 167int UncompressHelper(WrapperType wrapper_type, 168 Bytef* dest, 169 uLongf* dest_length, 170 const Bytef* source, 171 uLong source_length) { 172 z_stream stream; 173 174 // FIXME(cavalcantii): z_const is not defined as 'const'. 175 stream.next_in = static_cast<z_const Bytef*>(const_cast<Bytef*>(source)); 176 stream.avail_in = static_cast<uInt>(source_length); 177 if (static_cast<uLong>(stream.avail_in) != source_length) 178 return Z_BUF_ERROR; 179 180 stream.next_out = dest; 181 stream.avail_out = static_cast<uInt>(*dest_length); 182 if (static_cast<uLong>(stream.avail_out) != *dest_length) 183 return Z_BUF_ERROR; 184 185 stream.zalloc = static_cast<alloc_func>(0); 186 stream.zfree = static_cast<free_func>(0); 187 188 int err = inflateInit2(&stream, ZlibStreamWrapperType(wrapper_type)); 189 if (err != Z_OK) 190 return err; 191 192 err = inflate(&stream, Z_FINISH); 193 if (err != Z_STREAM_END) { 194 inflateEnd(&stream); 195 if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) 196 return Z_DATA_ERROR; 197 return err; 198 } 199 *dest_length = stream.total_out; 200 201 err = inflateEnd(&stream); 202 return err; 203} 204 205} // namespace zlib_internal 206