decompressionwriter.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  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(ZstdDecompressionWriter__doc,
  11. """A context manager used for writing decompressed output.\n"
  12. );
  13. static void ZstdDecompressionWriter_dealloc(ZstdDecompressionWriter* self) {
  14. Py_XDECREF(self->decompressor);
  15. Py_XDECREF(self->writer);
  16. PyObject_Del(self);
  17. }
  18. static PyObject* ZstdDecompressionWriter_enter(ZstdDecompressionWriter* self) {
  19. if (self->closed) {
  20. PyErr_SetString(PyExc_ValueError, "stream is closed");
  21. return NULL;
  22. }
  23. if (self->entered) {
  24. PyErr_SetString(ZstdError, "cannot __enter__ multiple times");
  25. return NULL;
  26. }
  27. self->entered = 1;
  28. Py_INCREF(self);
  29. return (PyObject*)self;
  30. }
  31. static PyObject* ZstdDecompressionWriter_exit(ZstdDecompressionWriter* self, PyObject* args) {
  32. self->entered = 0;
  33. if (NULL == PyObject_CallMethod((PyObject*)self, "close", NULL)) {
  34. return NULL;
  35. }
  36. Py_RETURN_FALSE;
  37. }
  38. static PyObject* ZstdDecompressionWriter_memory_size(ZstdDecompressionWriter* self) {
  39. return PyLong_FromSize_t(ZSTD_sizeof_DCtx(self->decompressor->dctx));
  40. }
  41. static PyObject* ZstdDecompressionWriter_write(ZstdDecompressionWriter* self, PyObject* args, PyObject* kwargs) {
  42. static char* kwlist[] = {
  43. "data",
  44. NULL
  45. };
  46. PyObject* result = NULL;
  47. Py_buffer source;
  48. size_t zresult = 0;
  49. ZSTD_inBuffer input;
  50. ZSTD_outBuffer output;
  51. PyObject* res;
  52. Py_ssize_t totalWrite = 0;
  53. #if PY_MAJOR_VERSION >= 3
  54. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*:write",
  55. #else
  56. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*:write",
  57. #endif
  58. kwlist, &source)) {
  59. return NULL;
  60. }
  61. if (!PyBuffer_IsContiguous(&source, 'C') || source.ndim > 1) {
  62. PyErr_SetString(PyExc_ValueError,
  63. "data buffer should be contiguous and have at most one dimension");
  64. goto finally;
  65. }
  66. if (self->closed) {
  67. PyErr_SetString(PyExc_ValueError, "stream is closed");
  68. return NULL;
  69. }
  70. output.dst = PyMem_Malloc(self->outSize);
  71. if (!output.dst) {
  72. PyErr_NoMemory();
  73. goto finally;
  74. }
  75. output.size = self->outSize;
  76. output.pos = 0;
  77. input.src = source.buf;
  78. input.size = source.len;
  79. input.pos = 0;
  80. while (input.pos < (size_t)source.len) {
  81. Py_BEGIN_ALLOW_THREADS
  82. zresult = ZSTD_decompressStream(self->decompressor->dctx, &output, &input);
  83. Py_END_ALLOW_THREADS
  84. if (ZSTD_isError(zresult)) {
  85. PyMem_Free(output.dst);
  86. PyErr_Format(ZstdError, "zstd decompress error: %s",
  87. ZSTD_getErrorName(zresult));
  88. goto finally;
  89. }
  90. if (output.pos) {
  91. #if PY_MAJOR_VERSION >= 3
  92. res = PyObject_CallMethod(self->writer, "write", "y#",
  93. #else
  94. res = PyObject_CallMethod(self->writer, "write", "s#",
  95. #endif
  96. output.dst, output.pos);
  97. Py_XDECREF(res);
  98. totalWrite += output.pos;
  99. output.pos = 0;
  100. }
  101. }
  102. PyMem_Free(output.dst);
  103. if (self->writeReturnRead) {
  104. result = PyLong_FromSize_t(input.pos);
  105. }
  106. else {
  107. result = PyLong_FromSsize_t(totalWrite);
  108. }
  109. finally:
  110. PyBuffer_Release(&source);
  111. return result;
  112. }
  113. static PyObject* ZstdDecompressionWriter_close(ZstdDecompressionWriter* self) {
  114. PyObject* result;
  115. if (self->closed) {
  116. Py_RETURN_NONE;
  117. }
  118. result = PyObject_CallMethod((PyObject*)self, "flush", NULL);
  119. self->closed = 1;
  120. if (NULL == result) {
  121. return NULL;
  122. }
  123. /* Call close on underlying stream as well. */
  124. if (PyObject_HasAttrString(self->writer, "close")) {
  125. return PyObject_CallMethod(self->writer, "close", NULL);
  126. }
  127. Py_RETURN_NONE;
  128. }
  129. static PyObject* ZstdDecompressionWriter_fileno(ZstdDecompressionWriter* self) {
  130. if (PyObject_HasAttrString(self->writer, "fileno")) {
  131. return PyObject_CallMethod(self->writer, "fileno", NULL);
  132. }
  133. else {
  134. PyErr_SetString(PyExc_OSError, "fileno not available on underlying writer");
  135. return NULL;
  136. }
  137. }
  138. static PyObject* ZstdDecompressionWriter_flush(ZstdDecompressionWriter* self) {
  139. if (self->closed) {
  140. PyErr_SetString(PyExc_ValueError, "stream is closed");
  141. return NULL;
  142. }
  143. if (PyObject_HasAttrString(self->writer, "flush")) {
  144. return PyObject_CallMethod(self->writer, "flush", NULL);
  145. }
  146. else {
  147. Py_RETURN_NONE;
  148. }
  149. }
  150. static PyObject* ZstdDecompressionWriter_false(PyObject* self, PyObject* args) {
  151. Py_RETURN_FALSE;
  152. }
  153. static PyObject* ZstdDecompressionWriter_true(PyObject* self, PyObject* args) {
  154. Py_RETURN_TRUE;
  155. }
  156. static PyObject* ZstdDecompressionWriter_unsupported(PyObject* self, PyObject* args, PyObject* kwargs) {
  157. PyObject* iomod;
  158. PyObject* exc;
  159. iomod = PyImport_ImportModule("io");
  160. if (NULL == iomod) {
  161. return NULL;
  162. }
  163. exc = PyObject_GetAttrString(iomod, "UnsupportedOperation");
  164. if (NULL == exc) {
  165. Py_DECREF(iomod);
  166. return NULL;
  167. }
  168. PyErr_SetNone(exc);
  169. Py_DECREF(exc);
  170. Py_DECREF(iomod);
  171. return NULL;
  172. }
  173. static PyMethodDef ZstdDecompressionWriter_methods[] = {
  174. { "__enter__", (PyCFunction)ZstdDecompressionWriter_enter, METH_NOARGS,
  175. PyDoc_STR("Enter a decompression context.") },
  176. { "__exit__", (PyCFunction)ZstdDecompressionWriter_exit, METH_VARARGS,
  177. PyDoc_STR("Exit a decompression context.") },
  178. { "memory_size", (PyCFunction)ZstdDecompressionWriter_memory_size, METH_NOARGS,
  179. PyDoc_STR("Obtain the memory size in bytes of the underlying decompressor.") },
  180. { "close", (PyCFunction)ZstdDecompressionWriter_close, METH_NOARGS, NULL },
  181. { "fileno", (PyCFunction)ZstdDecompressionWriter_fileno, METH_NOARGS, NULL },
  182. { "flush", (PyCFunction)ZstdDecompressionWriter_flush, METH_NOARGS, NULL },
  183. { "isatty", ZstdDecompressionWriter_false, METH_NOARGS, NULL },
  184. { "readable", ZstdDecompressionWriter_false, METH_NOARGS, NULL },
  185. { "readline", (PyCFunction)ZstdDecompressionWriter_unsupported, METH_VARARGS | METH_KEYWORDS, NULL },
  186. { "readlines", (PyCFunction)ZstdDecompressionWriter_unsupported, METH_VARARGS | METH_KEYWORDS, NULL },
  187. { "seek", (PyCFunction)ZstdDecompressionWriter_unsupported, METH_VARARGS | METH_KEYWORDS, NULL },
  188. { "seekable", ZstdDecompressionWriter_false, METH_NOARGS, NULL },
  189. { "tell", (PyCFunction)ZstdDecompressionWriter_unsupported, METH_VARARGS | METH_KEYWORDS, NULL },
  190. { "truncate", (PyCFunction)ZstdDecompressionWriter_unsupported, METH_VARARGS | METH_KEYWORDS, NULL },
  191. { "writable", ZstdDecompressionWriter_true, METH_NOARGS, NULL },
  192. { "writelines" , (PyCFunction)ZstdDecompressionWriter_unsupported, METH_VARARGS | METH_KEYWORDS, NULL },
  193. { "read", (PyCFunction)ZstdDecompressionWriter_unsupported, METH_VARARGS | METH_KEYWORDS, NULL },
  194. { "readall", (PyCFunction)ZstdDecompressionWriter_unsupported, METH_VARARGS | METH_KEYWORDS, NULL },
  195. { "readinto", (PyCFunction)ZstdDecompressionWriter_unsupported, METH_VARARGS | METH_KEYWORDS, NULL },
  196. { "write", (PyCFunction)ZstdDecompressionWriter_write, METH_VARARGS | METH_KEYWORDS,
  197. PyDoc_STR("Compress data") },
  198. { NULL, NULL }
  199. };
  200. static PyMemberDef ZstdDecompressionWriter_members[] = {
  201. { "closed", T_BOOL, offsetof(ZstdDecompressionWriter, closed), READONLY, NULL },
  202. { NULL }
  203. };
  204. PyTypeObject ZstdDecompressionWriterType = {
  205. PyVarObject_HEAD_INIT(NULL, 0)
  206. "zstd.ZstdDecompressionWriter", /* tp_name */
  207. sizeof(ZstdDecompressionWriter),/* tp_basicsize */
  208. 0, /* tp_itemsize */
  209. (destructor)ZstdDecompressionWriter_dealloc, /* tp_dealloc */
  210. 0, /* tp_print */
  211. 0, /* tp_getattr */
  212. 0, /* tp_setattr */
  213. 0, /* tp_compare */
  214. 0, /* tp_repr */
  215. 0, /* tp_as_number */
  216. 0, /* tp_as_sequence */
  217. 0, /* tp_as_mapping */
  218. 0, /* tp_hash */
  219. 0, /* tp_call */
  220. 0, /* tp_str */
  221. 0, /* tp_getattro */
  222. 0, /* tp_setattro */
  223. 0, /* tp_as_buffer */
  224. Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
  225. ZstdDecompressionWriter__doc, /* tp_doc */
  226. 0, /* tp_traverse */
  227. 0, /* tp_clear */
  228. 0, /* tp_richcompare */
  229. 0, /* tp_weaklistoffset */
  230. 0, /* tp_iter */
  231. 0, /* tp_iternext */
  232. ZstdDecompressionWriter_methods,/* tp_methods */
  233. ZstdDecompressionWriter_members,/* tp_members */
  234. 0, /* tp_getset */
  235. 0, /* tp_base */
  236. 0, /* tp_dict */
  237. 0, /* tp_descr_get */
  238. 0, /* tp_descr_set */
  239. 0, /* tp_dictoffset */
  240. 0, /* tp_init */
  241. 0, /* tp_alloc */
  242. PyType_GenericNew, /* tp_new */
  243. };
  244. void decompressionwriter_module_init(PyObject* mod) {
  245. Py_TYPE(&ZstdDecompressionWriterType) = &PyType_Type;
  246. if (PyType_Ready(&ZstdDecompressionWriterType) < 0) {
  247. return;
  248. }
  249. }