decompressobj.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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(DecompressionObj__doc__,
  11. "Perform decompression using a standard library compatible API.\n"
  12. );
  13. static void DecompressionObj_dealloc(ZstdDecompressionObj* self) {
  14. Py_XDECREF(self->decompressor);
  15. PyObject_Del(self);
  16. }
  17. static PyObject* DecompressionObj_decompress(ZstdDecompressionObj* self, PyObject* args, PyObject* kwargs) {
  18. static char* kwlist[] = {
  19. "data",
  20. NULL
  21. };
  22. Py_buffer source;
  23. size_t zresult;
  24. ZSTD_inBuffer input;
  25. ZSTD_outBuffer output;
  26. PyObject* result = NULL;
  27. Py_ssize_t resultSize = 0;
  28. output.dst = NULL;
  29. if (self->finished) {
  30. PyErr_SetString(ZstdError, "cannot use a decompressobj multiple times");
  31. return NULL;
  32. }
  33. #if PY_MAJOR_VERSION >= 3
  34. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*:decompress",
  35. #else
  36. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*:decompress",
  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. /* Special case of empty input. Output will always be empty. */
  47. if (source.len == 0) {
  48. result = PyBytes_FromString("");
  49. goto finally;
  50. }
  51. input.src = source.buf;
  52. input.size = source.len;
  53. input.pos = 0;
  54. output.dst = PyMem_Malloc(self->outSize);
  55. if (!output.dst) {
  56. PyErr_NoMemory();
  57. goto except;
  58. }
  59. output.size = self->outSize;
  60. output.pos = 0;
  61. while (1) {
  62. Py_BEGIN_ALLOW_THREADS
  63. zresult = ZSTD_decompressStream(self->decompressor->dctx, &output, &input);
  64. Py_END_ALLOW_THREADS
  65. if (ZSTD_isError(zresult)) {
  66. PyErr_Format(ZstdError, "zstd decompressor error: %s",
  67. ZSTD_getErrorName(zresult));
  68. goto except;
  69. }
  70. if (0 == zresult) {
  71. self->finished = 1;
  72. }
  73. if (output.pos) {
  74. if (result) {
  75. resultSize = PyBytes_GET_SIZE(result);
  76. if (-1 == safe_pybytes_resize(&result, resultSize + output.pos)) {
  77. Py_XDECREF(result);
  78. goto except;
  79. }
  80. memcpy(PyBytes_AS_STRING(result) + resultSize,
  81. output.dst, output.pos);
  82. }
  83. else {
  84. result = PyBytes_FromStringAndSize(output.dst, output.pos);
  85. if (!result) {
  86. goto except;
  87. }
  88. }
  89. }
  90. if (zresult == 0 || (input.pos == input.size && output.pos == 0)) {
  91. break;
  92. }
  93. output.pos = 0;
  94. }
  95. if (!result) {
  96. result = PyBytes_FromString("");
  97. }
  98. goto finally;
  99. except:
  100. Py_CLEAR(result);
  101. finally:
  102. PyMem_Free(output.dst);
  103. PyBuffer_Release(&source);
  104. return result;
  105. }
  106. static PyObject* DecompressionObj_flush(ZstdDecompressionObj* self, PyObject* args, PyObject* kwargs) {
  107. static char* kwlist[] = {
  108. "length",
  109. NULL
  110. };
  111. PyObject* length = NULL;
  112. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:flush", kwlist, &length)) {
  113. return NULL;
  114. }
  115. Py_RETURN_NONE;
  116. }
  117. static PyMethodDef DecompressionObj_methods[] = {
  118. { "decompress", (PyCFunction)DecompressionObj_decompress,
  119. METH_VARARGS | METH_KEYWORDS, PyDoc_STR("decompress data") },
  120. { "flush", (PyCFunction)DecompressionObj_flush,
  121. METH_VARARGS | METH_KEYWORDS, PyDoc_STR("no-op") },
  122. { NULL, NULL }
  123. };
  124. PyTypeObject ZstdDecompressionObjType = {
  125. PyVarObject_HEAD_INIT(NULL, 0)
  126. "zstd.ZstdDecompressionObj", /* tp_name */
  127. sizeof(ZstdDecompressionObj), /* tp_basicsize */
  128. 0, /* tp_itemsize */
  129. (destructor)DecompressionObj_dealloc, /* tp_dealloc */
  130. 0, /* tp_print */
  131. 0, /* tp_getattr */
  132. 0, /* tp_setattr */
  133. 0, /* tp_compare */
  134. 0, /* tp_repr */
  135. 0, /* tp_as_number */
  136. 0, /* tp_as_sequence */
  137. 0, /* tp_as_mapping */
  138. 0, /* tp_hash */
  139. 0, /* tp_call */
  140. 0, /* tp_str */
  141. 0, /* tp_getattro */
  142. 0, /* tp_setattro */
  143. 0, /* tp_as_buffer */
  144. Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
  145. DecompressionObj__doc__, /* tp_doc */
  146. 0, /* tp_traverse */
  147. 0, /* tp_clear */
  148. 0, /* tp_richcompare */
  149. 0, /* tp_weaklistoffset */
  150. 0, /* tp_iter */
  151. 0, /* tp_iternext */
  152. DecompressionObj_methods, /* tp_methods */
  153. 0, /* tp_members */
  154. 0, /* tp_getset */
  155. 0, /* tp_base */
  156. 0, /* tp_dict */
  157. 0, /* tp_descr_get */
  158. 0, /* tp_descr_set */
  159. 0, /* tp_dictoffset */
  160. 0, /* tp_init */
  161. 0, /* tp_alloc */
  162. PyType_GenericNew, /* tp_new */
  163. };
  164. void decompressobj_module_init(PyObject* module) {
  165. Py_TYPE(&ZstdDecompressionObjType) = &PyType_Type;
  166. if (PyType_Ready(&ZstdDecompressionObjType) < 0) {
  167. return;
  168. }
  169. }