1 /* compression_utils_portable.cc
2 *
3 * Copyright 2019 The Chromium Authors. All rights reserved.
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
14 namespace zlib_internal {
15
16 // The difference in bytes between a zlib header and a gzip header.
17 const 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().
21 const 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).
26 const 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.
GzipExpectedCompressedSize(uLongf input_size)31 uLongf 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
GetGzipUncompressedSize(const Bytef * compressed_data,size_t length)37 uint32_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
ZlibStreamWrapperType(WrapperType type)52 inline 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
GzipCompressHelper(Bytef * dest,uLongf * dest_length,const Bytef * source,uLong source_length,void * (* malloc_fn)(size_t),void (* free_fn)(void *))62 int 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.
CompressHelper(WrapperType wrapper_type,Bytef * dest,uLongf * dest_length,const Bytef * source,uLong source_length,int compression_level,void * (* malloc_fn)(size_t),void (* free_fn)(void *))79 int 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
GzipUncompressHelper(Bytef * dest,uLongf * dest_length,const Bytef * source,uLong source_length)156 int 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.
UncompressHelper(WrapperType wrapper_type,Bytef * dest,uLongf * dest_length,const Bytef * source,uLong source_length)167 int 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