Skip to content

Commit

Permalink
Added compressFileAdv and advanced options
Browse files Browse the repository at this point in the history
  • Loading branch information
darkdragn committed Sep 4, 2014
1 parent 745a050 commit ac88c21
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 17 deletions.
100 changes: 90 additions & 10 deletions src/python-lz4.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@

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

static PyObject *inputError;

typedef int (*compressor)(const char *source, char *dest, int isize);

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

static inline char* add_extension(char* input) {
char* output;

output = (char*)malloc(strlen(input)+4);
strcpy(output, input);
strcat(output, ".lz4");

return output;
}

static const int hdr_size = sizeof(uint32_t);

static PyObject *compress_with(compressor compress, PyObject *self, PyObject *args) {
Expand Down Expand Up @@ -129,25 +141,86 @@ static PyObject *py_lz4_uncompress(PyObject *self, PyObject *args) {
return result;
}


static PyObject *py_lz4_compressFileDefault(PyObject *self, PyObject *args) {
char* input;
char* output;
int compLevel;
int compLevel = 0;

(void)self;
if (!PyArg_ParseTuple(args, "si", &input, &compLevel)) {
if (!PyArg_ParseTuple(args, "s|i", &input, &compLevel)) {
return NULL;
}

output = (char*)malloc(strlen(input)+4);
strcpy(output, input);
strcat(output, ".lz4");
output = add_extension(input);

LZ4IO_compressFilename(input, output, compLevel);
return Py_None;
}

static PyObject *py_lz4_compressFileAdv(PyObject *self, PyObject *args, \
PyObject *keywds) {
char* input;
char* output = NULL;
int compLevel = 0;
int overwrite = NULL;
int blockSizeID = NULL;
int blockMode = 1;
int blockCheck = NULL;
int streamCheck = NULL;
int verbosity = NULL;

static char *kwlist[] = {"input", "compLevel", "output", "overwrite",
"blockSizeID", "blockMode", "blockCheck",
"streamCheck", "verbosity", NULL};
(void)self;
if (!PyArg_ParseTupleAndKeywords(args, keywds, "si|siiiiii", kwlist,
&input, &compLevel, &output, &overwrite,
&blockSizeID, &blockMode, &blockCheck,
&streamCheck, &verbosity)) {
return NULL;
}

if (!output) { output = add_extension(input); }
if (overwrite) { LZ4IO_setOverwrite(overwrite); }
if (blockSizeID) {
if ((3 < blockSizeID) && (blockSizeID < 8)) {
LZ4IO_setBlockSizeID(blockSizeID);
}
else {
PyErr_SetString(inputError, "Invalid input for blockSizeID");
}
}
if ((blockMode == 0) || (blockMode == 1)) {
if (blockMode == 0 ) {LZ4IO_setBlockMode(chainedBlocks);}
}
else {
PyErr_SetString(inputError, "Invalid input for blockMode");
return NULL;
}
if ((blockCheck == 0) || (blockCheck == 1)) {
if (blockCheck == 1) {LZ4IO_setBlockChecksumMode(blockCheck);}
}
else {
PyErr_SetString(inputError, "Invalid input for blockCheck");
return NULL;
}
if ((streamCheck == 0) || (streamCheck == 1)) {
if (streamCheck == 0) {LZ4IO_setStreamChecksumMode(streamCheck);}
}
else {
PyErr_SetString(inputError, "Invalid input for streamCheck");
return NULL;
}
if ((-1 < verbosity) && (verbosity < 5)) {
LZ4IO_setNotificationLevel(verbosity);
}
else {
PyErr_SetString(inputError, "Invalid input for verbosity");
}

LZ4IO_compressFilename(input, output, compLevel);
return Py_None;
}

static PyObject *py_lz4_decompressFileDefault(PyObject *self, PyObject *args) {
char* input;
Expand All @@ -163,8 +236,6 @@ static PyObject *py_lz4_decompressFileDefault(PyObject *self, PyObject *args) {
output = (char*)calloc(outLen, sizeof(char));
strncpy(output, input, outLen);

printf("%s \n", output);

LZ4IO_decompressFilename(input, output);
return Py_None;
}
Expand All @@ -179,8 +250,9 @@ static PyMethodDef Lz4Methods[] = {
{"decompress", py_lz4_uncompress, METH_VARARGS, UNCOMPRESS_DOCSTRING},
{"dumps", py_lz4_compress, METH_VARARGS, COMPRESS_DOCSTRING},
{"loads", py_lz4_uncompress, METH_VARARGS, UNCOMPRESS_DOCSTRING},
{"compressFileDefault", py_lz4_compressFileDefault, METH_VARARGS, COMPRESS_FILE_DOCSTRING},
{"decompressFileDefault", py_lz4_decompressFileDefault, METH_VARARGS, DECOMPRESS_FILE_DOCSTRING},
{"compressFileAdv", (PyCFunction)py_lz4_compressFileAdv, METH_VARARGS | METH_KEYWORDS, COMPF_ADV_DOCSTRING},
{"compressFileDefault", py_lz4_compressFileDefault, METH_VARARGS, COMPF_DEFAULT_DOCSTRING},
{"decompressFileDefault", py_lz4_decompressFileDefault, METH_VARARGS, DECOMP_FILE_DOCSTRING},
{NULL, NULL, 0, NULL}
};

Expand All @@ -190,6 +262,7 @@ struct module_state {
PyObject *error;
};


#if PY_MAJOR_VERSION >= 3
#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
#else
Expand Down Expand Up @@ -248,6 +321,13 @@ void initlz4(void)
Py_DECREF(module);
INITERROR;
}
inputError = PyErr_NewException("input.error", NULL, NULL);
Py_INCREF(inputError);
PyModule_AddObject(module, "error", inputError);

PyModule_AddStringConstant(module, "VERSION", VERSION);
PyModule_AddStringConstant(module, "__version__", VERSION);
PyModule_AddStringConstant(module, "LZ4_VERSION", LZ4_VERSION);

#if PY_MAJOR_VERSION >= 3
return module;
Expand Down
11 changes: 8 additions & 3 deletions src/python-lz4.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

static PyObject *py_lz4_compress(PyObject *self, PyObject *args);
static PyObject *py_lz4_uncompress(PyObject *self, PyObject *args);
static PyObject *py_lz4_compressFileAdv(PyObject *self, PyObject *args, PyObject *keywds);
static PyObject *py_lz4_compressFileDefault(PyObject *self, PyObject *args);
static PyObject *py_lz4_decompressFileDefault(PyObject *self, PyObject *args);

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

#if defined(_WIN32) && defined(_MSC_VER)
# define inline __inline
Expand Down
35 changes: 31 additions & 4 deletions tests/test.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,43 @@
import hashlib
import lz4
import os
import shutil
import sys


import unittest
import os

class TestLZ4(unittest.TestCase):

def test_random(self):
DATA = os.urandom(128 * 1024) # Read 128kb
self.assertEqual(DATA, lz4.loads(lz4.dumps(DATA)))


def test_file(self):
fileName = 'src/lz4.c'
os.mkdir('testHold')
testNames = []
origDigest = hashlib.md5()

with open('src/lz4.c', 'rb') as lz4Orig:
origDigest.update(lz4Orig.read())

for num in range(1, 6):
testNames.append('testHold/test.%d.lz4' % num)

lz4.compressFileAdv(fileName, 9, output=testNames[0])
lz4.compressFileAdv(fileName, 9, output=testNames[1], blockSizeID=4)
lz4.compressFileAdv(fileName, 9, output=testNames[2], blockSizeID=7)
lz4.compressFileAdv(fileName, 9, output=testNames[3], blockCheck=1)
lz4.compressFileAdv(fileName, 9, output=testNames[4], streamCheck=0)

for test in testNames:
lz4.decompressFileDefault(test)
testDigest = hashlib.md5()
with open(test.replace('.lz4', ''), 'rb') as testFile:
testDigest.update(testFile.read())
self.assertEqual(origDigest.hexdigest(), testDigest.hexdigest())
del testDigest
shutil.rmtree('testHold')

if __name__ == '__main__':
unittest.main()

0 comments on commit ac88c21

Please sign in to comment.