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 26static const size_t kMinBufferSize = 1024; 27static const size_t kMaxBufferSize = 1024 * 1024; 28static const size_t kDefaultBufferSize = 4 * 1024; 29 30 31static size_t 32sanitize_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 43BZlibCompressionParameters::BZlibCompressionParameters( 44 int compressionLevel) 45 : 46 BCompressionParameters(), 47 fCompressionLevel(compressionLevel), 48 fBufferSize(kDefaultBufferSize), 49 fGzipFormat(false) 50{ 51} 52 53 54BZlibCompressionParameters::~BZlibCompressionParameters() 55{ 56} 57 58 59int32 60BZlibCompressionParameters::CompressionLevel() const 61{ 62 return fCompressionLevel; 63} 64 65 66void 67BZlibCompressionParameters::SetCompressionLevel(int32 level) 68{ 69 fCompressionLevel = level; 70} 71 72 73size_t 74BZlibCompressionParameters::BufferSize() const 75{ 76 return fBufferSize; 77} 78 79 80void 81BZlibCompressionParameters::SetBufferSize(size_t size) 82{ 83 fBufferSize = sanitize_buffer_size(size); 84} 85 86 87bool 88BZlibCompressionParameters::IsGzipFormat() const 89{ 90 return fGzipFormat; 91} 92 93 94void 95BZlibCompressionParameters::SetGzipFormat(bool gzipFormat) 96{ 97 fGzipFormat = gzipFormat; 98} 99 100 101// #pragma mark - BZlibDecompressionParameters 102 103 104BZlibDecompressionParameters::BZlibDecompressionParameters() 105 : 106 BDecompressionParameters(), 107 fBufferSize(kDefaultBufferSize) 108{ 109} 110 111 112BZlibDecompressionParameters::~BZlibDecompressionParameters() 113{ 114} 115 116 117size_t 118BZlibDecompressionParameters::BufferSize() const 119{ 120 return fBufferSize; 121} 122 123 124void 125BZlibDecompressionParameters::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 137struct 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 177struct 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 204template<typename BaseClass, typename Strategy> 205struct 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 279private: 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 303private: 304 z_stream fStream; 305 bool fStreamInitialized; 306}; 307 308 309// #pragma mark - BZlibCompressionAlgorithm 310 311 312BZlibCompressionAlgorithm::BZlibCompressionAlgorithm() 313 : 314 BCompressionAlgorithm() 315{ 316} 317 318 319BZlibCompressionAlgorithm::~BZlibCompressionAlgorithm() 320{ 321} 322 323 324status_t 325BZlibCompressionAlgorithm::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 337status_t 338BZlibCompressionAlgorithm::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 350status_t 351BZlibCompressionAlgorithm::CreateDecompressingInputStream(BDataIO* input, 352 const BDecompressionParameters* parameters, BDataIO*& _stream) 353{ 354 return Stream<BAbstractInputStream, DecompressionStrategy>::Create( 355 input, parameters, _stream); 356} 357 358 359status_t 360BZlibCompressionAlgorithm::CreateDecompressingOutputStream(BDataIO* output, 361 const BDecompressionParameters* parameters, BDataIO*& _stream) 362{ 363 return Stream<BAbstractOutputStream, DecompressionStrategy>::Create( 364 output, parameters, _stream); 365} 366 367 368status_t 369BZlibCompressionAlgorithm::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 394status_t 395BZlibCompressionAlgorithm::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 411BZlibCompressionAlgorithm::_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