compressionreader.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  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 set_unsupported_operation(void) {
  11. PyObject* iomod;
  12. PyObject* exc;
  13. iomod = PyImport_ImportModule("io");
  14. if (NULL == iomod) {
  15. return;
  16. }
  17. exc = PyObject_GetAttrString(iomod, "UnsupportedOperation");
  18. if (NULL == exc) {
  19. Py_DECREF(iomod);
  20. return;
  21. }
  22. PyErr_SetNone(exc);
  23. Py_DECREF(exc);
  24. Py_DECREF(iomod);
  25. }
  26. static void reader_dealloc(ZstdCompressionReader* self) {
  27. Py_XDECREF(self->compressor);
  28. Py_XDECREF(self->reader);
  29. if (self->buffer.buf) {
  30. PyBuffer_Release(&self->buffer);
  31. memset(&self->buffer, 0, sizeof(self->buffer));
  32. }
  33. PyObject_Del(self);
  34. }
  35. static ZstdCompressionReader* reader_enter(ZstdCompressionReader* self) {
  36. if (self->entered) {
  37. PyErr_SetString(PyExc_ValueError, "cannot __enter__ multiple times");
  38. return NULL;
  39. }
  40. self->entered = 1;
  41. Py_INCREF(self);
  42. return self;
  43. }
  44. static PyObject* reader_exit(ZstdCompressionReader* self, PyObject* args) {
  45. PyObject* exc_type;
  46. PyObject* exc_value;
  47. PyObject* exc_tb;
  48. if (!PyArg_ParseTuple(args, "OOO:__exit__", &exc_type, &exc_value, &exc_tb)) {
  49. return NULL;
  50. }
  51. self->entered = 0;
  52. self->closed = 1;
  53. /* Release resources associated with source. */
  54. Py_CLEAR(self->reader);
  55. if (self->buffer.buf) {
  56. PyBuffer_Release(&self->buffer);
  57. memset(&self->buffer, 0, sizeof(self->buffer));
  58. }
  59. Py_CLEAR(self->compressor);
  60. Py_RETURN_FALSE;
  61. }
  62. static PyObject* reader_readable(ZstdCompressionReader* self) {
  63. Py_RETURN_TRUE;
  64. }
  65. static PyObject* reader_writable(ZstdCompressionReader* self) {
  66. Py_RETURN_FALSE;
  67. }
  68. static PyObject* reader_seekable(ZstdCompressionReader* self) {
  69. Py_RETURN_FALSE;
  70. }
  71. static PyObject* reader_readline(PyObject* self, PyObject* args) {
  72. set_unsupported_operation();
  73. return NULL;
  74. }
  75. static PyObject* reader_readlines(PyObject* self, PyObject* args) {
  76. set_unsupported_operation();
  77. return NULL;
  78. }
  79. static PyObject* reader_write(PyObject* self, PyObject* args) {
  80. PyErr_SetString(PyExc_OSError, "stream is not writable");
  81. return NULL;
  82. }
  83. static PyObject* reader_writelines(PyObject* self, PyObject* args) {
  84. PyErr_SetString(PyExc_OSError, "stream is not writable");
  85. return NULL;
  86. }
  87. static PyObject* reader_isatty(PyObject* self) {
  88. Py_RETURN_FALSE;
  89. }
  90. static PyObject* reader_flush(PyObject* self) {
  91. Py_RETURN_NONE;
  92. }
  93. static PyObject* reader_close(ZstdCompressionReader* self) {
  94. self->closed = 1;
  95. Py_RETURN_NONE;
  96. }
  97. static PyObject* reader_tell(ZstdCompressionReader* self) {
  98. /* TODO should this raise OSError since stream isn't seekable? */
  99. return PyLong_FromUnsignedLongLong(self->bytesCompressed);
  100. }
  101. int read_compressor_input(ZstdCompressionReader* self) {
  102. if (self->finishedInput) {
  103. return 0;
  104. }
  105. if (self->input.pos != self->input.size) {
  106. return 0;
  107. }
  108. if (self->reader) {
  109. Py_buffer buffer;
  110. assert(self->readResult == NULL);
  111. self->readResult = PyObject_CallMethod(self->reader, "read",
  112. "k", self->readSize);
  113. if (NULL == self->readResult) {
  114. return -1;
  115. }
  116. memset(&buffer, 0, sizeof(buffer));
  117. if (0 != PyObject_GetBuffer(self->readResult, &buffer, PyBUF_CONTIG_RO)) {
  118. return -1;
  119. }
  120. /* EOF */
  121. if (0 == buffer.len) {
  122. self->finishedInput = 1;
  123. Py_CLEAR(self->readResult);
  124. }
  125. else {
  126. self->input.src = buffer.buf;
  127. self->input.size = buffer.len;
  128. self->input.pos = 0;
  129. }
  130. PyBuffer_Release(&buffer);
  131. }
  132. else {
  133. assert(self->buffer.buf);
  134. self->input.src = self->buffer.buf;
  135. self->input.size = self->buffer.len;
  136. self->input.pos = 0;
  137. }
  138. return 1;
  139. }
  140. int compress_input(ZstdCompressionReader* self, ZSTD_outBuffer* output) {
  141. size_t oldPos;
  142. size_t zresult;
  143. /* If we have data left over, consume it. */
  144. if (self->input.pos < self->input.size) {
  145. oldPos = output->pos;
  146. Py_BEGIN_ALLOW_THREADS
  147. zresult = ZSTD_compressStream2(self->compressor->cctx,
  148. output, &self->input, ZSTD_e_continue);
  149. Py_END_ALLOW_THREADS
  150. self->bytesCompressed += output->pos - oldPos;
  151. /* Input exhausted. Clear out state tracking. */
  152. if (self->input.pos == self->input.size) {
  153. memset(&self->input, 0, sizeof(self->input));
  154. Py_CLEAR(self->readResult);
  155. if (self->buffer.buf) {
  156. self->finishedInput = 1;
  157. }
  158. }
  159. if (ZSTD_isError(zresult)) {
  160. PyErr_Format(ZstdError, "zstd compress error: %s", ZSTD_getErrorName(zresult));
  161. return -1;
  162. }
  163. }
  164. if (output->pos && output->pos == output->size) {
  165. return 1;
  166. }
  167. else {
  168. return 0;
  169. }
  170. }
  171. static PyObject* reader_read(ZstdCompressionReader* self, PyObject* args, PyObject* kwargs) {
  172. static char* kwlist[] = {
  173. "size",
  174. NULL
  175. };
  176. Py_ssize_t size = -1;
  177. PyObject* result = NULL;
  178. char* resultBuffer;
  179. Py_ssize_t resultSize;
  180. size_t zresult;
  181. size_t oldPos;
  182. int readResult, compressResult;
  183. if (self->closed) {
  184. PyErr_SetString(PyExc_ValueError, "stream is closed");
  185. return NULL;
  186. }
  187. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|n", kwlist, &size)) {
  188. return NULL;
  189. }
  190. if (size < -1) {
  191. PyErr_SetString(PyExc_ValueError, "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. self->output.dst = resultBuffer;
  206. self->output.size = resultSize;
  207. self->output.pos = 0;
  208. readinput:
  209. compressResult = compress_input(self, &self->output);
  210. if (-1 == compressResult) {
  211. Py_XDECREF(result);
  212. return NULL;
  213. }
  214. else if (0 == compressResult) {
  215. /* There is room in the output. We fall through to below, which will
  216. * either get more input for us or will attempt to end the stream.
  217. */
  218. }
  219. else if (1 == compressResult) {
  220. memset(&self->output, 0, sizeof(self->output));
  221. return result;
  222. }
  223. else {
  224. assert(0);
  225. }
  226. readResult = read_compressor_input(self);
  227. if (-1 == readResult) {
  228. return NULL;
  229. }
  230. else if (0 == readResult) { }
  231. else if (1 == readResult) { }
  232. else {
  233. assert(0);
  234. }
  235. if (self->input.size) {
  236. goto readinput;
  237. }
  238. /* Else EOF */
  239. oldPos = self->output.pos;
  240. zresult = ZSTD_compressStream2(self->compressor->cctx, &self->output,
  241. &self->input, ZSTD_e_end);
  242. self->bytesCompressed += self->output.pos - oldPos;
  243. if (ZSTD_isError(zresult)) {
  244. PyErr_Format(ZstdError, "error ending compression stream: %s",
  245. ZSTD_getErrorName(zresult));
  246. Py_XDECREF(result);
  247. return NULL;
  248. }
  249. assert(self->output.pos);
  250. if (0 == zresult) {
  251. self->finishedOutput = 1;
  252. }
  253. if (safe_pybytes_resize(&result, self->output.pos)) {
  254. Py_XDECREF(result);
  255. return NULL;
  256. }
  257. memset(&self->output, 0, sizeof(self->output));
  258. return result;
  259. }
  260. static PyObject* reader_read1(ZstdCompressionReader* self, PyObject* args, PyObject* kwargs) {
  261. static char* kwlist[] = {
  262. "size",
  263. NULL
  264. };
  265. Py_ssize_t size = -1;
  266. PyObject* result = NULL;
  267. char* resultBuffer;
  268. Py_ssize_t resultSize;
  269. ZSTD_outBuffer output;
  270. int compressResult;
  271. size_t oldPos;
  272. size_t zresult;
  273. if (self->closed) {
  274. PyErr_SetString(PyExc_ValueError, "stream is closed");
  275. return NULL;
  276. }
  277. if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|n:read1", kwlist, &size)) {
  278. return NULL;
  279. }
  280. if (size < -1) {
  281. PyErr_SetString(PyExc_ValueError, "cannot read negative amounts less than -1");
  282. return NULL;
  283. }
  284. if (self->finishedOutput || size == 0) {
  285. return PyBytes_FromStringAndSize("", 0);
  286. }
  287. if (size == -1) {
  288. size = ZSTD_CStreamOutSize();
  289. }
  290. result = PyBytes_FromStringAndSize(NULL, size);
  291. if (NULL == result) {
  292. return NULL;
  293. }
  294. PyBytes_AsStringAndSize(result, &resultBuffer, &resultSize);
  295. output.dst = resultBuffer;
  296. output.size = resultSize;
  297. output.pos = 0;
  298. /* read1() is supposed to use at most 1 read() from the underlying stream.
  299. However, we can't satisfy this requirement with compression because
  300. not every input will generate output. We /could/ flush the compressor,
  301. but this may not be desirable. We allow multiple read() from the
  302. underlying stream. But unlike read(), we return as soon as output data
  303. is available.
  304. */
  305. compressResult = compress_input(self, &output);
  306. if (-1 == compressResult) {
  307. Py_XDECREF(result);
  308. return NULL;
  309. }
  310. else if (0 == compressResult || 1 == compressResult) { }
  311. else {
  312. assert(0);
  313. }
  314. if (output.pos) {
  315. goto finally;
  316. }
  317. while (!self->finishedInput) {
  318. int readResult = read_compressor_input(self);
  319. if (-1 == readResult) {
  320. Py_XDECREF(result);
  321. return NULL;
  322. }
  323. else if (0 == readResult || 1 == readResult) { }
  324. else {
  325. assert(0);
  326. }
  327. compressResult = compress_input(self, &output);
  328. if (-1 == compressResult) {
  329. Py_XDECREF(result);
  330. return NULL;
  331. }
  332. else if (0 == compressResult || 1 == compressResult) { }
  333. else {
  334. assert(0);
  335. }
  336. if (output.pos) {
  337. goto finally;
  338. }
  339. }
  340. /* EOF */
  341. oldPos = output.pos;
  342. zresult = ZSTD_compressStream2(self->compressor->cctx, &output, &self->input,
  343. ZSTD_e_end);
  344. self->bytesCompressed += output.pos - oldPos;
  345. if (ZSTD_isError(zresult)) {
  346. PyErr_Format(ZstdError, "error ending compression stream: %s",
  347. ZSTD_getErrorName(zresult));
  348. Py_XDECREF(result);
  349. return NULL;
  350. }
  351. if (zresult == 0) {
  352. self->finishedOutput = 1;
  353. }
  354. finally:
  355. if (result) {
  356. if (safe_pybytes_resize(&result, output.pos)) {
  357. Py_XDECREF(result);
  358. return NULL;
  359. }
  360. }
  361. return result;
  362. }
  363. static PyObject* reader_readall(PyObject* self) {
  364. PyObject* chunks = NULL;
  365. PyObject* empty = NULL;
  366. PyObject* result = NULL;
  367. /* Our strategy is to collect chunks into a list then join all the
  368. * chunks at the end. We could potentially use e.g. an io.BytesIO. But
  369. * this feels simple enough to implement and avoids potentially expensive
  370. * reallocations of large buffers.
  371. */
  372. chunks = PyList_New(0);
  373. if (NULL == chunks) {
  374. return NULL;
  375. }
  376. while (1) {
  377. PyObject* chunk = PyObject_CallMethod(self, "read", "i", 1048576);
  378. if (NULL == chunk) {
  379. Py_DECREF(chunks);
  380. return NULL;
  381. }
  382. if (!PyBytes_Size(chunk)) {
  383. Py_DECREF(chunk);
  384. break;
  385. }
  386. if (PyList_Append(chunks, chunk)) {
  387. Py_DECREF(chunk);
  388. Py_DECREF(chunks);
  389. return NULL;
  390. }
  391. Py_DECREF(chunk);
  392. }
  393. empty = PyBytes_FromStringAndSize("", 0);
  394. if (NULL == empty) {
  395. Py_DECREF(chunks);
  396. return NULL;
  397. }
  398. result = PyObject_CallMethod(empty, "join", "O", chunks);
  399. Py_DECREF(empty);
  400. Py_DECREF(chunks);
  401. return result;
  402. }
  403. static PyObject* reader_readinto(ZstdCompressionReader* self, PyObject* args) {
  404. Py_buffer dest;
  405. ZSTD_outBuffer output;
  406. int readResult, compressResult;
  407. PyObject* result = NULL;
  408. size_t zresult;
  409. size_t oldPos;
  410. if (self->closed) {
  411. PyErr_SetString(PyExc_ValueError, "stream is closed");
  412. return NULL;
  413. }
  414. if (self->finishedOutput) {
  415. return PyLong_FromLong(0);
  416. }
  417. if (!PyArg_ParseTuple(args, "w*:readinto", &dest)) {
  418. return NULL;
  419. }
  420. if (!PyBuffer_IsContiguous(&dest, 'C') || dest.ndim > 1) {
  421. PyErr_SetString(PyExc_ValueError,
  422. "destination buffer should be contiguous and have at most one dimension");
  423. goto finally;
  424. }
  425. output.dst = dest.buf;
  426. output.size = dest.len;
  427. output.pos = 0;
  428. compressResult = compress_input(self, &output);
  429. if (-1 == compressResult) {
  430. goto finally;
  431. }
  432. else if (0 == compressResult) { }
  433. else if (1 == compressResult) {
  434. result = PyLong_FromSize_t(output.pos);
  435. goto finally;
  436. }
  437. else {
  438. assert(0);
  439. }
  440. while (!self->finishedInput) {
  441. readResult = read_compressor_input(self);
  442. if (-1 == readResult) {
  443. goto finally;
  444. }
  445. else if (0 == readResult || 1 == readResult) {}
  446. else {
  447. assert(0);
  448. }
  449. compressResult = compress_input(self, &output);
  450. if (-1 == compressResult) {
  451. goto finally;
  452. }
  453. else if (0 == compressResult) { }
  454. else if (1 == compressResult) {
  455. result = PyLong_FromSize_t(output.pos);
  456. goto finally;
  457. }
  458. else {
  459. assert(0);
  460. }
  461. }
  462. /* EOF */
  463. oldPos = output.pos;
  464. zresult = ZSTD_compressStream2(self->compressor->cctx, &output, &self->input,
  465. ZSTD_e_end);
  466. self->bytesCompressed += self->output.pos - oldPos;
  467. if (ZSTD_isError(zresult)) {
  468. PyErr_Format(ZstdError, "error ending compression stream: %s",
  469. ZSTD_getErrorName(zresult));
  470. goto finally;
  471. }
  472. assert(output.pos);
  473. if (0 == zresult) {
  474. self->finishedOutput = 1;
  475. }
  476. result = PyLong_FromSize_t(output.pos);
  477. finally:
  478. PyBuffer_Release(&dest);
  479. return result;
  480. }
  481. static PyObject* reader_readinto1(ZstdCompressionReader* self, PyObject* args) {
  482. Py_buffer dest;
  483. PyObject* result = NULL;
  484. ZSTD_outBuffer output;
  485. int compressResult;
  486. size_t oldPos;
  487. size_t zresult;
  488. if (self->closed) {
  489. PyErr_SetString(PyExc_ValueError, "stream is closed");
  490. return NULL;
  491. }
  492. if (self->finishedOutput) {
  493. return PyLong_FromLong(0);
  494. }
  495. if (!PyArg_ParseTuple(args, "w*:readinto1", &dest)) {
  496. return NULL;
  497. }
  498. if (!PyBuffer_IsContiguous(&dest, 'C') || dest.ndim > 1) {
  499. PyErr_SetString(PyExc_ValueError,
  500. "destination buffer should be contiguous and have at most one dimension");
  501. goto finally;
  502. }
  503. output.dst = dest.buf;
  504. output.size = dest.len;
  505. output.pos = 0;
  506. compressResult = compress_input(self, &output);
  507. if (-1 == compressResult) {
  508. goto finally;
  509. }
  510. else if (0 == compressResult || 1 == compressResult) { }
  511. else {
  512. assert(0);
  513. }
  514. if (output.pos) {
  515. result = PyLong_FromSize_t(output.pos);
  516. goto finally;
  517. }
  518. while (!self->finishedInput) {
  519. int readResult = read_compressor_input(self);
  520. if (-1 == readResult) {
  521. goto finally;
  522. }
  523. else if (0 == readResult || 1 == readResult) { }
  524. else {
  525. assert(0);
  526. }
  527. compressResult = compress_input(self, &output);
  528. if (-1 == compressResult) {
  529. goto finally;
  530. }
  531. else if (0 == compressResult) { }
  532. else if (1 == compressResult) {
  533. result = PyLong_FromSize_t(output.pos);
  534. goto finally;
  535. }
  536. else {
  537. assert(0);
  538. }
  539. /* If we produced output and we're not done with input, emit
  540. * that output now, as we've hit restrictions of read1().
  541. */
  542. if (output.pos && !self->finishedInput) {
  543. result = PyLong_FromSize_t(output.pos);
  544. goto finally;
  545. }
  546. /* Otherwise we either have no output or we've exhausted the
  547. * input. Either we try to get more input or we fall through
  548. * to EOF below */
  549. }
  550. /* EOF */
  551. oldPos = output.pos;
  552. zresult = ZSTD_compressStream2(self->compressor->cctx, &output, &self->input,
  553. ZSTD_e_end);
  554. self->bytesCompressed += self->output.pos - oldPos;
  555. if (ZSTD_isError(zresult)) {
  556. PyErr_Format(ZstdError, "error ending compression stream: %s",
  557. ZSTD_getErrorName(zresult));
  558. goto finally;
  559. }
  560. assert(output.pos);
  561. if (0 == zresult) {
  562. self->finishedOutput = 1;
  563. }
  564. result = PyLong_FromSize_t(output.pos);
  565. finally:
  566. PyBuffer_Release(&dest);
  567. return result;
  568. }
  569. static PyObject* reader_iter(PyObject* self) {
  570. set_unsupported_operation();
  571. return NULL;
  572. }
  573. static PyObject* reader_iternext(PyObject* self) {
  574. set_unsupported_operation();
  575. return NULL;
  576. }
  577. static PyMethodDef reader_methods[] = {
  578. { "__enter__", (PyCFunction)reader_enter, METH_NOARGS,
  579. PyDoc_STR("Enter a compression context") },
  580. { "__exit__", (PyCFunction)reader_exit, METH_VARARGS,
  581. PyDoc_STR("Exit a compression context") },
  582. { "close", (PyCFunction)reader_close, METH_NOARGS,
  583. PyDoc_STR("Close the stream so it cannot perform any more operations") },
  584. { "flush", (PyCFunction)reader_flush, METH_NOARGS, PyDoc_STR("no-ops") },
  585. { "isatty", (PyCFunction)reader_isatty, METH_NOARGS, PyDoc_STR("Returns False") },
  586. { "readable", (PyCFunction)reader_readable, METH_NOARGS,
  587. PyDoc_STR("Returns True") },
  588. { "read", (PyCFunction)reader_read, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read compressed data") },
  589. { "read1", (PyCFunction)reader_read1, METH_VARARGS | METH_KEYWORDS, NULL },
  590. { "readall", (PyCFunction)reader_readall, METH_NOARGS, PyDoc_STR("Not implemented") },
  591. { "readinto", (PyCFunction)reader_readinto, METH_VARARGS, NULL },
  592. { "readinto1", (PyCFunction)reader_readinto1, METH_VARARGS, NULL },
  593. { "readline", (PyCFunction)reader_readline, METH_VARARGS, PyDoc_STR("Not implemented") },
  594. { "readlines", (PyCFunction)reader_readlines, METH_VARARGS, PyDoc_STR("Not implemented") },
  595. { "seekable", (PyCFunction)reader_seekable, METH_NOARGS,
  596. PyDoc_STR("Returns False") },
  597. { "tell", (PyCFunction)reader_tell, METH_NOARGS,
  598. PyDoc_STR("Returns current number of bytes compressed") },
  599. { "writable", (PyCFunction)reader_writable, METH_NOARGS,
  600. PyDoc_STR("Returns False") },
  601. { "write", reader_write, METH_VARARGS, PyDoc_STR("Raises OSError") },
  602. { "writelines", reader_writelines, METH_VARARGS, PyDoc_STR("Not implemented") },
  603. { NULL, NULL }
  604. };
  605. static PyMemberDef reader_members[] = {
  606. { "closed", T_BOOL, offsetof(ZstdCompressionReader, closed),
  607. READONLY, "whether stream is closed" },
  608. { NULL }
  609. };
  610. PyTypeObject ZstdCompressionReaderType = {
  611. PyVarObject_HEAD_INIT(NULL, 0)
  612. "zstd.ZstdCompressionReader", /* tp_name */
  613. sizeof(ZstdCompressionReader), /* tp_basicsize */
  614. 0, /* tp_itemsize */
  615. (destructor)reader_dealloc, /* tp_dealloc */
  616. 0, /* tp_print */
  617. 0, /* tp_getattr */
  618. 0, /* tp_setattr */
  619. 0, /* tp_compare */
  620. 0, /* tp_repr */
  621. 0, /* tp_as_number */
  622. 0, /* tp_as_sequence */
  623. 0, /* tp_as_mapping */
  624. 0, /* tp_hash */
  625. 0, /* tp_call */
  626. 0, /* tp_str */
  627. 0, /* tp_getattro */
  628. 0, /* tp_setattro */
  629. 0, /* tp_as_buffer */
  630. Py_TPFLAGS_DEFAULT, /* tp_flags */
  631. 0, /* tp_doc */
  632. 0, /* tp_traverse */
  633. 0, /* tp_clear */
  634. 0, /* tp_richcompare */
  635. 0, /* tp_weaklistoffset */
  636. reader_iter, /* tp_iter */
  637. reader_iternext, /* tp_iternext */
  638. reader_methods, /* tp_methods */
  639. reader_members, /* tp_members */
  640. 0, /* tp_getset */
  641. 0, /* tp_base */
  642. 0, /* tp_dict */
  643. 0, /* tp_descr_get */
  644. 0, /* tp_descr_set */
  645. 0, /* tp_dictoffset */
  646. 0, /* tp_init */
  647. 0, /* tp_alloc */
  648. PyType_GenericNew, /* tp_new */
  649. };
  650. void compressionreader_module_init(PyObject* mod) {
  651. /* TODO make reader a sub-class of io.RawIOBase */
  652. Py_TYPE(&ZstdCompressionReaderType) = &PyType_Type;
  653. if (PyType_Ready(&ZstdCompressionReaderType) < 0) {
  654. return;
  655. }
  656. }