Skip to content

Commit b77b080

Browse files
Merge pull request python#22 from dpkp/content_size
Optionally disable contentSize frame header
2 parents feb2a00 + 040af9a commit b77b080

File tree

3 files changed

+45
-6
lines changed

3 files changed

+45
-6
lines changed

lz4/frame/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ class LZ4FrameCompressor(object):
3535
- lz4.frame.CONTENTCHECKSUM_DISABLED or 0: disables checksumming
3636
- lz4.frame.CONTENTCHECKSUM_ENABLED or 1: enables checksumming
3737
The default is CONTENTCHECKSUM_DISABLED.
38+
content_size (bool): Specifies whether to include an optional 8-byte header
39+
field that is the uncompressed size of data included within the frame.
40+
Including the content-size header is optional, and is enabled by default.
3841
frame_type (int): Specifies whether user data can be injected between
3942
frames. Options:
4043
- lz4.frame.FRAMETYPE_FRAME or 0: disables user data injection
@@ -50,12 +53,14 @@ def __init__(self,
5053
block_mode=BLOCKMODE_LINKED,
5154
compression_level=COMPRESSIONLEVEL_MIN,
5255
content_checksum=CONTENTCHECKSUM_DISABLED,
56+
content_size=True,
5357
frame_type=FRAMETYPE_FRAME,
5458
auto_flush=True):
5559
self.block_size = block_size
5660
self.block_mode = block_mode
5761
self.compression_level = compression_level
5862
self.content_checksum = content_checksum
63+
self.content_size = content_size
5964
self.frame_type = frame_type
6065
self.auto_flush = auto_flush
6166
self._context = create_compression_context()
@@ -92,6 +97,7 @@ def compress_begin(self, source_size=0):
9297
frame_type=self.frame_type,
9398
compression_level=self.compression_level,
9499
content_checksum=self.content_checksum,
100+
content_size=self.content_size,
95101
auto_flush=self.auto_flush,
96102
source_size=source_size)
97103

lz4/frame/_frame.c

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,17 @@ create_compression_context (PyObject * Py_UNUSED (self))
152152
" - lz4.frame.CONTENTCHECKSUM_DISABLED or 0: disables checksumming\n" \
153153
" - lz4.frame.CONTENTCHECKSUM_ENABLED or 1: enables checksumming\n\n" \
154154
" The default is CONTENTCHECKSUM_DISABLED.\n" \
155+
" content_size (bool): Specifies whether to include an optional 8-byte header\n" \
156+
" field that is the uncompressed size of data included within the frame.\n" \
157+
" Including the content-size header is optional, and is enabled by default.\n" \
155158
" frame_type (int): Specifies whether user data can be injected between\n" \
156159
" frames. Options:\n\n" \
157160
" - lz4.frame.FRAMETYPE_FRAME or 0: disables user data injection\n" \
158161
" - lz4.frame.FRAMETYPE_SKIPPABLEFRAME or 1: enables user data injection\n\n" \
159162
" The default is lz4.frame.FRAMETYPE_FRAME.\n" \
160163

