1 /* 2 * Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <ZlibCompressionAlgorithm.h> 8 9 #include <errno.h> 10 #include <string.h> 11 12 #include <algorithm> 13 #include <new> 14 15 #include <zlib.h> 16 17 #include <DataIO.h> 18 19 20 // build compression support only for userland 21 #if !defined(_KERNEL_MODE) && !defined(_BOOT_MODE) 22 # define B_ZLIB_COMPRESSION_SUPPORT 1 23 #endif 24 25 26 static const size_t kMinBufferSize = 1024; 27 static const size_t kMaxBufferSize = 1024 * 1024; 28 static const size_t kDefaultBufferSize = 4 * 1024; 29 30 31 static size_t 32 sanitize_buffer_size(size_t size) 33 { 34 if (size < kMinBufferSize) 35 return kMinBufferSize; 36 return std::min(size, kMaxBufferSize); 37 } 38 39 40 // #pragma mark - BZlibCompressionParameters 41 42 43 BZlibCompressionParameters::BZlibCompressionParameters( 44 int compressionLevel) 45 : 46 BCompressionParameters(), 47 fCompressionLevel(compressionLevel), 48 fBufferSize(kDefaultBufferSize), 49 fGzipFormat(false) 50 { 51 } 52 53 54 BZlibCompressionParameters::~BZlibCompressionParameters() 55 { 56 } 57 58 59 int32 60 BZlibCompressionParameters::CompressionLevel() const 61 { 62 return fCompressionLevel; 63 } 64 65 66 void 67 BZlibCompressionParameters::SetCompressionLevel(int32 level) 68 { 69 fCompressionLevel = level; 70 } 71 72 73 size_t 74 BZlibCompressionParameters::BufferSize() const 75 { 76 return fBufferSize; 77 } 78 79 80 void 81 BZlibCompressionParameters::SetBufferSize(size_t size) 82 { 83 fBufferSize = sanitize_buffer_size(size); 84 } 85 86 87 bool 88 BZlibCompressionParameters::IsGzipFormat() const 89 { 90 return fGzipFormat; 91 } 92 93 94 void 95 BZlibCompressionParameters::SetGzipFormat(bool gzipFormat) 96 { 97 fGzipFormat = gzipFormat; 98 } 99 100 101 // #pragma mark - BZlibDecompressionParameters 102 103 104 BZlibDecompressionParameters::BZlibDecompressionParameters() 105 : 106 BDecompressionParameters(), 107 fBufferSize(kDefaultBufferSize) 108 { 109 } 110 111 112 BZlibDecompressionParameters::~BZlibDecompressionParameters() 113 { 114 } 115 116 117 size_t 118 BZlibDecompressionParameters::BufferSize() const 119 { 120 return fBufferSize; 121 } 122 123 124 void 125 BZlibDecompressionParameters::SetBufferSize(size_t size) 126 { 127 fBufferSize = sanitize_buffer_size(size); 128 } 129 130 131 // #pragma mark - CompressionStrategy 132 133 134 #ifdef B_ZLIB_COMPRESSION_SUPPORT 135 136 137 struct BZlibCompressionAlgorithm::CompressionStrategy { 138 typedef BZlibCompressionParameters Parameters; 139 140 static const bool kNeedsFinalFlush = true; 141 142 static int Init(z_stream& stream, 143 const BZlibCompressionParameters* parameters) 144 { 145 int32 compressionLevel = B_ZLIB_COMPRESSION_DEFAULT; 146 bool gzipFormat = false; 147 if (parameters != NULL) { 148 compressionLevel = parameters->CompressionLevel(); 149 gzipFormat = parameters->IsGzipFormat(); 150 } 151 152 return deflateInit2(&stream, compressionLevel, 153 Z_DEFLATED, 154 MAX_WBITS + (gzipFormat ? 16 : 0), 155 MAX_MEM_LEVEL, 156 Z_DEFAULT_STRATEGY); 157 } 158 159 static void Uninit(z_stream& stream) 160 { 161 deflateEnd(&stream); 162 } 163 164 static int Process(z_stream& stream, bool flush) 165 { 166 return deflate(&stream, flush ? Z_FINISH : 0); 167 } 168 }; 169 170 171 #endif // B_ZLIB_COMPRESSION_SUPPORT 172 173 174 // #pragma mark - DecompressionStrategy 175 176 177 struct BZlibCompressionAlgorithm::DecompressionStrategy { 178 typedef BZlibDecompressionParameters Parameters; 179 180 static const bool kNeedsFinalFlush = false; 181 182 static int Init(z_stream& stream, 183 const BZlibDecompressionParameters* /*parameters*/) 184 { 185 // auto-detect zlib/gzip header 186 return inflateInit2(&stream, 32 + MAX_WBITS); 187 } 188 189 static void Uninit(z_stream& stream) 190 { 191 inflateEnd(&stream); 192 } 193 194 static int Process(z_stream& stream, bool flush) 195 { 196 return inflate(&stream, flush ? Z_FINISH : 0); 197 } 198 }; 199 200 201 // #pragma mark - Stream 202 203 204 template<typename BaseClass, typename Strategy> 205 struct BZlibCompressionAlgorithm::Stream : BaseClass { 206 Stream(BDataIO* io) 207 : 208 BaseClass(io), 209 fStreamInitialized(false) 210 { 211 } 212 213 ~Stream() 214 { 215 if (fStreamInitialized) { 216 if (Strategy::kNeedsFinalFlush) 217 this->Flush(); 218 Strategy::Uninit(fStream); 219 } 220 } 221 222 status_t Init(const typename Strategy::Parameters* parameters) 223 { 224 status_t error = this->BaseClass::Init( 225 parameters != NULL ? parameters->BufferSize() : kDefaultBufferSize); 226 if (error != B_OK) 227 return error; 228 229 memset(&fStream, 0, sizeof(fStream)); 230 231 int zlibError = Strategy::Init(fStream, parameters); 232 if (zlibError != Z_OK) 233 return _TranslateZlibError(zlibError); 234 235 fStreamInitialized = true; 236 return B_OK; 237 } 238 239 virtual status_t ProcessData(const void* input, size_t inputSize, 240 void* output, size_t outputSize, size_t& bytesConsumed, 241 size_t& bytesProduced) 242 { 243 return _ProcessData(input, inputSize, output, outputSize, 244 bytesConsumed, bytesProduced, false); 245 } 246 247 virtual status_t FlushPendingData(void* output, size_t outputSize, 248 size_t& bytesProduced) 249 { 250 size_t bytesConsumed; 251 return _ProcessData(NULL, 0, output, outputSize, 252 bytesConsumed, bytesProduced, true); 253 } 254 255 template<typename BaseParameters> 256 static status_t Create(BDataIO* io, BaseParameters* _parameters, 257 BDataIO*& _stream) 258 { 259 const typename Strategy::Parameters* parameters 260 #ifdef _BOOT_MODE 261 = static_cast<const typename Strategy::Parameters*>(_parameters); 262 #else 263 = dynamic_cast<const typename Strategy::Parameters*>(_parameters); 264 #endif 265 Stream* stream = new(std::nothrow) Stream(io); 266 if (stream == NULL) 267 return B_NO_MEMORY; 268 269 status_t error = stream->Init(parameters); 270 if (error != B_OK) { 271 delete stream; 272 return error; 273 } 274 275 _stream = stream; 276 return B_OK; 277 } 278 279 private: 280 status_t _ProcessData(const void* input, size_t inputSize, 281 void* output, size_t outputSize, size_t& bytesConsumed, 282 size_t& bytesProduced, bool flush) 283 { 284 fStream.next_in = (Bytef*)input; 285 fStream.avail_in = inputSize; 286 fStream.next_out = (Bytef*)output; 287 fStream.avail_out = outputSize; 288 289 int zlibError = Strategy::Process(fStream, flush); 290 if (zlibError != Z_OK) { 291 if (zlibError == Z_STREAM_END) { 292 if (fStream.avail_in != 0) 293 return B_BAD_DATA; 294 } else 295 return _TranslateZlibError(zlibError); 296 } 297 298 bytesConsumed = inputSize - (size_t)fStream.avail_in; 299 bytesProduced = outputSize - (size_t)fStream.avail_out; 300 return B_OK; 301 } 302 303 private: 304 z_stream fStream; 305 bool fStreamInitialized; 306 }; 307 308 309 // #pragma mark - BZlibCompressionAlgorithm 310 311 312 BZlibCompressionAlgorithm::BZlibCompressionAlgorithm() 313 : 314 BCompressionAlgorithm() 315 { 316 } 317 318 319 BZlibCompressionAlgorithm::~BZlibCompressionAlgorithm() 320 { 321 } 322 323 324 status_t 325 BZlibCompressionAlgorithm::CreateCompressingInputStream(BDataIO* input, 326 const BCompressionParameters* parameters, BDataIO*& _stream) 327 { 328 #ifdef B_ZLIB_COMPRESSION_SUPPORT 329 return Stream<BAbstractInputStream, CompressionStrategy>::Create( 330 input, parameters, _stream); 331 #else 332 return B_NOT_SUPPORTED; 333 #endif 334 } 335 336 337 status_t 338 BZlibCompressionAlgorithm::CreateCompressingOutputStream(BDataIO* output, 339 const BCompressionParameters* parameters, BDataIO*& _stream) 340 { 341 #ifdef B_ZLIB_COMPRESSION_SUPPORT 342 return Stream<BAbstractOutputStream, CompressionStrategy>::Create( 343 output, parameters, _stream); 344 #else 345 return B_NOT_SUPPORTED; 346 #endif 347 } 348 349 350 status_t 351 BZlibCompressionAlgorithm::CreateDecompressingInputStream(BDataIO* input, 352 const BDecompressionParameters* parameters, BDataIO*& _stream) 353 { 354 return Stream<BAbstractInputStream, DecompressionStrategy>::Create( 355 input, parameters, _stream); 356 } 357 358 359 status_t 360 BZlibCompressionAlgorithm::CreateDecompressingOutputStream(BDataIO* output, 361 const BDecompressionParameters* parameters, BDataIO*& _stream) 362 { 363 return Stream<BAbstractOutputStream, DecompressionStrategy>::Create( 364 output, parameters, _stream); 365 } 366 367 368 status_t 369 BZlibCompressionAlgorithm::CompressBuffer(const void* input, 370 size_t inputSize, void* output, size_t outputSize, size_t& _compressedSize, 371 const BCompressionParameters* parameters) 372 { 373 #ifdef B_ZLIB_COMPRESSION_SUPPORT 374 const BZlibCompressionParameters* zlibParameters 375 = dynamic_cast<const BZlibCompressionParameters*>(parameters); 376 int compressionLevel = zlibParameters != NULL 377 ? zlibParameters->CompressionLevel() 378 : B_ZLIB_COMPRESSION_DEFAULT; 379 380 uLongf bytesUsed = outputSize; 381 int zlibError = compress2((Bytef*)output, &bytesUsed, (const Bytef*)input, 382 (uLong)inputSize, compressionLevel); 383 if (zlibError != Z_OK) 384 return _TranslateZlibError(zlibError); 385 386 _compressedSize = (size_t)bytesUsed; 387 return B_OK; 388 #else 389 return B_NOT_SUPPORTED; 390 #endif 391 } 392 393 394 status_t 395 BZlibCompressionAlgorithm::DecompressBuffer(const void* input, 396 size_t inputSize, void* output, size_t outputSize, 397 size_t& _uncompressedSize, const BDecompressionParameters* parameters) 398 { 399 uLongf bytesUsed = outputSize; 400 int zlibError = uncompress((Bytef*)output, &bytesUsed, (const Bytef*)input, 401 (uLong)inputSize); 402 if (zlibError != Z_OK) 403 return _TranslateZlibError(zlibError); 404 405 _uncompressedSize = (size_t)bytesUsed; 406 return B_OK; 407 } 408 409 410 /*static*/ status_t 411 BZlibCompressionAlgorithm::_TranslateZlibError(int error) 412 { 413 switch (error) { 414 case Z_OK: 415 return B_OK; 416 case Z_STREAM_END: 417 case Z_NEED_DICT: 418 // a special event (no error), but the caller doesn't seem to handle 419 // it 420 return B_ERROR; 421 case Z_ERRNO: 422 return errno; 423 case Z_STREAM_ERROR: 424 return B_BAD_VALUE; 425 case Z_DATA_ERROR: 426 return B_BAD_DATA; 427 case Z_MEM_ERROR: 428 return B_NO_MEMORY; 429 case Z_BUF_ERROR: 430 return B_BUFFER_OVERFLOW; 431 case Z_VERSION_ERROR: 432 return B_BAD_VALUE; 433 default: 434 return B_ERROR; 435 } 436 } 437