1 /***********************************************************************************************************************************
2 LZ4 Decompress
3 ***********************************************************************************************************************************/
4 #include "build.auto.h"
5
6 #ifdef HAVE_LIBLZ4
7
8 #include <stdio.h>
9 #include <lz4frame.h>
10
11 #include "common/compress/lz4/common.h"
12 #include "common/compress/lz4/decompress.h"
13 #include "common/debug.h"
14 #include "common/io/filter/filter.h"
15 #include "common/log.h"
16 #include "common/memContext.h"
17 #include "common/type/object.h"
18
19 /***********************************************************************************************************************************
20 Filter type constant
21 ***********************************************************************************************************************************/
22 STRING_EXTERN(LZ4_DECOMPRESS_FILTER_TYPE_STR, LZ4_DECOMPRESS_FILTER_TYPE);
23
24 /***********************************************************************************************************************************
25 Object type
26 ***********************************************************************************************************************************/
27 typedef struct Lz4Decompress
28 {
29 MemContext *memContext; // Context to store data
30 LZ4F_decompressionContext_t context; // LZ4 decompression context
31 IoFilter *filter; // Filter interface
32
33 bool inputSame; // Is the same input required on the next process call?
34 size_t inputOffset; // Current offset from the start of the buffer
35 bool frameDone; // Has the current frame completed?
36 bool done; // Is decompression done?
37 } Lz4Decompress;
38
39 /***********************************************************************************************************************************
40 Render as string for logging
41 ***********************************************************************************************************************************/
42 static String *
lz4DecompressToLog(const Lz4Decompress * this)43 lz4DecompressToLog(const Lz4Decompress *this)
44 {
45 return strNewFmt(
46 "{inputSame: %s, inputOffset: %zu, frameDone %s, done: %s}", cvtBoolToConstZ(this->inputSame), this->inputOffset,
47 cvtBoolToConstZ(this->frameDone), cvtBoolToConstZ(this->done));
48 }
49
50 #define FUNCTION_LOG_LZ4_DECOMPRESS_TYPE \
51 Lz4Decompress *
52 #define FUNCTION_LOG_LZ4_DECOMPRESS_FORMAT(value, buffer, bufferSize) \
53 FUNCTION_LOG_STRING_OBJECT_FORMAT(value, lz4DecompressToLog, buffer, bufferSize)
54
55 /***********************************************************************************************************************************
56 Free decompression context
57 ***********************************************************************************************************************************/
58 static void
lz4DecompressFreeResource(THIS_VOID)59 lz4DecompressFreeResource(THIS_VOID)
60 {
61 THIS(Lz4Decompress);
62
63 FUNCTION_LOG_BEGIN(logLevelTrace);
64 FUNCTION_LOG_PARAM(LZ4_DECOMPRESS, this);
65 FUNCTION_LOG_END();
66
67 ASSERT(this != NULL);
68
69 LZ4F_freeDecompressionContext(this->context);
70
71 FUNCTION_LOG_RETURN_VOID();
72 }
73
74 /***********************************************************************************************************************************
75 Decompress data
76 ***********************************************************************************************************************************/
77 static void
lz4DecompressProcess(THIS_VOID,const Buffer * compressed,Buffer * decompressed)78 lz4DecompressProcess(THIS_VOID, const Buffer *compressed, Buffer *decompressed)
79 {
80 THIS(Lz4Decompress);
81
82 FUNCTION_LOG_BEGIN(logLevelTrace);
83 FUNCTION_LOG_PARAM(LZ4_DECOMPRESS, this);
84 FUNCTION_LOG_PARAM(BUFFER, compressed);
85 FUNCTION_LOG_PARAM(BUFFER, decompressed);
86 FUNCTION_LOG_END();
87
88 ASSERT(this != NULL);
89 ASSERT(this->context != NULL);
90 ASSERT(decompressed != NULL);
91
92 // When there is no more input then decompression is done
93 if (compressed == NULL)
94 {
95 // If the current frame being decompressed was not completed then error
96 if (!this->frameDone)
97 THROW(FormatError, "unexpected eof in compressed data");
98
99 this->done = true;
100 }
101 else
102 {
103 // Decompress as much data as possible
104 size_t srcSize = bufUsed(compressed) - this->inputOffset;
105 size_t dstSize = bufRemains(decompressed);
106
107 this->frameDone = lz4Error(
108 LZ4F_decompress(
109 this->context, bufRemainsPtr(decompressed), &dstSize, bufPtrConst(compressed) + this->inputOffset, &srcSize,
110 NULL)) == 0;
111
112 bufUsedInc(decompressed, dstSize);
113
114 // If the compressed data was not fully processed then update the offset and set inputSame
115 if (srcSize < bufUsed(compressed) - this->inputOffset)
116 {
117 this->inputOffset += srcSize;
118 this->inputSame = true;
119 }
120 // Else all compressed data was processed
121 else
122 {
123 this->inputOffset = 0;
124 this->inputSame = false;
125 }
126 }
127
128 FUNCTION_LOG_RETURN_VOID();
129 }
130
131 /***********************************************************************************************************************************
132 Is decompress done?
133 ***********************************************************************************************************************************/
134 static bool
lz4DecompressDone(const THIS_VOID)135 lz4DecompressDone(const THIS_VOID)
136 {
137 THIS(const Lz4Decompress);
138
139 FUNCTION_TEST_BEGIN();
140 FUNCTION_TEST_PARAM(LZ4_DECOMPRESS, this);
141 FUNCTION_TEST_END();
142
143 ASSERT(this != NULL);
144
145 FUNCTION_TEST_RETURN(this->done);
146 }
147
148 /***********************************************************************************************************************************
149 Is the same input required on the next process call?
150 ***********************************************************************************************************************************/
151 static bool
lz4DecompressInputSame(const THIS_VOID)152 lz4DecompressInputSame(const THIS_VOID)
153 {
154 THIS(const Lz4Decompress);
155
156 FUNCTION_TEST_BEGIN();
157 FUNCTION_TEST_PARAM(LZ4_DECOMPRESS, this);
158 FUNCTION_TEST_END();
159
160 ASSERT(this != NULL);
161
162 FUNCTION_TEST_RETURN(this->inputSame);
163 }
164
165 /**********************************************************************************************************************************/
166 IoFilter *
lz4DecompressNew(void)167 lz4DecompressNew(void)
168 {
169 FUNCTION_LOG_VOID(logLevelTrace);
170
171 IoFilter *this = NULL;
172
173 MEM_CONTEXT_NEW_BEGIN("Lz4Decompress")
174 {
175 Lz4Decompress *driver = memNew(sizeof(Lz4Decompress));
176
177 *driver = (Lz4Decompress)
178 {
179 .memContext = MEM_CONTEXT_NEW(),
180 };
181
182 // Create lz4 context
183 lz4Error(LZ4F_createDecompressionContext(&driver->context, LZ4F_VERSION));
184
185 // Set callback to ensure lz4 context is freed
186 memContextCallbackSet(driver->memContext, lz4DecompressFreeResource, driver);
187
188 // Create filter interface
189 this = ioFilterNewP(
190 LZ4_DECOMPRESS_FILTER_TYPE_STR, driver, NULL, .done = lz4DecompressDone, .inOut = lz4DecompressProcess,
191 .inputSame = lz4DecompressInputSame);
192 }
193 MEM_CONTEXT_NEW_END();
194
195 FUNCTION_LOG_RETURN(IO_FILTER, this);
196 }
197
198 #endif // HAVE_LIBLZ4
199