mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 21:51:50 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			713 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			713 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Low level interface to the Zstandard algorthm & 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 "zstddict.h"
 | |
| 
 | |
| #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;
 | |
| } ZstdCompressor;
 | |
| 
 | |
| #define ZstdCompressor_CAST(op) ((ZstdCompressor *)op)
 | |
| 
 | |
| #include "clinic/compressor.c.h"
 | |
| 
 | |
| static int
 | |
| _zstd_set_c_parameters(ZstdCompressor *self, PyObject *level_or_options,
 | |
|                        const char *arg_name, const char* arg_type)
 | |
| {
 | |
|     size_t zstd_ret;
 | |
|     _zstd_state* const mod_state = PyType_GetModuleState(Py_TYPE(self));
 | |
|     if (mod_state == NULL) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* Integer compression level */
 | |
|     if (PyLong_Check(level_or_options)) {
 | |
|         int level = PyLong_AsInt(level_or_options);
 | |
|         if (level == -1 && PyErr_Occurred()) {
 | |
|             PyErr_Format(PyExc_ValueError,
 | |
|                             "Compression level should be an int value between %d and %d.",
 | |
|                             ZSTD_minCLevel(), ZSTD_maxCLevel());
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         /* Save for generating ZSTD_CDICT */
 | |
|         self->compression_level = level;
 | |
| 
 | |
|         /* Set compressionLevel to compression context */
 | |
|         zstd_ret = ZSTD_CCtx_setParameter(self->cctx,
 | |
|                                           ZSTD_c_compressionLevel,
 | |
|                                           level);
 | |
| 
 | |
|         /* Check error */
 | |
|         if (ZSTD_isError(zstd_ret)) {
 | |
|             set_zstd_error(mod_state, ERR_SET_C_LEVEL, zstd_ret);
 | |
|             return -1;
 | |
|         }
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     /* Options dict */
 | |
|     if (PyDict_Check(level_or_options)) {
 | |
|         PyObject *key, *value;
 | |
|         Py_ssize_t pos = 0;
 | |
| 
 | |
|         while (PyDict_Next(level_or_options, &pos, &key, &value)) {
 | |
|             /* Check key type */
 | |
|             if (Py_TYPE(key) == mod_state->DParameter_type) {
 | |
|                 PyErr_SetString(PyExc_TypeError,
 | |
|                                 "Key of compression option dict should "
 | |
|                                 "NOT be DecompressionParameter.");
 | |
|                 return -1;
 | |
|             }
 | |
| 
 | |
|             int key_v = PyLong_AsInt(key);
 | |
|             if (key_v == -1 && PyErr_Occurred()) {
 | |
|                 PyErr_SetString(PyExc_ValueError,
 | |
|                                 "Key of options dict should be a CompressionParameter attribute.");
 | |
|                 return -1;
 | |
|             }
 | |
| 
 | |
|             // TODO(emmatyping): check bounds when there is a value error here for better
 | |
|             // error message?
 | |
|             int value_v = PyLong_AsInt(value);
 | |
|             if (value_v == -1 && PyErr_Occurred()) {
 | |
|                 PyErr_SetString(PyExc_ValueError,
 | |
|                                 "Value of option dict should be an int.");
 | |
|                 return -1;
 | |
|             }
 | |
| 
 | |
|             if (key_v == ZSTD_c_compressionLevel) {
 | |
|                 /* Save for generating ZSTD_CDICT */
 | |
|                 self->compression_level = value_v;
 | |
|             }
 | |
|             else 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 */
 | |
|             zstd_ret = ZSTD_CCtx_setParameter(self->cctx, key_v, value_v);
 | |
|             if (ZSTD_isError(zstd_ret)) {
 | |
|                 set_parameter_error(mod_state, 1, key_v, value_v);
 | |
|                 return -1;
 | |
|             }
 | |
|         }
 | |
|         return 0;
 | |
|     }
 | |
|     PyErr_Format(PyExc_TypeError, "Invalid type for %s. Expected %s", arg_name, arg_type);
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| 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)
 | |
| {
 | |
|     PyObject *level = NULL;
 | |
|     PyObject *capsule;
 | |
|     ZSTD_CDict *cdict;
 | |
| 
 | |
|     // TODO(emmatyping): refactor critical section code into a lock_held function
 | |
|     Py_BEGIN_CRITICAL_SECTION(self);
 | |
| 
 | |
|     /* int level object */
 | |
|     level = PyLong_FromLong(compressionLevel);
 | |
|     if (level == NULL) {
 | |
|         goto error;
 | |
|     }
 | |
| 
 | |
|     /* Get PyCapsule object from self->c_dicts */
 | |
|     capsule = PyDict_GetItemWithError(self->c_dicts, level);
 | |
|     if (capsule == NULL) {
 | |
|         if (PyErr_Occurred()) {
 | |
|             goto error;
 | |
|         }
 | |
| 
 | |
|         /* Create ZSTD_CDict instance */
 | |
|         char *dict_buffer = PyBytes_AS_STRING(self->dict_content);
 | |
|         Py_ssize_t dict_len = Py_SIZE(self->dict_content);
 | |
|         Py_BEGIN_ALLOW_THREADS
 | |
|         cdict = ZSTD_createCDict(dict_buffer,
 | |
|                                  dict_len,
 | |
|                                  compressionLevel);
 | |
|         Py_END_ALLOW_THREADS
 | |
| 
 | |
|         if (cdict == NULL) {
 | |
|             _zstd_state* const 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 */
 | |
|         if (PyDict_SetItem(self->c_dicts, level, capsule) < 0) {
 | |
|             Py_DECREF(capsule);
 | |
|             goto error;
 | |
|         }
 | |
|         Py_DECREF(capsule);
 | |
|     }
 | |
|     else {
 | |
|         /* ZSTD_CDict instance already exists */
 | |
|         cdict = PyCapsule_GetPointer(capsule, NULL);
 | |
|     }
 | |
|     goto success;
 | |
| 
 | |
| error:
 | |
|     cdict = NULL;
 | |
| success:
 | |
|     Py_XDECREF(level);
 | |
|     Py_END_CRITICAL_SECTION();
 | |
|     return cdict;
 | |
| }
 | |
| 
 | |
| static int
 | |
| _zstd_load_c_dict(ZstdCompressor *self, PyObject *dict)
 | |
| {
 | |
| 
 | |
|     size_t zstd_ret;
 | |
|     _zstd_state* const mod_state = PyType_GetModuleState(Py_TYPE(self));
 | |
|     if (mod_state == NULL) {
 | |
|         return -1;
 | |
|     }
 | |
|     ZstdDict *zd;
 | |
|     int type, ret;
 | |
| 
 | |
|     /* Check ZstdDict */
 | |
|     ret = PyObject_IsInstance(dict, (PyObject*)mod_state->ZstdDict_type);
 | |
|     if (ret < 0) {
 | |
|         return -1;
 | |
|     }
 | |
|     else if (ret > 0) {
 | |
|         /* When compressing, use undigested dictionary by default. */
 | |
|         zd = (ZstdDict*)dict;
 | |
|         type = DICT_TYPE_UNDIGESTED;
 | |
|         goto load;
 | |
|     }
 | |
| 
 | |
|     /* Check (ZstdDict, type) */
 | |
|     if (PyTuple_CheckExact(dict) && PyTuple_GET_SIZE(dict) == 2) {
 | |
|         /* Check ZstdDict */
 | |
|         ret = PyObject_IsInstance(PyTuple_GET_ITEM(dict, 0),
 | |
|                                   (PyObject*)mod_state->ZstdDict_type);
 | |
|         if (ret < 0) {
 | |
|             return -1;
 | |
|         }
 | |
|         else if (ret > 0) {
 | |
|             /* type == -1 may indicate an error. */
 | |
|             type = PyLong_AsInt(PyTuple_GET_ITEM(dict, 1));
 | |
|             if (type == DICT_TYPE_DIGESTED ||
 | |
|                 type == DICT_TYPE_UNDIGESTED ||
 | |
|                 type == DICT_TYPE_PREFIX)
 | |
|             {
 | |
|                 assert(type >= 0);
 | |
|                 zd = (ZstdDict*)PyTuple_GET_ITEM(dict, 0);
 | |
|                 goto load;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Wrong type */
 | |
|     PyErr_SetString(PyExc_TypeError,
 | |
|                     "zstd_dict argument should be ZstdDict object.");
 | |
|     return -1;
 | |
| 
 | |
| load:
 | |
|     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. */
 | |
|         Py_BEGIN_CRITICAL_SECTION(self);
 | |
|         zstd_ret = ZSTD_CCtx_refCDict(self->cctx, c_dict);
 | |
|         Py_END_CRITICAL_SECTION();
 | |
|     }
 | |
|     else if (type == DICT_TYPE_UNDIGESTED) {
 | |
|         /* Load a dictionary.
 | |
|            It doesn't override compression context's parameters. */
 | |
|         Py_BEGIN_CRITICAL_SECTION2(self, zd);
 | |
|         zstd_ret = ZSTD_CCtx_loadDictionary(
 | |
|                             self->cctx,
 | |
|                             PyBytes_AS_STRING(zd->dict_content),
 | |
|                             Py_SIZE(zd->dict_content));
 | |
|         Py_END_CRITICAL_SECTION2();
 | |
|     }
 | |
|     else if (type == DICT_TYPE_PREFIX) {
 | |
|         /* Load a prefix */
 | |
|         Py_BEGIN_CRITICAL_SECTION2(self, zd);
 | |
|         zstd_ret = ZSTD_CCtx_refPrefix(
 | |
|                             self->cctx,
 | |
|                             PyBytes_AS_STRING(zd->dict_content),
 | |
|                             Py_SIZE(zd->dict_content));
 | |
|         Py_END_CRITICAL_SECTION2();
 | |
|     }
 | |
|     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;
 | |
| }
 | |
| 
 | |
| /*[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;
 | |
| 
 | |
|     /* Compression context */
 | |
|     self->cctx = ZSTD_createCCtx();
 | |
|     if (self->cctx == NULL) {
 | |
|         _zstd_state* const 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_RuntimeError, "Only one of level or options should be used.");
 | |
|         goto error;
 | |
|     }
 | |
| 
 | |
|     /* Set compressLevel/options to compression context */
 | |
|     if (level != Py_None) {
 | |
|         if (_zstd_set_c_parameters(self, level, "level", "int") < 0) {
 | |
|             goto error;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (options != Py_None) {
 | |
|         if (_zstd_set_c_parameters(self, options, "options", "dict") < 0) {
 | |
|             goto error;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Load Zstandard dictionary to compression context */
 | |
|     self->dict = NULL;
 | |
|     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:
 | |
|     if (self != NULL) {
 | |
|         PyObject_GC_Del(self);
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| static void
 | |
| ZstdCompressor_dealloc(PyObject *ob)
 | |
| {
 | |
|     ZstdCompressor *self = ZstdCompressor_CAST(ob);
 | |
| 
 | |
|     PyObject_GC_UnTrack(self);
 | |
| 
 | |
|     /* Free compression context */
 | |
|     ZSTD_freeCCtx(self->cctx);
 | |
| 
 | |
|     /* 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_impl(ZstdCompressor *self, Py_buffer *data,
 | |
|               ZSTD_EndDirective end_directive)
 | |
| {
 | |
|     ZSTD_inBuffer in;
 | |
|     ZSTD_outBuffer out;
 | |
|     _BlocksOutputBuffer buffer = {.list = 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* const mod_state = PyType_GetModuleState(Py_TYPE(self));
 | |
|             if (mod_state != NULL) {
 | |
|                 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;
 | |
| }
 | |
| 
 | |
| static inline int
 | |
| mt_continue_should_break(ZSTD_inBuffer *in, ZSTD_outBuffer *out)
 | |
| {
 | |
|     return in->size == in->pos && out->size != out->pos;
 | |
| }
 | |
| 
 | |
| static PyObject *
 | |
| compress_mt_continue_impl(ZstdCompressor *self, Py_buffer *data)
 | |
| {
 | |
|     ZSTD_inBuffer in;
 | |
|     ZSTD_outBuffer out;
 | |
|     _BlocksOutputBuffer buffer = {.list = 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* const mod_state = PyType_GetModuleState(Py_TYPE(self));
 | |
|             if (mod_state != NULL) {
 | |
|                 set_zstd_error(mod_state, ERR_COMPRESS, zstd_ret);
 | |
|             }
 | |
|             goto error;
 | |
|         }
 | |
| 
 | |
|         /* Like compress_impl(), 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]
 | |
| _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=ac2c21d180f579ea]*/
 | |
| {
 | |
|     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 */
 | |
|     Py_BEGIN_CRITICAL_SECTION(self);
 | |
| 
 | |
|     /* Compress */
 | |
|     if (self->use_multithread && mode == ZSTD_e_continue) {
 | |
|         ret = compress_mt_continue_impl(self, data);
 | |
|     }
 | |
|     else {
 | |
|         ret = compress_impl(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);
 | |
|     }
 | |
|     Py_END_CRITICAL_SECTION();
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /*[clinic input]
 | |
| _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=0ab19627f323cdbc]*/
 | |
| {
 | |
|     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 */
 | |
|     Py_BEGIN_CRITICAL_SECTION(self);
 | |
|     ret = compress_impl(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);
 | |
|     }
 | |
|     Py_END_CRITICAL_SECTION();
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static PyMethodDef ZstdCompressor_methods[] = {
 | |
|     _ZSTD_ZSTDCOMPRESSOR_COMPRESS_METHODDEF
 | |
|     _ZSTD_ZSTDCOMPRESSOR_FLUSH_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 flushed,\n"
 | |
| "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,
 | |
| };
 | 
