xref: /haiku/src/kits/support/ZstdCompressionAlgorithm.cpp (revision b3731f13abd12c63cf626cfa8138943f500e1471)
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