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
sanitize_buffer_size(size_t size)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
BZlibCompressionParameters(int compressionLevel)43 BZlibCompressionParameters::BZlibCompressionParameters(
44 int compressionLevel)
45 :
46 BCompressionParameters(),
47 fCompressionLevel(compressionLevel),
48 fBufferSize(kDefaultBufferSize),
49 fGzipFormat(false)
50 {
51 }
52
53
~BZlibCompressionParameters()54 BZlibCompressionParameters::~BZlibCompressionParameters()
55 {
56 }
57
58
59 int32
CompressionLevel() const60 BZlibCompressionParameters::CompressionLevel() const
61 {
62 return fCompressionLevel;
63 }
64
65
66 void
SetCompressionLevel(int32 level)67 BZlibCompressionParameters::SetCompressionLevel(int32 level)
68 {
69 fCompressionLevel = level;
70 }
71
72
73 size_t
BufferSize() const74 BZlibCompressionParameters::BufferSize() const
75 {
76 return fBufferSize;
77 }
78
79
80 void
SetBufferSize(size_t size)81 BZlibCompressionParameters::SetBufferSize(size_t size)
82 {
83 fBufferSize = sanitize_buffer_size(size);
84 }
85
86
87 bool
IsGzipFormat() const88 BZlibCompressionParameters::IsGzipFormat() const
89 {
90 return fGzipFormat;
91 }
92
93
94 void
SetGzipFormat(bool gzipFormat)95 BZlibCompressionParameters::SetGzipFormat(bool gzipFormat)
96 {
97 fGzipFormat = gzipFormat;
98 }
99
100
101 // #pragma mark - BZlibDecompressionParameters
102
103
BZlibDecompressionParameters()104 BZlibDecompressionParameters::BZlibDecompressionParameters()
105 :
106 BDecompressionParameters(),
107 fBufferSize(kDefaultBufferSize)
108 {
109 }
110
111
~BZlibDecompressionParameters()112 BZlibDecompressionParameters::~BZlibDecompressionParameters()
113 {
114 }
115
116
117 size_t
BufferSize() const118 BZlibDecompressionParameters::BufferSize() const
119 {
120 return fBufferSize;
121 }
122
123
124 void
SetBufferSize(size_t size)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
InitBZlibCompressionAlgorithm::CompressionStrategy142 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
UninitBZlibCompressionAlgorithm::CompressionStrategy159 static void Uninit(z_stream& stream)
160 {
161 deflateEnd(&stream);
162 }
163
ProcessBZlibCompressionAlgorithm::CompressionStrategy164 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
InitBZlibCompressionAlgorithm::DecompressionStrategy182 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
UninitBZlibCompressionAlgorithm::DecompressionStrategy189 static void Uninit(z_stream& stream)
190 {
191 inflateEnd(&stream);
192 }
193
ProcessBZlibCompressionAlgorithm::DecompressionStrategy194 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 {
StreamBZlibCompressionAlgorithm::Stream206 Stream(BDataIO* io)
207 :
208 BaseClass(io),
209 fStreamInitialized(false)
210 {
211 }
212
~StreamBZlibCompressionAlgorithm::Stream213 ~Stream()
214 {
215 if (fStreamInitialized) {
216 if (Strategy::kNeedsFinalFlush)
217 this->Flush();
218 Strategy::Uninit(fStream);
219 }
220 }
221
InitBZlibCompressionAlgorithm::Stream222 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
ProcessDataBZlibCompressionAlgorithm::Stream239 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
FlushPendingDataBZlibCompressionAlgorithm::Stream247 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>
CreateBZlibCompressionAlgorithm::Stream256 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:
_ProcessDataBZlibCompressionAlgorithm::Stream280 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
BZlibCompressionAlgorithm()312 BZlibCompressionAlgorithm::BZlibCompressionAlgorithm()
313 :
314 BCompressionAlgorithm()
315 {
316 }
317
318
~BZlibCompressionAlgorithm()319 BZlibCompressionAlgorithm::~BZlibCompressionAlgorithm()
320 {
321 }
322
323
324 status_t
CreateCompressingInputStream(BDataIO * input,const BCompressionParameters * parameters,BDataIO * & _stream)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
CreateCompressingOutputStream(BDataIO * output,const BCompressionParameters * parameters,BDataIO * & _stream)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
CreateDecompressingInputStream(BDataIO * input,const BDecompressionParameters * parameters,BDataIO * & _stream)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
CreateDecompressingOutputStream(BDataIO * output,const BDecompressionParameters * parameters,BDataIO * & _stream)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
CompressBuffer(const void * input,size_t inputSize,void * output,size_t outputSize,size_t & _compressedSize,const BCompressionParameters * parameters)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
DecompressBuffer(const void * input,size_t inputSize,void * output,size_t outputSize,size_t & _uncompressedSize,const BDecompressionParameters * parameters)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
_TranslateZlibError(int error)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