Skip to content

Commit ac88c21

Browse files
committed
Added compressFileAdv and advanced options
1 parent 745a050 commit ac88c21

File tree

3 files changed

+129
-17
lines changed

3 files changed

+129
-17
lines changed

src/python-lz4.c

Lines changed: 90 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141

4242
#define MAX(a, b) ((a) > (b) ? (a) : (b))
4343

44+
static PyObject *inputError;
45+
4446
typedef int (*compressor)(const char *source, char *dest, int isize);
4547

4648
static inline void store_le32(char *c, uint32_t x) {
@@ -55,6 +57,16 @@ static inline uint32_t load_le32(const char *c) {
5557
return d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
5658
}
5759

60+
static inline char* add_extension(char* input) {
61+
char* output;
62+
63+
output = (char*)malloc(strlen(input)+4);
64+
strcpy(output, input);
65+
strcat(output, ".lz4");
66+
67+
return output;
68+
}
69+
5870
static const int hdr_size = sizeof(uint32_t);
5971

6072
static PyObject *compress_with(compressor compress, PyObject *self, PyObject *args) {
@@ -129,25 +141,86 @@ static PyObject *py_lz4_uncompress(PyObject *self, PyObject *args) {
129141
return result;
130142
}
131143

132-
133144
static PyObject *py_lz4_compressFileDefault(PyObject *self, PyObject *args) {
134145
char* input;
135146
char* output;
136-
int compLevel;
147+
int compLevel = 0;
137148

138149
(void)self;
139-
if (!PyArg_ParseTuple(args, "si", &input, &compLevel)) {
150+
if (!PyArg_ParseTuple(args, "s|i", &input, &compLevel)) {
140151
return NULL;
141152
}
142153

143-
output = (char*)malloc(strlen(input)+4);
144-
strcpy(output, input);
145-
strcat(output, ".lz4");
154+
output = add_extension(input);
146155

147156
LZ4IO_compressFilename(input, output, compLevel);
148157
return Py_None;
149158
}
150159

160+
static PyObject *py_lz4_compressFileAdv(PyObject *self, PyObject *args, \
161+
PyObject *keywds) {
162+
char* input;
163+
char* output = NULL;
164+
int compLevel = 0;
165+
int overwrite = NULL;
166+
int blockSizeID = NULL;
167+
int blockMode = 1;
168+
int blockCheck = NULL;
169+
int streamCheck = NULL;
170+
int verbosity = NULL;
171+
172+
static char *kwlist[] = {"input", "compLevel", "output", "overwrite",
173+
"blockSizeID", "blockMode", "blockCheck",
174+
"streamCheck", "verbosity", NULL};
175+
(void)self;
176+
if (!PyArg_ParseTupleAndKeywords(args, keywds, "si|siiiiii", kwlist,
177+
&input, &compLevel, &output, &overwrite,
178+
&blockSizeID, &blockMode, &blockCheck,
179+
&streamCheck, &verbosity)) {
180+
return NULL;
181+
}
182+
183+
if (!output) { output = add_extension(input); }
184+
if (overwrite) { LZ4IO_setOverwrite(overwrite); }
185+
if (blockSizeID) {
186+
if ((3 < blockSizeID) && (blockSizeID < 8)) {
187+
LZ4IO_setBlockSizeID(blockSizeID);
188+
}
189+
else {
190+
PyErr_SetString(inputError, "Invalid input for blockSizeID");
191+
}
192+
}
193+
if ((blockMode == 0) || (blockMode == 1)) {
194+
if (blockMode == 0 ) {LZ4IO_setBlockMode(chainedBlocks);}
195+
}
196+
else {
197+
PyErr_SetString(inputError, "Invalid input for blockMode");
198+
return NULL;
199+
}
200+
if ((blockCheck == 0) || (blockCheck == 1)) {
201+
if (blockCheck == 1) {LZ4IO_setBlockChecksumMode(blockCheck);}
202+
}
203+
else {
204+
PyErr_SetString(inputError, "Invalid input for blockCheck");
205+
return NULL;
206+
}
207+
if ((streamCheck == 0) || (streamCheck == 1)) {
208+
if (streamCheck == 0) {LZ4IO_setStreamChecksumMode(streamCheck);}
209+
}
210+
else {
211+
PyErr_SetString(inputError, "Invalid input for streamCheck");
212+
return NULL;
213+
}
214+
if ((-1 < verbosity) && (verbosity < 5)) {
215+
LZ4IO_setNotificationLevel(verbosity);
216+
}
217+
else {
218+
PyErr_SetString(inputError, "Invalid input for verbosity");
219+
}
220+
221+
LZ4IO_compressFilename(input, output, compLevel);
222+
return Py_None;
223+
}
151224

152225
static PyObject *py_lz4_decompressFileDefault(PyObject *self, PyObject *args) {
153226
char* input;
@@ -163,8 +236,6 @@ static PyObject *py_lz4_decompressFileDefault(PyObject *self, PyObject *args) {
163236
output = (char*)calloc(outLen, sizeof(char));
164237
strncpy(output, input, outLen);
165238

166-
printf("%s \n", output);
167-
168239
LZ4IO_decompressFilename(input, output);
169240
return Py_None;
170241
}
@@ -179,8 +250,9 @@ static PyMethodDef Lz4Methods[] = {
179250
{"decompress", py_lz4_uncompress, METH_VARARGS, UNCOMPRESS_DOCSTRING},
180251
{"dumps", py_lz4_compress, METH_VARARGS, COMPRESS_DOCSTRING},
181252
{"loads", py_lz4_uncompress, METH_VARARGS, UNCOMPRESS_DOCSTRING},
182-
{"compressFileDefault", py_lz4_compressFileDefault, METH_VARARGS, COMPRESS_FILE_DOCSTRING},
183-
{"decompressFileDefault", py_lz4_decompressFileDefault, METH_VARARGS, DECOMPRESS_FILE_DOCSTRING},
253+
{"compressFileAdv", (PyCFunction)py_lz4_compressFileAdv, METH_VARARGS | METH_KEYWORDS, COMPF_ADV_DOCSTRING},
254+
{"compressFileDefault", py_lz4_compressFileDefault, METH_VARARGS, COMPF_DEFAULT_DOCSTRING},
255+
{"decompressFileDefault", py_lz4_decompressFileDefault, METH_VARARGS, DECOMP_FILE_DOCSTRING},
184256
{NULL, NULL, 0, NULL}
185257
};
186258

@@ -190,6 +262,7 @@ struct module_state {
190262
PyObject *error;
191263
};
192264

265+
193266
#if PY_MAJOR_VERSION >= 3
194267
#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
195268
#else
@@ -248,6 +321,13 @@ void initlz4(void)
248321
Py_DECREF(module);
249322
INITERROR;
250323
}
324+
inputError = PyErr_NewException("input.error", NULL, NULL);
325+
Py_INCREF(inputError);
326+
PyModule_AddObject(module, "error", inputError);
327+
328+
PyModule_AddStringConstant(module, "VERSION", VERSION);
329+
PyModule_AddStringConstant(module, "__version__", VERSION);
330+
PyModule_AddStringConstant(module, "LZ4_VERSION", LZ4_VERSION);
251331

252332
#if PY_MAJOR_VERSION >= 3
253333
return module;

src/python-lz4.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
static PyObject *py_lz4_compress(PyObject *self, PyObject *args);
3535
static PyObject *py_lz4_uncompress(PyObject *self, PyObject *args);
36+
static PyObject *py_lz4_compressFileAdv(PyObject *self, PyObject *args, PyObject *keywds);
3637
static PyObject *py_lz4_compressFileDefault(PyObject *self, PyObject *args);
3738
static PyObject *py_lz4_decompressFileDefault(PyObject *self, PyObject *args);
3839

@@ -41,9 +42,13 @@ PyMODINIT_FUNC initlz4(void);
4142
#define COMPRESS_DOCSTRING "Compress string, returning the compressed data.\nRaises an exception if any error occurs."
4243
#define COMPRESSHC_DOCSTRING COMPRESS_DOCSTRING "\n\nCompared to compress, this gives a better compression ratio, but is much slower."
4344
#define UNCOMPRESS_DOCSTRING "Decompress string, returning the uncompressed data.\nRaises an exception if any error occurs."
44-
#define COMPRESS_FILE_DOCSTRING "Compress file, by default adds .lz4 extension to original filename."\
45-
"\nAccepts two arguments, inputFile and compression level."
46-
#define DECOMPRESS_FILE_DOCSTRING "Decompresses file, removes the extension by default, preserves original."
45+
#define COMPRESS_FILE_DOCSTRING "Compress file, by default adds .lz4 extension to original filename."
46+
#define COMPF_DEFAULT_DOCSTRING COMPRESS_FILE_DOCSTRING "\nAccepts two positional arguments, inputFile and compression level."
47+
#define COMPF_ADV_DOCSTRING COMPRESS_FILE_DOCSTRING "\nRequires the first two keyword arugments and accepts any number of the"\
48+
"\nfollowing: input, compLevel, output, overwrite, blockSizeID, blockCheck, streamCheck"\
49+
"\nValid values are as follows(def=default): input='string', compLevel=0(low, def)-9(High), output='string',"\
50+
"\noverwrite=0/1(def), blockSizeID=4-7(def), blockCheck=0(def)/1, streamCheck=0/1(def), verbosity=0(def)-4"
51+
#define DECOMP_FILE_DOCSTRING "Decompresses file, removes the extension by default, preserves original."
4752

4853
#if defined(_WIN32) && defined(_MSC_VER)
4954
# define inline __inline

tests/test.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,43 @@
1+
import hashlib
12
import lz4
3+
import os
4+
import shutil
25
import sys
3-
4-
56
import unittest
6-
import os
77

88
class TestLZ4(unittest.TestCase):
99

1010
def test_random(self):
1111
DATA = os.urandom(128 * 1024) # Read 128kb
1212
self.assertEqual(DATA, lz4.loads(lz4.dumps(DATA)))
13-
13+
14+
def test_file(self):
15+
fileName = 'src/lz4.c'
16+
os.mkdir('testHold')
17+
testNames = []
18+
origDigest = hashlib.md5()
19+
20+
with open('src/lz4.c', 'rb') as lz4Orig:
21+
origDigest.update(lz4Orig.read())
22+
23+
for num in range(1, 6):
24+
testNames.append('testHold/test.%d.lz4' % num)
25+
26+
lz4.compressFileAdv(fileName, 9, output=testNames[0])
27+
lz4.compressFileAdv(fileName, 9, output=testNames[1], blockSizeID=4)
28+
lz4.compressFileAdv(fileName, 9, output=testNames[2], blockSizeID=7)
29+
lz4.compressFileAdv(fileName, 9, output=testNames[3], blockCheck=1)
30+
lz4.compressFileAdv(fileName, 9, output=testNames[4], streamCheck=0)
31+
32+
for test in testNames:
33+
lz4.decompressFileDefault(test)
34+
testDigest = hashlib.md5()
35+
with open(test.replace('.lz4', ''), 'rb') as testFile:
36+
testDigest.update(testFile.read())
37+
self.assertEqual(origDigest.hexdigest(), testDigest.hexdigest())
38+
del testDigest
39+
shutil.rmtree('testHold')
40+
1441
if __name__ == '__main__':
1542
unittest.main()
1643

0 commit comments

Comments
 (0)