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 }