| /* Low level interface to the Zstandard algorithm & the zstd library. */ |
| |
| /* ZstdCompressor class definitions */ |
| |
| /*[clinic input] |
| module _zstd |
| class _zstd.ZstdCompressor "ZstdCompressor *" "&zstd_compressor_type_spec" |
| [clinic start generated code]*/ |
| /*[clinic end generated code: output=da39a3ee5e6b4b0d input=7166021db1ef7df8]*/ |
| |
| #ifndef Py_BUILD_CORE_BUILTIN |
| # define Py_BUILD_CORE_MODULE 1 |
| #endif |
| |
| #include "Python.h" |
| |
| #include "_zstdmodule.h" |
| #include "buffer.h" |
| #include "internal/pycore_lock.h" // PyMutex_IsLocked |
| |
| #include <stddef.h> // offsetof() |
| #include <zstd.h> // ZSTD_*() |
| |
| typedef struct { |
| PyObject_HEAD |
| |
| /* Compression context */ |
| ZSTD_CCtx *cctx; |
| |
| /* ZstdDict object in use */ |
| PyObject *dict; |
| |
| /* Last mode, initialized to ZSTD_e_end */ |
| int last_mode; |
| |
| /* (nbWorker >= 1) ? 1 : 0 */ |
| int use_multithread; |
| |
| /* Compression level */ |
| int compression_level; |
| |
| /* Lock to protect the compression context */ |
| PyMutex lock; |
| } ZstdCompressor; |
| |
| #define ZstdCompressor_CAST(op) ((ZstdCompressor *)op) |
| |
| /*[python input] |
| |
| class zstd_contentsize_converter(CConverter): |
| type = 'unsigned long long' |
| converter = 'zstd_contentsize_converter' |
| |
| [python start generated code]*/ |
| /*[python end generated code: output=da39a3ee5e6b4b0d input=0932c350d633c7de]*/ |
| |
| |
| static int |
| zstd_contentsize_converter(PyObject *size, unsigned long long *p) |
| { |
| // None means the user indicates the size is unknown. |
| if (size == Py_None) { |
| *p = ZSTD_CONTENTSIZE_UNKNOWN; |
| } |
| else { |
| /* ZSTD_CONTENTSIZE_UNKNOWN is 0ULL - 1 |
| ZSTD_CONTENTSIZE_ERROR is 0ULL - 2 |
| Users should only pass values < ZSTD_CONTENTSIZE_ERROR */ |
| unsigned long long pledged_size = PyLong_AsUnsignedLongLong(size); |
| /* Here we check for (unsigned long long)-1 as a sign of an error in |
| PyLong_AsUnsignedLongLong */ |
| if (pledged_size == (unsigned long long)-1 && PyErr_Occurred()) { |
| *p = ZSTD_CONTENTSIZE_ERROR; |
| if (PyErr_ExceptionMatches(PyExc_OverflowError)) { |
| PyErr_Format(PyExc_ValueError, |
| "size argument should be a positive int less " |
| "than %ull", ZSTD_CONTENTSIZE_ERROR); |
| return 0; |
| } |
| return 0; |
| } |
| if (pledged_size >= ZSTD_CONTENTSIZE_ERROR) { |
| *p = ZSTD_CONTENTSIZE_ERROR; |
| PyErr_Format(PyExc_ValueError, |
| "size argument should be a positive int less " |
| "than %ull", ZSTD_CONTENTSIZE_ERROR); |
| return 0; |
| } |
| *p = pledged_size; |
| } |
| return 1; |
| } |
| |
| #include "clinic/compressor.c.h" |
| |
| static int |
| _zstd_set_c_level(ZstdCompressor *self, int level) |
| { |
| /* Set integer compression level */ |
| int min_level = ZSTD_minCLevel(); |
| int max_level = ZSTD_maxCLevel(); |
| if (level < min_level || level > max_level) { |
| PyErr_Format(PyExc_ValueError, |
| "illegal compression level %d; the valid range is [%d, %d]", |
| level, min_level, max_level); |
| return -1; |
| } |
| |
| /* Save for generating ZSTD_CDICT */ |
| self->compression_level = level; |
| |
| /* Set compressionLevel to compression context */ |
| size_t zstd_ret = ZSTD_CCtx_setParameter( |
| self->cctx, ZSTD_c_compressionLevel, level); |
| |
| /* Check error */ |
| if (ZSTD_isError(zstd_ret)) { |
| _zstd_state* mod_state = PyType_GetModuleState(Py_TYPE(self)); |
| set_zstd_error(mod_state, ERR_SET_C_LEVEL, zstd_ret); |
| return -1; |
| } |
| return 0; |
| } |
| |
| static int |
| _zstd_set_c_parameters(ZstdCompressor *self, PyObject *options) |
| { |
| _zstd_state* mod_state = PyType_GetModuleState(Py_TYPE(self)); |
| if (mod_state == NULL) { |
| return -1; |
| } |
| |
| if (!PyDict_Check(options)) { |
| PyErr_Format(PyExc_TypeError, |
| "ZstdCompressor() argument 'options' must be dict, not %T", |
| options); |
| return -1; |
| } |
| |
| Py_ssize_t pos = 0; |
| PyObject *key, *value; |
| while (PyDict_Next(options, &pos, &key, &value)) { |
| /* Check key type */ |
| if (Py_TYPE(key) == mod_state->DParameter_type) { |
| PyErr_SetString(PyExc_TypeError, |
| "compression options dictionary key must not be a " |
| "DecompressionParameter attribute"); |
| return -1; |
| } |
| |
| Py_INCREF(key); |
| Py_INCREF(value); |
| int key_v = PyLong_AsInt(key); |
| Py_DECREF(key); |
| if (key_v == -1 && PyErr_Occurred()) { |
| Py_DECREF(value); |
| return -1; |
| } |
| |
| int value_v = PyLong_AsInt(value); |
| Py_DECREF(value); |
| if (value_v == -1 && PyErr_Occurred()) { |
| return -1; |
| } |
| |
| if (key_v == ZSTD_c_compressionLevel) { |
| if (_zstd_set_c_level(self, value_v) < 0) { |
| return -1; |
| } |
| continue; |
| } |
| if (key_v == ZSTD_c_nbWorkers) { |
| /* From the zstd library docs: |
| 1. When nbWorkers >= 1, triggers asynchronous mode when |
| used with ZSTD_compressStream2(). |
| 2, Default value is `0`, aka "single-threaded mode" : no |
| worker is spawned, compression is performed inside |
| caller's thread, all invocations are blocking. */ |
| if (value_v != 0) { |
| self->use_multithread = 1; |
| } |
| } |
| |
| /* Set parameter to compression context */ |
| size_t zstd_ret = ZSTD_CCtx_setParameter(self->cctx, key_v, value_v); |
| |
| /* Check error */ |
| if (ZSTD_isError(zstd_ret)) { |
| set_parameter_error(1, key_v, value_v); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| static void |
| capsule_free_cdict(PyObject *capsule) |
| { |
| ZSTD_CDict *cdict = PyCapsule_GetPointer(capsule, NULL); |
| ZSTD_freeCDict(cdict); |
| } |
| |
| ZSTD_CDict * |
| _get_CDict(ZstdDict *self, int compressionLevel) |
| { |
| assert(PyMutex_IsLocked(&self->lock)); |
| PyObject *level = NULL; |
| PyObject *capsule = NULL; |
| ZSTD_CDict *cdict; |
| int ret; |
| |
| |
| /* int level object */ |
| level = PyLong_FromLong(compressionLevel); |
| if (level == NULL) { |
| goto error; |
| } |
| |
| /* Get PyCapsule object from self->c_dicts */ |
| ret = PyDict_GetItemRef(self->c_dicts, level, &capsule); |
| if (ret < 0) { |
| goto error; |
| } |
| if (capsule == NULL) { |
| /* Create ZSTD_CDict instance */ |
| Py_BEGIN_ALLOW_THREADS |
| cdict = ZSTD_createCDict(self->dict_buffer, self->dict_len, |
| compressionLevel); |
| Py_END_ALLOW_THREADS |
| |
| if (cdict == NULL) { |
| _zstd_state* mod_state = PyType_GetModuleState(Py_TYPE(self)); |
| if (mod_state != NULL) { |
| PyErr_SetString(mod_state->ZstdError, |
| "Failed to create a ZSTD_CDict instance from " |
| "Zstandard dictionary content."); |
| } |
| goto error; |
| } |
| |
| /* Put ZSTD_CDict instance into PyCapsule object */ |
| capsule = PyCapsule_New(cdict, NULL, capsule_free_cdict); |
| if (capsule == NULL) { |
| ZSTD_freeCDict(cdict); |
| goto error; |
| } |
| |
| /* Add PyCapsule object to self->c_dicts */ |
| ret = PyDict_SetItem(self->c_dicts, level, capsule); |
| if (ret < 0) { |
| goto error; |
| } |
| } |
| else { |
| /* ZSTD_CDict instance already exists */ |
| cdict = PyCapsule_GetPointer(capsule, NULL); |
| } |
| goto success; |
| |
| error: |
| cdict = NULL; |
| success: |
| Py_XDECREF(level); |
| Py_XDECREF(capsule); |
| return cdict; |
| } |
| |
| static int |
| _zstd_load_impl(ZstdCompressor *self, ZstdDict *zd, |
| _zstd_state *mod_state, int type) |
| { |
| size_t zstd_ret; |
| if (type == DICT_TYPE_DIGESTED) { |
| /* Get ZSTD_CDict */ |
| ZSTD_CDict *c_dict = _get_CDict(zd, self->compression_level); |
| if (c_dict == NULL) { |
| return -1; |
| } |
| /* Reference a prepared dictionary. |
| It overrides some compression context's parameters. */ |
| zstd_ret = ZSTD_CCtx_refCDict(self->cctx, c_dict); |
| } |
| else if (type == DICT_TYPE_UNDIGESTED) { |
| /* Load a dictionary. |
| It doesn't override compression context's parameters. */ |
| zstd_ret = ZSTD_CCtx_loadDictionary(self->cctx, zd->dict_buffer, |
| zd->dict_len); |
| } |
| else if (type == DICT_TYPE_PREFIX) { |
| /* Load a prefix */ |
| zstd_ret = ZSTD_CCtx_refPrefix(self->cctx, zd->dict_buffer, |
| zd->dict_len); |
| } |
| else { |
| Py_UNREACHABLE(); |
| } |
| |
| /* Check error */ |
| if (ZSTD_isError(zstd_ret)) { |
| set_zstd_error(mod_state, ERR_LOAD_C_DICT, zstd_ret); |
| return -1; |
| } |
| return 0; |
| } |
| |
| static int |
| _zstd_load_c_dict(ZstdCompressor *self, PyObject *dict) |
| { |
| _zstd_state* mod_state = PyType_GetModuleState(Py_TYPE(self)); |
| /* When compressing, use undigested dictionary by default. */ |
| int type = DICT_TYPE_UNDIGESTED; |
| ZstdDict *zd = _Py_parse_zstd_dict(mod_state, dict, &type); |
| if (zd == NULL) { |
| return -1; |
| } |
| int ret; |
| PyMutex_Lock(&zd->lock); |
| ret = _zstd_load_impl(self, zd, mod_state, type); |
| PyMutex_Unlock(&zd->lock); |
| return ret; |
| } |
| |
| /*[clinic input] |
| @classmethod |
| _zstd.ZstdCompressor.__new__ as _zstd_ZstdCompressor_new |
| level: object = None |
| The compression level to use. Defaults to COMPRESSION_LEVEL_DEFAULT. |
| options: object = None |
| A dict object that contains advanced compression parameters. |
| zstd_dict: object = None |
| A ZstdDict object, a pre-trained Zstandard dictionary. |
| |
| Create a compressor object for compressing data incrementally. |
| |
| Thread-safe at method level. For one-shot compression, use the compress() |
| function instead. |
| [clinic start generated code]*/ |
| |
| static PyObject * |
| _zstd_ZstdCompressor_new_impl(PyTypeObject *type, PyObject *level, |
| PyObject *options, PyObject *zstd_dict) |
| /*[clinic end generated code: output=cdef61eafecac3d7 input=92de0211ae20ffdc]*/ |
| { |
| ZstdCompressor* self = PyObject_GC_New(ZstdCompressor, type); |
| if (self == NULL) { |
| goto error; |
| } |
| |
| self->use_multithread = 0; |
| self->dict = NULL; |
| self->lock = (PyMutex){0}; |
| |
| /* Compression context */ |
| self->cctx = ZSTD_createCCtx(); |
| if (self->cctx == NULL) { |
| _zstd_state* mod_state = PyType_GetModuleState(Py_TYPE(self)); |
| if (mod_state != NULL) { |
| PyErr_SetString(mod_state->ZstdError, |
| "Unable to create ZSTD_CCtx instance."); |
| } |
| goto error; |
| } |
| |
| /* Last mode */ |
| self->last_mode = ZSTD_e_end; |
| |
| if (level != Py_None && options != Py_None) { |
| PyErr_SetString(PyExc_TypeError, |
| "Only one of level or options should be used."); |
| goto error; |
| } |
| |
| /* Set compression level */ |
| if (level != Py_None) { |
| if (!PyLong_Check(level)) { |
| PyErr_SetString(PyExc_TypeError, |
| "invalid type for level, expected int"); |
| goto error; |
| } |
| int level_v = PyLong_AsInt(level); |
| if (level_v == -1 && PyErr_Occurred()) { |
| if (PyErr_ExceptionMatches(PyExc_OverflowError)) { |
| PyErr_Format(PyExc_ValueError, |
| "illegal compression level; the valid range is [%d, %d]", |
| ZSTD_minCLevel(), ZSTD_maxCLevel()); |
| } |
| goto error; |
| } |
| if (_zstd_set_c_level(self, level_v) < 0) { |
| goto error; |
| } |
| } |
| |
| /* Set options dictionary */ |
| if (options != Py_None) { |
| if (_zstd_set_c_parameters(self, options) < 0) { |
| goto error; |
| } |
| } |
| |
| /* Load Zstandard dictionary to compression context */ |
| if (zstd_dict != Py_None) { |
| if (_zstd_load_c_dict(self, zstd_dict) < 0) { |
| goto error; |
| } |
| Py_INCREF(zstd_dict); |
| self->dict = zstd_dict; |
| } |
| |
| // We can only start GC tracking once self->dict is set. |
| PyObject_GC_Track(self); |
| |
| return (PyObject*)self; |
| |
| error: |
| Py_XDECREF(self); |
| return NULL; |
| } |
| |
| static void |
| ZstdCompressor_dealloc(PyObject *ob) |
| { |
| ZstdCompressor *self = ZstdCompressor_CAST(ob); |
| |
| PyObject_GC_UnTrack(self); |
| |
| /* Free compression context */ |
| if (self->cctx) { |
| ZSTD_freeCCtx(self->cctx); |
| } |
| |
| assert(!PyMutex_IsLocked(&self->lock)); |
| |
| /* Py_XDECREF the dict after free the compression context */ |
| Py_CLEAR(self->dict); |
| |
| PyTypeObject *tp = Py_TYPE(self); |
| PyObject_GC_Del(ob); |
| Py_DECREF(tp); |
| } |
| |
| static PyObject * |
| compress_lock_held(ZstdCompressor *self, Py_buffer *data, |
| ZSTD_EndDirective end_directive) |
| { |
| assert(PyMutex_IsLocked(&self->lock)); |
| ZSTD_inBuffer in; |
| ZSTD_outBuffer out; |
| _BlocksOutputBuffer buffer = {.writer = NULL}; |
| size_t zstd_ret; |
| PyObject *ret; |
| |
| /* Prepare input & output buffers */ |
| if (data != NULL) { |
| in.src = data->buf; |
| in.size = data->len; |
| in.pos = 0; |
| } |
| else { |
| in.src = ∈ |
| in.size = 0; |
| in.pos = 0; |
| } |
| |
| /* Calculate output buffer's size */ |
| size_t output_buffer_size = ZSTD_compressBound(in.size); |
| if (output_buffer_size > (size_t) PY_SSIZE_T_MAX) { |
| PyErr_NoMemory(); |
| goto error; |
| } |
| |
| if (_OutputBuffer_InitWithSize(&buffer, &out, -1, |
| (Py_ssize_t) output_buffer_size) < 0) { |
| goto error; |
| } |
| |
| |
| /* Zstandard stream compress */ |
| while (1) { |
| Py_BEGIN_ALLOW_THREADS |
| zstd_ret = ZSTD_compressStream2(self->cctx, &out, &in, end_directive); |
| Py_END_ALLOW_THREADS |
| |
| /* Check error */ |
| if (ZSTD_isError(zstd_ret)) { |
| _zstd_state* mod_state = PyType_GetModuleState(Py_TYPE(self)); |
| set_zstd_error(mod_state, ERR_COMPRESS, zstd_ret); |
| goto error; |
| } |
| |
| /* Finished */ |
| if (zstd_ret == 0) { |
| break; |
| } |
| |
| /* Output buffer should be exhausted, grow the buffer. */ |
| assert(out.pos == out.size); |
| if (out.pos == out.size) { |
| if (_OutputBuffer_Grow(&buffer, &out) < 0) { |
| goto error; |
| } |
| } |
| } |
| |
| /* Return a bytes object */ |
| ret = _OutputBuffer_Finish(&buffer, &out); |
| if (ret != NULL) { |
| return ret; |
| } |
| |
| error: |
| _OutputBuffer_OnError(&buffer); |
| return NULL; |
| } |
| |
| #ifndef NDEBUG |
| static inline int |
| mt_continue_should_break(ZSTD_inBuffer *in, ZSTD_outBuffer *out) |
| { |
| return in->size == in->pos && out->size != out->pos; |
| } |
| #endif |
| |
| static PyObject * |
| compress_mt_continue_lock_held(ZstdCompressor *self, Py_buffer *data) |
| { |
| assert(PyMutex_IsLocked(&self->lock)); |
| ZSTD_inBuffer in; |
| ZSTD_outBuffer out; |
| _BlocksOutputBuffer buffer = {.writer = NULL}; |
| size_t zstd_ret; |
| PyObject *ret; |
| |
| /* Prepare input & output buffers */ |
| in.src = data->buf; |
| in.size = data->len; |
| in.pos = 0; |
| |
| if (_OutputBuffer_InitAndGrow(&buffer, &out, -1) < 0) { |
| goto error; |
| } |
| |
| /* Zstandard stream compress */ |
| while (1) { |
| Py_BEGIN_ALLOW_THREADS |
| do { |
| zstd_ret = ZSTD_compressStream2(self->cctx, &out, &in, |
| ZSTD_e_continue); |
| } while (out.pos != out.size |
| && in.pos != in.size |
| && !ZSTD_isError(zstd_ret)); |
| Py_END_ALLOW_THREADS |
| |
| /* Check error */ |
| if (ZSTD_isError(zstd_ret)) { |
| _zstd_state* mod_state = PyType_GetModuleState(Py_TYPE(self)); |
| set_zstd_error(mod_state, ERR_COMPRESS, zstd_ret); |
| goto error; |
| } |
| |
| /* Like compress_lock_held(), output as much as possible. */ |
| if (out.pos == out.size) { |
| if (_OutputBuffer_Grow(&buffer, &out) < 0) { |
| goto error; |
| } |
| } |
| else if (in.pos == in.size) { |
| /* Finished */ |
| assert(mt_continue_should_break(&in, &out)); |
| break; |
| } |
| } |
| |
| /* Return a bytes object */ |
| ret = _OutputBuffer_Finish(&buffer, &out); |
| if (ret != NULL) { |
| return ret; |
| } |
| |
| error: |
| _OutputBuffer_OnError(&buffer); |
| return NULL; |
| } |
| |
| /*[clinic input] |
| @permit_long_docstring_body |
| _zstd.ZstdCompressor.compress |
| |
| data: Py_buffer |
| mode: int(c_default="ZSTD_e_continue") = ZstdCompressor.CONTINUE |
| Can be these 3 values ZstdCompressor.CONTINUE, |
| ZstdCompressor.FLUSH_BLOCK, ZstdCompressor.FLUSH_FRAME |
| |
| Provide data to the compressor object. |
| |
| Return a chunk of compressed data if possible, or b'' otherwise. When you have |
| finished providing data to the compressor, call the flush() method to finish |
| the compression process. |
| [clinic start generated code]*/ |
| |
| static PyObject * |
| _zstd_ZstdCompressor_compress_impl(ZstdCompressor *self, Py_buffer *data, |
| int mode) |
| /*[clinic end generated code: output=ed7982d1cf7b4f98 input=6018ed6cc729cea6]*/ |
| { |
| PyObject *ret; |
| |
| /* Check mode value */ |
| if (mode != ZSTD_e_continue && |
| mode != ZSTD_e_flush && |
| mode != ZSTD_e_end) |
| { |
| PyErr_SetString(PyExc_ValueError, |
| "mode argument wrong value, it should be one of " |
| "ZstdCompressor.CONTINUE, ZstdCompressor.FLUSH_BLOCK, " |
| "ZstdCompressor.FLUSH_FRAME."); |
| return NULL; |
| } |
| |
| /* Thread-safe code */ |
| PyMutex_Lock(&self->lock); |
| |
| /* Compress */ |
| if (self->use_multithread && mode == ZSTD_e_continue) { |
| ret = compress_mt_continue_lock_held(self, data); |
| } |
| else { |
| ret = compress_lock_held(self, data, mode); |
| } |
| |
| if (ret) { |
| self->last_mode = mode; |
| } |
| else { |
| self->last_mode = ZSTD_e_end; |
| |
| /* Resetting cctx's session never fail */ |
| ZSTD_CCtx_reset(self->cctx, ZSTD_reset_session_only); |
| } |
| PyMutex_Unlock(&self->lock); |
| |
| return ret; |
| } |
| |
| /*[clinic input] |
| @permit_long_docstring_body |
| _zstd.ZstdCompressor.flush |
| |
| mode: int(c_default="ZSTD_e_end") = ZstdCompressor.FLUSH_FRAME |
| Can be these 2 values ZstdCompressor.FLUSH_FRAME, |
| ZstdCompressor.FLUSH_BLOCK |
| |
| Finish the compression process. |
| |
| Flush any remaining data left in internal buffers. Since Zstandard data |
| consists of one or more independent frames, the compressor object can still |
| be used after this method is called. |
| [clinic start generated code]*/ |
| |
| static PyObject * |
| _zstd_ZstdCompressor_flush_impl(ZstdCompressor *self, int mode) |
| /*[clinic end generated code: output=b7cf2c8d64dcf2e3 input=a9871ec742d79003]*/ |
| { |
| PyObject *ret; |
| |
| /* Check mode value */ |
| if (mode != ZSTD_e_end && mode != ZSTD_e_flush) { |
| PyErr_SetString(PyExc_ValueError, |
| "mode argument wrong value, it should be " |
| "ZstdCompressor.FLUSH_FRAME or " |
| "ZstdCompressor.FLUSH_BLOCK."); |
| return NULL; |
| } |
| |
| /* Thread-safe code */ |
| PyMutex_Lock(&self->lock); |
| |
| ret = compress_lock_held(self, NULL, mode); |
| |
| if (ret) { |
| self->last_mode = mode; |
| } |
| else { |
| self->last_mode = ZSTD_e_end; |
| |
| /* Resetting cctx's session never fail */ |
| ZSTD_CCtx_reset(self->cctx, ZSTD_reset_session_only); |
| } |
| PyMutex_Unlock(&self->lock); |
| |
| return ret; |
| } |
| |
| |
| /*[clinic input] |
| @permit_long_docstring_body |
| _zstd.ZstdCompressor.set_pledged_input_size |
| |
| size: zstd_contentsize |
| The size of the uncompressed data to be provided to the compressor. |
| / |
| |
| Set the uncompressed content size to be written into the frame header. |
| |
| This method can be used to ensure the header of the frame about to be written |
| includes the size of the data, unless the CompressionParameter.content_size_flag |
| is set to False. If last_mode != FLUSH_FRAME, then a RuntimeError is raised. |
| |
| It is important to ensure that the pledged data size matches the actual data |
| size. If they do not match the compressed output data may be corrupted and the |
| final chunk written may be lost. |
| [clinic start generated code]*/ |
| |
| static PyObject * |
| _zstd_ZstdCompressor_set_pledged_input_size_impl(ZstdCompressor *self, |
| unsigned long long size) |
| /*[clinic end generated code: output=3a09e55cc0e3b4f9 input=b4c87bcbd5ce6111]*/ |
| { |
| // Error occurred while converting argument, should be unreachable |
| assert(size != ZSTD_CONTENTSIZE_ERROR); |
| |
| /* Thread-safe code */ |
| PyMutex_Lock(&self->lock); |
| |
| /* Check the current mode */ |
| if (self->last_mode != ZSTD_e_end) { |
| PyErr_SetString(PyExc_ValueError, |
| "set_pledged_input_size() method must be called " |
| "when last_mode == FLUSH_FRAME"); |
| PyMutex_Unlock(&self->lock); |
| return NULL; |
| } |
| |
| /* Set pledged content size */ |
| size_t zstd_ret = ZSTD_CCtx_setPledgedSrcSize(self->cctx, size); |
| PyMutex_Unlock(&self->lock); |
| if (ZSTD_isError(zstd_ret)) { |
| _zstd_state* mod_state = PyType_GetModuleState(Py_TYPE(self)); |
| set_zstd_error(mod_state, ERR_SET_PLEDGED_INPUT_SIZE, zstd_ret); |
| return NULL; |
| } |
| |
| Py_RETURN_NONE; |
| } |
| |
| static PyMethodDef ZstdCompressor_methods[] = { |
| _ZSTD_ZSTDCOMPRESSOR_COMPRESS_METHODDEF |
| _ZSTD_ZSTDCOMPRESSOR_FLUSH_METHODDEF |
| _ZSTD_ZSTDCOMPRESSOR_SET_PLEDGED_INPUT_SIZE_METHODDEF |
| {NULL, NULL} |
| }; |
| |
| PyDoc_STRVAR(ZstdCompressor_last_mode_doc, |
| "The last mode used to this compressor object, its value can be .CONTINUE,\n" |
| ".FLUSH_BLOCK, .FLUSH_FRAME. Initialized to .FLUSH_FRAME.\n\n" |
| "It can be used to get the current state of a compressor, such as, data\n" |
| "flushed, or a frame ended."); |
| |
| static PyMemberDef ZstdCompressor_members[] = { |
| {"last_mode", Py_T_INT, offsetof(ZstdCompressor, last_mode), |
| Py_READONLY, ZstdCompressor_last_mode_doc}, |
| {NULL} |
| }; |
| |
| static int |
| ZstdCompressor_traverse(PyObject *ob, visitproc visit, void *arg) |
| { |
| ZstdCompressor *self = ZstdCompressor_CAST(ob); |
| Py_VISIT(self->dict); |
| return 0; |
| } |
| |
| static int |
| ZstdCompressor_clear(PyObject *ob) |
| { |
| ZstdCompressor *self = ZstdCompressor_CAST(ob); |
| Py_CLEAR(self->dict); |
| return 0; |
| } |
| |
| static PyType_Slot zstdcompressor_slots[] = { |
| {Py_tp_new, _zstd_ZstdCompressor_new}, |
| {Py_tp_dealloc, ZstdCompressor_dealloc}, |
| {Py_tp_methods, ZstdCompressor_methods}, |
| {Py_tp_members, ZstdCompressor_members}, |
| {Py_tp_doc, (void *)_zstd_ZstdCompressor_new__doc__}, |
| {Py_tp_traverse, ZstdCompressor_traverse}, |
| {Py_tp_clear, ZstdCompressor_clear}, |
| {0, 0} |
| }; |
| |
| PyType_Spec zstd_compressor_type_spec = { |
| .name = "compression.zstd.ZstdCompressor", |
| .basicsize = sizeof(ZstdCompressor), |
| // Py_TPFLAGS_IMMUTABLETYPE is not used here as several |
| // associated constants need to be added to the type. |
| // PyType_Freeze is called later to set the flag. |
| .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, |
| .slots = zstdcompressor_slots, |
| }; |