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