1 /*
2 * reading_generator_t object and methods implementation for ijson's C backend
3 *
4 * Contributed by Rodrigo Tobar <rtobar@icrar.org>
5 *
6 * ICRAR - International Centre for Radio Astronomy Research
7 * (c) UWA - The University of Western Australia, 2020
8 * Copyright by UWA (in the framework of the ICRAR)
9 */
10
11 #include <assert.h>
12
13 #include "basic_parse_basecoro.h"
14 #include "common.h"
15 #include "reading_generator.h"
16
17
reading_generator_init(reading_generator_t * self,PyObject * args,pipeline_node * coro_pipeline)18 int reading_generator_init(reading_generator_t *self, PyObject *args, pipeline_node *coro_pipeline)
19 {
20 PyObject *file;
21 Py_ssize_t buf_size = 64 * 1024;
22 M1_Z(PyArg_ParseTuple(args, "On", &file, &buf_size));
23
24 // Handle both "read" and "readinto" functions.
25 // The latter allocates a bytearray, which is how we distinguish between
26 // the two cases later
27 if (PyObject_HasAttrString(file, "readinto")) {
28 M1_N(self->read_func = PyObject_GetAttrString(file, "readinto"));
29 PyObject *pbuf_size = Py_BuildValue("n", buf_size);
30 self->buffer = PyObject_CallFunctionObjArgs((PyObject *)&PyByteArray_Type, pbuf_size, NULL);
31 M1_N(self->buffer);
32 Py_DECREF(pbuf_size);
33 }
34 else {
35 M1_N(self->read_func = PyObject_GetAttrString(file, "read"));
36 self->buf_size = PyLong_FromSsize_t(buf_size);
37 self->buffer = NULL;
38 }
39
40 M1_N(self->events = PyList_New(0));
41 self->pos = 0;
42 self->finished = 0;
43
44 M1_N(self->coro = chain(self->events, coro_pipeline));
45 assert(("reading_generator works only with basic_parse_basecoro",
46 BasicParseBasecoro_Check(self->coro)));
47 return 0;
48 }
49
reading_generator_dealloc(reading_generator_t * self)50 void reading_generator_dealloc(reading_generator_t *self)
51 {
52 Py_XDECREF(self->read_func);
53 Py_XDECREF(self->events);
54 Py_XDECREF(self->buffer);
55 Py_XDECREF(self->buf_size);
56 Py_XDECREF(self->coro);
57 }
58
reading_generator_next(reading_generator_t * self)59 PyObject *reading_generator_next(reading_generator_t *self)
60 {
61 PyObject *events = self->events;
62 Py_ssize_t nevents = PyList_Size(events);
63 BasicParseBasecoro *basic_parse_basecoro = (BasicParseBasecoro *)self->coro;
64 while (nevents == 0) {
65
66 /* Read data and pass it down to the co-routine */
67 Py_buffer view;
68 Py_ssize_t length;
69 if (self->buffer == NULL) {
70 // read_func is "read"
71 PyObject *pbuffer = PyObject_CallFunctionObjArgs(self->read_func, self->buf_size, NULL);
72 N_N(pbuffer);
73 N_M1(PyObject_GetBuffer(pbuffer, &view, PyBUF_SIMPLE));
74 length = view.len;
75 PyObject *send_res = ijson_yajl_parse(basic_parse_basecoro->h, view.buf, view.len);
76 Py_DECREF(pbuffer);
77 N_N(send_res);
78 }
79 else {
80 // read_func is "readinto"
81 PyObject *plength = PyObject_CallFunctionObjArgs(self->read_func, self->buffer, NULL);
82 N_N(plength);
83 length = PyLong_AsLong(plength);
84 N_M1(length);
85 Py_DECREF(plength);
86 N_M1(PyObject_GetBuffer(self->buffer, &view, PyBUF_SIMPLE));
87 N_N(ijson_yajl_parse(basic_parse_basecoro->h, view.buf, length));
88 }
89 PyBuffer_Release(&view);
90 nevents = PyList_Size(events);
91
92 if (length == 0) {
93 break;
94 }
95 }
96
97 // events are now probably available
98 if (nevents > 0) {
99 PyObject *val = PyList_GetItem(events, self->pos++);
100 Py_INCREF(val);
101
102 /* empty the list if fully iterated over */
103 if (self->pos == nevents) {
104 self->pos = 0;
105 N_M1(PySequence_DelSlice(events, 0, nevents));
106 }
107 return val;
108 }
109
110 // no events, let's end the show
111 PyErr_SetNone(PyExc_StopIteration);
112 return NULL;
113 }