backend_c.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  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. /* A Python C extension for Zstandard. */
  9. #if defined(_WIN32)
  10. #define WIN32_LEAN_AND_MEAN
  11. #include <Windows.h>
  12. #elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || \
  13. defined(__NetBSD__) || defined(__DragonFly__)
  14. #include <sys/types.h>
  15. #include <sys/sysctl.h>
  16. #endif
  17. #include "python-zstandard.h"
  18. #include "bufferutil.c"
  19. #include "compressionchunker.c"
  20. #include "compressiondict.c"
  21. #include "compressionparams.c"
  22. #include "compressionreader.c"
  23. #include "compressionwriter.c"
  24. #include "compressobj.c"
  25. #include "compressor.c"
  26. #include "compressoriterator.c"
  27. #include "constants.c"
  28. #include "decompressionreader.c"
  29. #include "decompressionwriter.c"
  30. #include "decompressobj.c"
  31. #include "decompressor.c"
  32. #include "decompressoriterator.c"
  33. #include "frameparams.c"
  34. PyObject *ZstdError;
  35. static PyObject *estimate_decompression_context_size(PyObject *self) {
  36. return PyLong_FromSize_t(ZSTD_estimateDCtxSize());
  37. }
  38. static PyObject *frame_content_size(PyObject *self, PyObject *args,
  39. PyObject *kwargs) {
  40. static char *kwlist[] = {"source", NULL};
  41. Py_buffer source;
  42. PyObject *result = NULL;
  43. unsigned long long size;
  44. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*:frame_content_size",
  45. kwlist, &source)) {
  46. return NULL;
  47. }
  48. size = ZSTD_getFrameContentSize(source.buf, source.len);
  49. if (size == ZSTD_CONTENTSIZE_ERROR) {
  50. PyErr_SetString(ZstdError, "error when determining content size");
  51. }
  52. else if (size == ZSTD_CONTENTSIZE_UNKNOWN) {
  53. result = PyLong_FromLong(-1);
  54. }
  55. else {
  56. result = PyLong_FromUnsignedLongLong(size);
  57. }
  58. PyBuffer_Release(&source);
  59. return result;
  60. }
  61. static PyObject *frame_header_size(PyObject *self, PyObject *args,
  62. PyObject *kwargs) {
  63. static char *kwlist[] = {"source", NULL};
  64. Py_buffer source;
  65. PyObject *result = NULL;
  66. size_t zresult;
  67. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*:frame_header_size",
  68. kwlist, &source)) {
  69. return NULL;
  70. }
  71. zresult = ZSTD_frameHeaderSize(source.buf, source.len);
  72. if (ZSTD_isError(zresult)) {
  73. PyErr_Format(ZstdError, "could not determine frame header size: %s",
  74. ZSTD_getErrorName(zresult));
  75. }
  76. else {
  77. result = PyLong_FromSize_t(zresult);
  78. }
  79. PyBuffer_Release(&source);
  80. return result;
  81. }
  82. static char zstd_doc[] = "Interface to zstandard";
  83. static PyMethodDef zstd_methods[] = {
  84. {"estimate_decompression_context_size",
  85. (PyCFunction)estimate_decompression_context_size, METH_NOARGS, NULL},
  86. {"frame_content_size", (PyCFunction)frame_content_size,
  87. METH_VARARGS | METH_KEYWORDS, NULL},
  88. {"frame_header_size", (PyCFunction)frame_header_size,
  89. METH_VARARGS | METH_KEYWORDS, NULL},
  90. {"get_frame_parameters", (PyCFunction)get_frame_parameters,
  91. METH_VARARGS | METH_KEYWORDS, NULL},
  92. {"train_dictionary", (PyCFunction)train_dictionary,
  93. METH_VARARGS | METH_KEYWORDS, NULL},
  94. {NULL, NULL}};
  95. void bufferutil_module_init(PyObject *mod);
  96. void compressobj_module_init(PyObject *mod);
  97. void compressor_module_init(PyObject *mod);
  98. void compressionparams_module_init(PyObject *mod);
  99. void constants_module_init(PyObject *mod);
  100. void compressionchunker_module_init(PyObject *mod);
  101. void compressiondict_module_init(PyObject *mod);
  102. void compressionreader_module_init(PyObject *mod);
  103. void compressionwriter_module_init(PyObject *mod);
  104. void compressoriterator_module_init(PyObject *mod);
  105. void decompressor_module_init(PyObject *mod);
  106. void decompressobj_module_init(PyObject *mod);
  107. void decompressionreader_module_init(PyObject *mod);
  108. void decompressionwriter_module_init(PyObject *mod);
  109. void decompressoriterator_module_init(PyObject *mod);
  110. void frameparams_module_init(PyObject *mod);
  111. void zstd_module_init(PyObject *m) {
  112. /* python-zstandard relies on unstable zstd C API features. This means
  113. that changes in zstd may break expectations in python-zstandard.
  114. python-zstandard is distributed with a copy of the zstd sources.
  115. python-zstandard is only guaranteed to work with the bundled version
  116. of zstd.
  117. However, downstream redistributors or packagers may unbundle zstd
  118. from python-zstandard. This can result in a mismatch between zstd
  119. versions and API semantics. This essentially "voids the warranty"
  120. of python-zstandard and may cause undefined behavior.
  121. We detect this mismatch here and refuse to load the module if this
  122. scenario is detected.
  123. */
  124. PyObject *features = NULL;
  125. PyObject *feature = NULL;
  126. unsigned zstd_ver_no = ZSTD_versionNumber();
  127. unsigned our_hardcoded_version = 10506;
  128. if (ZSTD_VERSION_NUMBER != our_hardcoded_version ||
  129. zstd_ver_no != our_hardcoded_version) {
  130. PyErr_Format(
  131. PyExc_ImportError,
  132. "zstd C API versions mismatch; Python bindings were not "
  133. "compiled/linked against expected zstd version (%u returned by the "
  134. "lib, %u hardcoded in zstd headers, %u hardcoded in the cext)",
  135. zstd_ver_no, ZSTD_VERSION_NUMBER, our_hardcoded_version);
  136. return;
  137. }
  138. features = PySet_New(NULL);
  139. if (NULL == features) {
  140. PyErr_SetString(PyExc_ImportError, "could not create empty set");
  141. return;
  142. }
  143. feature = PyUnicode_FromString("buffer_types");
  144. if (NULL == feature) {
  145. PyErr_SetString(PyExc_ImportError, "could not create feature string");
  146. return;
  147. }
  148. if (PySet_Add(features, feature) == -1) {
  149. return;
  150. }
  151. Py_DECREF(feature);
  152. #ifdef HAVE_ZSTD_POOL_APIS
  153. feature = PyUnicode_FromString("multi_compress_to_buffer");
  154. if (NULL == feature) {
  155. PyErr_SetString(PyExc_ImportError, "could not create feature string");
  156. return;
  157. }
  158. if (PySet_Add(features, feature) == -1) {
  159. return;
  160. }
  161. Py_DECREF(feature);
  162. #endif
  163. #ifdef HAVE_ZSTD_POOL_APIS
  164. feature = PyUnicode_FromString("multi_decompress_to_buffer");
  165. if (NULL == feature) {
  166. PyErr_SetString(PyExc_ImportError, "could not create feature string");
  167. return;
  168. }
  169. if (PySet_Add(features, feature) == -1) {
  170. return;
  171. }
  172. Py_DECREF(feature);
  173. #endif
  174. if (PyObject_SetAttrString(m, "backend_features", features) == -1) {
  175. return;
  176. }
  177. Py_DECREF(features);
  178. bufferutil_module_init(m);
  179. compressionparams_module_init(m);
  180. compressiondict_module_init(m);
  181. compressobj_module_init(m);
  182. compressor_module_init(m);
  183. compressionchunker_module_init(m);
  184. compressionreader_module_init(m);
  185. compressionwriter_module_init(m);
  186. compressoriterator_module_init(m);
  187. constants_module_init(m);
  188. decompressor_module_init(m);
  189. decompressobj_module_init(m);
  190. decompressionreader_module_init(m);
  191. decompressionwriter_module_init(m);
  192. decompressoriterator_module_init(m);
  193. frameparams_module_init(m);
  194. }
  195. #if defined(__GNUC__) && (__GNUC__ >= 4)
  196. #define PYTHON_ZSTD_VISIBILITY __attribute__((visibility("default")))
  197. #else
  198. #define PYTHON_ZSTD_VISIBILITY
  199. #endif
  200. static struct PyModuleDef zstd_module = {PyModuleDef_HEAD_INIT, "backend_c",
  201. zstd_doc, -1, zstd_methods};
  202. PYTHON_ZSTD_VISIBILITY PyMODINIT_FUNC PyInit_backend_c(void) {
  203. PyObject *m = PyModule_Create(&zstd_module);
  204. if (m) {
  205. zstd_module_init(m);
  206. if (PyErr_Occurred()) {
  207. Py_DECREF(m);
  208. m = NULL;
  209. }
  210. }
  211. return m;
  212. }
  213. /* Attempt to resolve the number of CPUs in the system. */
  214. int cpu_count() {
  215. int count = 0;
  216. #if defined(_WIN32)
  217. SYSTEM_INFO si;
  218. si.dwNumberOfProcessors = 0;
  219. GetSystemInfo(&si);
  220. count = si.dwNumberOfProcessors;
  221. #elif defined(__APPLE__)
  222. int num;
  223. size_t size = sizeof(int);
  224. if (0 == sysctlbyname("hw.logicalcpu", &num, &size, NULL, 0)) {
  225. count = num;
  226. }
  227. #elif defined(__linux__)
  228. count = sysconf(_SC_NPROCESSORS_ONLN);
  229. #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
  230. defined(__DragonFly__)
  231. int mib[2];
  232. size_t len = sizeof(count);
  233. mib[0] = CTL_HW;
  234. mib[1] = HW_NCPU;
  235. if (0 != sysctl(mib, 2, &count, &len, NULL, 0)) {
  236. count = 0;
  237. }
  238. #elif defined(__hpux)
  239. count = mpctl(MPC_GETNUMSPUS, NULL, NULL);
  240. #endif
  241. return count;
  242. }
  243. size_t roundpow2(size_t i) {
  244. i--;
  245. i |= i >> 1;
  246. i |= i >> 2;
  247. i |= i >> 4;
  248. i |= i >> 8;
  249. i |= i >> 16;
  250. i++;
  251. return i;
  252. }
  253. /* Safer version of _PyBytes_Resize().
  254. *
  255. * _PyBytes_Resize() only works if the refcount is 1. In some scenarios,
  256. * we can get an object with a refcount > 1, even if it was just created
  257. * with PyBytes_FromStringAndSize()! That's because (at least) CPython
  258. * pre-allocates PyBytes instances of size 1 for every possible byte value.
  259. *
  260. * If non-0 is returned, obj may or may not be NULL.
  261. */
  262. int safe_pybytes_resize(PyObject **obj, Py_ssize_t size) {
  263. PyObject *tmp;
  264. if ((*obj)->ob_refcnt == 1) {
  265. return _PyBytes_Resize(obj, size);
  266. }
  267. tmp = PyBytes_FromStringAndSize(NULL, size);
  268. if (!tmp) {
  269. return -1;
  270. }
  271. memcpy(PyBytes_AS_STRING(tmp), PyBytes_AS_STRING(*obj),
  272. PyBytes_GET_SIZE(*obj));
  273. Py_DECREF(*obj);
  274. *obj = tmp;
  275. return 0;
  276. }
  277. // Set/raise an `io.UnsupportedOperation` exception.
  278. void set_io_unsupported_operation(void) {
  279. PyObject *iomod;
  280. PyObject *exc;
  281. iomod = PyImport_ImportModule("io");
  282. if (NULL == iomod) {
  283. return;
  284. }
  285. exc = PyObject_GetAttrString(iomod, "UnsupportedOperation");
  286. if (NULL == exc) {
  287. Py_DECREF(iomod);
  288. return;
  289. }
  290. PyErr_SetNone(exc);
  291. Py_DECREF(exc);
  292. Py_DECREF(iomod);
  293. }