1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "components/metrics/compression_utils.h" 6 7 #include <vector> 8 9 #include "base/basictypes.h" 10 #include "base/logging.h" 11 #include "base/sys_byteorder.h" 12 #include "third_party/zlib/zlib.h" 13 14 namespace { 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 // This code is taken almost verbatim from third_party/zlib/compress.c. The only 29 // difference is deflateInit2() is called which sets the window bits to be > 16. 30 // That causes a gzip header to be emitted rather than a zlib header. 31 int GzipCompressHelper(Bytef* dest, 32 uLongf* dest_length, 33 const Bytef* source, 34 uLong source_length) { 35 z_stream stream; 36 37 stream.next_in = bit_cast<Bytef*>(source); 38 stream.avail_in = static_cast<uInt>(source_length); 39 stream.next_out = dest; 40 stream.avail_out = static_cast<uInt>(*dest_length); 41 if (static_cast<uLong>(stream.avail_out) != *dest_length) 42 return Z_BUF_ERROR; 43 44 stream.zalloc = static_cast<alloc_func>(0); 45 stream.zfree = static_cast<free_func>(0); 46 stream.opaque = static_cast<voidpf>(0); 47 48 gz_header gzip_header; 49 memset(&gzip_header, 0, sizeof(gzip_header)); 50 int err = deflateInit2(&stream, 51 Z_DEFAULT_COMPRESSION, 52 Z_DEFLATED, 53 MAX_WBITS + kWindowBitsToGetGzipHeader, 54 kZlibMemoryLevel, 55 Z_DEFAULT_STRATEGY); 56 if (err != Z_OK) 57 return err; 58 59 err = deflateSetHeader(&stream, &gzip_header); 60 if (err != Z_OK) 61 return err; 62 63 err = deflate(&stream, Z_FINISH); 64 if (err != Z_STREAM_END) { 65 deflateEnd(&stream); 66 return err == Z_OK ? Z_BUF_ERROR : err; 67 } 68 *dest_length = stream.total_out; 69 70 err = deflateEnd(&stream); 71 return err; 72 } 73 74 // This code is taken almost verbatim from third_party/zlib/uncompr.c. The only 75 // difference is inflateInit2() is called which sets the window bits to be > 16. 76 // That causes a gzip header to be parsed rather than a zlib header. 77 int GzipUncompressHelper(Bytef* dest, 78 uLongf* dest_length, 79 const Bytef* source, 80 uLong source_length) { 81 z_stream stream; 82 83 stream.next_in = bit_cast<Bytef*>(source); 84 stream.avail_in = static_cast<uInt>(source_length); 85 if (static_cast<uLong>(stream.avail_in) != source_length) 86 return Z_BUF_ERROR; 87 88 stream.next_out = dest; 89 stream.avail_out = static_cast<uInt>(*dest_length); 90 if (static_cast<uLong>(stream.avail_out) != *dest_length) 91 return Z_BUF_ERROR; 92 93 stream.zalloc = static_cast<alloc_func>(0); 94 stream.zfree = static_cast<free_func>(0); 95 96 int err = inflateInit2(&stream, MAX_WBITS + kWindowBitsToGetGzipHeader); 97 if (err != Z_OK) 98 return err; 99 100 err = inflate(&stream, Z_FINISH); 101 if (err != Z_STREAM_END) { 102 inflateEnd(&stream); 103 if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) 104 return Z_DATA_ERROR; 105 return err; 106 } 107 *dest_length = stream.total_out; 108 109 err = inflateEnd(&stream); 110 return err; 111 } 112 113 // Returns the uncompressed size from GZIP-compressed |compressed_data|. 114 uint32 GetUncompressedSize(const std::string& compressed_data) { 115 // The uncompressed size is stored in the last 4 bytes of |input| in LE. 116 uint32 size; 117 if (compressed_data.length() < sizeof(size)) 118 return 0; 119 memcpy(&size, &compressed_data[compressed_data.length() - sizeof(size)], 120 sizeof(size)); 121 return base::ByteSwapToLE32(size); 122 } 123 124 } // namespace 125 126 namespace metrics { 127 128 bool GzipCompress(const std::string& input, std::string* output) { 129 const uLongf input_size = static_cast<uLongf>(input.size()); 130 std::vector<Bytef> compressed_data(kGzipZlibHeaderDifferenceBytes + 131 compressBound(input_size)); 132 133 uLongf compressed_size = static_cast<uLongf>(compressed_data.size()); 134 if (GzipCompressHelper(&compressed_data.front(), 135 &compressed_size, 136 bit_cast<const Bytef*>(input.data()), 137 input_size) != Z_OK) { 138 return false; 139 } 140 141 compressed_data.resize(compressed_size); 142 output->assign(compressed_data.begin(), compressed_data.end()); 143 DCHECK_EQ(input.size(), GetUncompressedSize(*output)); 144 return true; 145 } 146 147 bool GzipUncompress(const std::string& input, std::string* output) { 148 output->resize(GetUncompressedSize(input)); 149 uLongf uncompressed_size = static_cast<uLongf>(output->length()); 150 return GzipUncompressHelper(bit_cast<Bytef*>(output->data()), 151 &uncompressed_size, 152 bit_cast<const Bytef*>(input.data()), 153 static_cast<uLongf>(input.length())) == Z_OK; 154 } 155 156 } // namespace metrics 157