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