_roslz4module.c
Go to the documentation of this file.
00001 /*********************************************************************
00002 * Software License Agreement (BSD License)
00003 *
00004 *  Copyright (c) 2014, Ben Charrow
00005 *  All rights reserved.
00006 *
00007 *  Redistribution and use in source and binary forms, with or without
00008 *  modification, are permitted provided that the following conditions
00009 *  are met:
00010 *
00011 *   * Redistributions of source code must retain the above copyright
00012 *     notice, this list of conditions and the following disclaimer.
00013 *   * Redistributions in binary form must reproduce the above
00014 *     copyright notice, this list of conditions and the following
00015 *     disclaimer in the documentation and/or other materials provided
00016 *     with the distribution.
00017 *   * Neither the name of Willow Garage, Inc. nor the names of its
00018 *     contributors may be used to endorse or promote products derived
00019 *     from this software without specific prior written permission.
00020 *
00021 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032 *  POSSIBILITY OF SUCH DAMAGE.
00033 ********************************************************************/
00034 
00035 #include "Python.h"
00036 
00037 #include "roslz4/lz4s.h"
00038 
00039 struct module_state {
00040   PyObject *error;
00041 };
00042 
00043 #if PY_MAJOR_VERSION >= 3
00044 #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
00045 #else
00046 #define GETSTATE(m) (&_state)
00047 static struct module_state _state;
00048 #endif
00049 
00050 /* Taken from Python's _bz2module.c */
00051 static int
00052 grow_buffer(PyObject **buf)
00053 {
00054     /* Expand the buffer by an amount proportional to the current size,
00055        giving us amortized linear-time behavior. Use a less-than-double
00056        growth factor to avoid excessive allocation. */
00057     size_t size = PyBytes_GET_SIZE(*buf);
00058     size_t new_size = size + (size >> 3) + 6;
00059     if (new_size > size) {
00060         return _PyBytes_Resize(buf, new_size);
00061     } else {  /* overflow */
00062         PyErr_SetString(PyExc_OverflowError,
00063                         "Unable to allocate buffer - output too large");
00064         return -1;
00065     }
00066 }
00067 
00068 /*============================== LZ4Compressor ==============================*/
00069 
00070 typedef struct {
00071     PyObject_HEAD
00072     roslz4_stream stream;
00073 } LZ4Compressor;
00074 
00075 static void
00076 LZ4Compressor_dealloc(LZ4Compressor *self)
00077 {
00078   roslz4_compressEnd(&self->stream);
00079   Py_TYPE(self)->tp_free((PyObject*)self);
00080 }
00081 
00082 static int
00083 LZ4Compressor_init(LZ4Compressor *self, PyObject *args, PyObject *kwds)
00084 {
00085   (void)kwds;
00086   if (!PyArg_ParseTuple(args, ":__init__")) {
00087     return -1;
00088   }
00089 
00090   int ret = roslz4_compressStart(&self->stream, 6);
00091   if (ret != ROSLZ4_OK) {
00092     PyErr_SetString(PyExc_RuntimeError, "error initializing roslz4 stream");
00093     return -1;
00094   }
00095   return 0;
00096 }
00097 
00098 static PyObject *
00099 compress_impl(LZ4Compressor *self, Py_buffer *input, PyObject *output)
00100 {
00101   /* Allocate output string */
00102   int initial_size = roslz4_blockSizeFromIndex(self->stream.block_size_id) + 64;
00103   output = PyBytes_FromStringAndSize(NULL, initial_size);
00104   if (!output) {
00105     if (input != NULL) { PyBuffer_Release(input); }
00106     return NULL;
00107   }
00108 
00109   /* Setup stream */
00110   int action;
00111   if (input != NULL) {
00112     action = ROSLZ4_RUN;
00113     self->stream.input_next = input->buf;
00114     self->stream.input_left = input->len;
00115   } else {
00116     action = ROSLZ4_FINISH;
00117     self->stream.input_next = NULL;
00118     self->stream.input_left = 0;
00119   }
00120   self->stream.output_next = PyBytes_AS_STRING(output);
00121   self->stream.output_left = PyBytes_GET_SIZE(output);
00122 
00123   /* Compress data */
00124   int status;
00125   int output_written = 0;
00126   while ((action == ROSLZ4_FINISH) ||
00127          (action == ROSLZ4_RUN && self->stream.input_left > 0)) {
00128     int out_start = self->stream.total_out;
00129     status = roslz4_compress(&self->stream, action);
00130     output_written += self->stream.total_out - out_start;
00131     if (status == ROSLZ4_OK) {
00132       continue;
00133     } else if (status == ROSLZ4_STREAM_END) {
00134       break;
00135     } else if (status == ROSLZ4_OUTPUT_SMALL) {
00136       if (grow_buffer(&output) < 0) {
00137         goto error;
00138       }
00139       self->stream.output_next = PyBytes_AS_STRING(output) + output_written;
00140       self->stream.output_left = PyBytes_GET_SIZE(output) - output_written;
00141     } else if (status == ROSLZ4_PARAM_ERROR) {
00142       PyErr_SetString(PyExc_IOError, "bad block size parameter");
00143       goto error;
00144     } else if (status == ROSLZ4_ERROR) {
00145       PyErr_SetString(PyExc_IOError, "error compressing");
00146       goto error;
00147     } else {
00148       PyErr_Format(PyExc_RuntimeError, "unhandled return code %i", status);
00149       goto error;
00150     }
00151   }
00152 
00153   /* Shrink return buffer */
00154   if (output_written != PyBytes_GET_SIZE(output)) {
00155     _PyBytes_Resize(&output, output_written);
00156   }
00157 
00158   if (input != NULL) { PyBuffer_Release(input); }
00159   return output;
00160 
00161 error:
00162   if (input != NULL) { PyBuffer_Release(input); }
00163   Py_XDECREF(output);
00164   return NULL;
00165 }
00166 
00167 static PyObject *
00168 LZ4Compressor_compress(LZ4Compressor *self, PyObject *args)
00169 {
00170   Py_buffer input;
00171   PyObject *output = NULL;
00172 
00173   /* TODO: Keyword argument */
00174   if (!PyArg_ParseTuple(args, "s*:compress", &input)) {
00175     return NULL;
00176   }
00177 
00178   return compress_impl(self, &input, output);
00179 }
00180 
00181 static PyObject *
00182 LZ4Compressor_flush(LZ4Compressor *self, PyObject *args)
00183 {
00184   PyObject *output = NULL;
00185 
00186   if (!PyArg_ParseTuple(args, ":flush")) {
00187     return NULL;
00188   }
00189 
00190   return compress_impl(self, NULL, output);
00191 }
00192 
00193 
00194 static PyMethodDef LZ4Compressor_methods[] = {
00195   {"compress", (PyCFunction)LZ4Compressor_compress, METH_VARARGS, "method doc"},
00196   {"flush", (PyCFunction)LZ4Compressor_flush, METH_VARARGS, "method doc"},
00197   {NULL} /* Sentinel */
00198 };
00199 
00200 static PyTypeObject LZ4Compressor_Type = {
00201     PyVarObject_HEAD_INIT(NULL, 0)
00202     "_roslz4.LZ4Compressor",           /* tp_name */
00203     sizeof(LZ4Compressor),             /* tp_basicsize */
00204     0,                                 /* tp_itemsize */
00205     (destructor)LZ4Compressor_dealloc, /* tp_dealloc */
00206     0,                                 /* tp_print */
00207     0,                                 /* tp_getattr */
00208     0,                                 /* tp_setattr */
00209     0,                                 /* tp_compare */
00210     0,                                 /* tp_repr */
00211     0,                                 /* tp_as_number */
00212     0,                                 /* tp_as_sequence */
00213     0,                                 /* tp_as_mapping */
00214     0,                                 /* tp_hash */
00215     0,                                 /* tp_call */
00216     0,                                 /* tp_str */
00217     0,                                 /* tp_getattro */
00218     0,                                 /* tp_setattro */
00219     0,                                 /* tp_as_buffer */
00220     Py_TPFLAGS_DEFAULT,                /* tp_flags */
00221     "LZ4Compressor objects",           /* tp_doc */
00222     0,                                 /* tp_traverse */
00223     0,                                 /* tp_clear */
00224     0,                                 /* tp_richcompare */
00225     0,                                 /* tp_weaklistoffset */
00226     0,                                 /* tp_iter */
00227     0,                                 /* tp_iternext */
00228     LZ4Compressor_methods,             /* tp_methods */
00229     0,                                 /* tp_members */
00230     0,                                 /* tp_getset */
00231     0,                                 /* tp_base */
00232     0,                                 /* tp_dict */
00233     0,                                 /* tp_descr_get */
00234     0,                                 /* tp_descr_set */
00235     0,                                 /* tp_dictoffset */
00236     (initproc)LZ4Compressor_init       /* tp_init */
00237 };
00238 
00239 /*============================= LZ4Decompressor =============================*/
00240 
00241 typedef struct {
00242     PyObject_HEAD
00243     roslz4_stream stream;
00244 } LZ4Decompressor;
00245 
00246 static void
00247 LZ4Decompressor_dealloc(LZ4Decompressor *self)
00248 {
00249   roslz4_decompressEnd(&self->stream);
00250   Py_TYPE(self)->tp_free((PyObject*)self);
00251 }
00252 
00253 static int
00254 LZ4Decompressor_init(LZ4Decompressor *self, PyObject *args, PyObject *kwds)
00255 {
00256   (void)kwds;
00257   if (!PyArg_ParseTuple(args, ":__init__")) {
00258     return -1;
00259   }
00260 
00261   int ret = roslz4_decompressStart(&self->stream);
00262   if (ret != ROSLZ4_OK) {
00263     PyErr_SetString(PyExc_RuntimeError, "error initializing roslz4 stream");
00264     return -1;
00265   }
00266   return 0;
00267 }
00268 
00269 static PyObject *
00270 LZ4Decompressor_decompress(LZ4Decompressor *self, PyObject *args)
00271 {
00272   Py_buffer input;
00273   PyObject *output = NULL;
00274 
00275   /* TODO: Keyword argument */
00276   if (!PyArg_ParseTuple(args, "s*:decompress", &input)) {
00277     return NULL;
00278   }
00279 
00280   /* Allocate 1 output block. If header not read, use compression block size */
00281   int block_size;
00282   if (self->stream.block_size_id == -1 ) {
00283     block_size = roslz4_blockSizeFromIndex(6);
00284   } else {
00285     block_size = roslz4_blockSizeFromIndex(self->stream.block_size_id);
00286   }
00287 
00288   output = PyBytes_FromStringAndSize(NULL, block_size);
00289   if (!output) {
00290     PyBuffer_Release(&input);
00291     return NULL;
00292   }
00293 
00294   /* Setup stream */
00295   self->stream.input_next = input.buf;
00296   self->stream.input_left = input.len;
00297   self->stream.output_next = PyBytes_AS_STRING(output);
00298   self->stream.output_left = PyBytes_GET_SIZE(output);
00299 
00300   int output_written = 0;
00301   while (self->stream.input_left > 0) {
00302     int out_start = self->stream.total_out;
00303     int status = roslz4_decompress(&self->stream);
00304     output_written += self->stream.total_out - out_start;
00305     if (status == ROSLZ4_OK) {
00306       continue;
00307     } else if (status == ROSLZ4_STREAM_END) {
00308       break;
00309     } else if (status == ROSLZ4_OUTPUT_SMALL) {
00310       if (grow_buffer(&output) < 0) {
00311         goto error;
00312       }
00313       self->stream.output_next = PyBytes_AS_STRING(output) + output_written;
00314       self->stream.output_left = PyBytes_GET_SIZE(output) - output_written;
00315     } else if (status == ROSLZ4_ERROR) {
00316       PyErr_SetString(PyExc_IOError, "error decompressing");
00317       goto error;
00318     } else if (status == ROSLZ4_DATA_ERROR) {
00319       PyErr_SetString(PyExc_IOError, "malformed data to decompress");
00320       goto error;
00321     } else {
00322       PyErr_Format(PyExc_RuntimeError, "unhandled return code %i", status);
00323       goto error;
00324     }
00325   }
00326 
00327   if (output_written != PyBytes_GET_SIZE(output)) {
00328     _PyBytes_Resize(&output, output_written);
00329   }
00330 
00331   PyBuffer_Release(&input);
00332   return output;
00333 
00334 error:
00335   PyBuffer_Release(&input);
00336   Py_XDECREF(output);
00337   return NULL;
00338 }
00339 
00340 static PyMethodDef LZ4Decompressor_methods[] = {
00341   {"decompress", (PyCFunction)LZ4Decompressor_decompress, METH_VARARGS, "method doc"},
00342   {NULL} /* Sentinel */
00343 };
00344 
00345 static PyTypeObject LZ4Decompressor_Type = {
00346     PyVarObject_HEAD_INIT(NULL, 0)
00347     "_roslz4.LZ4Decompressor",           /* tp_name */
00348     sizeof(LZ4Decompressor),             /* tp_basicsize */
00349     0,                                   /* tp_itemsize */
00350     (destructor)LZ4Decompressor_dealloc, /* tp_dealloc */
00351     0,                                   /* tp_print */
00352     0,                                   /* tp_getattr */
00353     0,                                   /* tp_setattr */
00354     0,                                   /* tp_compare */
00355     0,                                   /* tp_repr */
00356     0,                                   /* tp_as_number */
00357     0,                                   /* tp_as_sequence */
00358     0,                                   /* tp_as_mapping */
00359     0,                                   /* tp_hash */
00360     0,                                   /* tp_call */
00361     0,                                   /* tp_str */
00362     0,                                   /* tp_getattro */
00363     0,                                   /* tp_setattro */
00364     0,                                   /* tp_as_buffer */
00365     Py_TPFLAGS_DEFAULT,                  /* tp_flags */
00366     "LZ4Decompressor objects",           /* tp_doc */
00367     0,                                   /* tp_traverse */
00368     0,                                   /* tp_clear */
00369     0,                                   /* tp_richcompare */
00370     0,                                   /* tp_weaklistoffset */
00371     0,                                   /* tp_iter */
00372     0,                                   /* tp_iternext */
00373     LZ4Decompressor_methods,             /* tp_methods */
00374     0,                                   /* tp_members */
00375     0,                                   /* tp_getset */
00376     0,                                   /* tp_base */
00377     0,                                   /* tp_dict */
00378     0,                                   /* tp_descr_get */
00379     0,                                   /* tp_descr_set */
00380     0,                                   /* tp_dictoffset */
00381     (initproc)LZ4Decompressor_init       /* tp_init */
00382 };
00383 
00384 
00385 /*========================== Module initialization ==========================*/
00386 
00387 #if PY_MAJOR_VERSION >= 3
00388 
00389 static int roslz4_traverse(PyObject *m, visitproc visit, void *arg) {
00390   Py_VISIT(GETSTATE(m)->error);
00391   return 0;
00392 }
00393 
00394 static int roslz4_clear(PyObject *m) {
00395   Py_CLEAR(GETSTATE(m)->error);
00396   return 0;
00397 }
00398 
00399 static struct PyModuleDef moduledef = {
00400   PyModuleDef_HEAD_INIT,
00401   "_roslz4",
00402   NULL,
00403   sizeof(struct module_state),
00404   NULL,
00405   NULL,
00406   roslz4_traverse,
00407   roslz4_clear,
00408   NULL
00409 };
00410 #define INITERROR return NULL
00411 
00412 PyObject *
00413 PyInit__roslz4(void)
00414 
00415 #else
00416 #define INITERROR return
00417 
00418 void
00419 init_roslz4(void)
00420 #endif
00421 {
00422   PyObject *m;
00423 
00424   LZ4Compressor_Type.tp_new = PyType_GenericNew;
00425   if (PyType_Ready(&LZ4Compressor_Type) < 0) {
00426     INITERROR;
00427   }
00428 
00429   LZ4Decompressor_Type.tp_new = PyType_GenericNew;
00430   if (PyType_Ready(&LZ4Decompressor_Type) < 0) {
00431     INITERROR;
00432   }
00433 
00434 #if PY_MAJOR_VERSION >= 3
00435   m = PyModule_Create(&moduledef);
00436 #else
00437   m = Py_InitModule("_roslz4", NULL);
00438 #endif
00439 
00440   if (m == NULL) {
00441     INITERROR;
00442   }
00443 
00444   Py_INCREF(&LZ4Compressor_Type);
00445   PyModule_AddObject(m, "LZ4Compressor", (PyObject *)&LZ4Compressor_Type);
00446   Py_INCREF(&LZ4Decompressor_Type);
00447   PyModule_AddObject(m, "LZ4Decompressor", (PyObject *)&LZ4Decompressor_Type);
00448 
00449 #if PY_MAJOR_VERSION >= 3
00450   return m;
00451 #endif
00452 }


roslz4
Author(s): Ben Charrow
autogenerated on Thu Jun 6 2019 21:09:51