• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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