Implement RPNIterExpr.shape() method + test + todo done

This commit is contained in:
Yann Weber 2023-09-09 14:31:11 +02:00
commit 3eed2b7f3e
11 changed files with 186 additions and 33 deletions

View file

@ -3,7 +3,7 @@ NASM=nasm
LD=ld
ifeq ($(DEBUG), 1)
CFLAGS=-ggdb -fPIC -Wall -DDEBUG
CFLAGS=-ggdb -fPIC -Wall -DDEBUG -Wsign-compare
LDFLAGS=-g
NASMCFLAGS=-g -f elf64
#PYTHON=python3dm

View file

@ -7,7 +7,6 @@
* @ingroup pymod_pyrpn_RPNMutationParams
*/
/** @todo implement GC with m_clear */
PyModuleDef rpnconstmodule = {
PyModuleDef_HEAD_INIT,
"pyrpn.const",
@ -138,10 +137,9 @@ int Py_rpnconst_add(PyObject* mod, const char* name, int value)
return 0;
}
/**@todo rename this function ! (bad prefix)
* @brief Module initialisation function
/**@brief Module initialisation function
* @return The initialized module */
PyObject *Py_rpnconst_init(void)
PyObject *rpnconst_init(void)
{
PyObject *mod;
mod = PyModule_Create(&rpnconstmodule);

View file

@ -80,6 +80,6 @@ extern rpn_mutation_params_t rpn_mutation_params_default;
/**@brief pyrpn.const module initialisation function
* @return The initialized module
* @ingroup pymod_pyrpn */
PyObject *Py_rpnconst_init(void);
PyObject *rpnconst_init(void);
#endif

View file

@ -12,6 +12,10 @@ static PyMethodDef RPNIterExpr_methods[] = {
METH_NOARGS,
"self, /",
"Get a name tuple with parameters"),
PYRPN_method("shape", rpnif_shape,
METH_NOARGS,
"self, /",
"Get the shape of the data buffer"),
PYRPN_method("keys", rpnif_keys,
METH_NOARGS,
"self, /",
@ -390,6 +394,64 @@ PyObject *rpnif_get_params(PyObject *self)
return res;
}
PyObject *rpnif_shape(PyObject *self)
{
PyRPNIterExpr_t *expr_self = (PyRPNIterExpr_t*)self;
rpn_if_default_data_t *params;
params = (rpn_if_default_data_t*)(expr_self->rif->params->data);
size_t res_sz;
switch(params->res_flag)
{
case RPN_IF_RES_RGB:
res_sz = 3;
break;
case RPN_IF_RES_RGBA:
res_sz = 4;
break;
default:
res_sz = 1;
break;
}
const size_t shape_sz = params->ndim + (res_sz > 1 ?1:0);
PyObject *ret = PyTuple_New(shape_sz);
if(!ret)
{
return NULL;
}
size_t i;
for(i = 0; i < params->ndim; i++)
{
size_t idx = params->pos_flag == RPN_IF_POSITION_XDIM ? i+1:i;
PyObject *d = PyLong_FromLong(params->size_lim[idx]);
if(!d)
{
goto item_err;
}
PyTuple_SET_ITEM(ret, i, d);
}
if(res_sz > 1)
{
PyObject *d = PyLong_FromLong(res_sz);
if(!d)
{
goto item_err;
}
PyTuple_SET_ITEM(ret, i, d);
}
return ret;
item_err:
Py_DECREF(ret);
return NULL;
}
PyObject *rpnif_step(PyObject *self, PyObject* opos)
{
PyRPNIterExpr_t *expr_self = (PyRPNIterExpr_t*)self;
@ -418,7 +480,7 @@ PyObject *rpnif_to_pos(PyObject *self, PyObject** argv, Py_ssize_t argc)
const short idx_off = rif_data->pos_flag == RPN_IF_POSITION_XDIM?1:0;
long long value;
if(argc != rif_data->ndim)
if((size_t)argc != rif_data->ndim)
{
PyErr_Format(PyExc_IndexError,
"Expression expect %lu dimentions coordinates,"
@ -430,7 +492,7 @@ PyObject *rpnif_to_pos(PyObject *self, PyObject** argv, Py_ssize_t argc)
rpn_value_t result=0;
size_t cur_dim_sz = 1;
for(Py_ssize_t i=0; i<rif_data->ndim; i++)
for(size_t i=0; i<rif_data->ndim; i++)
{
if(!PyLong_Check(argv[i]))
{
@ -439,8 +501,11 @@ PyObject *rpnif_to_pos(PyObject *self, PyObject** argv, Py_ssize_t argc)
return NULL;
}
value = PyLong_AsLongLong(argv[i]);
value = value >= 0?value:(-(rpn_value_t)-value)%rif_data->size_lim[i+idx_off];
if(value >= rif_data->size_lim[i+idx_off])
if(value < 0)
{
value = (-(rpn_value_t)-value)%rif_data->size_lim[i+idx_off];
}
if((size_t)value >= rif_data->size_lim[i+idx_off])
{
PyErr_Format(PyExc_IndexError,
"Coordinate %ld overflows : %R/%lu",
@ -875,7 +940,11 @@ int rpnif_getbuffer(PyObject *self, Py_buffer *view, int flags)
{
view->ndim++;
}
view->shape = malloc(sizeof(Py_ssize_t) * view->ndim); /**@todo check error*/
view->shape = malloc(sizeof(Py_ssize_t) * view->ndim);
if(!view->shape)
{
goto buff_error;
}
for(size_t i=0; i<expr_self->ndim; i++)
{
int idx = i;
@ -895,6 +964,12 @@ int rpnif_getbuffer(PyObject *self, Py_buffer *view, int flags)
Py_INCREF(self);
return 0;
buff_error:
PyErr_Format(PyExc_BufferError,
"Unable to provide buffer : %s", strerror(errno));
view->obj = NULL;
return -1;
}
void rpnif_releasebuffer(PyObject *self, Py_buffer *view)

View file

@ -124,6 +124,13 @@ void rpnif_releasebuffer(PyObject *self, Py_buffer *view);
*/
PyObject *rpnif_get_params(PyObject *self);
/**@brief Return a tuple with data buffer's shape
* @param self RPNIterExpr instance
* @return A new ref on a tuple
* @ingroup pymod_pyrpn_RPNExprIter
*/
PyObject *rpnif_shape(PyObject *self);
/**@brief Runs an IF on given position
* @param self RPNInterExpr instance
* @param pos The start position

View file

@ -61,7 +61,7 @@ PyInit_pyrpn(void)
if(mod == NULL) { return NULL; }
//init constants module
const_mod = Py_rpnconst_init();
const_mod = rpnconst_init();
if(const_mod == NULL)
{
goto fail_init;

View file

@ -332,7 +332,6 @@ PyObject* rpnexpr_getstate(PyObject *self, PyObject *noargs)
}
if(resbuf.token_sz)
{
/**@todo can lead to stack overflow on huge expressions ? */
rpn_token_t tokens[resbuf.token_sz];
bzero(tokens, sizeof(rpn_token_t) * resbuf.token_sz);
@ -576,7 +575,7 @@ PyObject* rpnexpr_token_item(PyObject *self, Py_ssize_t idx)
{
idx = expr_self->rpn->toks.tokens_sz - 1 + idx;
}
if(idx < 0 || idx >= expr_self->rpn->toks.tokens_sz)
if(idx < 0 || (size_t)idx >= expr_self->rpn->toks.tokens_sz)
{
PyErr_Format(PyExc_IndexError,
"No token %ld in expression of size %ld",
@ -596,7 +595,7 @@ int rpnexpr_token_ass_item(PyObject *self, Py_ssize_t idx, PyObject* elt)
{
idx = expr_self->rpn->toks.tokens_sz - 1 + idx;
}
if(idx < 0 || idx > expr_self->rpn->toks.tokens_sz)
if(idx < 0 || (size_t)idx > expr_self->rpn->toks.tokens_sz)
{
PyErr_Format(PyExc_IndexError,
"Cannot set token %ld in expression of size %ld",
@ -612,7 +611,7 @@ int rpnexpr_token_ass_item(PyObject *self, Py_ssize_t idx, PyObject* elt)
}
short new_elt = 0;
if(idx == expr_self->rpn->toks.tokens_sz)
if((size_t)idx == expr_self->rpn->toks.tokens_sz)
{
new_elt = 1;
expr_self->rpn->toks.tokens_sz++;

View file

@ -368,7 +368,7 @@ int rpntokenop_init(PyObject *_self, PyObject *args, PyObject *kwds)
PyErr_SetString(PyExc_ValueError, "Opcode cannot be negative");
return -1;
}
else if (opcode >= rpn_op_sz())
else if ((size_t)opcode >= rpn_op_sz())
{
PyErr_Format(PyExc_ValueError,
"Maximum opcode is %ld but %ld given",

View file

@ -255,22 +255,21 @@ int rpn_if_setres_default(rpn_if_t *rif, size_t *pos)
rpn_if_default_data_t *data;
size_t cur_arg, i, rgb_imax;
rpn_value_t *values;
rpn_value_t *res = rif->rpn_res;
data = (rpn_if_default_data_t*)rif->params->data;
switch(data->pos_flag)
{
case RPN_IF_POSITION_LINEAR:
rpn_if_resf_linear(rif, pos, res);
rpn_if_resf_linear(rif, pos);
cur_arg = 1;
break;
case RPN_IF_POSITION_XY:
rpn_if_resf_xy(rif, pos, res);
rpn_if_resf_xy(rif, pos);
cur_arg = 2;
break;
case RPN_IF_POSITION_XDIM:
rpn_if_resf_xdim(rif, pos, res);
rpn_if_resf_xdim(rif, pos);
cur_arg = *(data->size_lim);
break;
default:
@ -283,19 +282,21 @@ int rpn_if_setres_default(rpn_if_t *rif, size_t *pos)
}
rgb_imax = 3; /* rgb */
values = rpn_if_getitem(rif, *pos);
/**@todo if(res) set the values in res too ! */
switch(data->res_flag)
{
case RPN_IF_RES_BOOL:
*values = 1;
*rif->rpn_res = *values;
break;
case RPN_IF_RES_CONST:
*values = *(data->const_val);
*rif->rpn_res = *values;
break;
case RPN_IF_RES_COUNT:
(*values)++;
*rif->rpn_res = *values;
break;
case RPN_IF_RES_CONST_RGBA:
@ -304,6 +305,7 @@ int rpn_if_setres_default(rpn_if_t *rif, size_t *pos)
for(i=0;i<rgb_imax;i++)
{
values[i] = data->const_val[i];
rif->rpn_res[i] = values[i];
}
break;
@ -346,7 +348,7 @@ int rpn_if_argf_linear(rpn_if_t *rif, size_t pos, rpn_value_t *args)
return 0;
}
int rpn_if_resf_linear(rpn_if_t *rif, size_t *pos, rpn_value_t *_data)
int rpn_if_resf_linear(rpn_if_t *rif, size_t *pos)
{
rpn_if_default_data_t *data;
size_t res;
@ -386,7 +388,7 @@ int rpn_if_argf_xy(rpn_if_t *rif, size_t pos, rpn_value_t *args)
return 0;
}
int rpn_if_resf_xy(rpn_if_t *rif, size_t *pos, rpn_value_t *_data)
int rpn_if_resf_xy(rpn_if_t *rif, size_t *pos)
{
rpn_if_default_data_t *data;
size_t xy[2];
@ -429,7 +431,6 @@ int rpn_if_argf_xdim(rpn_if_t *rif, size_t pos, rpn_value_t *args)
{
return -1;
}
/**@todo check if *(data->size_lim) overflow rif->params->rpn_argc */
curdim_sz = 1;
curpos = pos;
@ -451,7 +452,7 @@ int rpn_if_argf_xdim(rpn_if_t *rif, size_t pos, rpn_value_t *args)
return 0;
}
int rpn_if_resf_xdim(rpn_if_t *rif, size_t *pos, rpn_value_t *_data)
int rpn_if_resf_xdim(rpn_if_t *rif, size_t *pos)
{
rpn_if_default_data_t *data;
size_t i, res, cur, curlim, dim_sz;
@ -467,7 +468,6 @@ int rpn_if_resf_xdim(rpn_if_t *rif, size_t *pos, rpn_value_t *_data)
{
return -1;
}
/**@todo check if *(data->size_lim) overflow rif->params->rpn_argc */
dim_sz = 1;
for(i=0; i < *(data->size_lim); i++)

View file

@ -166,12 +166,11 @@ int rpn_if_argf_linear(rpn_if_t *rif, size_t pos, rpn_value_t *args);
/**@brief Transform 1st expression result to position
* @param rif Expressions
* @param pos Pointer on resulting position in memory map
* @param data Pointer on resulting data
* @return 0 or -1 on error
* @note Data from position fecth is done by generic @ref rpn_if_setres_default
* function
*/
int rpn_if_resf_linear(rpn_if_t *rif, size_t *pos, rpn_value_t *data);
int rpn_if_resf_linear(rpn_if_t *rif, size_t *pos);
/**@brief Set the 1st & 2nd argument from position
* @param rif Expressions
@ -185,12 +184,11 @@ int rpn_if_argf_xy(rpn_if_t *rif, size_t pos, rpn_value_t *args);
/**@brief Transform 1st and 2nd result into a memory map's offset
* @param rif Expressions
* @param pos Memory map offset pointer
* @param data Pointer on resulting data
* @return 0 or -1 on error
* @note Data from position fetch is done by generic @ref rpn_if_setres_default
* function
*/
int rpn_if_resf_xy(rpn_if_t *rif, size_t *pos, rpn_value_t *data);
int rpn_if_resf_xy(rpn_if_t *rif, size_t *pos);
/**@brief Set X first arguments from position
* @param rif Expressions
@ -204,11 +202,10 @@ int rpn_if_argf_xdim(rpn_if_t *rif, size_t pos, rpn_value_t *args);
/**@brief Transform X arguments into a memory map's offset
* @param rif Expressions
* @param pos Memory map offset pointer
* @param data Pointer on resulting data
* @return 0 or -1 on error
* @note Data from position fetch is done by generic @ref rpn_if_setres_default
* function
*/
int rpn_if_resf_xdim(rpn_if_t *rif, size_t *pos, rpn_value_t *data);
int rpn_if_resf_xdim(rpn_if_t *rif, size_t *pos);
#endif

View file

@ -124,6 +124,29 @@ class TestRPNIterInit(unittest.TestCase):
self.assertEqual(params.size_lim, arg[2])
self.assertIsNone(params.const_values)
def test_shape(self):
""" Test the shape method """
tests = [((pyrpn.const.POS_XY,
pyrpn.const.RESULT_COUNT,
(640,480)), (640,480)),
((pyrpn.const.POS_LINEAR,
pyrpn.const.RESULT_BOOL,
(1024,)), (1024,)),
((pyrpn.const.POS_LINEAR,
pyrpn.const.RESULT_RGBA,
(1024,)), (1024,4)),
((pyrpn.const.POS_XDIM,
pyrpn.const.RESULT_BOOL,
(2,640,480)), (640,480)),
((pyrpn.const.POS_XDIM,
pyrpn.const.RESULT_RGB,
(5,13,37,13,12,42)), (13,37,13,12,42,3)),
]
for args, expt in tests:
with self.subTest(args=args, expt_shape=expt):
rif = pyrpn.RPNIterExpr(*args)
self.assertEqual(rif.shape(), expt)
@skipIfNoNumpy()
def test_buffer(self):
""" Test the len on buffer interface using numpy """
@ -445,6 +468,60 @@ class TestRPNIterConst(unittest.TestCase):
expt_pos = rif.to_pos(*expt)
self.assertEqual(pos, expt_pos)
def test_linear_rgb(self):
rif = pyrpn.RPNIterExpr(pyrpn.const.POS_LINEAR,
pyrpn.const.RESULT_RGB,
(512,))
rif['X'] = 'A0 2 +'
rif['R'] = 'A0 1 +'
rif['G'] = 'A1 A2 + 255 %'
rif['B'] = 'A2 2 * A3 +'
pos = rif.step(0)
self.assertEqual(pos, 2)
data = np.frombuffer(rif, dtype=np.uint64).reshape(rif.shape())
colors = list(data[2])
self.assertEqual(colors, [1,0,0])
pos = rif.step(2)
self.assertEqual(pos, 4)
data = np.frombuffer(rif, dtype=np.uint64).reshape(rif.shape())
colors = list(data[4])
self.assertEqual(colors, [3, 1, 0])
def test_linear_rgba(self):
rif = pyrpn.RPNIterExpr(pyrpn.const.POS_LINEAR,
pyrpn.const.RESULT_RGBA,
(512,))
rif['X'] = 'A0 2 +'
rif['R'] = 'A0 1 +'
rif['G'] = 'A1 A2 + 255 %'
rif['B'] = 'A2 2 * A3 +'
rif['A'] = 'A1 A2 A3 + +'
pos = rif.step(0)
self.assertEqual(pos, 2)
data = np.frombuffer(rif, dtype=np.uint64).reshape(rif.shape())
colors = list(data[2])
self.assertEqual(colors, [1,0,0,0])
pos = rif.step(2)
self.assertEqual(pos, 4)
data = np.frombuffer(rif, dtype=np.uint64).reshape(rif.shape())
colors = list(data[4])
self.assertEqual(colors, [3, 1, 0, 1])
pos = rif.step(4)
self.assertEqual(pos, 6)
data = np.frombuffer(rif, dtype=np.uint64).reshape(rif.shape())
colors = list(data[6])
self.assertEqual(colors, [5, 4, 2, 4])
pos = rif.step(6)
self.assertEqual(pos, 8)
data = np.frombuffer(rif, dtype=np.uint64).reshape(rif.shape())
colors = list(data[8])
self.assertEqual(colors, [7, 9, 10, 11])
if __name__ == '__main__':