compressobj.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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. PyDoc_STRVAR(ZstdCompressionObj__doc__,
  11. "Perform compression using a standard library compatible API.\n"
  12. );
  13. static void ZstdCompressionObj_dealloc(ZstdCompressionObj* self) {
  14. PyMem_Free(self->output.dst);
  15. self->output.dst = NULL;
  16. Py_XDECREF(self->compressor);
  17. PyObject_Del(self);
  18. }
  19. static PyObject* ZstdCompressionObj_compress(ZstdCompressionObj* self, PyObject* args, PyObject* kwargs) {
  20. static char* kwlist[] = {
  21. "data",
  22. NULL
  23. };
  24. Py_buffer source;
  25. ZSTD_inBuffer input;
  26. size_t zresult;
  27. PyObject* result = NULL;
  28. Py_ssize_t resultSize = 0;
  29. if (self->finished) {
  30. PyErr_SetString(ZstdError, "cannot call compress() after compressor finished");
  31. return NULL;
  32. }
  33. #if PY_MAJOR_VERSION >= 3
  34. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*:compress",
  35. #else
  36. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*:compress",
  37. #endif
  38. kwlist, &source)) {
  39. return NULL;
  40. }
  41. if (!PyBuffer_IsContiguous(&source, 'C') || source.ndim > 1) {
  42. PyErr_SetString(PyExc_ValueError,
  43. "data buffer should be contiguous and have at most one dimension");
  44. goto finally;
  45. }
  46. input.src = source.buf;
  47. input.size = source.len;
  48. input.pos = 0;
  49. while (input.pos < (size_t)source.len) {
  50. Py_BEGIN_ALLOW_THREADS
  51. zresult = ZSTD_compressStream2(self->compressor->cctx, &self->output,
  52. &input, ZSTD_e_continue);
  53. Py_END_ALLOW_THREADS
  54. if (ZSTD_isError(zresult)) {
  55. PyErr_Format(ZstdError, "zstd compress error: %s", ZSTD_getErrorName(zresult));
  56. Py_CLEAR(result);
  57. goto finally;
  58. }
  59. if (self->output.pos) {
  60. if (result) {
  61. resultSize = PyBytes_GET_SIZE(result);
  62. if (safe_pybytes_resize(&result, resultSize + self->output.pos)) {
  63. Py_CLEAR(result);
  64. goto finally;
  65. }
  66. memcpy(PyBytes_AS_STRING(result) + resultSize,
  67. self->output.dst, self->output.pos);
  68. }
  69. else {
  70. result = PyBytes_FromStringAndSize(self->output.dst, self->output.pos);
  71. if (!result) {
  72. goto finally;
  73. }
  74. }
  75. self->output.pos = 0;
  76. }
  77. }
  78. if (NULL == result) {
  79. result = PyBytes_FromString("");
  80. }
  81. finally:
  82. PyBuffer_Release(&source);
  83. return result;
  84. }
  85. static PyObject* ZstdCompressionObj_flush(ZstdCompressionObj* self, PyObject* args, PyObject* kwargs) {
  86. static char* kwlist[] = {
  87. "flush_mode",
  88. NULL
  89. };
  90. int flushMode = compressorobj_flush_finish;
  91. size_t zresult;
  92. PyObject* result = NULL;
  93. Py_ssize_t resultSize = 0;
  94. ZSTD_inBuffer input;
  95. ZSTD_EndDirective zFlushMode;
  96. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:flush", kwlist, &flushMode)) {
  97. return NULL;
  98. }
  99. if (flushMode != compressorobj_flush_finish && flushMode != compressorobj_flush_block) {
  100. PyErr_SetString(PyExc_ValueError, "flush mode not recognized");
  101. return NULL;
  102. }
  103. if (self->finished) {
  104. PyErr_SetString(ZstdError, "compressor object already finished");
  105. return NULL;
  106. }
  107. switch (flushMode) {
  108. case compressorobj_flush_block:
  109. zFlushMode = ZSTD_e_flush;
  110. break;
  111. case compressorobj_flush_finish:
  112. zFlushMode = ZSTD_e_end;
  113. self->finished = 1;
  114. break;
  115. default:
  116. PyErr_SetString(ZstdError, "unhandled flush mode");
  117. return NULL;
  118. }
  119. assert(self->output.pos == 0);
  120. input.src = NULL;
  121. input.size = 0;
  122. input.pos = 0;
  123. while (1) {
  124. Py_BEGIN_ALLOW_THREADS
  125. zresult = ZSTD_compressStream2(self->compressor->cctx, &self->output,
  126. &input, zFlushMode);
  127. Py_END_ALLOW_THREADS
  128. if (ZSTD_isError(zresult)) {
  129. PyErr_Format(ZstdError, "error ending compression stream: %s",
  130. ZSTD_getErrorName(zresult));
  131. return NULL;
  132. }
  133. if (self->output.pos) {
  134. if (result) {
  135. resultSize = PyBytes_GET_SIZE(result);
  136. if (safe_pybytes_resize(&result, resultSize + self->output.pos)) {
  137. Py_XDECREF(result);
  138. return NULL;
  139. }
  140. memcpy(PyBytes_AS_STRING(result) + resultSize,
  141. self->output.dst, self->output.pos);
  142. }
  143. else {
  144. result = PyBytes_FromStringAndSize(self->output.dst, self->output.pos);
  145. if (!result) {
  146. return NULL;
  147. }
  148. }
  149. self->output.pos = 0;
  150. }
  151. if (!zresult) {
  152. break;
  153. }
  154. }
  155. if (result) {
  156. return result;
  157. }
  158. else {
  159. return PyBytes_FromString("");
  160. }
  161. }
  162. static PyMethodDef ZstdCompressionObj_methods[] = {
  163. { "compress", (PyCFunction)ZstdCompressionObj_compress, METH_VARARGS | METH_KEYWORDS,
  164. PyDoc_STR("compress data") },
  165. { "flush", (PyCFunction)ZstdCompressionObj_flush, METH_VARARGS | METH_KEYWORDS,
  166. PyDoc_STR("finish compression operation") },
  167. { NULL, NULL }
  168. };
  169. PyTypeObject ZstdCompressionObjType = {
  170. PyVarObject_HEAD_INIT(NULL, 0)
  171. "zstd.ZstdCompressionObj", /* tp_name */
  172. sizeof(ZstdCompressionObj), /* tp_basicsize */
  173. 0, /* tp_itemsize */
  174. (destructor)ZstdCompressionObj_dealloc, /* tp_dealloc */
  175. 0, /* tp_print */
  176. 0, /* tp_getattr */
  177. 0, /* tp_setattr */
  178. 0, /* tp_compare */
  179. 0, /* tp_repr */
  180. 0, /* tp_as_number */
  181. 0, /* tp_as_sequence */
  182. 0, /* tp_as_mapping */
  183. 0, /* tp_hash */
  184. 0, /* tp_call */
  185. 0, /* tp_str */
  186. 0, /* tp_getattro */
  187. 0, /* tp_setattro */
  188. 0, /* tp_as_buffer */
  189. Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
  190. ZstdCompressionObj__doc__, /* tp_doc */
  191. 0, /* tp_traverse */
  192. 0, /* tp_clear */
  193. 0, /* tp_richcompare */
  194. 0, /* tp_weaklistoffset */
  195. 0, /* tp_iter */
  196. 0, /* tp_iternext */
  197. ZstdCompressionObj_methods, /* tp_methods */
  198. 0, /* tp_members */
  199. 0, /* tp_getset */
  200. 0, /* tp_base */
  201. 0, /* tp_dict */
  202. 0, /* tp_descr_get */
  203. 0, /* tp_descr_set */
  204. 0, /* tp_dictoffset */
  205. 0, /* tp_init */
  206. 0, /* tp_alloc */
  207. PyType_GenericNew, /* tp_new */
  208. };
  209. void compressobj_module_init(PyObject* module) {
  210. Py_TYPE(&ZstdCompressionObjType) = &PyType_Type;
  211. if (PyType_Ready(&ZstdCompressionObjType) < 0) {
  212. return;
  213. }
  214. }