compressobj.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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. static void ZstdCompressionObj_dealloc(ZstdCompressionObj *self) {
  11. PyMem_Free(self->output.dst);
  12. self->output.dst = NULL;
  13. Py_XDECREF(self->compressor);
  14. PyObject_Del(self);
  15. }
  16. static PyObject *ZstdCompressionObj_compress(ZstdCompressionObj *self,
  17. PyObject *args, PyObject *kwargs) {
  18. static char *kwlist[] = {"data", NULL};
  19. Py_buffer source;
  20. ZSTD_inBuffer input;
  21. size_t zresult;
  22. PyObject *result = NULL;
  23. Py_ssize_t resultSize = 0;
  24. if (self->finished) {
  25. PyErr_SetString(ZstdError,
  26. "cannot call compress() after compressor finished");
  27. return NULL;
  28. }
  29. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*:compress", kwlist,
  30. &source)) {
  31. return NULL;
  32. }
  33. input.src = source.buf;
  34. input.size = source.len;
  35. input.pos = 0;
  36. while (input.pos < (size_t)source.len) {
  37. Py_BEGIN_ALLOW_THREADS zresult = ZSTD_compressStream2(
  38. self->compressor->cctx, &self->output, &input, ZSTD_e_continue);
  39. Py_END_ALLOW_THREADS
  40. if (ZSTD_isError(zresult)) {
  41. PyErr_Format(ZstdError, "zstd compress error: %s",
  42. ZSTD_getErrorName(zresult));
  43. Py_CLEAR(result);
  44. goto finally;
  45. }
  46. if (self->output.pos) {
  47. if (result) {
  48. resultSize = PyBytes_GET_SIZE(result);
  49. if (safe_pybytes_resize(&result,
  50. resultSize + self->output.pos)) {
  51. Py_CLEAR(result);
  52. goto finally;
  53. }
  54. memcpy(PyBytes_AS_STRING(result) + resultSize, self->output.dst,
  55. self->output.pos);
  56. }
  57. else {
  58. result = PyBytes_FromStringAndSize(self->output.dst,
  59. self->output.pos);
  60. if (!result) {
  61. goto finally;
  62. }
  63. }
  64. self->output.pos = 0;
  65. }
  66. }
  67. if (NULL == result) {
  68. result = PyBytes_FromString("");
  69. }
  70. finally:
  71. PyBuffer_Release(&source);
  72. return result;
  73. }
  74. static PyObject *ZstdCompressionObj_flush(ZstdCompressionObj *self,
  75. PyObject *args, PyObject *kwargs) {
  76. static char *kwlist[] = {"flush_mode", NULL};
  77. int flushMode = compressorobj_flush_finish;
  78. size_t zresult;
  79. PyObject *result = NULL;
  80. Py_ssize_t resultSize = 0;
  81. ZSTD_inBuffer input;
  82. ZSTD_EndDirective zFlushMode;
  83. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:flush", kwlist,
  84. &flushMode)) {
  85. return NULL;
  86. }
  87. if (flushMode != compressorobj_flush_finish &&
  88. flushMode != compressorobj_flush_block) {
  89. PyErr_SetString(PyExc_ValueError, "flush mode not recognized");
  90. return NULL;
  91. }
  92. if (self->finished) {
  93. PyErr_SetString(ZstdError, "compressor object already finished");
  94. return NULL;
  95. }
  96. switch (flushMode) {
  97. case compressorobj_flush_block:
  98. zFlushMode = ZSTD_e_flush;
  99. break;
  100. case compressorobj_flush_finish:
  101. zFlushMode = ZSTD_e_end;
  102. self->finished = 1;
  103. break;
  104. default:
  105. PyErr_SetString(ZstdError, "unhandled flush mode");
  106. return NULL;
  107. }
  108. assert(self->output.pos == 0);
  109. input.src = NULL;
  110. input.size = 0;
  111. input.pos = 0;
  112. while (1) {
  113. Py_BEGIN_ALLOW_THREADS zresult = ZSTD_compressStream2(
  114. self->compressor->cctx, &self->output, &input, zFlushMode);
  115. Py_END_ALLOW_THREADS
  116. if (ZSTD_isError(zresult)) {
  117. PyErr_Format(ZstdError, "error ending compression stream: %s",
  118. ZSTD_getErrorName(zresult));
  119. return NULL;
  120. }
  121. if (self->output.pos) {
  122. if (result) {
  123. resultSize = PyBytes_GET_SIZE(result);
  124. if (safe_pybytes_resize(&result,
  125. resultSize + self->output.pos)) {
  126. Py_XDECREF(result);
  127. return NULL;
  128. }
  129. memcpy(PyBytes_AS_STRING(result) + resultSize, self->output.dst,
  130. self->output.pos);
  131. }
  132. else {
  133. result = PyBytes_FromStringAndSize(self->output.dst,
  134. self->output.pos);
  135. if (!result) {
  136. return NULL;
  137. }
  138. }
  139. self->output.pos = 0;
  140. }
  141. if (!zresult) {
  142. break;
  143. }
  144. }
  145. if (result) {
  146. return result;
  147. }
  148. else {
  149. return PyBytes_FromString("");
  150. }
  151. }
  152. static PyMethodDef ZstdCompressionObj_methods[] = {
  153. {"compress", (PyCFunction)ZstdCompressionObj_compress,
  154. METH_VARARGS | METH_KEYWORDS, PyDoc_STR("compress data")},
  155. {"flush", (PyCFunction)ZstdCompressionObj_flush,
  156. METH_VARARGS | METH_KEYWORDS, PyDoc_STR("finish compression operation")},
  157. {NULL, NULL}};
  158. PyType_Slot ZstdCompressionObjSlots[] = {
  159. {Py_tp_dealloc, ZstdCompressionObj_dealloc},
  160. {Py_tp_methods, ZstdCompressionObj_methods},
  161. {Py_tp_new, PyType_GenericNew},
  162. {0, NULL},
  163. };
  164. PyType_Spec ZstdCompressionObjSpec = {
  165. "zstd.ZstdCompressionObj",
  166. sizeof(ZstdCompressionObj),
  167. 0,
  168. Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
  169. ZstdCompressionObjSlots,
  170. };
  171. PyTypeObject *ZstdCompressionObjType;
  172. void compressobj_module_init(PyObject *module) {
  173. ZstdCompressionObjType =
  174. (PyTypeObject *)PyType_FromSpec(&ZstdCompressionObjSpec);
  175. if (PyType_Ready(ZstdCompressionObjType) < 0) {
  176. return;
  177. }
  178. }