| /* Low level interface to the Zstandard algorithm & the zstd library. */ | |
| #ifndef Py_BUILD_CORE_BUILTIN | |
| # define Py_BUILD_CORE_MODULE 1 | |
| #endif | |
| #include "Python.h" | |
| #include "_zstdmodule.h" | |
| #include <zstd.h> // ZSTD_*() | |
| #include <zdict.h> // ZDICT_*() | |
| /*[clinic input] | |
| module _zstd | |
| [clinic start generated code]*/ | |
| /*[clinic end generated code: output=da39a3ee5e6b4b0d input=4b5f5587aac15c14]*/ | |
| #include "clinic/_zstdmodule.c.h" | |
| ZstdDict * | |
| _Py_parse_zstd_dict(const _zstd_state *state, PyObject *dict, int *ptype) | |
| { | |
| if (state == NULL) { | |
| return NULL; | |
| } | |
| /* Check ZstdDict */ | |
| if (PyObject_TypeCheck(dict, state->ZstdDict_type)) { | |
| return (ZstdDict*)dict; | |
| } | |
| /* Check (ZstdDict, type) */ | |
| if (PyTuple_CheckExact(dict) && PyTuple_GET_SIZE(dict) == 2 | |
| && PyObject_TypeCheck(PyTuple_GET_ITEM(dict, 0), state->ZstdDict_type) | |
| && PyLong_Check(PyTuple_GET_ITEM(dict, 1))) | |
| { | |
| int type = PyLong_AsInt(PyTuple_GET_ITEM(dict, 1)); | |
| if (type == -1 && PyErr_Occurred()) { | |
| return NULL; | |
| } | |
| if (type == DICT_TYPE_DIGESTED | |
| || type == DICT_TYPE_UNDIGESTED | |
| || type == DICT_TYPE_PREFIX) | |
| { | |
| *ptype = type; | |
| return (ZstdDict*)PyTuple_GET_ITEM(dict, 0); | |
| } | |
| } | |
| /* Wrong type */ | |
| PyErr_SetString(PyExc_TypeError, | |
| "zstd_dict argument should be a ZstdDict object."); | |
| return NULL; | |
| } | |
| /* Format error message and set ZstdError. */ | |
| void | |
| set_zstd_error(const _zstd_state *state, error_type type, size_t zstd_ret) | |
| { | |
| const char *msg; | |
| assert(ZSTD_isError(zstd_ret)); | |
| if (state == NULL) { | |
| return; | |
| } | |
| switch (type) { | |
| case ERR_DECOMPRESS: | |
| msg = "Unable to decompress Zstandard data: %s"; | |
| break; | |
| case ERR_COMPRESS: | |
| msg = "Unable to compress Zstandard data: %s"; | |
| break; | |
| case ERR_SET_PLEDGED_INPUT_SIZE: | |
| msg = "Unable to set pledged uncompressed content size: %s"; | |
| break; | |
| case ERR_LOAD_D_DICT: | |
| msg = "Unable to load Zstandard dictionary or prefix for " | |
| "decompression: %s"; | |
| break; | |
| case ERR_LOAD_C_DICT: | |
| msg = "Unable to load Zstandard dictionary or prefix for " | |
| "compression: %s"; | |
| break; | |
| case ERR_GET_C_BOUNDS: | |
| msg = "Unable to get zstd compression parameter bounds: %s"; | |
| break; | |
| case ERR_GET_D_BOUNDS: | |
| msg = "Unable to get zstd decompression parameter bounds: %s"; | |
| break; | |
| case ERR_SET_C_LEVEL: | |
| msg = "Unable to set zstd compression level: %s"; | |
| break; | |
| case ERR_TRAIN_DICT: | |
| msg = "Unable to train the Zstandard dictionary: %s"; | |
| break; | |
| case ERR_FINALIZE_DICT: | |
| msg = "Unable to finalize the Zstandard dictionary: %s"; | |
| break; | |
| default: | |
| Py_UNREACHABLE(); | |
| } | |
| PyErr_Format(state->ZstdError, msg, ZSTD_getErrorName(zstd_ret)); | |
| } | |
| typedef struct { | |
| int parameter; | |
| char parameter_name[32]; | |
| } ParameterInfo; | |
| static const ParameterInfo cp_list[] = { | |
| {ZSTD_c_compressionLevel, "compression_level"}, | |
| {ZSTD_c_windowLog, "window_log"}, | |
| {ZSTD_c_hashLog, "hash_log"}, | |
| {ZSTD_c_chainLog, "chain_log"}, | |
| {ZSTD_c_searchLog, "search_log"}, | |
| {ZSTD_c_minMatch, "min_match"}, | |
| {ZSTD_c_targetLength, "target_length"}, | |
| {ZSTD_c_strategy, "strategy"}, | |
| {ZSTD_c_enableLongDistanceMatching, "enable_long_distance_matching"}, | |
| {ZSTD_c_ldmHashLog, "ldm_hash_log"}, | |
| {ZSTD_c_ldmMinMatch, "ldm_min_match"}, | |
| {ZSTD_c_ldmBucketSizeLog, "ldm_bucket_size_log"}, | |
| {ZSTD_c_ldmHashRateLog, "ldm_hash_rate_log"}, | |
| {ZSTD_c_contentSizeFlag, "content_size_flag"}, | |
| {ZSTD_c_checksumFlag, "checksum_flag"}, | |
| {ZSTD_c_dictIDFlag, "dict_id_flag"}, | |
| {ZSTD_c_nbWorkers, "nb_workers"}, | |
| {ZSTD_c_jobSize, "job_size"}, | |
| {ZSTD_c_overlapLog, "overlap_log"} | |
| }; | |
| static const ParameterInfo dp_list[] = { | |
| {ZSTD_d_windowLogMax, "window_log_max"} | |
| }; | |
| void | |
| set_parameter_error(int is_compress, int key_v, int value_v) | |
| { | |
| ParameterInfo const *list; | |
| int list_size; | |
| char *type; | |
| ZSTD_bounds bounds; | |
| char pos_msg[64]; | |
| if (is_compress) { | |
| list = cp_list; | |
| list_size = Py_ARRAY_LENGTH(cp_list); | |
| type = "compression"; | |
| } | |
| else { | |
| list = dp_list; | |
| list_size = Py_ARRAY_LENGTH(dp_list); | |
| type = "decompression"; | |
| } | |
| /* Find parameter's name */ | |
| char const *name = NULL; | |
| for (int i = 0; i < list_size; i++) { | |
| if (key_v == (list+i)->parameter) { | |
| name = (list+i)->parameter_name; | |
| break; | |
| } | |
| } | |
| /* Unknown parameter */ | |
| if (name == NULL) { | |
| PyOS_snprintf(pos_msg, sizeof(pos_msg), | |
| "unknown parameter (key %d)", key_v); | |
| name = pos_msg; | |
| } | |
| /* Get parameter bounds */ | |
| if (is_compress) { | |
| bounds = ZSTD_cParam_getBounds(key_v); | |
| } | |
| else { | |
| bounds = ZSTD_dParam_getBounds(key_v); | |
| } | |
| if (ZSTD_isError(bounds.error)) { | |
| PyErr_Format(PyExc_ValueError, "invalid %s parameter '%s'", | |
| type, name); | |
| return; | |
| } | |
| /* Error message */ | |
| PyErr_Format(PyExc_ValueError, | |
| "%s parameter '%s' received an illegal value %d; " | |
| "the valid range is [%d, %d]", | |
| type, name, value_v, bounds.lowerBound, bounds.upperBound); | |
| } | |
| static inline _zstd_state* | |
| get_zstd_state(PyObject *module) | |
| { | |
| void *state = PyModule_GetState(module); | |
| assert(state != NULL); | |
| return (_zstd_state *)state; | |
| } | |
| static Py_ssize_t | |
| calculate_samples_stats(PyBytesObject *samples_bytes, PyObject *samples_sizes, | |
| size_t **chunk_sizes) | |
| { | |
| Py_ssize_t chunks_number; | |
| Py_ssize_t sizes_sum; | |
| Py_ssize_t i; | |
| chunks_number = PyTuple_GET_SIZE(samples_sizes); | |
| if ((size_t) chunks_number > UINT32_MAX) { | |
| PyErr_Format(PyExc_ValueError, | |
| "The number of samples should be <= %u.", UINT32_MAX); | |
| return -1; | |
| } | |
| /* Prepare chunk_sizes */ | |
| *chunk_sizes = PyMem_New(size_t, chunks_number); | |
| if (*chunk_sizes == NULL) { | |
| PyErr_NoMemory(); | |
| return -1; | |
| } | |
| sizes_sum = PyBytes_GET_SIZE(samples_bytes); | |
| for (i = 0; i < chunks_number; i++) { | |
| size_t size = PyLong_AsSize_t(PyTuple_GET_ITEM(samples_sizes, i)); | |
| (*chunk_sizes)[i] = size; | |
| if (size == (size_t)-1 && PyErr_Occurred()) { | |
| if (PyErr_ExceptionMatches(PyExc_OverflowError)) { | |
| goto sum_error; | |
| } | |
| return -1; | |
| } | |
| if ((size_t)sizes_sum < size) { | |
| goto sum_error; | |
| } | |
| sizes_sum -= size; | |
| } | |
| if (sizes_sum != 0) { | |
| sum_error: | |
| PyErr_SetString(PyExc_ValueError, | |
| "The samples size tuple doesn't match the " | |
| "concatenation's size."); | |
| return -1; | |
| } | |
| return chunks_number; | |
| } | |
| /*[clinic input] | |
| _zstd.train_dict | |
| samples_bytes: PyBytesObject | |
| Concatenation of samples. | |
| samples_sizes: object(subclass_of='&PyTuple_Type') | |
| Tuple of samples' sizes. | |
| dict_size: Py_ssize_t | |
| The size of the dictionary. | |
| / | |
| Train a Zstandard dictionary on sample data. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| _zstd_train_dict_impl(PyObject *module, PyBytesObject *samples_bytes, | |
| PyObject *samples_sizes, Py_ssize_t dict_size) | |
| /*[clinic end generated code: output=8e87fe43935e8f77 input=d20dedb21c72cb62]*/ | |
| { | |
| PyBytesWriter *dst_dict_bytes = NULL; | |
| size_t *chunk_sizes = NULL; | |
| Py_ssize_t chunks_number; | |
| size_t zstd_ret; | |
| /* Check arguments */ | |
| if (dict_size <= 0) { | |
| PyErr_SetString(PyExc_ValueError, | |
| "dict_size argument should be positive number."); | |
| return NULL; | |
| } | |
| /* Check that the samples are valid and get their sizes */ | |
| chunks_number = calculate_samples_stats(samples_bytes, samples_sizes, | |
| &chunk_sizes); | |
| if (chunks_number < 0) { | |
| goto error; | |
| } | |
| /* Allocate dict buffer */ | |
| dst_dict_bytes = PyBytesWriter_Create(dict_size); | |
| if (dst_dict_bytes == NULL) { | |
| goto error; | |
| } | |
| /* Train the dictionary */ | |
| char *dst_dict_buffer = PyBytesWriter_GetData(dst_dict_bytes); | |
| const char *samples_buffer = PyBytes_AS_STRING(samples_bytes); | |
| Py_BEGIN_ALLOW_THREADS | |
| zstd_ret = ZDICT_trainFromBuffer(dst_dict_buffer, dict_size, | |
| samples_buffer, | |
| chunk_sizes, (uint32_t)chunks_number); | |
| Py_END_ALLOW_THREADS | |
| /* Check Zstandard dict error */ | |
| if (ZDICT_isError(zstd_ret)) { | |
| _zstd_state* mod_state = get_zstd_state(module); | |
| set_zstd_error(mod_state, ERR_TRAIN_DICT, zstd_ret); | |
| goto error; | |
| } | |
| PyMem_Free(chunk_sizes); | |
| /* Resize dict_buffer */ | |
| return PyBytesWriter_FinishWithSize(dst_dict_bytes, zstd_ret); | |
| error: | |
| PyBytesWriter_Discard(dst_dict_bytes); | |
| PyMem_Free(chunk_sizes); | |
| return NULL; | |
| } | |
| /*[clinic input] | |
| _zstd.finalize_dict | |
| custom_dict_bytes: PyBytesObject | |
| Custom dictionary content. | |
| samples_bytes: PyBytesObject | |
| Concatenation of samples. | |
| samples_sizes: object(subclass_of='&PyTuple_Type') | |
| Tuple of samples' sizes. | |
| dict_size: Py_ssize_t | |
| The size of the dictionary. | |
| compression_level: int | |
| Optimize for a specific Zstandard compression level, 0 means default. | |
| / | |
| Finalize a Zstandard dictionary. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| _zstd_finalize_dict_impl(PyObject *module, PyBytesObject *custom_dict_bytes, | |
| PyBytesObject *samples_bytes, | |
| PyObject *samples_sizes, Py_ssize_t dict_size, | |
| int compression_level) | |
| /*[clinic end generated code: output=f91821ba5ae85bda input=3c7e2480aa08fb56]*/ | |
| { | |
| Py_ssize_t chunks_number; | |
| size_t *chunk_sizes = NULL; | |
| PyBytesWriter *dst_dict_bytes = NULL; | |
| PyObject *result = NULL; | |
| size_t zstd_ret; | |
| ZDICT_params_t params; | |
| /* Check arguments */ | |
| if (dict_size <= 0) { | |
| PyErr_SetString(PyExc_ValueError, | |
| "dict_size argument should be positive number."); | |
| return NULL; | |
| } | |
| /* Check that the samples are valid and get their sizes */ | |
| chunks_number = calculate_samples_stats(samples_bytes, samples_sizes, | |
| &chunk_sizes); | |
| if (chunks_number < 0) { | |
| goto error; | |
| } | |
| /* Allocate dict buffer */ | |
| dst_dict_bytes = PyBytesWriter_Create(dict_size); | |
| if (dst_dict_bytes == NULL) { | |
| goto error; | |
| } | |
| /* Parameters */ | |
| /* Optimize for a specific Zstandard compression level, 0 means default. */ | |
| params.compressionLevel = compression_level; | |
| /* Write log to stderr, 0 = none. */ | |
| params.notificationLevel = 0; | |
| /* Force dictID value, 0 means auto mode (32-bits random value). */ | |
| params.dictID = 0; | |
| /* Finalize the dictionary */ | |
| Py_BEGIN_ALLOW_THREADS | |
| zstd_ret = ZDICT_finalizeDictionary( | |
| PyBytesWriter_GetData(dst_dict_bytes), | |
| PyBytesWriter_GetSize(dst_dict_bytes), | |
| PyBytes_AS_STRING(custom_dict_bytes), | |
| Py_SIZE(custom_dict_bytes), | |
| PyBytes_AS_STRING(samples_bytes), chunk_sizes, | |
| (uint32_t)chunks_number, params); | |
| Py_END_ALLOW_THREADS | |
| /* Check Zstandard dict error */ | |
| if (ZDICT_isError(zstd_ret)) { | |
| _zstd_state* mod_state = get_zstd_state(module); | |
| set_zstd_error(mod_state, ERR_FINALIZE_DICT, zstd_ret); | |
| goto error; | |
| } | |
| /* Resize dict_buffer */ | |
| result = PyBytesWriter_FinishWithSize(dst_dict_bytes, zstd_ret); | |
| goto done; | |
| error: | |
| PyBytesWriter_Discard(dst_dict_bytes); | |
| done: | |
| PyMem_Free(chunk_sizes); | |
| return result; | |
| } | |
| /*[clinic input] | |
| _zstd.get_param_bounds | |
| parameter: int | |
| The parameter to get bounds. | |
| is_compress: bool | |
| True for CompressionParameter, False for DecompressionParameter. | |
| Get CompressionParameter/DecompressionParameter bounds. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| _zstd_get_param_bounds_impl(PyObject *module, int parameter, int is_compress) | |
| /*[clinic end generated code: output=4acf5a876f0620ca input=45742ef0a3531b65]*/ | |
| { | |
| ZSTD_bounds bound; | |
| if (is_compress) { | |
| bound = ZSTD_cParam_getBounds(parameter); | |
| if (ZSTD_isError(bound.error)) { | |
| _zstd_state* mod_state = get_zstd_state(module); | |
| set_zstd_error(mod_state, ERR_GET_C_BOUNDS, bound.error); | |
| return NULL; | |
| } | |
| } | |
| else { | |
| bound = ZSTD_dParam_getBounds(parameter); | |
| if (ZSTD_isError(bound.error)) { | |
| _zstd_state* mod_state = get_zstd_state(module); | |
| set_zstd_error(mod_state, ERR_GET_D_BOUNDS, bound.error); | |
| return NULL; | |
| } | |
| } | |
| return Py_BuildValue("ii", bound.lowerBound, bound.upperBound); | |
| } | |
| /*[clinic input] | |
| @permit_long_summary | |
| _zstd.get_frame_size | |
| frame_buffer: Py_buffer | |
| A bytes-like object, it should start from the beginning of a frame, | |
| and contains at least one complete frame. | |
| Get the size of a Zstandard frame, including the header and optional checksum. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| _zstd_get_frame_size_impl(PyObject *module, Py_buffer *frame_buffer) | |
| /*[clinic end generated code: output=a7384c2f8780f442 input=aac83b33045b5f43]*/ | |
| { | |
| size_t frame_size; | |
| frame_size = ZSTD_findFrameCompressedSize(frame_buffer->buf, | |
| frame_buffer->len); | |
| if (ZSTD_isError(frame_size)) { | |
| _zstd_state* mod_state = get_zstd_state(module); | |
| PyErr_Format(mod_state->ZstdError, | |
| "Error when finding the compressed size of a Zstandard frame. " | |
| "Ensure the frame_buffer argument starts from the " | |
| "beginning of a frame, and its length is not less than this " | |
| "complete frame. Zstd error message: %s.", | |
| ZSTD_getErrorName(frame_size)); | |
| return NULL; | |
| } | |
| return PyLong_FromSize_t(frame_size); | |
| } | |
| /*[clinic input] | |
| _zstd.get_frame_info | |
| frame_buffer: Py_buffer | |
| A bytes-like object, containing the header of a Zstandard frame. | |
| Get Zstandard frame infomation from a frame header. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| _zstd_get_frame_info_impl(PyObject *module, Py_buffer *frame_buffer) | |
| /*[clinic end generated code: output=56e033cf48001929 input=94b240583ae22ca5]*/ | |
| { | |
| uint64_t decompressed_size; | |
| uint32_t dict_id; | |
| /* ZSTD_getFrameContentSize */ | |
| decompressed_size = ZSTD_getFrameContentSize(frame_buffer->buf, | |
| frame_buffer->len); | |
| /* #define ZSTD_CONTENTSIZE_UNKNOWN (0ULL - 1) | |
| #define ZSTD_CONTENTSIZE_ERROR (0ULL - 2) */ | |
| if (decompressed_size == ZSTD_CONTENTSIZE_ERROR) { | |
| _zstd_state* mod_state = get_zstd_state(module); | |
| PyErr_SetString(mod_state->ZstdError, | |
| "Error when getting information from the header of " | |
| "a Zstandard frame. Ensure the frame_buffer argument " | |
| "starts from the beginning of a frame, and its length " | |
| "is not less than the frame header (6~18 bytes)."); | |
| return NULL; | |
| } | |
| /* ZSTD_getDictID_fromFrame */ | |
| dict_id = ZSTD_getDictID_fromFrame(frame_buffer->buf, frame_buffer->len); | |
| /* Build tuple */ | |
| if (decompressed_size == ZSTD_CONTENTSIZE_UNKNOWN) { | |
| return Py_BuildValue("OI", Py_None, dict_id); | |
| } | |
| return Py_BuildValue("KI", decompressed_size, dict_id); | |
| } | |
| /*[clinic input] | |
| @permit_long_summary | |
| _zstd.set_parameter_types | |
| c_parameter_type: object(subclass_of='&PyType_Type') | |
| CompressionParameter IntEnum type object | |
| d_parameter_type: object(subclass_of='&PyType_Type') | |
| DecompressionParameter IntEnum type object | |
| Set CompressionParameter and DecompressionParameter types for validity check. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| _zstd_set_parameter_types_impl(PyObject *module, PyObject *c_parameter_type, | |
| PyObject *d_parameter_type) | |
| /*[clinic end generated code: output=f3313b1294f19502 input=0529e918dfe54863]*/ | |
| { | |
| _zstd_state* mod_state = get_zstd_state(module); | |
| Py_INCREF(c_parameter_type); | |
| Py_XSETREF(mod_state->CParameter_type, (PyTypeObject*)c_parameter_type); | |
| Py_INCREF(d_parameter_type); | |
| Py_XSETREF(mod_state->DParameter_type, (PyTypeObject*)d_parameter_type); | |
| Py_RETURN_NONE; | |
| } | |
| static PyMethodDef _zstd_methods[] = { | |
| _ZSTD_TRAIN_DICT_METHODDEF | |
| _ZSTD_FINALIZE_DICT_METHODDEF | |
| _ZSTD_GET_PARAM_BOUNDS_METHODDEF | |
| _ZSTD_GET_FRAME_SIZE_METHODDEF | |
| _ZSTD_GET_FRAME_INFO_METHODDEF | |
| _ZSTD_SET_PARAMETER_TYPES_METHODDEF | |
| {NULL, NULL} | |
| }; | |
| PyDoc_STRVAR(zstd_version_info__doc__, | |
| "_zstd.zstd_version_info\n\ | |
| \n\ | |
| Zstd version information as a named tuple."); | |
| static PyStructSequence_Field zstd_version_info_fields[] = { | |
| {"major", "Major release number"}, | |
| {"minor", "Minor release number"}, | |
| {"patch", "Patch release number"}, | |
| {0} | |
| }; | |
| static PyStructSequence_Desc zstd_version_info_desc = { | |
| "_zstd.zstd_version_info", /* name */ | |
| zstd_version_info__doc__, /* doc */ | |
| zstd_version_info_fields, /* fields */ | |
| 3 | |
| }; | |
| static PyObject * | |
| make_zstd_version_info(PyTypeObject *type, unsigned int number) | |
| { | |
| PyObject *version; | |
| int pos = 0; | |
| unsigned int major = number / 10000u; | |
| unsigned int minor = (number % 10000u) / 100u; | |
| unsigned int patch = number % 100u; | |
| version = PyStructSequence_New(type); | |
| if (version == NULL) { | |
| return NULL; | |
| } | |
| #define SetIntItem(VALUE) \ | |
| PyStructSequence_SET_ITEM(version, pos++, PyLong_FromUnsignedLong(VALUE)); \ | |
| if (PyErr_Occurred()) { \ | |
| Py_DECREF(version); \ | |
| return NULL; \ | |
| } | |
| SetIntItem(major) | |
| SetIntItem(minor) | |
| SetIntItem(patch) | |
| #undef SetIntItem | |
| return version; | |
| } | |
| static int | |
| _zstd_exec(PyObject *m) | |
| { | |
| #define ADD_TYPE(TYPE, SPEC) \ | |
| do { \ | |
| TYPE = (PyTypeObject *)PyType_FromModuleAndSpec(m, &(SPEC), NULL); \ | |
| if (TYPE == NULL) { \ | |
| return -1; \ | |
| } \ | |
| if (PyModule_AddType(m, TYPE) < 0) { \ | |
| return -1; \ | |
| } \ | |
| } while (0) | |
| #define ADD_INT_MACRO(MACRO) \ | |
| if (PyModule_AddIntConstant((m), #MACRO, (MACRO)) < 0) { \ | |
| return -1; \ | |
| } | |
| #define ADD_INT_CONST_TO_TYPE(TYPE, NAME, VALUE) \ | |
| do { \ | |
| PyObject *v = PyLong_FromLong((VALUE)); \ | |
| if (v == NULL || PyObject_SetAttrString((PyObject *)(TYPE), \ | |
| (NAME), v) < 0) { \ | |
| Py_XDECREF(v); \ | |
| return -1; \ | |
| } \ | |
| Py_DECREF(v); \ | |
| } while (0) | |
| _zstd_state* mod_state = get_zstd_state(m); | |
| /* Reusable objects & variables */ | |
| mod_state->CParameter_type = NULL; | |
| mod_state->DParameter_type = NULL; | |
| /* Create and add heap types */ | |
| ADD_TYPE(mod_state->ZstdDict_type, zstd_dict_type_spec); | |
| ADD_TYPE(mod_state->ZstdCompressor_type, zstd_compressor_type_spec); | |
| ADD_TYPE(mod_state->ZstdDecompressor_type, zstd_decompressor_type_spec); | |
| mod_state->ZstdError = PyErr_NewExceptionWithDoc( | |
| "compression.zstd.ZstdError", | |
| "An error occurred in the zstd library.", | |
| NULL, NULL); | |
| if (mod_state->ZstdError == NULL) { | |
| return -1; | |
| } | |
| if (PyModule_AddType(m, (PyTypeObject *)mod_state->ZstdError) < 0) { | |
| return -1; | |
| } | |
| /* Add constants */ | |
| if (PyModule_AddStringConstant(m, "ZSTD_VERSION", | |
| ZSTD_VERSION_STRING) < 0) { | |
| return -1; | |
| } | |
| if (PyModule_AddStringConstant(m, "zstd_version", | |
| ZSTD_versionString()) < 0) { | |
| return -1; | |
| } | |
| PyTypeObject *version_type; | |
| version_type = PyStructSequence_NewType(&zstd_version_info_desc); | |
| if (version_type == NULL) { | |
| return -1; | |
| } | |
| if (PyModule_Add(m, "ZSTD_VERSION_INFO", | |
| make_zstd_version_info(version_type, ZSTD_VERSION_NUMBER)) < 0) | |
| { | |
| Py_DECREF(version_type); | |
| return -1; | |
| } | |
| if (PyModule_Add(m, "zstd_version_info", | |
| make_zstd_version_info(version_type, ZSTD_versionNumber())) < 0) | |
| { | |
| Py_DECREF(version_type); | |
| return -1; | |
| } | |
| Py_DECREF(version_type); | |
| #if ZSTD_VERSION_NUMBER >= 10500 | |
| if (PyModule_AddIntConstant(m, "ZSTD_CLEVEL_DEFAULT", | |
| ZSTD_defaultCLevel()) < 0) { | |
| return -1; | |
| } | |
| #else | |
| ADD_INT_MACRO(ZSTD_CLEVEL_DEFAULT); | |
| #endif | |
| if (PyModule_Add(m, "ZSTD_DStreamOutSize", | |
| PyLong_FromSize_t(ZSTD_DStreamOutSize())) < 0) { | |
| return -1; | |
| } | |
| /* Add zstd compression parameters. All should also be in cp_list. */ | |
| ADD_INT_MACRO(ZSTD_c_compressionLevel); | |
| ADD_INT_MACRO(ZSTD_c_windowLog); | |
| ADD_INT_MACRO(ZSTD_c_hashLog); | |
| ADD_INT_MACRO(ZSTD_c_chainLog); | |
| ADD_INT_MACRO(ZSTD_c_searchLog); | |
| ADD_INT_MACRO(ZSTD_c_minMatch); | |
| ADD_INT_MACRO(ZSTD_c_targetLength); | |
| ADD_INT_MACRO(ZSTD_c_strategy); | |
| ADD_INT_MACRO(ZSTD_c_enableLongDistanceMatching); | |
| ADD_INT_MACRO(ZSTD_c_ldmHashLog); | |
| ADD_INT_MACRO(ZSTD_c_ldmMinMatch); | |
| ADD_INT_MACRO(ZSTD_c_ldmBucketSizeLog); | |
| ADD_INT_MACRO(ZSTD_c_ldmHashRateLog); | |
| ADD_INT_MACRO(ZSTD_c_contentSizeFlag); | |
| ADD_INT_MACRO(ZSTD_c_checksumFlag); | |
| ADD_INT_MACRO(ZSTD_c_dictIDFlag); | |
| ADD_INT_MACRO(ZSTD_c_nbWorkers); | |
| ADD_INT_MACRO(ZSTD_c_jobSize); | |
| ADD_INT_MACRO(ZSTD_c_overlapLog); | |
| /* Add zstd decompression parameters. All should also be in dp_list. */ | |
| ADD_INT_MACRO(ZSTD_d_windowLogMax); | |
| /* Add ZSTD_strategy enum members */ | |
| ADD_INT_MACRO(ZSTD_fast); | |
| ADD_INT_MACRO(ZSTD_dfast); | |
| ADD_INT_MACRO(ZSTD_greedy); | |
| ADD_INT_MACRO(ZSTD_lazy); | |
| ADD_INT_MACRO(ZSTD_lazy2); | |
| ADD_INT_MACRO(ZSTD_btlazy2); | |
| ADD_INT_MACRO(ZSTD_btopt); | |
| ADD_INT_MACRO(ZSTD_btultra); | |
| ADD_INT_MACRO(ZSTD_btultra2); | |
| /* Add ZSTD_EndDirective enum members to ZstdCompressor */ | |
| ADD_INT_CONST_TO_TYPE(mod_state->ZstdCompressor_type, | |
| "CONTINUE", ZSTD_e_continue); | |
| ADD_INT_CONST_TO_TYPE(mod_state->ZstdCompressor_type, | |
| "FLUSH_BLOCK", ZSTD_e_flush); | |
| ADD_INT_CONST_TO_TYPE(mod_state->ZstdCompressor_type, | |
| "FLUSH_FRAME", ZSTD_e_end); | |
| /* Make ZstdCompressor immutable (set Py_TPFLAGS_IMMUTABLETYPE) */ | |
| PyType_Freeze(mod_state->ZstdCompressor_type); | |
| #undef ADD_TYPE | |
| #undef ADD_INT_MACRO | |
| #undef ADD_ZSTD_COMPRESSOR_INT_CONST | |
| return 0; | |
| } | |
| static int | |
| _zstd_traverse(PyObject *module, visitproc visit, void *arg) | |
| { | |
| _zstd_state* mod_state = get_zstd_state(module); | |
| Py_VISIT(mod_state->ZstdDict_type); | |
| Py_VISIT(mod_state->ZstdCompressor_type); | |
| Py_VISIT(mod_state->ZstdDecompressor_type); | |
| Py_VISIT(mod_state->ZstdError); | |
| Py_VISIT(mod_state->CParameter_type); | |
| Py_VISIT(mod_state->DParameter_type); | |
| return 0; | |
| } | |
| static int | |
| _zstd_clear(PyObject *module) | |
| { | |
| _zstd_state* mod_state = get_zstd_state(module); | |
| Py_CLEAR(mod_state->ZstdDict_type); | |
| Py_CLEAR(mod_state->ZstdCompressor_type); | |
| Py_CLEAR(mod_state->ZstdDecompressor_type); | |
| Py_CLEAR(mod_state->ZstdError); | |
| Py_CLEAR(mod_state->CParameter_type); | |
| Py_CLEAR(mod_state->DParameter_type); | |
| return 0; | |
| } | |
| static void | |
| _zstd_free(void *module) | |
| { | |
| (void)_zstd_clear((PyObject *)module); | |
| } | |
| static struct PyModuleDef_Slot _zstd_slots[] = { | |
| _Py_ABI_SLOT, | |
| {Py_mod_exec, _zstd_exec}, | |
| {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, | |
| {Py_mod_gil, Py_MOD_GIL_NOT_USED}, | |
| {0, NULL}, | |
| }; | |
| static struct PyModuleDef _zstdmodule = { | |
| .m_base = PyModuleDef_HEAD_INIT, | |
| .m_name = "_zstd", | |
| .m_doc = "Implementation module for Zstandard compression.", | |
| .m_size = sizeof(_zstd_state), | |
| .m_slots = _zstd_slots, | |
| .m_methods = _zstd_methods, | |
| .m_traverse = _zstd_traverse, | |
| .m_clear = _zstd_clear, | |
| .m_free = _zstd_free, | |
| }; | |
| PyMODINIT_FUNC | |
| PyInit__zstd(void) | |
| { | |
| return PyModuleDef_Init(&_zstdmodule); | |
| } |