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