tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

compression_utils_portable.cc (7217B)


      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 
     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.
     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
     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
     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 
     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.
     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 
    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.
    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