zlibbytearray.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053 #include <QString>
00054
00055 #include "config.h"
00056 #ifdef HAVE_LIMITS_H
00057 #include <limits.h>
00058 #elif HAVE_SYS_LIMITS_H
00059 #include <sys/limits.h>
00060 #endif
00061
00062
00063
00064 #ifndef UINT_MAX
00065 #if (SIZEOF_INT == 2)
00066 #define UINT_MAX 0xffffu
00067 #elif (SIZEOF_INT == 4)
00068 #define UINT_MAX 0xffffffffu
00069 #elif (SIZEOF_INT == 8)
00070 #define UINT_MAX 0xffffffffffffffffu
00071 #else
00072 #error "Your platform uses a sizeof(int) that we don't understand."
00073 #endif
00074 #endif
00075
00076 #include "zlib.h"
00077 #include "zlibbytearray.h"
00078
00079
00080
00081 ZlibByteArray::ZlibByteArray(QByteArray data)
00082 : QByteArray(data)
00083 {
00084 }
00085
00086
00087 int
00088 ZlibByteArray::methodBits(CompressionMethod method)
00089 {
00090
00091 return (method == Gzip ? 15+16 : 15);
00092 }
00093
00094
00095 QString
00096 ZlibByteArray::methodString(CompressionMethod method)
00097 {
00098 switch (method) {
00099 case None: return "None";
00100 case Zlib: return "Zlib";
00101 case Gzip: return "Gzip";
00102 default: return "Unknown";
00103 }
00104 }
00105
00106
00107 bool
00108 ZlibByteArray::isZlibAvailable()
00109 {
00110 static int isZlibAvailable = -1;
00111 if (isZlibAvailable >= 0)
00112 return isZlibAvailable;
00113
00114
00115
00116
00117
00118 QString libVersion(zlibVersion());
00119 QString headerVersion(ZLIB_VERSION);
00120 if (libVersion.isEmpty() || headerVersion.isEmpty() ||
00121 libVersion.at(0) != headerVersion.at(0))
00122 isZlibAvailable = 0;
00123 else
00124 isZlibAvailable = 1;
00125
00126 return isZlibAvailable;
00127 }
00128
00129
00130
00131 bool
00132 ZlibByteArray::isGzipSupported()
00133 {
00134 static int isGzipSupported = -1;
00135 if (isGzipSupported >= 0)
00136 return isGzipSupported;
00137
00138 QString version(zlibVersion());
00139 if (version.startsWith("0.") ||
00140 version.startsWith("1.0") ||
00141 version.startsWith("1.1"))
00142 isGzipSupported = 0;
00143 else
00144 isGzipSupported = 1;
00145
00146 return isGzipSupported;
00147 }
00148
00149
00150
00151
00152
00153 QByteArray
00154 ZlibByteArray::compress(const CompressionMethod method,
00155 QString *errmsg) const
00156 {
00157 return compress(QByteArray(data()), method, errmsg);
00158 }
00159
00160
00161
00162
00163 QByteArray
00164 ZlibByteArray::compress(const QByteArray in,
00165 const CompressionMethod method,
00166 QString *errmsg)
00167 {
00168 QByteArray out;
00169 QString errorstr;
00170 struct z_stream_s *stream = NULL;
00171 size_t out_size;
00172 size_t out_len;
00173 size_t in_len = in.length();
00174 off_t offset;
00175
00176 if (method == None)
00177 return in;
00178 if (method == Gzip && !isGzipSupported()) {
00179
00180 if (errmsg)
00181 *errmsg = QString("Gzip not supported with zlib %1")
00182 .arg(ZLIB_VERSION);
00183 return QByteArray();
00184 }
00185
00186 stream = new struct z_stream_s;
00187 stream->zalloc = Z_NULL;
00188 stream->zfree = Z_NULL;
00189 stream->opaque = NULL;
00190 stream->next_in = (unsigned char*)in.data();
00191 stream->avail_in = in_len;
00192
00193 if (deflateInit2(stream, Z_BEST_COMPRESSION, Z_DEFLATED,
00194 methodBits(method),
00195 8, Z_DEFAULT_STRATEGY) != Z_OK) {
00196 errorstr = QString("Error from deflateInit2: %1")
00197 .arg(stream->msg ? stream->msg : "<no message>");
00198 goto err;
00199 }
00200
00201
00202 out_size = in_len / 2;
00203 if (out_size < 1024) out_size = 1024;
00204
00205 out.resize(out_size);
00206 stream->next_out = (unsigned char*)out.data();
00207 stream->avail_out = out_size;
00208
00209 while (1) {
00210 switch (deflate(stream, Z_FINISH))
00211 {
00212 case Z_STREAM_END:
00213 goto done;
00214 case Z_OK:
00215
00216 if (stream->avail_out >= stream->avail_in+16)
00217 break;
00218 case Z_BUF_ERROR:
00219 offset = stream->next_out - ((unsigned char*)out.data());
00220 out_size *= 2;
00221 out.resize(out_size);
00222 stream->next_out = (unsigned char*)(out.data() + offset);
00223 if (out_size - offset > UINT_MAX) {
00224 errorstr =
00225 "Ran over unsigned int limit of zlib while uncompressing";
00226 goto err;
00227 }
00228 stream->avail_out = (unsigned int)(out_size - offset);
00229 break;
00230 default:
00231 errorstr = QString("%1 compression didn't finish: %2")
00232 .arg(methodString(method))
00233 .arg(stream->msg ? stream->msg : "<no message>");
00234 goto err;
00235 }
00236 }
00237 done:
00238 out_len = stream->total_out;
00239 if (deflateEnd(stream)!=Z_OK) {
00240 errorstr = "Error freeing zlib structures";
00241 goto err;
00242 }
00243 out.resize(out_len);
00244 delete stream;
00245 return out;
00246 err:
00247 if (stream) {
00248 deflateEnd(stream);
00249 delete stream;
00250 }
00251 if (errmsg)
00252 *errmsg = errorstr;
00253 return QByteArray();
00254 }
00255
00256
00257
00258
00259
00260 QByteArray
00261 ZlibByteArray::uncompress(const CompressionMethod method,
00262 QString *errmsg) const
00263 {
00264 return uncompress(QByteArray(data()), method, errmsg);
00265 }
00266
00267
00268
00269
00270 QByteArray
00271 ZlibByteArray::uncompress(const QByteArray in,
00272 const CompressionMethod method,
00273 QString *errmsg)
00274 {
00275 QByteArray out;
00276 QString errorstr;
00277 struct z_stream_s *stream = NULL;
00278 size_t out_size;
00279 size_t out_len;
00280 size_t in_len = in.length();
00281 off_t offset;
00282 int r;
00283
00284 if (method == None)
00285 return in;
00286 if (method == Gzip && !isGzipSupported()) {
00287
00288 if (errmsg)
00289 *errmsg = QString("Gzip not supported with zlib %1")
00290 .arg(ZLIB_VERSION);
00291 return QByteArray();
00292 }
00293
00294 stream = new struct z_stream_s;
00295 stream->zalloc = Z_NULL;
00296 stream->zfree = Z_NULL;
00297 stream->opaque = NULL;
00298 stream->msg = NULL;
00299 stream->next_in = (unsigned char*) in.data();
00300 stream->avail_in = in_len;
00301
00302 if (inflateInit2(stream,
00303 methodBits(method)) != Z_OK) {
00304 errorstr = QString("Error from inflateInit2: %1")
00305 .arg(stream->msg ? stream->msg : "<no message>");
00306 goto err;
00307 }
00308
00309 out_size = in_len * 2;
00310 if (out_size < 1024) out_size = 1024;
00311
00312 out.resize(out_size);
00313 stream->next_out = (unsigned char*)out.data();
00314 stream->avail_out = out_size;
00315
00316 while (1) {
00317 switch (inflate(stream, Z_FINISH))
00318 {
00319 case Z_STREAM_END:
00320 if (stream->avail_in == 0)
00321 goto done;
00322
00323 if ((r = inflateEnd(stream)) != Z_OK) {
00324 errorstr = "Error freeing zlib structures";
00325 goto err;
00326 }
00327 if (inflateInit2(stream, methodBits(method)) != Z_OK) {
00328 errorstr = QString("Error from second inflateInit2: %1")
00329 .arg(stream->msg ? stream->msg : "<no message>");
00330 goto err;
00331 }
00332 break;
00333 case Z_OK:
00334 if (stream->avail_in == 0)
00335 goto done;
00336
00337 if (stream->avail_out >= stream->avail_in+16)
00338 break;
00339 case Z_BUF_ERROR:
00340 if (stream->avail_out > 0) {
00341 errorstr = QString("Possible truncated or corrupt %1 data")
00342 .arg(methodString(method));
00343 goto err;
00344 }
00345 offset = stream->next_out - (unsigned char*)out.data();
00346 out_size *= 2;
00347 out.resize(out_size);
00348 stream->next_out = (unsigned char*)(out.data() + offset);
00349 if (out_size - offset > UINT_MAX) {
00350 errorstr =
00351 "Ran over unsigned int limit of zlib while uncompressing";
00352 goto err;
00353 }
00354 stream->avail_out = (unsigned int)(out_size - offset);
00355 break;
00356 default:
00357 errorstr = QString("%1 decompression returned an error: %2")
00358 .arg(methodString(method))
00359 .arg(stream->msg ? stream->msg : "<no message>");
00360 goto err;
00361 }
00362 }
00363 done:
00364 out_len = stream->next_out - (unsigned char*)out.data();
00365 r = inflateEnd(stream);
00366 delete stream;
00367 if (r != Z_OK) {
00368 errorstr = "Error freeing zlib structure";
00369 goto err;
00370 }
00371 out.resize(out_len);
00372 return out;
00373 err:
00374 if (stream) {
00375 inflateEnd(stream);
00376 delete stream;
00377 }
00378 if (errmsg)
00379 *errmsg = errorstr;
00380 return QByteArray();
00381 }
00382