161164
PyDoc_STRVAR(compress__doc,
162-
"compress(source, compression_level=0, block_size=0, content_checksum=0, block_mode=0, frame_type=0)\n\n" \
165+
"compress(source, compression_level=0, block_size=0, content_checksum=0, content_size=1, block_mode=0, frame_type=0)\n\n" \
163166
"Accepts a string, and compresses the string in one go, returning the\n" \
164167
"compressed string as a string of bytes. The compressed string includes\n" \
165168
"a header and endmark and so is suitable for writing to a file.\n\n" \
@@ -178,6 +181,7 @@ compress (PyObject * Py_UNUSED (self), PyObject * args,
178181
{
179182
const char *source;
180183
int source_size;
184+
int content_size_header = 1;
181185
LZ4F_preferences_t preferences;
182186
size_t compressed_bound;
183187
Py_ssize_t dest_size;
@@ -188,6 +192,7 @@ compress (PyObject * Py_UNUSED (self), PyObject * args,
188192
"compression_level",
189193
"block_size",
190194
"content_checksum",
195+
"content_size",
191196
"block_mode",
192197
"frame_type",
193198
NULL
@@ -196,20 +201,28 @@ compress (PyObject * Py_UNUSED (self), PyObject * args,
196201

197202
memset (&preferences, 0, sizeof (preferences));
198203

199-
if (!PyArg_ParseTupleAndKeywords (args, keywds, "s#|iiiii", kwlist,
204+
if (!PyArg_ParseTupleAndKeywords (args, keywds, "s#|iiiiii", kwlist,
200205
&source, &source_size,
201206
&preferences.compressionLevel,
202207
&preferences.frameInfo.blockSizeID,
203208
&preferences.
204209
frameInfo.contentChecksumFlag,
210+
&content_size_header,
205211
&preferences.frameInfo.blockMode,
206212
&preferences.frameInfo.frameType))
207213
{
208214
return NULL;
209215
}
210216

211217
preferences.autoFlush = 0;
212-
preferences.frameInfo.contentSize = source_size;
218+
if (content_size_header)
219+
{
220+
preferences.frameInfo.contentSize = source_size;
221+
}
222+
else
223+
{
224+
preferences.frameInfo.contentSize = 0;
225+
}
213226

214227
Py_BEGIN_ALLOW_THREADS
215228
compressed_bound =
@@ -272,7 +285,7 @@ compress (PyObject * Py_UNUSED (self), PyObject * args,
272285
******************/
273286
PyDoc_STRVAR(compress_begin__doc,
274287
"compress_begin(cCtx, source_size=0, compression_level=0, block_size=0,\n" \
275-
" content_checksum=0, block_mode=0, frame_type=0, auto_flush=1)\n\n"\
288+
" content_checksum=0, content_size=1, block_mode=0, frame_type=0, auto_flush=1)\n\n"\
276289
"Creates a frame header from a compression context.\n\n" \
277290
"Args:\n" \
278291
" context (cCtx): A compression context.\n\n" \
@@ -296,6 +309,7 @@ compress_begin (PyObject * Py_UNUSED (self), PyObject * args,
296309
{
297310
PyObject *py_context = NULL;
298311
unsigned long source_size = 0;
312+
int content_size_header = 1;
299313
LZ4F_preferences_t preferences;
300314
/* Only needs to be large enough for a header, which is 15 bytes.
301315
* Unfortunately, the lz4 library doesn't provide a #define for this.
@@ -308,6 +322,7 @@ compress_begin (PyObject * Py_UNUSED (self), PyObject * args,
308322
"compression_level",
309323
"block_size",
310324
"content_checksum",
325+
"content_size",
311326
"block_mode",
312327
"frame_type",
313328
"auto_flush",
@@ -320,12 +335,13 @@ compress_begin (PyObject * Py_UNUSED (self), PyObject * args,
320335
argument */
321336
preferences.autoFlush = 1;
322337

323-
if (!PyArg_ParseTupleAndKeywords (args, keywds, "O|kiiiiii", kwlist,
338+
if (!PyArg_ParseTupleAndKeywords (args, keywds, "O|kiiiiiii", kwlist,
324339
&py_context,
325340
&source_size,
326341
&preferences.compressionLevel,
327342
&preferences.frameInfo.blockSizeID,
328343
&preferences.frameInfo.contentChecksumFlag,
344+
&content_size_header,
329345
&preferences.frameInfo.blockMode,
330346
&preferences.frameInfo.frameType,
331347
&preferences.autoFlush
@@ -334,7 +350,14 @@ compress_begin (PyObject * Py_UNUSED (self), PyObject * args,
334350
return NULL;
335351
}
336352

337-
preferences.frameInfo.contentSize = source_size;
353+
if (content_size_header)
354+
{
355+
preferences.frameInfo.contentSize = source_size;
356+
}
357+
else
358+
{
359+
preferences.frameInfo.contentSize = 0;
360+
}
338361

339362
context =
340363
(struct compression_context *) PyCapsule_GetPointer (py_context, capsule_name);

tests/test_frame.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ def test_compress_begin_update_end_not_defaults(self):
138138
block_size=lz4frame.BLOCKSIZE_MAX256KB,
139139
block_mode=lz4frame.BLOCKMODE_LINKED,
140140
compression_level=lz4frame.COMPRESSIONLEVEL_MINHC,
141+
content_size=False,
141142
auto_flush=1
142143
)
143144
chunk_size = 128 * 1024 # 128 kb, half of block size
@@ -305,6 +306,15 @@ def test_LZ4FrameCompressor_fails(self):
305306
compressed += compressor.flush()
306307
compressed = compressor.compress(input_data)
307308

309+
def test_compress_without_content_size(self):
310+
input_data = b"2099023098234882923049823094823094898239230982349081231290381209380981203981209381238901283098908123109238098123"
311+
compressed = lz4frame.compress(input_data, content_size=False)
312+
frame = lz4frame.get_frame_info(compressed)
313+
self.assertEqual(frame['contentSize'], 0)
314+
decompressed = lz4frame.decompress(compressed)
315+
self.assertEqual(input_data, decompressed)
316+
317+
308318
if sys.version_info < (2, 7):
309319
# Poor-man unittest.TestCase.skip for Python 2.6
310320
del TestLZ4FrameModern

0 commit comments

Comments
 (0)