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 30static const size_t kMinBufferSize = 1024; 31static const size_t kMaxBufferSize = 1024 * 1024; 32static const size_t kDefaultBufferSize = 4 * 1024; 33 34 35static size_t 36sanitize_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 47BZstdCompressionParameters::BZstdCompressionParameters( 48 int compressionLevel) 49 : 50 BCompressionParameters(), 51 fCompressionLevel(compressionLevel), 52 fBufferSize(kDefaultBufferSize) 53{ 54} 55 56 57BZstdCompressionParameters::~BZstdCompressionParameters() 58{ 59} 60 61 62int32 63BZstdCompressionParameters::CompressionLevel() const 64{ 65 return fCompressionLevel; 66} 67 68 69void 70BZstdCompressionParameters::SetCompressionLevel(int32 level) 71{ 72 fCompressionLevel = level; 73} 74 75 76size_t 77BZstdCompressionParameters::BufferSize() const 78{ 79 return fBufferSize; 80} 81 82 83void 84BZstdCompressionParameters::SetBufferSize(size_t size) 85{ 86 fBufferSize = sanitize_buffer_size(size); 87} 88 89 90// #pragma mark - BZstdDecompressionParameters 91 92 93BZstdDecompressionParameters::BZstdDecompressionParameters() 94 : 95 BDecompressionParameters(), 96 fBufferSize(kDefaultBufferSize) 97{ 98} 99 100 101BZstdDecompressionParameters::~BZstdDecompressionParameters() 102{ 103} 104 105 106size_t 107BZstdDecompressionParameters::BufferSize() const 108{ 109 return fBufferSize; 110} 111 112 113void 114BZstdDecompressionParameters::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 126struct 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 168struct 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 197template<typename BaseClass, typename Strategy, typename StreamType> 198struct 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 270private: 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 291private: 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 305BZstdCompressionAlgorithm::BZstdCompressionAlgorithm() 306 : 307 BCompressionAlgorithm() 308{ 309} 310 311 312BZstdCompressionAlgorithm::~BZstdCompressionAlgorithm() 313{ 314} 315 316 317status_t 318BZstdCompressionAlgorithm::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 330status_t 331BZstdCompressionAlgorithm::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 343status_t 344BZstdCompressionAlgorithm::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 356status_t 357BZstdCompressionAlgorithm::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 369status_t 370BZstdCompressionAlgorithm::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 394status_t 395BZstdCompressionAlgorithm::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 414BZstdCompressionAlgorithm::_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_dstSize_tooSmall: 428 return B_BUFFER_OVERFLOW; 429 default: 430 return B_ERROR; 431 } 432#else 433 return B_NOT_SUPPORTED; 434#endif 435} 436