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
sanitize_buffer_size(size_t size)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
BZstdCompressionParameters(int compressionLevel)47 BZstdCompressionParameters::BZstdCompressionParameters(
48 int compressionLevel)
49 :
50 BCompressionParameters(),
51 fCompressionLevel(compressionLevel),
52 fBufferSize(kDefaultBufferSize)
53 {
54 }
55
56
~BZstdCompressionParameters()57 BZstdCompressionParameters::~BZstdCompressionParameters()
58 {
59 }
60
61
62 int32
CompressionLevel() const63 BZstdCompressionParameters::CompressionLevel() const
64 {
65 return fCompressionLevel;
66 }
67
68
69 void
SetCompressionLevel(int32 level)70 BZstdCompressionParameters::SetCompressionLevel(int32 level)
71 {
72 fCompressionLevel = level;
73 }
74
75
76 size_t
BufferSize() const77 BZstdCompressionParameters::BufferSize() const
78 {
79 return fBufferSize;
80 }
81
82
83 void
SetBufferSize(size_t size)84 BZstdCompressionParameters::SetBufferSize(size_t size)
85 {
86 fBufferSize = sanitize_buffer_size(size);
87 }
88
89
90 // #pragma mark - BZstdDecompressionParameters
91
92
BZstdDecompressionParameters()93 BZstdDecompressionParameters::BZstdDecompressionParameters()
94 :
95 BDecompressionParameters(),
96 fBufferSize(kDefaultBufferSize)
97 {
98 }
99
100
~BZstdDecompressionParameters()101 BZstdDecompressionParameters::~BZstdDecompressionParameters()
102 {
103 }
104
105
106 size_t
BufferSize() const107 BZstdDecompressionParameters::BufferSize() const
108 {
109 return fBufferSize;
110 }
111
112
113 void
SetBufferSize(size_t size)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
InitBZstdCompressionAlgorithm::CompressionStrategy131 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
UninitBZstdCompressionAlgorithm::CompressionStrategy143 static void Uninit(ZSTD_CStream *stream)
144 {
145 ZSTD_freeCStream(stream);
146 }
147
ProcessBZstdCompressionAlgorithm::CompressionStrategy148 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
InitBZstdCompressionAlgorithm::DecompressionStrategy173 static size_t Init(ZSTD_DStream **stream,
174 const BZstdDecompressionParameters* /*parameters*/)
175 {
176 *stream = ZSTD_createDStream();
177 return ZSTD_initDStream(*stream);
178 }
179
UninitBZstdCompressionAlgorithm::DecompressionStrategy180 static void Uninit(ZSTD_DStream *stream)
181 {
182 ZSTD_freeDStream(stream);
183 }
184
ProcessBZstdCompressionAlgorithm::DecompressionStrategy185 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 {
StreamBZstdCompressionAlgorithm::Stream199 Stream(BDataIO* io)
200 :
201 BaseClass(io),
202 fStreamInitialized(false)
203 {
204 }
205
~StreamBZstdCompressionAlgorithm::Stream206 ~Stream()
207 {
208 if (fStreamInitialized) {
209 if (Strategy::kNeedsFinalFlush)
210 this->Flush();
211 Strategy::Uninit(fStream);
212 }
213 }
214
InitBZstdCompressionAlgorithm::Stream215 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
ProcessDataBZstdCompressionAlgorithm::Stream230 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
FlushPendingDataBZstdCompressionAlgorithm::Stream238 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>
CreateBZstdCompressionAlgorithm::Stream247 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:
_ProcessDataBZstdCompressionAlgorithm::Stream271 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
BZstdCompressionAlgorithm()305 BZstdCompressionAlgorithm::BZstdCompressionAlgorithm()
306 :
307 BCompressionAlgorithm()
308 {
309 }
310
311
~BZstdCompressionAlgorithm()312 BZstdCompressionAlgorithm::~BZstdCompressionAlgorithm()
313 {
314 }
315
316
317 status_t
CreateCompressingInputStream(BDataIO * input,const BCompressionParameters * parameters,BDataIO * & _stream)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
CreateCompressingOutputStream(BDataIO * output,const BCompressionParameters * parameters,BDataIO * & _stream)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
CreateDecompressingInputStream(BDataIO * input,const BDecompressionParameters * parameters,BDataIO * & _stream)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
CreateDecompressingOutputStream(BDataIO * output,const BDecompressionParameters * parameters,BDataIO * & _stream)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
CompressBuffer(const void * input,size_t inputSize,void * output,size_t outputSize,size_t & _compressedSize,const BCompressionParameters * parameters)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
DecompressBuffer(const void * input,size_t inputSize,void * output,size_t outputSize,size_t & _uncompressedSize,const BDecompressionParameters * parameters)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
_TranslateZstdError(size_t error)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