Edit

kc3-lang/brotli/python/_brotli.cc

Branch :

  • Show log

    Commit

  • Author : Alex Nicksay
    Date : 2016-09-29 15:14:16
    Hash : 595a5246
    Message : Python: Create an extension Compressor object - Create a `Compressor` object in the extension module - Move the `compress` method into the native module and use the new `Compressor` object to do the compression Note: This does not change the module-level Python API. The `Compressor` object will not be publicly exposed until its methods have stabilized.

  • python/_brotli.cc
  • #define PY_SSIZE_T_CLEAN 1
    #include <Python.h>
    #include <bytesobject.h>
    #include <structmember.h>
    #include <cstdio>
    #include <vector>
    #include "../common/version.h"
    #include <brotli/decode.h>
    #include <brotli/encode.h>
    
    #if PY_MAJOR_VERSION >= 3
    #define PyInt_Check PyLong_Check
    #define PyInt_AsLong PyLong_AsLong
    #endif
    
    static PyObject *BrotliError;
    
    static int as_bounded_int(PyObject *o, int* result, int lower_bound, int upper_bound) {
      long value = PyInt_AsLong(o);
      if ((value < (long) lower_bound) || (value > (long) upper_bound)) {
        return 0;
      }
      *result = (int) value;
      return 1;
    }
    
    static int mode_convertor(PyObject *o, BrotliEncoderMode *mode) {
      if (!PyInt_Check(o)) {
        PyErr_SetString(BrotliError, "Invalid mode");
        return 0;
      }
    
      int mode_value = -1;
      if (!as_bounded_int(o, &mode_value, 0, 255)) {
        PyErr_SetString(BrotliError, "Invalid mode");
        return 0;
      }
      *mode = (BrotliEncoderMode) mode_value;
      if (*mode != BROTLI_MODE_GENERIC &&
          *mode != BROTLI_MODE_TEXT &&
          *mode != BROTLI_MODE_FONT) {
        PyErr_SetString(BrotliError, "Invalid mode");
        return 0;
      }
    
      return 1;
    }
    
    static int quality_convertor(PyObject *o, int *quality) {
      if (!PyInt_Check(o)) {
        PyErr_SetString(BrotliError, "Invalid quality");
        return 0;
      }
    
      if (!as_bounded_int(o, quality, 0, 11)) {
        PyErr_SetString(BrotliError, "Invalid quality. Range is 0 to 11.");
        return 0;
      }
    
      return 1;
    }
    
    static int lgwin_convertor(PyObject *o, int *lgwin) {
      if (!PyInt_Check(o)) {
        PyErr_SetString(BrotliError, "Invalid lgwin");
        return 0;
      }
    
      if (!as_bounded_int(o, lgwin, 10, 24)) {
        PyErr_SetString(BrotliError, "Invalid lgwin. Range is 10 to 24.");
        return 0;
      }
    
      return 1;
    }
    
    static int lgblock_convertor(PyObject *o, int *lgblock) {
      if (!PyInt_Check(o)) {
        PyErr_SetString(BrotliError, "Invalid lgblock");
        return 0;
      }
    
      if (!as_bounded_int(o, lgblock, 0, 24) || (*lgblock != 0 && *lgblock < 16)) {
        PyErr_SetString(BrotliError, "Invalid lgblock. Can be 0 or in range 16 to 24.");
        return 0;
      }
    
      return 1;
    }
    
    PyDoc_STRVAR(brotli_Compressor_doc,
    "An object to compress a byte string.\n"
    "\n"
    "Signature:\n"
    "  Compressor(mode=MODE_GENERIC, quality=11, lgwin=22, lgblock=0, dictionary='')\n"
    "\n"
    "Args:\n"
    "  mode (int, optional): The compression mode can be MODE_GENERIC (default),\n"
    "    MODE_TEXT (for UTF-8 format text input) or MODE_FONT (for WOFF 2.0). \n"
    "  quality (int, optional): Controls the compression-speed vs compression-\n"
    "    density tradeoff. The higher the quality, the slower the compression.\n"
    "    Range is 0 to 11. Defaults to 11.\n"
    "  lgwin (int, optional): Base 2 logarithm of the sliding window size. Range\n"
    "    is 10 to 24. Defaults to 22.\n"
    "  lgblock (int, optional): Base 2 logarithm of the maximum input block size.\n"
    "    Range is 16 to 24. If set to 0, the value will be set based on the\n"
    "    quality. Defaults to 0.\n"
    "  dictionary (bytes, optional): Custom dictionary. Only last sliding window\n"
    "     size bytes will be used.\n"
    "\n"
    "Raises:\n"
    "  brotli.error: If arguments are invalid.\n");
    
    typedef struct {
      PyObject_HEAD
      BrotliEncoderState* enc;
    } brotli_Compressor;
    
    static void brotli_Compressor_dealloc(brotli_Compressor* self) {
      BrotliEncoderDestroyInstance(self->enc);
      #if PY_MAJOR_VERSION >= 3
      Py_TYPE(self)->tp_free((PyObject*)self);
      #else
      self->ob_type->tp_free((PyObject*)self);
      #endif
    }
    
    static PyObject* brotli_Compressor_new(PyTypeObject *type, PyObject *args, PyObject *keywds) {
      brotli_Compressor *self;
      self = (brotli_Compressor *)type->tp_alloc(type, 0);
    
      if (self != NULL) {
        self->enc = BrotliEncoderCreateInstance(0, 0, 0);
      }
    
      return (PyObject *)self;
    }
    
    static int brotli_Compressor_init(brotli_Compressor *self, PyObject *args, PyObject *keywds) {
      BrotliEncoderMode mode = (BrotliEncoderMode) -1;
      int quality = -1;
      int lgwin = -1;
      int lgblock = -1;
      uint8_t* custom_dictionary = NULL;
      size_t custom_dictionary_length = 0;
      int ok;
    
      static const char *kwlist[] = {
          "mode", "quality", "lgwin", "lgblock", "dictionary", NULL};
    
      ok = PyArg_ParseTupleAndKeywords(args, keywds, "|O&O&O&O&s#:Compressor",
                        const_cast<char **>(kwlist),
                        &mode_convertor, &mode,
                        &quality_convertor, &quality,
                        &lgwin_convertor, &lgwin,
                        &lgblock_convertor, &lgblock,
                        &custom_dictionary, &custom_dictionary_length);
      if (!ok)
        return -1;
      if (!self->enc)
        return -1;
    
      if ((int) mode != -1)
        BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_MODE, (uint32_t)mode);
      if (quality != -1)
        BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_QUALITY, (uint32_t)quality);
      if (lgwin != -1)
        BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_LGWIN, (uint32_t)lgwin);
      if (lgblock != -1)
        BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_LGBLOCK, (uint32_t)lgblock);
    
      if (custom_dictionary_length != 0) {
        BrotliEncoderSetCustomDictionary(self->enc, custom_dictionary_length,
                                         custom_dictionary);
      }
    
      return 0;
    }
    
    PyDoc_STRVAR(brotli_Compressor_compress_doc,
    "Compress a byte string.\n"
    "\n"
    "Signature:\n"
    "  compress(string)\n"
    "\n"
    "Args:\n"
    "  string (bytes): The input data.\n"
    "\n"
    "Returns:\n"
    "  The compressed byte string.\n"
    "\n"
    "Raises:\n"
    "  brotli.error: If compression fails.\n");
    
    static PyObject* brotli_Compressor_compress(brotli_Compressor *self, PyObject *args) {
      PyObject* ret = NULL;
      uint8_t* input;
      uint8_t* output = NULL;
      uint8_t* next_out;
      const uint8_t *next_in;
      size_t input_length;
      size_t output_length;
      size_t available_in;
      size_t available_out;
      int ok;
    
      ok = PyArg_ParseTuple(args, "s#:compress", &input, &input_length);
      if (!ok)
        return NULL;
    
      output_length = input_length + (input_length >> 2) + 10240;
    
      if (!self->enc) {
        ok = false;
        goto end;
      }
    
      output = new uint8_t[output_length];
      available_out = output_length;
      next_out = output;
      available_in = input_length;
      next_in = input;
    
      BrotliEncoderCompressStream(self->enc, BROTLI_OPERATION_FINISH,
                                  &available_in, &next_in,
                                  &available_out, &next_out, 0);
      ok = BrotliEncoderIsFinished(self->enc);
    
    end:
      if (ok) {
        ret = PyBytes_FromStringAndSize((char*)output, output_length - available_out);
      } else {
        PyErr_SetString(BrotliError, "BrotliCompressBuffer failed");
      }
    
      delete[] output;
    
      return ret;
    }
    
    static PyMemberDef brotli_Compressor_members[] = {
      {NULL}  /* Sentinel */
    };
    
    static PyMethodDef brotli_Compressor_methods[] = {
      {"compress", (PyCFunction)brotli_Compressor_compress, METH_VARARGS, brotli_Compressor_compress_doc},
      {NULL}  /* Sentinel */
    };
    
    static PyTypeObject brotli_CompressorType = {
      #if PY_MAJOR_VERSION >= 3
      PyVarObject_HEAD_INIT(NULL, 0)
      #else
      PyObject_HEAD_INIT(NULL)
      0,                                     /* ob_size*/
      #endif
      "brotli.Compressor",                   /* tp_name */
      sizeof(brotli_Compressor),             /* tp_basicsize */
      0,                                     /* tp_itemsize */
      (destructor)brotli_Compressor_dealloc, /* tp_dealloc */
      0,                                     /* tp_print */
      0,                                     /* tp_getattr */
      0,                                     /* tp_setattr */
      0,                                     /* tp_compare */
      0,                                     /* tp_repr */
      0,                                     /* tp_as_number */
      0,                                     /* tp_as_sequence */
      0,                                     /* tp_as_mapping */
      0,                                     /* tp_hash  */
      0,                                     /* tp_call */
      0,                                     /* tp_str */
      0,                                     /* tp_getattro */
      0,                                     /* tp_setattro */
      0,                                     /* tp_as_buffer */
      Py_TPFLAGS_DEFAULT,                    /* tp_flags */
      brotli_Compressor_doc,                 /* tp_doc */
      0,                                     /* tp_traverse */
      0,                                     /* tp_clear */
      0,                                     /* tp_richcompare */
      0,                                     /* tp_weaklistoffset */
      0,                                     /* tp_iter */
      0,                                     /* tp_iternext */
      brotli_Compressor_methods,             /* tp_methods */
      brotli_Compressor_members,             /* tp_members */
      0,                                     /* tp_getset */
      0,                                     /* tp_base */
      0,                                     /* tp_dict */
      0,                                     /* tp_descr_get */
      0,                                     /* tp_descr_set */
      0,                                     /* tp_dictoffset */
      (initproc)brotli_Compressor_init,      /* tp_init */
      0,                                     /* tp_alloc */
      brotli_Compressor_new,                 /* tp_new */
    };
    
    PyDoc_STRVAR(brotli_decompress__doc__,
    "Decompress a compressed byte string.\n"
    "\n"
    "Signature:\n"
    "  decompress(string)\n"
    "\n"
    "Args:\n"
    "  string (bytes): The compressed input data.\n"
    "  dictionary (bytes, optional): Custom dictionary. MUST be the same data\n"
    "     as passed to compress method.\n"
    "\n"
    "Returns:\n"
    "  The decompressed byte string.\n"
    "\n"
    "Raises:\n"
    "  brotli.error: If decompressor fails.\n");
    
    static PyObject* brotli_decompress(PyObject *self, PyObject *args, PyObject *keywds) {
      PyObject *ret = NULL;
      const uint8_t *input, *custom_dictionary;
      size_t length, custom_dictionary_length;
      int ok;
    
      static const char *kwlist[] = {"string", "dictionary", NULL};
    
      custom_dictionary = NULL;
      custom_dictionary_length = 0;
    
      ok = PyArg_ParseTupleAndKeywords(args, keywds, "s#|s#:decompress",
                            const_cast<char **>(kwlist),
                            &input, &length,
                            &custom_dictionary, &custom_dictionary_length);
      if (!ok)
        return NULL;
    
      std::vector<uint8_t> output;
      const size_t kBufferSize = 65536;
      uint8_t* buffer = new uint8_t[kBufferSize];
      BrotliDecoderState* state = BrotliDecoderCreateInstance(0, 0, 0);
      if (custom_dictionary_length != 0) {
        BrotliDecoderSetCustomDictionary(state, custom_dictionary_length, custom_dictionary);
      }
    
      BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
      while (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
        size_t available_out = kBufferSize;
        uint8_t* next_out = buffer;
        result = BrotliDecoderDecompressStream(state, &length, &input,
                                               &available_out, &next_out, 0);
        size_t used_out = kBufferSize - available_out;
        if (used_out != 0)
          output.insert(output.end(), buffer, buffer + used_out);
      }
      ok = result == BROTLI_DECODER_RESULT_SUCCESS;
      if (ok) {
        ret = PyBytes_FromStringAndSize((char*)(output.size() ? &output[0] : NULL), output.size());
      } else {
        PyErr_SetString(BrotliError, "BrotliDecompress failed");
      }
    
      BrotliDecoderDestroyInstance(state);
      delete[] buffer;
    
      return ret;
    }
    
    static PyMethodDef brotli_methods[] = {
      {"decompress", (PyCFunction)brotli_decompress, METH_VARARGS | METH_KEYWORDS, brotli_decompress__doc__},
      {NULL, NULL, 0, NULL}
    };
    
    PyDoc_STRVAR(brotli_doc, "Implementation module for the Brotli library.");
    
    #if PY_MAJOR_VERSION >= 3
    #define INIT_BROTLI   PyInit__brotli
    #define CREATE_BROTLI PyModule_Create(&brotli_module)
    #define RETURN_BROTLI return m
    #define RETURN_NULL return NULL
    
    static struct PyModuleDef brotli_module = {
      PyModuleDef_HEAD_INIT,
      "_brotli",
      brotli_doc,
      0,
      brotli_methods,
      NULL,
      NULL,
      NULL
    };
    #else
    #define INIT_BROTLI   init_brotli
    #define CREATE_BROTLI Py_InitModule3("_brotli", brotli_methods, brotli_doc)
    #define RETURN_BROTLI return
    #define RETURN_NULL return
    #endif
    
    PyMODINIT_FUNC INIT_BROTLI(void) {
      PyObject *m = CREATE_BROTLI;
    
      BrotliError = PyErr_NewException((char*) "brotli.error", NULL, NULL);
      if (BrotliError != NULL) {
        Py_INCREF(BrotliError);
        PyModule_AddObject(m, "error", BrotliError);
      }
    
      if (PyType_Ready(&brotli_CompressorType) < 0) {
        RETURN_NULL;
      }
      Py_INCREF(&brotli_CompressorType);
      PyModule_AddObject(m, "Compressor", (PyObject *)&brotli_CompressorType);
    
      PyModule_AddIntConstant(m, "MODE_GENERIC", (int) BROTLI_MODE_GENERIC);
      PyModule_AddIntConstant(m, "MODE_TEXT", (int) BROTLI_MODE_TEXT);
      PyModule_AddIntConstant(m, "MODE_FONT", (int) BROTLI_MODE_FONT);
    
      char version[16];
      snprintf(version, sizeof(version), "%d.%d.%d",
          BROTLI_VERSION >> 24, (BROTLI_VERSION >> 12) & 0xFFF, BROTLI_VERSION & 0xFFF);
      PyModule_AddStringConstant(m, "__version__", version);
    
      RETURN_BROTLI;
    }