1 /***********************************************************************************************************************************
2 BZ2 Compress
3 ***********************************************************************************************************************************/
4 #include "build.auto.h"
5
6 #include <stdio.h>
7 #include <bzlib.h>
8
9 #include "common/compress/bz2/common.h"
10 #include "common/compress/bz2/compress.h"
11 #include "common/debug.h"
12 #include "common/io/filter/filter.h"
13 #include "common/log.h"
14 #include "common/macro.h"
15 #include "common/memContext.h"
16 #include "common/type/object.h"
17
18 /***********************************************************************************************************************************
19 Filter type constant
20 ***********************************************************************************************************************************/
21 STRING_EXTERN(BZ2_COMPRESS_FILTER_TYPE_STR, BZ2_COMPRESS_FILTER_TYPE);
22
23 /***********************************************************************************************************************************
24 Object type
25 ***********************************************************************************************************************************/
26 typedef struct Bz2Compress
27 {
28 MemContext *memContext; // Context to store data
29 bz_stream stream; // Compression stream
30
31 bool inputSame; // Is the same input required on the next process call?
32 bool flushing; // Is input complete and flushing in progress?
33 bool done; // Is compression done?
34 } Bz2Compress;
35
36 /***********************************************************************************************************************************
37 Render as string for logging
38 ***********************************************************************************************************************************/
39 static String *
bz2CompressToLog(const Bz2Compress * this)40 bz2CompressToLog(const Bz2Compress *this)
41 {
42 return strNewFmt(
43 "{inputSame: %s, done: %s, flushing: %s, avail_in: %u}", cvtBoolToConstZ(this->inputSame), cvtBoolToConstZ(this->done),
44 cvtBoolToConstZ(this->flushing), this->stream.avail_in);
45 }
46
47 #define FUNCTION_LOG_BZ2_COMPRESS_TYPE \
48 Bz2Compress *
49 #define FUNCTION_LOG_BZ2_COMPRESS_FORMAT(value, buffer, bufferSize) \
50 FUNCTION_LOG_STRING_OBJECT_FORMAT(value, bz2CompressToLog, buffer, bufferSize)
51
52 /***********************************************************************************************************************************
53 Free compression stream
54 ***********************************************************************************************************************************/
55 static void
bz2CompressFreeResource(THIS_VOID)56 bz2CompressFreeResource(THIS_VOID)
57 {
58 THIS(Bz2Compress);
59
60 FUNCTION_LOG_BEGIN(logLevelTrace);
61 FUNCTION_LOG_PARAM(BZ2_COMPRESS, this);
62 FUNCTION_LOG_END();
63
64 ASSERT(this != NULL);
65
66 BZ2_bzCompressEnd(&this->stream);
67
68 FUNCTION_LOG_RETURN_VOID();
69 }
70
71 /***********************************************************************************************************************************
72 Compress data
73 ***********************************************************************************************************************************/
74 static void
bz2CompressProcess(THIS_VOID,const Buffer * uncompressed,Buffer * compressed)75 bz2CompressProcess(THIS_VOID, const Buffer *uncompressed, Buffer *compressed)
76 {
77 THIS(Bz2Compress);
78
79 FUNCTION_LOG_BEGIN(logLevelTrace);
80 FUNCTION_LOG_PARAM(BZ2_COMPRESS, this);
81 FUNCTION_LOG_PARAM(BUFFER, uncompressed);
82 FUNCTION_LOG_PARAM(BUFFER, compressed);
83 FUNCTION_LOG_END();
84
85 ASSERT(this != NULL);
86 ASSERT(!this->done);
87 ASSERT(compressed != NULL);
88 ASSERT(!this->flushing || uncompressed == NULL);
89 ASSERT(this->flushing || (!this->inputSame || this->stream.avail_in != 0));
90
91 // If input is NULL then start flushing
92 if (uncompressed == NULL)
93 {
94 this->stream.avail_in = 0;
95 this->flushing = true;
96 }
97 // Else still have input data
98 else
99 {
100 // Is new input allowed?
101 if (!this->inputSame)
102 {
103 this->stream.avail_in = (unsigned int)bufUsed(uncompressed);
104
105 // bzip2 does not accept const input buffers
106 this->stream.next_in = (char *)UNCONSTIFY(unsigned char *, bufPtrConst(uncompressed));
107 }
108 }
109
110 // Initialize compressed output buffer
111 this->stream.avail_out = (unsigned int)bufRemains(compressed);
112 this->stream.next_out = (char *)bufPtr(compressed) + bufUsed(compressed);
113
114 // Perform compression, check for error
115 int result = bz2Error(BZ2_bzCompress(&this->stream, this->flushing ? BZ_FINISH : BZ_RUN));
116
117 // Set buffer used space
118 bufUsedSet(compressed, bufSize(compressed) - (size_t)this->stream.avail_out);
119
120 // Is compression done?
121 if (this->flushing && result == BZ_STREAM_END)
122 this->done = true;
123
124 // Can more input be provided on the next call?
125 this->inputSame = this->flushing ? !this->done : this->stream.avail_in != 0;
126
127 FUNCTION_LOG_RETURN_VOID();
128 }
129
130 /***********************************************************************************************************************************
131 Is compress done?
132 ***********************************************************************************************************************************/
133 static bool
bz2CompressDone(const THIS_VOID)134 bz2CompressDone(const THIS_VOID)
135 {
136 THIS(const Bz2Compress);
137
138 FUNCTION_TEST_BEGIN();
139 FUNCTION_TEST_PARAM(BZ2_COMPRESS, this);
140 FUNCTION_TEST_END();
141
142 ASSERT(this != NULL);
143
144 FUNCTION_TEST_RETURN(this->done);
145 }
146
147 /***********************************************************************************************************************************
148 Is the same input required on the next process call?
149 ***********************************************************************************************************************************/
150 static bool
bz2CompressInputSame(const THIS_VOID)151 bz2CompressInputSame(const THIS_VOID)
152 {
153 THIS(const Bz2Compress);
154
155 FUNCTION_TEST_BEGIN();
156 FUNCTION_TEST_PARAM(BZ2_COMPRESS, this);
157 FUNCTION_TEST_END();
158
159 ASSERT(this != NULL);
160
161 FUNCTION_TEST_RETURN(this->inputSame);
162 }
163
164 /**********************************************************************************************************************************/
165 IoFilter *
bz2CompressNew(int level)166 bz2CompressNew(int level)
167 {
168 FUNCTION_LOG_BEGIN(logLevelTrace);
169 FUNCTION_LOG_PARAM(INT, level);
170 FUNCTION_LOG_END();
171
172 ASSERT(level > 0);
173
174 IoFilter *this = NULL;
175
176 MEM_CONTEXT_NEW_BEGIN("Bz2Compress")
177 {
178 Bz2Compress *driver = memNew(sizeof(Bz2Compress));
179
180 *driver = (Bz2Compress)
181 {
182 .memContext = MEM_CONTEXT_NEW(),
183 .stream = {.bzalloc = NULL},
184 };
185
186 // Initialize context
187 bz2Error(BZ2_bzCompressInit(&driver->stream, level, 0, 0));
188
189 // Set callback to ensure bz2 stream is freed
190 memContextCallbackSet(driver->memContext, bz2CompressFreeResource, driver);
191
192 // Create param list
193 VariantList *paramList = varLstNew();
194 varLstAdd(paramList, varNewInt(level));
195
196 // Create filter interface
197 this = ioFilterNewP(
198 BZ2_COMPRESS_FILTER_TYPE_STR, driver, paramList, .done = bz2CompressDone, .inOut = bz2CompressProcess,
199 .inputSame = bz2CompressInputSame);
200 }
201 MEM_CONTEXT_NEW_END();
202
203 FUNCTION_LOG_RETURN(IO_FILTER, this);
204 }
205