compressionparams.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. /**
  2. * Copyright (c) 2016-present, Gregory Szorc
  3. * All rights reserved.
  4. *
  5. * This software may be modified and distributed under the terms
  6. * of the BSD license. See the LICENSE file for details.
  7. */
  8. #include "python-zstandard.h"
  9. extern PyObject *ZstdError;
  10. int set_parameter(ZSTD_CCtx_params *params, ZSTD_cParameter param, int value) {
  11. size_t zresult = ZSTD_CCtxParams_setParameter(params, param, value);
  12. if (ZSTD_isError(zresult)) {
  13. PyErr_Format(ZstdError,
  14. "unable to set compression context parameter: %s",
  15. ZSTD_getErrorName(zresult));
  16. return 1;
  17. }
  18. return 0;
  19. }
  20. #define TRY_SET_PARAMETER(params, param, value) \
  21. if (set_parameter(params, param, value)) \
  22. return -1;
  23. #define TRY_COPY_PARAMETER(source, dest, param) \
  24. { \
  25. int result; \
  26. size_t zresult = ZSTD_CCtxParams_getParameter(source, param, &result); \
  27. if (ZSTD_isError(zresult)) { \
  28. return 1; \
  29. } \
  30. zresult = ZSTD_CCtxParams_setParameter(dest, param, result); \
  31. if (ZSTD_isError(zresult)) { \
  32. return 1; \
  33. } \
  34. }
  35. int set_parameters(ZSTD_CCtx_params *params,
  36. ZstdCompressionParametersObject *obj) {
  37. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_nbWorkers);
  38. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_format);
  39. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_compressionLevel);
  40. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_windowLog);
  41. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_hashLog);
  42. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_chainLog);
  43. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_searchLog);
  44. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_minMatch);
  45. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_targetLength);
  46. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_strategy);
  47. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_contentSizeFlag);
  48. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_checksumFlag);
  49. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_dictIDFlag);
  50. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_jobSize);
  51. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_overlapLog);
  52. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_forceMaxWindow);
  53. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_enableLongDistanceMatching);
  54. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_ldmHashLog);
  55. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_ldmMinMatch);
  56. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_ldmBucketSizeLog);
  57. TRY_COPY_PARAMETER(obj->params, params, ZSTD_c_ldmHashRateLog);
  58. return 0;
  59. }
  60. int reset_params(ZstdCompressionParametersObject *params) {
  61. if (params->params) {
  62. ZSTD_CCtxParams_reset(params->params);
  63. }
  64. else {
  65. params->params = ZSTD_createCCtxParams();
  66. if (!params->params) {
  67. PyErr_NoMemory();
  68. return 1;
  69. }
  70. }
  71. return set_parameters(params->params, params);
  72. }
  73. #define TRY_GET_PARAMETER(params, param, value) \
  74. { \
  75. size_t zresult = ZSTD_CCtxParams_getParameter(params, param, value); \
  76. if (ZSTD_isError(zresult)) { \
  77. PyErr_Format(ZstdError, "unable to retrieve parameter: %s", \
  78. ZSTD_getErrorName(zresult)); \
  79. return 1; \
  80. } \
  81. }
  82. int to_cparams(ZstdCompressionParametersObject *params,
  83. ZSTD_compressionParameters *cparams) {
  84. int value;
  85. TRY_GET_PARAMETER(params->params, ZSTD_c_windowLog, &value);
  86. cparams->windowLog = value;
  87. TRY_GET_PARAMETER(params->params, ZSTD_c_chainLog, &value);
  88. cparams->chainLog = value;
  89. TRY_GET_PARAMETER(params->params, ZSTD_c_hashLog, &value);
  90. cparams->hashLog = value;
  91. TRY_GET_PARAMETER(params->params, ZSTD_c_searchLog, &value);
  92. cparams->searchLog = value;
  93. TRY_GET_PARAMETER(params->params, ZSTD_c_minMatch, &value);
  94. cparams->minMatch = value;
  95. TRY_GET_PARAMETER(params->params, ZSTD_c_targetLength, &value);
  96. cparams->targetLength = value;
  97. TRY_GET_PARAMETER(params->params, ZSTD_c_strategy, &value);
  98. cparams->strategy = value;
  99. return 0;
  100. }
  101. static int ZstdCompressionParameters_init(ZstdCompressionParametersObject *self,
  102. PyObject *args, PyObject *kwargs) {
  103. static char *kwlist[] = {"format",
  104. "compression_level",
  105. "window_log",
  106. "hash_log",
  107. "chain_log",
  108. "search_log",
  109. "min_match",
  110. "target_length",
  111. "strategy",
  112. "write_content_size",
  113. "write_checksum",
  114. "write_dict_id",
  115. "job_size",
  116. "overlap_log",
  117. "force_max_window",
  118. "enable_ldm",
  119. "ldm_hash_log",
  120. "ldm_min_match",
  121. "ldm_bucket_size_log",
  122. "ldm_hash_rate_log",
  123. "threads",
  124. NULL};
  125. int format = 0;
  126. int compressionLevel = 0;
  127. int windowLog = 0;
  128. int hashLog = 0;
  129. int chainLog = 0;
  130. int searchLog = 0;
  131. int minMatch = 0;
  132. int targetLength = 0;
  133. int strategy = -1;
  134. int contentSizeFlag = 1;
  135. int checksumFlag = 0;
  136. int dictIDFlag = 0;
  137. int jobSize = 0;
  138. int overlapLog = -1;
  139. int forceMaxWindow = 0;
  140. int enableLDM = 0;
  141. int ldmHashLog = 0;
  142. int ldmMinMatch = 0;
  143. int ldmBucketSizeLog = 0;
  144. int ldmHashRateLog = -1;
  145. int threads = 0;
  146. if (!PyArg_ParseTupleAndKeywords(
  147. args, kwargs, "|iiiiiiiiiiiiiiiiiiiii:ZstdCompressionParameters",
  148. kwlist, &format, &compressionLevel, &windowLog, &hashLog, &chainLog,
  149. &searchLog, &minMatch, &targetLength, &strategy, &contentSizeFlag,
  150. &checksumFlag, &dictIDFlag, &jobSize, &overlapLog, &forceMaxWindow,
  151. &enableLDM, &ldmHashLog, &ldmMinMatch, &ldmBucketSizeLog,
  152. &ldmHashRateLog, &threads)) {
  153. return -1;
  154. }
  155. if (reset_params(self)) {
  156. return -1;
  157. }
  158. if (threads < 0) {
  159. threads = cpu_count();
  160. }
  161. /* We need to set ZSTD_c_nbWorkers before ZSTD_c_jobSize and
  162. * ZSTD_c_overlapLog because setting ZSTD_c_nbWorkers resets the other
  163. * parameters. */
  164. TRY_SET_PARAMETER(self->params, ZSTD_c_nbWorkers, threads);
  165. TRY_SET_PARAMETER(self->params, ZSTD_c_format, format);
  166. TRY_SET_PARAMETER(self->params, ZSTD_c_compressionLevel, compressionLevel);
  167. TRY_SET_PARAMETER(self->params, ZSTD_c_windowLog, windowLog);
  168. TRY_SET_PARAMETER(self->params, ZSTD_c_hashLog, hashLog);
  169. TRY_SET_PARAMETER(self->params, ZSTD_c_chainLog, chainLog);
  170. TRY_SET_PARAMETER(self->params, ZSTD_c_searchLog, searchLog);
  171. TRY_SET_PARAMETER(self->params, ZSTD_c_minMatch, minMatch);
  172. TRY_SET_PARAMETER(self->params, ZSTD_c_targetLength, targetLength);
  173. if (strategy == -1) {
  174. strategy = 0;
  175. }
  176. TRY_SET_PARAMETER(self->params, ZSTD_c_strategy, strategy);
  177. TRY_SET_PARAMETER(self->params, ZSTD_c_contentSizeFlag, contentSizeFlag);
  178. TRY_SET_PARAMETER(self->params, ZSTD_c_checksumFlag, checksumFlag);
  179. TRY_SET_PARAMETER(self->params, ZSTD_c_dictIDFlag, dictIDFlag);
  180. TRY_SET_PARAMETER(self->params, ZSTD_c_jobSize, jobSize);
  181. if (overlapLog == -1) {
  182. overlapLog = 0;
  183. }
  184. TRY_SET_PARAMETER(self->params, ZSTD_c_overlapLog, overlapLog);
  185. TRY_SET_PARAMETER(self->params, ZSTD_c_forceMaxWindow, forceMaxWindow);
  186. TRY_SET_PARAMETER(self->params, ZSTD_c_enableLongDistanceMatching,
  187. enableLDM);
  188. TRY_SET_PARAMETER(self->params, ZSTD_c_ldmHashLog, ldmHashLog);
  189. TRY_SET_PARAMETER(self->params, ZSTD_c_ldmMinMatch, ldmMinMatch);
  190. TRY_SET_PARAMETER(self->params, ZSTD_c_ldmBucketSizeLog, ldmBucketSizeLog);
  191. if (ldmHashRateLog == -1) {
  192. ldmHashRateLog = 0;
  193. }
  194. TRY_SET_PARAMETER(self->params, ZSTD_c_ldmHashRateLog, ldmHashRateLog);
  195. return 0;
  196. }
  197. ZstdCompressionParametersObject *
  198. CompressionParameters_from_level(PyObject *undef, PyObject *args,
  199. PyObject *kwargs) {
  200. int managedKwargs = 0;
  201. int level;
  202. PyObject *sourceSize = NULL;
  203. PyObject *dictSize = NULL;
  204. unsigned PY_LONG_LONG iSourceSize = 0;
  205. Py_ssize_t iDictSize = 0;
  206. PyObject *val;
  207. ZSTD_compressionParameters params;
  208. ZstdCompressionParametersObject *result = NULL;
  209. int res;
  210. if (!PyArg_ParseTuple(args, "i:from_level", &level)) {
  211. return NULL;
  212. }
  213. if (!kwargs) {
  214. kwargs = PyDict_New();
  215. if (!kwargs) {
  216. return NULL;
  217. }
  218. managedKwargs = 1;
  219. }
  220. sourceSize = PyDict_GetItemString(kwargs, "source_size");
  221. if (sourceSize) {
  222. iSourceSize = PyLong_AsUnsignedLongLong(sourceSize);
  223. if (iSourceSize == (unsigned PY_LONG_LONG)(-1)) {
  224. goto cleanup;
  225. }
  226. PyDict_DelItemString(kwargs, "source_size");
  227. }
  228. dictSize = PyDict_GetItemString(kwargs, "dict_size");
  229. if (dictSize) {
  230. iDictSize = PyLong_AsSsize_t(dictSize);
  231. if (iDictSize == -1) {
  232. goto cleanup;
  233. }
  234. PyDict_DelItemString(kwargs, "dict_size");
  235. }
  236. params = ZSTD_getCParams(level, iSourceSize, iDictSize);
  237. /* Values derived from the input level and sizes are passed along to the
  238. constructor. But only if a value doesn't already exist. */
  239. val = PyDict_GetItemString(kwargs, "window_log");
  240. if (!val) {
  241. val = PyLong_FromUnsignedLong(params.windowLog);
  242. if (!val) {
  243. goto cleanup;
  244. }
  245. PyDict_SetItemString(kwargs, "window_log", val);
  246. Py_DECREF(val);
  247. }
  248. val = PyDict_GetItemString(kwargs, "chain_log");
  249. if (!val) {
  250. val = PyLong_FromUnsignedLong(params.chainLog);
  251. if (!val) {
  252. goto cleanup;
  253. }
  254. PyDict_SetItemString(kwargs, "chain_log", val);
  255. Py_DECREF(val);
  256. }
  257. val = PyDict_GetItemString(kwargs, "hash_log");
  258. if (!val) {
  259. val = PyLong_FromUnsignedLong(params.hashLog);
  260. if (!val) {
  261. goto cleanup;
  262. }
  263. PyDict_SetItemString(kwargs, "hash_log", val);
  264. Py_DECREF(val);
  265. }
  266. val = PyDict_GetItemString(kwargs, "search_log");
  267. if (!val) {
  268. val = PyLong_FromUnsignedLong(params.searchLog);
  269. if (!val) {
  270. goto cleanup;
  271. }
  272. PyDict_SetItemString(kwargs, "search_log", val);
  273. Py_DECREF(val);
  274. }
  275. val = PyDict_GetItemString(kwargs, "min_match");
  276. if (!val) {
  277. val = PyLong_FromUnsignedLong(params.minMatch);
  278. if (!val) {
  279. goto cleanup;
  280. }
  281. PyDict_SetItemString(kwargs, "min_match", val);
  282. Py_DECREF(val);
  283. }
  284. val = PyDict_GetItemString(kwargs, "target_length");
  285. if (!val) {
  286. val = PyLong_FromUnsignedLong(params.targetLength);
  287. if (!val) {
  288. goto cleanup;
  289. }
  290. PyDict_SetItemString(kwargs, "target_length", val);
  291. Py_DECREF(val);
  292. }
  293. val = PyDict_GetItemString(kwargs, "strategy");
  294. if (!val) {
  295. val = PyLong_FromUnsignedLong(params.strategy);
  296. if (!val) {
  297. goto cleanup;
  298. }
  299. PyDict_SetItemString(kwargs, "strategy", val);
  300. Py_DECREF(val);
  301. }
  302. result = PyObject_New(ZstdCompressionParametersObject,
  303. ZstdCompressionParametersType);
  304. if (!result) {
  305. goto cleanup;
  306. }
  307. result->params = NULL;
  308. val = PyTuple_New(0);
  309. if (!val) {
  310. Py_CLEAR(result);
  311. goto cleanup;
  312. }
  313. res = ZstdCompressionParameters_init(result, val, kwargs);
  314. Py_DECREF(val);
  315. if (res) {
  316. Py_CLEAR(result);
  317. goto cleanup;
  318. }
  319. cleanup:
  320. if (managedKwargs) {
  321. Py_DECREF(kwargs);
  322. }
  323. return result;
  324. }
  325. PyObject *ZstdCompressionParameters_estimated_compression_context_size(
  326. ZstdCompressionParametersObject *self) {
  327. return PyLong_FromSize_t(
  328. ZSTD_estimateCCtxSize_usingCCtxParams(self->params));
  329. }
  330. static void
  331. ZstdCompressionParameters_dealloc(ZstdCompressionParametersObject *self) {
  332. if (self->params) {
  333. ZSTD_freeCCtxParams(self->params);
  334. self->params = NULL;
  335. }
  336. PyObject_Del(self);
  337. }
  338. #define PARAM_GETTER(name, param) \
  339. PyObject *ZstdCompressionParameters_get_##name(PyObject *self, \
  340. void *unused) { \
  341. int result; \
  342. size_t zresult; \
  343. ZstdCompressionParametersObject *p = \
  344. (ZstdCompressionParametersObject *)(self); \
  345. zresult = ZSTD_CCtxParams_getParameter(p->params, param, &result); \
  346. if (ZSTD_isError(zresult)) { \
  347. PyErr_Format(ZstdError, "unable to get compression parameter: %s", \
  348. ZSTD_getErrorName(zresult)); \
  349. return NULL; \
  350. } \
  351. return PyLong_FromLong(result); \
  352. }
  353. PARAM_GETTER(format, ZSTD_c_format)
  354. PARAM_GETTER(compression_level, ZSTD_c_compressionLevel)
  355. PARAM_GETTER(window_log, ZSTD_c_windowLog)
  356. PARAM_GETTER(hash_log, ZSTD_c_hashLog)
  357. PARAM_GETTER(chain_log, ZSTD_c_chainLog)
  358. PARAM_GETTER(search_log, ZSTD_c_searchLog)
  359. PARAM_GETTER(min_match, ZSTD_c_minMatch)
  360. PARAM_GETTER(target_length, ZSTD_c_targetLength)
  361. PARAM_GETTER(strategy, ZSTD_c_strategy)
  362. PARAM_GETTER(write_content_size, ZSTD_c_contentSizeFlag)
  363. PARAM_GETTER(write_checksum, ZSTD_c_checksumFlag)
  364. PARAM_GETTER(write_dict_id, ZSTD_c_dictIDFlag)
  365. PARAM_GETTER(job_size, ZSTD_c_jobSize)
  366. PARAM_GETTER(overlap_log, ZSTD_c_overlapLog)
  367. PARAM_GETTER(force_max_window, ZSTD_c_forceMaxWindow)
  368. PARAM_GETTER(enable_ldm, ZSTD_c_enableLongDistanceMatching)
  369. PARAM_GETTER(ldm_hash_log, ZSTD_c_ldmHashLog)
  370. PARAM_GETTER(ldm_min_match, ZSTD_c_ldmMinMatch)
  371. PARAM_GETTER(ldm_bucket_size_log, ZSTD_c_ldmBucketSizeLog)
  372. PARAM_GETTER(ldm_hash_rate_log, ZSTD_c_ldmHashRateLog)
  373. PARAM_GETTER(threads, ZSTD_c_nbWorkers)
  374. static PyMethodDef ZstdCompressionParameters_methods[] = {
  375. {"from_level", (PyCFunction)CompressionParameters_from_level,
  376. METH_VARARGS | METH_KEYWORDS | METH_STATIC, NULL},
  377. {"estimated_compression_context_size",
  378. (PyCFunction)ZstdCompressionParameters_estimated_compression_context_size,
  379. METH_NOARGS, NULL},
  380. {NULL, NULL}};
  381. #define GET_SET_ENTRY(name) \
  382. { #name, ZstdCompressionParameters_get_##name, NULL, NULL, NULL }
  383. static PyGetSetDef ZstdCompressionParameters_getset[] = {
  384. GET_SET_ENTRY(format),
  385. GET_SET_ENTRY(compression_level),
  386. GET_SET_ENTRY(window_log),
  387. GET_SET_ENTRY(hash_log),
  388. GET_SET_ENTRY(chain_log),
  389. GET_SET_ENTRY(search_log),
  390. GET_SET_ENTRY(min_match),
  391. GET_SET_ENTRY(target_length),
  392. GET_SET_ENTRY(strategy),
  393. GET_SET_ENTRY(write_content_size),
  394. GET_SET_ENTRY(write_checksum),
  395. GET_SET_ENTRY(write_dict_id),
  396. GET_SET_ENTRY(threads),
  397. GET_SET_ENTRY(job_size),
  398. GET_SET_ENTRY(overlap_log),
  399. GET_SET_ENTRY(force_max_window),
  400. GET_SET_ENTRY(enable_ldm),
  401. GET_SET_ENTRY(ldm_hash_log),
  402. GET_SET_ENTRY(ldm_min_match),
  403. GET_SET_ENTRY(ldm_bucket_size_log),
  404. GET_SET_ENTRY(ldm_hash_rate_log),
  405. {NULL}};
  406. PyType_Slot ZstdCompressionParametersSlots[] = {
  407. {Py_tp_dealloc, ZstdCompressionParameters_dealloc},
  408. {Py_tp_methods, ZstdCompressionParameters_methods},
  409. {Py_tp_getset, ZstdCompressionParameters_getset},
  410. {Py_tp_init, ZstdCompressionParameters_init},
  411. {Py_tp_new, PyType_GenericNew},
  412. {0, NULL},
  413. };
  414. PyType_Spec ZstdCompressionParametersSpec = {
  415. "zstd.ZstdCompressionParameters",
  416. sizeof(ZstdCompressionParametersObject),
  417. 0,
  418. Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
  419. ZstdCompressionParametersSlots,
  420. };
  421. PyTypeObject *ZstdCompressionParametersType;
  422. void compressionparams_module_init(PyObject *mod) {
  423. ZstdCompressionParametersType =
  424. (PyTypeObject *)PyType_FromSpec(&ZstdCompressionParametersSpec);
  425. if (PyType_Ready(ZstdCompressionParametersType) < 0) {
  426. return;
  427. }
  428. Py_INCREF(ZstdCompressionParametersType);
  429. PyModule_AddObject(mod, "ZstdCompressionParameters",
  430. (PyObject *)ZstdCompressionParametersType);
  431. }