decompressionreader.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780
  1. /**
  2. * Copyright (c) 2017-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 decompressionreader_dealloc(ZstdDecompressionReader *self) {
  11. Py_XDECREF(self->decompressor);
  12. Py_XDECREF(self->reader);
  13. if (self->buffer.buf) {
  14. PyBuffer_Release(&self->buffer);
  15. }
  16. Py_CLEAR(self->readResult);
  17. PyObject_Del(self);
  18. }
  19. static ZstdDecompressionReader *
  20. decompressionreader_enter(ZstdDecompressionReader *self) {
  21. if (self->entered) {
  22. PyErr_SetString(PyExc_ValueError, "cannot __enter__ multiple times");
  23. return NULL;
  24. }
  25. if (self->closed) {
  26. PyErr_SetString(PyExc_ValueError, "stream is closed");
  27. return NULL;
  28. }
  29. self->entered = 1;
  30. Py_INCREF(self);
  31. return self;
  32. }
  33. static PyObject *decompressionreader_exit(ZstdDecompressionReader *self,
  34. PyObject *args) {
  35. PyObject *exc_type;
  36. PyObject *exc_value;
  37. PyObject *exc_tb;
  38. if (!PyArg_ParseTuple(args, "OOO:__exit__", &exc_type, &exc_value,
  39. &exc_tb)) {
  40. return NULL;
  41. }
  42. self->entered = 0;
  43. if (NULL == PyObject_CallMethod((PyObject *)self, "close", NULL)) {
  44. return NULL;
  45. }
  46. /* Release resources. */
  47. Py_CLEAR(self->reader);
  48. if (self->buffer.buf) {
  49. PyBuffer_Release(&self->buffer);
  50. memset(&self->buffer, 0, sizeof(self->buffer));
  51. }
  52. Py_CLEAR(self->decompressor);
  53. Py_RETURN_FALSE;
  54. }
  55. static PyObject *decompressionreader_readable(PyObject *self) {
  56. Py_RETURN_TRUE;
  57. }
  58. static PyObject *decompressionreader_writable(PyObject *self) {
  59. Py_RETURN_FALSE;
  60. }
  61. static PyObject *decompressionreader_seekable(PyObject *self) {
  62. Py_RETURN_FALSE;
  63. }
  64. static PyObject *decompressionreader_close(ZstdDecompressionReader *self) {
  65. if (self->closed) {
  66. Py_RETURN_NONE;
  67. }
  68. self->closed = 1;
  69. if (self->closefd && self->reader != NULL &&
  70. PyObject_HasAttrString(self->reader, "close")) {
  71. return PyObject_CallMethod(self->reader, "close", NULL);
  72. }
  73. Py_RETURN_NONE;
  74. }
  75. static PyObject *decompressionreader_flush(PyObject *self) {
  76. Py_RETURN_NONE;
  77. }
  78. static PyObject *decompressionreader_isatty(PyObject *self) {
  79. Py_RETURN_FALSE;
  80. }
  81. /**
  82. * Read available input.
  83. *
  84. * Returns 0 if no data was added to input.
  85. * Returns 1 if new input data is available.
  86. * Returns -1 on error and sets a Python exception as a side-effect.
  87. */
  88. int read_decompressor_input(ZstdDecompressionReader *self) {
  89. if (self->finishedInput) {
  90. return 0;
  91. }
  92. if (self->input.pos != self->input.size) {
  93. return 0;
  94. }
  95. if (self->reader) {
  96. Py_buffer buffer;
  97. assert(self->readResult == NULL);
  98. self->readResult =
  99. PyObject_CallMethod(self->reader, "read", "k", self->readSize);
  100. if (NULL == self->readResult) {
  101. return -1;
  102. }
  103. memset(&buffer, 0, sizeof(buffer));
  104. if (0 !=
  105. PyObject_GetBuffer(self->readResult, &buffer, PyBUF_CONTIG_RO)) {
  106. return -1;
  107. }
  108. /* EOF */
  109. if (0 == buffer.len) {
  110. self->finishedInput = 1;
  111. Py_CLEAR(self->readResult);
  112. }
  113. else {
  114. self->input.src = buffer.buf;
  115. self->input.size = buffer.len;
  116. self->input.pos = 0;
  117. }
  118. PyBuffer_Release(&buffer);
  119. }
  120. else {
  121. assert(self->buffer.buf);
  122. /*
  123. * We should only get here once since expectation is we always
  124. * exhaust input buffer before reading again.
  125. */
  126. assert(self->input.src == NULL);
  127. self->input.src = self->buffer.buf;
  128. self->input.size = self->buffer.len;
  129. self->input.pos = 0;
  130. }
  131. return 1;
  132. }
  133. /**
  134. * Decompresses available input into an output buffer.
  135. *
  136. * Returns 0 if we need more input.
  137. * Returns 1 if output buffer should be emitted.
  138. * Returns -1 on error and sets a Python exception.
  139. */
  140. int decompress_input(ZstdDecompressionReader *self, ZSTD_outBuffer *output) {
  141. size_t zresult;
  142. if (self->input.pos >= self->input.size) {
  143. return 0;
  144. }
  145. Py_BEGIN_ALLOW_THREADS zresult =
  146. ZSTD_decompressStream(self->decompressor->dctx, output, &self->input);
  147. Py_END_ALLOW_THREADS
  148. /* Input exhausted. Clear our state tracking. */
  149. if (self->input.pos == self->input.size) {
  150. memset(&self->input, 0, sizeof(self->input));
  151. Py_CLEAR(self->readResult);
  152. if (self->buffer.buf) {
  153. self->finishedInput = 1;
  154. }
  155. }
  156. if (ZSTD_isError(zresult)) {
  157. PyErr_Format(ZstdError, "zstd decompress error: %s",
  158. ZSTD_getErrorName(zresult));
  159. return -1;
  160. }
  161. /* We fulfilled the full read request. Signal to emit. */
  162. if (output->pos && output->pos == output->size) {
  163. return 1;
  164. }
  165. /* We're at the end of a frame and we aren't allowed to return data
  166. spanning frames. */
  167. else if (output->pos && zresult == 0 && !self->readAcrossFrames) {
  168. return 1;
  169. }
  170. /* There is more room in the output. Signal to collect more data. */
  171. return 0;
  172. }
  173. static PyObject *decompressionreader_read(ZstdDecompressionReader *self,
  174. PyObject *args, PyObject *kwargs) {
  175. static char *kwlist[] = {"size", NULL};
  176. Py_ssize_t size = -1;
  177. PyObject *result = NULL;
  178. char *resultBuffer;
  179. Py_ssize_t resultSize;
  180. ZSTD_outBuffer output;
  181. int decompressResult, readResult;
  182. if (self->closed) {
  183. PyErr_SetString(PyExc_ValueError, "stream is closed");
  184. return NULL;
  185. }
  186. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|n", kwlist, &size)) {
  187. return NULL;
  188. }
  189. if (size < -1) {
  190. PyErr_SetString(PyExc_ValueError,
  191. "cannot read negative amounts less than -1");
  192. return NULL;
  193. }
  194. if (size == -1) {
  195. return PyObject_CallMethod((PyObject *)self, "readall", NULL);
  196. }
  197. if (self->finishedOutput || size == 0) {
  198. return PyBytes_FromStringAndSize("", 0);
  199. }
  200. result = PyBytes_FromStringAndSize(NULL, size);
  201. if (NULL == result) {
  202. return NULL;
  203. }
  204. PyBytes_AsStringAndSize(result, &resultBuffer, &resultSize);
  205. output.dst = resultBuffer;
  206. output.size = resultSize;
  207. output.pos = 0;
  208. readinput:
  209. decompressResult = decompress_input(self, &output);
  210. if (-1 == decompressResult) {
  211. Py_XDECREF(result);
  212. return NULL;
  213. }
  214. else if (0 == decompressResult) {
  215. }
  216. else if (1 == decompressResult) {
  217. self->bytesDecompressed += output.pos;
  218. if (output.pos != output.size) {
  219. if (safe_pybytes_resize(&result, output.pos)) {
  220. Py_XDECREF(result);
  221. return NULL;
  222. }
  223. }
  224. return result;
  225. }
  226. else {
  227. assert(0);
  228. }
  229. readResult = read_decompressor_input(self);
  230. if (-1 == readResult) {
  231. Py_XDECREF(result);
  232. return NULL;
  233. }
  234. else if (0 == readResult) {
  235. }
  236. else if (1 == readResult) {
  237. }
  238. else {
  239. assert(0);
  240. }
  241. if (self->input.size) {
  242. goto readinput;
  243. }
  244. /* EOF */
  245. self->bytesDecompressed += output.pos;
  246. if (safe_pybytes_resize(&result, output.pos)) {
  247. Py_XDECREF(result);
  248. return NULL;
  249. }
  250. return result;
  251. }
  252. static PyObject *decompressionreader_read1(ZstdDecompressionReader *self,
  253. PyObject *args, PyObject *kwargs) {
  254. static char *kwlist[] = {"size", NULL};
  255. Py_ssize_t size = -1;
  256. PyObject *result = NULL;
  257. char *resultBuffer;
  258. Py_ssize_t resultSize;
  259. ZSTD_outBuffer output;
  260. if (self->closed) {
  261. PyErr_SetString(PyExc_ValueError, "stream is closed");
  262. return NULL;
  263. }
  264. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|n", kwlist, &size)) {
  265. return NULL;
  266. }
  267. if (size < -1) {
  268. PyErr_SetString(PyExc_ValueError,
  269. "cannot read negative amounts less than -1");
  270. return NULL;
  271. }
  272. if (self->finishedOutput || size == 0) {
  273. return PyBytes_FromStringAndSize("", 0);
  274. }
  275. if (size == -1) {
  276. size = ZSTD_DStreamOutSize();
  277. }
  278. result = PyBytes_FromStringAndSize(NULL, size);
  279. if (NULL == result) {
  280. return NULL;
  281. }
  282. PyBytes_AsStringAndSize(result, &resultBuffer, &resultSize);
  283. output.dst = resultBuffer;
  284. output.size = resultSize;
  285. output.pos = 0;
  286. /* read1() is supposed to use at most 1 read() from the underlying stream.
  287. * However, we can't satisfy this requirement with decompression due to the
  288. * nature of how decompression works. Our strategy is to read + decompress
  289. * until we get any output, at which point we return. This satisfies the
  290. * intent of the read1() API to limit read operations.
  291. */
  292. while (!self->finishedInput) {
  293. int readResult, decompressResult;
  294. readResult = read_decompressor_input(self);
  295. if (-1 == readResult) {
  296. Py_XDECREF(result);
  297. return NULL;
  298. }
  299. else if (0 == readResult || 1 == readResult) {
  300. }
  301. else {
  302. assert(0);
  303. }
  304. decompressResult = decompress_input(self, &output);
  305. if (-1 == decompressResult) {
  306. Py_XDECREF(result);
  307. return NULL;
  308. }
  309. else if (0 == decompressResult || 1 == decompressResult) {
  310. }
  311. else {
  312. assert(0);
  313. }
  314. if (output.pos) {
  315. break;
  316. }
  317. }
  318. self->bytesDecompressed += output.pos;
  319. if (safe_pybytes_resize(&result, output.pos)) {
  320. Py_XDECREF(result);
  321. return NULL;
  322. }
  323. return result;
  324. }
  325. static PyObject *decompressionreader_readinto(ZstdDecompressionReader *self,
  326. PyObject *args) {
  327. Py_buffer dest;
  328. ZSTD_outBuffer output;
  329. int decompressResult, readResult;
  330. PyObject *result = NULL;
  331. if (self->closed) {
  332. PyErr_SetString(PyExc_ValueError, "stream is closed");
  333. return NULL;
  334. }
  335. if (self->finishedOutput) {
  336. return PyLong_FromLong(0);
  337. }
  338. if (!PyArg_ParseTuple(args, "w*:readinto", &dest)) {
  339. return NULL;
  340. }
  341. output.dst = dest.buf;
  342. output.size = dest.len;
  343. output.pos = 0;
  344. readinput:
  345. decompressResult = decompress_input(self, &output);
  346. if (-1 == decompressResult) {
  347. goto finally;
  348. }
  349. else if (0 == decompressResult) {
  350. }
  351. else if (1 == decompressResult) {
  352. self->bytesDecompressed += output.pos;
  353. result = PyLong_FromSize_t(output.pos);
  354. goto finally;
  355. }
  356. else {
  357. assert(0);
  358. }
  359. readResult = read_decompressor_input(self);
  360. if (-1 == readResult) {
  361. goto finally;
  362. }
  363. else if (0 == readResult) {
  364. }
  365. else if (1 == readResult) {
  366. }
  367. else {
  368. assert(0);
  369. }
  370. if (self->input.size) {
  371. goto readinput;
  372. }
  373. /* EOF */
  374. self->bytesDecompressed += output.pos;
  375. result = PyLong_FromSize_t(output.pos);
  376. finally:
  377. PyBuffer_Release(&dest);
  378. return result;
  379. }
  380. static PyObject *decompressionreader_readinto1(ZstdDecompressionReader *self,
  381. PyObject *args) {
  382. Py_buffer dest;
  383. ZSTD_outBuffer output;
  384. PyObject *result = NULL;
  385. if (self->closed) {
  386. PyErr_SetString(PyExc_ValueError, "stream is closed");
  387. return NULL;
  388. }
  389. if (self->finishedOutput) {
  390. return PyLong_FromLong(0);
  391. }
  392. if (!PyArg_ParseTuple(args, "w*:readinto1", &dest)) {
  393. return NULL;
  394. }
  395. output.dst = dest.buf;
  396. output.size = dest.len;
  397. output.pos = 0;
  398. while (!self->finishedInput && !self->finishedOutput) {
  399. int decompressResult, readResult;
  400. readResult = read_decompressor_input(self);
  401. if (-1 == readResult) {
  402. goto finally;
  403. }
  404. else if (0 == readResult || 1 == readResult) {
  405. }
  406. else {
  407. assert(0);
  408. }
  409. decompressResult = decompress_input(self, &output);
  410. if (-1 == decompressResult) {
  411. goto finally;
  412. }
  413. else if (0 == decompressResult || 1 == decompressResult) {
  414. }
  415. else {
  416. assert(0);
  417. }
  418. if (output.pos) {
  419. break;
  420. }
  421. }
  422. self->bytesDecompressed += output.pos;
  423. result = PyLong_FromSize_t(output.pos);
  424. finally:
  425. PyBuffer_Release(&dest);
  426. return result;
  427. }
  428. static PyObject *decompressionreader_readall(PyObject *self) {
  429. PyObject *chunks = NULL;
  430. PyObject *empty = NULL;
  431. PyObject *result = NULL;
  432. /* Our strategy is to collect chunks into a list then join all the
  433. * chunks at the end. We could potentially use e.g. an io.BytesIO. But
  434. * this feels simple enough to implement and avoids potentially expensive
  435. * reallocations of large buffers.
  436. */
  437. chunks = PyList_New(0);
  438. if (NULL == chunks) {
  439. return NULL;
  440. }
  441. while (1) {
  442. PyObject *chunk = PyObject_CallMethod(self, "read", "i", 1048576);
  443. if (NULL == chunk) {
  444. Py_DECREF(chunks);
  445. return NULL;
  446. }
  447. if (!PyBytes_Size(chunk)) {
  448. Py_DECREF(chunk);
  449. break;
  450. }
  451. if (PyList_Append(chunks, chunk)) {
  452. Py_DECREF(chunk);
  453. Py_DECREF(chunks);
  454. return NULL;
  455. }
  456. Py_DECREF(chunk);
  457. }
  458. empty = PyBytes_FromStringAndSize("", 0);
  459. if (NULL == empty) {
  460. Py_DECREF(chunks);
  461. return NULL;
  462. }
  463. result = PyObject_CallMethod(empty, "join", "O", chunks);
  464. Py_DECREF(empty);
  465. Py_DECREF(chunks);
  466. return result;
  467. }
  468. static PyObject *decompressionreader_readline(PyObject *self, PyObject *args,
  469. PyObject *kwargs) {
  470. set_io_unsupported_operation();
  471. return NULL;
  472. }
  473. static PyObject *decompressionreader_readlines(PyObject *self, PyObject *args,
  474. PyObject *kwargs) {
  475. set_io_unsupported_operation();
  476. return NULL;
  477. }
  478. static PyObject *decompressionreader_seek(ZstdDecompressionReader *self,
  479. PyObject *args) {
  480. Py_ssize_t pos;
  481. int whence = 0;
  482. unsigned long long readAmount = 0;
  483. size_t defaultOutSize = ZSTD_DStreamOutSize();
  484. if (self->closed) {
  485. PyErr_SetString(PyExc_ValueError, "stream is closed");
  486. return NULL;
  487. }
  488. if (!PyArg_ParseTuple(args, "n|i:seek", &pos, &whence)) {
  489. return NULL;
  490. }
  491. if (whence == SEEK_SET) {
  492. if (pos < 0) {
  493. PyErr_SetString(PyExc_OSError,
  494. "cannot seek to negative position with SEEK_SET");
  495. return NULL;
  496. }
  497. if ((unsigned long long)pos < self->bytesDecompressed) {
  498. PyErr_SetString(PyExc_OSError,
  499. "cannot seek zstd decompression stream backwards");
  500. return NULL;
  501. }
  502. readAmount = pos - self->bytesDecompressed;
  503. }
  504. else if (whence == SEEK_CUR) {
  505. if (pos < 0) {
  506. PyErr_SetString(PyExc_OSError,
  507. "cannot seek zstd decompression stream backwards");
  508. return NULL;
  509. }
  510. readAmount = pos;
  511. }
  512. else if (whence == SEEK_END) {
  513. /* We /could/ support this with pos==0. But let's not do that until
  514. someone needs it. */
  515. PyErr_SetString(
  516. PyExc_OSError,
  517. "zstd decompression streams cannot be seeked with SEEK_END");
  518. return NULL;
  519. }
  520. /* It is a bit inefficient to do this via the Python API. But since there
  521. is a bit of state tracking involved to read from this type, it is the
  522. easiest to implement. */
  523. while (readAmount) {
  524. Py_ssize_t readSize;
  525. PyObject *readResult = PyObject_CallMethod(
  526. (PyObject *)self, "read", "K",
  527. readAmount < defaultOutSize ? readAmount : defaultOutSize);
  528. if (!readResult) {
  529. return NULL;
  530. }
  531. readSize = PyBytes_GET_SIZE(readResult);
  532. Py_CLEAR(readResult);
  533. /* Empty read means EOF. */
  534. if (!readSize) {
  535. break;
  536. }
  537. readAmount -= readSize;
  538. }
  539. return PyLong_FromUnsignedLongLong(self->bytesDecompressed);
  540. }
  541. static PyObject *decompressionreader_tell(ZstdDecompressionReader *self) {
  542. /* TODO should this raise OSError since stream isn't seekable? */
  543. return PyLong_FromUnsignedLongLong(self->bytesDecompressed);
  544. }
  545. static PyObject *decompressionreader_write(PyObject *self, PyObject *args) {
  546. set_io_unsupported_operation();
  547. return NULL;
  548. }
  549. static PyObject *decompressionreader_writelines(PyObject *self,
  550. PyObject *args) {
  551. set_io_unsupported_operation();
  552. return NULL;
  553. }
  554. static PyObject *decompressionreader_iter(PyObject *self) {
  555. set_io_unsupported_operation();
  556. return NULL;
  557. }
  558. static PyObject *decompressionreader_iternext(PyObject *self) {
  559. set_io_unsupported_operation();
  560. return NULL;
  561. }
  562. static PyMethodDef decompressionreader_methods[] = {
  563. {"__enter__", (PyCFunction)decompressionreader_enter, METH_NOARGS,
  564. PyDoc_STR("Enter a compression context")},
  565. {"__exit__", (PyCFunction)decompressionreader_exit, METH_VARARGS,
  566. PyDoc_STR("Exit a compression context")},
  567. {"close", (PyCFunction)decompressionreader_close, METH_NOARGS,
  568. PyDoc_STR("Close the stream so it cannot perform any more operations")},
  569. {"flush", (PyCFunction)decompressionreader_flush, METH_NOARGS,
  570. PyDoc_STR("no-ops")},
  571. {"isatty", (PyCFunction)decompressionreader_isatty, METH_NOARGS,
  572. PyDoc_STR("Returns False")},
  573. {"readable", (PyCFunction)decompressionreader_readable, METH_NOARGS,
  574. PyDoc_STR("Returns True")},
  575. {"read", (PyCFunction)decompressionreader_read,
  576. METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read compressed data")},
  577. {"read1", (PyCFunction)decompressionreader_read1,
  578. METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read compressed data")},
  579. {"readinto", (PyCFunction)decompressionreader_readinto, METH_VARARGS, NULL},
  580. {"readinto1", (PyCFunction)decompressionreader_readinto1, METH_VARARGS,
  581. NULL},
  582. {"readall", (PyCFunction)decompressionreader_readall, METH_NOARGS,
  583. PyDoc_STR("Not implemented")},
  584. {"readline", (PyCFunction)decompressionreader_readline,
  585. METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Not implemented")},
  586. {"readlines", (PyCFunction)decompressionreader_readlines,
  587. METH_VARARGS | METH_KEYWORDS, PyDoc_STR("Not implemented")},
  588. {"seek", (PyCFunction)decompressionreader_seek, METH_VARARGS,
  589. PyDoc_STR("Seek the stream")},
  590. {"seekable", (PyCFunction)decompressionreader_seekable, METH_NOARGS,
  591. PyDoc_STR("Returns False")},
  592. {"tell", (PyCFunction)decompressionreader_tell, METH_NOARGS,
  593. PyDoc_STR("Returns current number of bytes compressed")},
  594. {"writable", (PyCFunction)decompressionreader_writable, METH_NOARGS,
  595. PyDoc_STR("Returns False")},
  596. {"write", (PyCFunction)decompressionreader_write, METH_VARARGS,
  597. PyDoc_STR("unsupported operation")},
  598. {"writelines", (PyCFunction)decompressionreader_writelines, METH_VARARGS,
  599. PyDoc_STR("unsupported operation")},
  600. {NULL, NULL}};
  601. static PyMemberDef decompressionreader_members[] = {
  602. {"closed", T_BOOL, offsetof(ZstdDecompressionReader, closed), READONLY,
  603. "whether stream is closed"},
  604. {NULL}};
  605. PyType_Slot ZstdDecompressionReaderSlots[] = {
  606. {Py_tp_dealloc, decompressionreader_dealloc},
  607. {Py_tp_iter, decompressionreader_iter},
  608. {Py_tp_iternext, decompressionreader_iternext},
  609. {Py_tp_methods, decompressionreader_methods},
  610. {Py_tp_members, decompressionreader_members},
  611. {Py_tp_new, PyType_GenericNew},
  612. {0, NULL},
  613. };
  614. PyType_Spec ZstdDecompressionReaderSpec = {
  615. "zstd.ZstdDecompressionReader",
  616. sizeof(ZstdDecompressionReader),
  617. 0,
  618. Py_TPFLAGS_DEFAULT,
  619. ZstdDecompressionReaderSlots,
  620. };
  621. PyTypeObject *ZstdDecompressionReaderType;
  622. void decompressionreader_module_init(PyObject *mod) {
  623. /* TODO make reader a sub-class of io.RawIOBase */
  624. ZstdDecompressionReaderType =
  625. (PyTypeObject *)PyType_FromSpec(&ZstdDecompressionReaderSpec);
  626. if (PyType_Ready(ZstdDecompressionReaderType) < 0) {
  627. return;
  628. }
  629. Py_INCREF((PyObject *)ZstdDecompressionReaderType);
  630. PyModule_AddObject(mod, "ZstdDecompressionReader",
  631. (PyObject *)ZstdDecompressionReaderType);
  632. }