| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 1 | // Copyright (c) 2009 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 "update_engine/gzip.h" | 
 | 6 | #include <stdlib.h> | 
 | 7 | #include <algorithm> | 
 | 8 | #include <zlib.h> | 
| Chris Masone | 790e62e | 2010-08-12 10:41:18 -0700 | [diff] [blame] | 9 | #include "base/logging.h" | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 10 | #include "update_engine/utils.h" | 
 | 11 |  | 
 | 12 | using std::max; | 
 | 13 | using std::string; | 
 | 14 | using std::vector; | 
 | 15 |  | 
 | 16 | namespace chromeos_update_engine { | 
 | 17 |  | 
 | 18 | bool GzipDecompressData(const char* const in, const size_t in_size, | 
 | 19 |                         char** out, size_t* out_size) { | 
 | 20 |   if (in_size == 0) { | 
 | 21 |     // malloc(0) may legally return NULL, so do malloc(1) | 
 | 22 |     *out = reinterpret_cast<char*>(malloc(1)); | 
 | 23 |     *out_size = 0; | 
 | 24 |     return true; | 
 | 25 |   } | 
 | 26 |   TEST_AND_RETURN_FALSE(out); | 
 | 27 |   TEST_AND_RETURN_FALSE(out_size); | 
 | 28 |   z_stream stream; | 
 | 29 |   memset(&stream, 0, sizeof(stream)); | 
 | 30 |   TEST_AND_RETURN_FALSE(inflateInit2(&stream, 16 + MAX_WBITS) == Z_OK); | 
 | 31 |  | 
 | 32 |   // guess that output will be roughly double the input size | 
 | 33 |   *out_size = in_size * 2; | 
 | 34 |   *out = reinterpret_cast<char*>(malloc(*out_size)); | 
 | 35 |   TEST_AND_RETURN_FALSE(*out); | 
 | 36 |  | 
 | 37 |   // TODO(adlr): ensure that this const_cast is safe. | 
 | 38 |   stream.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(in)); | 
 | 39 |   stream.avail_in = in_size; | 
 | 40 |   stream.next_out = reinterpret_cast<Bytef*>(*out); | 
 | 41 |   stream.avail_out = *out_size; | 
 | 42 |   for (;;) { | 
 | 43 |     int rc = inflate(&stream, Z_FINISH); | 
 | 44 |     switch (rc) { | 
 | 45 |       case Z_STREAM_END: { | 
 | 46 |         *out_size = reinterpret_cast<char*>(stream.next_out) - (*out); | 
 | 47 |         TEST_AND_RETURN_FALSE(inflateEnd(&stream) == Z_OK); | 
 | 48 |         return true; | 
 | 49 |       } | 
 | 50 |       case Z_OK:  // fall through | 
 | 51 |       case Z_BUF_ERROR: { | 
 | 52 |         // allocate more space | 
 | 53 |         ptrdiff_t out_length = | 
 | 54 |             reinterpret_cast<char*>(stream.next_out) - (*out); | 
 | 55 |         *out_size *= 2; | 
 | 56 |         char* new_out = reinterpret_cast<char*>(realloc(*out, *out_size)); | 
 | 57 |         if (!new_out) { | 
 | 58 |           free(*out); | 
 | 59 |           return false; | 
 | 60 |         } | 
 | 61 |         *out = new_out; | 
 | 62 |         stream.next_out = reinterpret_cast<Bytef*>((*out) + out_length); | 
 | 63 |         stream.avail_out = (*out_size) - out_length; | 
 | 64 |         break; | 
 | 65 |       } | 
 | 66 |       default: | 
 | 67 |         LOG(INFO) << "Unknown inflate() return value: " << rc; | 
 | 68 |         if (stream.msg) | 
 | 69 |           LOG(INFO) << " message: " << stream.msg; | 
 | 70 |         free(*out); | 
 | 71 |         return false; | 
 | 72 |     } | 
 | 73 |   } | 
 | 74 | } | 
 | 75 |  | 
 | 76 | bool GzipCompressData(const char* const in, const size_t in_size, | 
 | 77 |                       char** out, size_t* out_size) { | 
 | 78 |   if (in_size == 0) { | 
 | 79 |     // malloc(0) may legally return NULL, so do malloc(1) | 
 | 80 |     *out = reinterpret_cast<char*>(malloc(1)); | 
 | 81 |     *out_size = 0; | 
 | 82 |     return true; | 
 | 83 |   } | 
 | 84 |   TEST_AND_RETURN_FALSE(out); | 
 | 85 |   TEST_AND_RETURN_FALSE(out_size); | 
 | 86 |   z_stream stream; | 
 | 87 |   memset(&stream, 0, sizeof(stream)); | 
 | 88 |   TEST_AND_RETURN_FALSE(deflateInit2(&stream, | 
 | 89 |                                       Z_BEST_COMPRESSION, | 
 | 90 |                                       Z_DEFLATED, | 
 | 91 |                                       16 + MAX_WBITS, | 
 | 92 |                                       9,  // most memory used/best compression | 
 | 93 |                                       Z_DEFAULT_STRATEGY) == Z_OK); | 
 | 94 |  | 
 | 95 |   // guess that output will be roughly half the input size | 
| Andrew de los Reyes | 08c4e27 | 2010-04-15 14:02:17 -0700 | [diff] [blame] | 96 |   *out_size = max(static_cast<size_t>(1), in_size / 2); | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 97 |   *out = reinterpret_cast<char*>(malloc(*out_size)); | 
 | 98 |   TEST_AND_RETURN_FALSE(*out); | 
 | 99 |  | 
 | 100 |   // TODO(adlr): ensure that this const_cast is safe. | 
 | 101 |   stream.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(in)); | 
 | 102 |   stream.avail_in = in_size; | 
 | 103 |   stream.next_out = reinterpret_cast<Bytef*>(*out); | 
 | 104 |   stream.avail_out = *out_size; | 
 | 105 |   for (;;) { | 
 | 106 |     int rc = deflate(&stream, Z_FINISH); | 
 | 107 |     switch (rc) { | 
 | 108 |       case Z_STREAM_END: { | 
 | 109 |         *out_size = reinterpret_cast<char*>(stream.next_out) - (*out); | 
 | 110 |         TEST_AND_RETURN_FALSE(deflateEnd(&stream) == Z_OK); | 
 | 111 |         return true; | 
 | 112 |       } | 
 | 113 |       case Z_OK:  // fall through | 
 | 114 |       case Z_BUF_ERROR: { | 
 | 115 |         // allocate more space | 
 | 116 |         ptrdiff_t out_length = | 
 | 117 |             reinterpret_cast<char*>(stream.next_out) - (*out); | 
 | 118 |         *out_size *= 2; | 
 | 119 |         char* new_out = reinterpret_cast<char*>(realloc(*out, *out_size)); | 
 | 120 |         if (!new_out) { | 
 | 121 |           free(*out); | 
 | 122 |           return false; | 
 | 123 |         } | 
 | 124 |         *out = new_out; | 
 | 125 |         stream.next_out = reinterpret_cast<Bytef*>((*out) + out_length); | 
 | 126 |         stream.avail_out = (*out_size) - out_length; | 
 | 127 |         break; | 
 | 128 |       } | 
 | 129 |       default: | 
 | 130 |         LOG(INFO) << "Unknown defalate() return value: " << rc; | 
 | 131 |         if (stream.msg) | 
 | 132 |           LOG(INFO) << " message: " << stream.msg; | 
 | 133 |         free(*out); | 
 | 134 |         return false; | 
 | 135 |     } | 
 | 136 |   } | 
 | 137 | } | 
 | 138 |  | 
 | 139 | bool GzipDecompress(const std::vector<char>& in, std::vector<char>* out) { | 
 | 140 |   TEST_AND_RETURN_FALSE(out); | 
 | 141 |   char* out_buf; | 
 | 142 |   size_t out_size; | 
 | 143 |   TEST_AND_RETURN_FALSE(GzipDecompressData(&in[0], in.size(), | 
 | 144 |                                             &out_buf, &out_size)); | 
 | 145 |   out->insert(out->end(), out_buf, out_buf + out_size); | 
 | 146 |   free(out_buf); | 
 | 147 |   return true; | 
 | 148 | } | 
 | 149 |  | 
 | 150 | bool GzipCompress(const std::vector<char>& in, std::vector<char>* out) { | 
 | 151 |   TEST_AND_RETURN_FALSE(out); | 
 | 152 |   char* out_buf; | 
 | 153 |   size_t out_size; | 
 | 154 |   TEST_AND_RETURN_FALSE(GzipCompressData(&in[0], in.size(), | 
 | 155 |                                           &out_buf, &out_size)); | 
 | 156 |   out->insert(out->end(), out_buf, out_buf + out_size); | 
 | 157 |   free(out_buf); | 
 | 158 |   return true; | 
 | 159 | } | 
 | 160 |  | 
 | 161 | bool GzipCompressString(const std::string& str, | 
 | 162 |                         std::vector<char>* out) { | 
 | 163 |   TEST_AND_RETURN_FALSE(out); | 
 | 164 |   char* out_buf; | 
 | 165 |   size_t out_size; | 
 | 166 |   TEST_AND_RETURN_FALSE(GzipCompressData(str.data(), str.size(), | 
 | 167 |                                           &out_buf, &out_size)); | 
 | 168 |   out->insert(out->end(), out_buf, out_buf + out_size); | 
 | 169 |   free(out_buf); | 
 | 170 |   return true; | 
 | 171 | } | 
 | 172 |  | 
 | 173 | bool GzipDecompressString(const std::string& str, | 
 | 174 |                           std::vector<char>* out) { | 
 | 175 |   TEST_AND_RETURN_FALSE(out); | 
 | 176 |   char* out_buf; | 
 | 177 |   size_t out_size; | 
 | 178 |   TEST_AND_RETURN_FALSE(GzipDecompressData(str.data(), str.size(), | 
 | 179 |                                             &out_buf, &out_size)); | 
 | 180 |   out->insert(out->end(), out_buf, out_buf + out_size); | 
 | 181 |   free(out_buf); | 
 | 182 |   return true; | 
 | 183 | } | 
 | 184 |  | 
 | 185 | } // namespace chromeos_update_engine |