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 LD=ld
ifeq ($(DEBUG), 1) ifeq ($(DEBUG), 1)
CFLAGS=-ggdb -fPIC -Wall -DDEBUG CFLAGS=-ggdb -fPIC -Wall -DDEBUG -Wsign-compare
LDFLAGS=-g LDFLAGS=-g
NASMCFLAGS=-g -f elf64 NASMCFLAGS=-g -f elf64
#PYTHON=python3dm #PYTHON=python3dm

View file

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

View file

@ -12,6 +12,10 @@ static PyMethodDef RPNIterExpr_methods[] = {
METH_NOARGS, METH_NOARGS,
"self, /", "self, /",
"Get a name tuple with parameters"), "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, PYRPN_method("keys", rpnif_keys,
METH_NOARGS, METH_NOARGS,
"self, /", "self, /",
@ -390,6 +394,64 @@ PyObject *rpnif_get_params(PyObject *self)
return res; 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) PyObject *rpnif_step(PyObject *self, PyObject* opos)
{ {
PyRPNIterExpr_t *expr_self = (PyRPNIterExpr_t*)self; 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; const short idx_off = rif_data->pos_flag == RPN_IF_POSITION_XDIM?1:0;
long long value; long long value;
if(argc != rif_data->ndim) if((size_t)argc != rif_data->ndim)
{ {
PyErr_Format(PyExc_IndexError, PyErr_Format(PyExc_IndexError,
"Expression expect %lu dimentions coordinates," "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; rpn_value_t result=0;
size_t cur_dim_sz = 1; 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])) if(!PyLong_Check(argv[i]))
{ {
@ -439,8 +501,11 @@ PyObject *rpnif_to_pos(PyObject *self, PyObject** argv, Py_ssize_t argc)
return NULL; return NULL;
} }
value = PyLong_AsLongLong(argv[i]); value = PyLong_AsLongLong(argv[i]);
value = value >= 0?value:(-(rpn_value_t)-value)%rif_data->size_lim[i+idx_off]; if(value < 0)
if(value >= rif_data->size_lim[i+idx_off]) {
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, PyErr_Format(PyExc_IndexError,
"Coordinate %ld overflows : %R/%lu", "Coordinate %ld overflows : %R/%lu",
@ -875,7 +940,11 @@ int rpnif_getbuffer(PyObject *self, Py_buffer *view, int flags)
{ {
view->ndim++; 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++) for(size_t i=0; i<expr_self->ndim; i++)
{ {
int idx = i; int idx = i;
@ -895,6 +964,12 @@ int rpnif_getbuffer(PyObject *self, Py_buffer *view, int flags)
Py_INCREF(self); Py_INCREF(self);
return 0; 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) 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); 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 /**@brief Runs an IF on given position
* @param self RPNInterExpr instance * @param self RPNInterExpr instance
* @param pos The start position * @param pos The start position

View file

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

View file

@ -332,7 +332,6 @@ PyObject* rpnexpr_getstate(PyObject *self, PyObject *noargs)
} }
if(resbuf.token_sz) if(resbuf.token_sz)
{ {
/**@todo can lead to stack overflow on huge expressions ? */
rpn_token_t tokens[resbuf.token_sz]; rpn_token_t tokens[resbuf.token_sz];
bzero(tokens, sizeof(rpn_token_t) * 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; 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, PyErr_Format(PyExc_IndexError,
"No token %ld in expression of size %ld", "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; 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, PyErr_Format(PyExc_IndexError,
"Cannot set token %ld in expression of size %ld", "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; 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; new_elt = 1;
expr_self->rpn->toks.tokens_sz++; 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"); PyErr_SetString(PyExc_ValueError, "Opcode cannot be negative");
return -1; return -1;
} }
else if (opcode >= rpn_op_sz()) else if ((size_t)opcode >= rpn_op_sz())
{ {
PyErr_Format(PyExc_ValueError, PyErr_Format(PyExc_ValueError,
"Maximum opcode is %ld but %ld given", "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; rpn_if_default_data_t *data;
size_t cur_arg, i, rgb_imax; size_t cur_arg, i, rgb_imax;
rpn_value_t *values; rpn_value_t *values;
rpn_value_t *res = rif->rpn_res;
data = (rpn_if_default_data_t*)rif->params->data; data = (rpn_if_default_data_t*)rif->params->data;
switch(data->pos_flag) switch(data->pos_flag)
{ {
case RPN_IF_POSITION_LINEAR: case RPN_IF_POSITION_LINEAR:
rpn_if_resf_linear(rif, pos, res); rpn_if_resf_linear(rif, pos);
cur_arg = 1; cur_arg = 1;
break; break;
case RPN_IF_POSITION_XY: case RPN_IF_POSITION_XY:
rpn_if_resf_xy(rif, pos, res); rpn_if_resf_xy(rif, pos);
cur_arg = 2; cur_arg = 2;
break; break;
case RPN_IF_POSITION_XDIM: case RPN_IF_POSITION_XDIM:
rpn_if_resf_xdim(rif, pos, res); rpn_if_resf_xdim(rif, pos);
cur_arg = *(data->size_lim); cur_arg = *(data->size_lim);
break; break;
default: default:
@ -283,19 +282,21 @@ int rpn_if_setres_default(rpn_if_t *rif, size_t *pos)
} }
rgb_imax = 3; /* rgb */ rgb_imax = 3; /* rgb */
values = rpn_if_getitem(rif, *pos); values = rpn_if_getitem(rif, *pos);
/**@todo if(res) set the values in res too ! */
switch(data->res_flag) switch(data->res_flag)
{ {
case RPN_IF_RES_BOOL: case RPN_IF_RES_BOOL:
*values = 1; *values = 1;
*rif->rpn_res = *values;
break; break;
case RPN_IF_RES_CONST: case RPN_IF_RES_CONST:
*values = *(data->const_val); *values = *(data->const_val);
*rif->rpn_res = *values;
break; break;
case RPN_IF_RES_COUNT: case RPN_IF_RES_COUNT:
(*values)++; (*values)++;
*rif->rpn_res = *values;
break; break;
case RPN_IF_RES_CONST_RGBA: 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++) for(i=0;i<rgb_imax;i++)
{ {
values[i] = data->const_val[i]; values[i] = data->const_val[i];
rif->rpn_res[i] = values[i];
} }
break; break;
@ -346,7 +348,7 @@ int rpn_if_argf_linear(rpn_if_t *rif, size_t pos, rpn_value_t *args)
return 0; 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; rpn_if_default_data_t *data;
size_t res; 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; 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; rpn_if_default_data_t *data;
size_t xy[2]; 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; return -1;
} }
/**@todo check if *(data->size_lim) overflow rif->params->rpn_argc */
curdim_sz = 1; curdim_sz = 1;
curpos = pos; curpos = pos;
@ -451,7 +452,7 @@ int rpn_if_argf_xdim(rpn_if_t *rif, size_t pos, rpn_value_t *args)
return 0; 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; rpn_if_default_data_t *data;
size_t i, res, cur, curlim, dim_sz; 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; return -1;
} }
/**@todo check if *(data->size_lim) overflow rif->params->rpn_argc */
dim_sz = 1; dim_sz = 1;
for(i=0; i < *(data->size_lim); i++) 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 /**@brief Transform 1st expression result to position
* @param rif Expressions * @param rif Expressions
* @param pos Pointer on resulting position in memory map * @param pos Pointer on resulting position in memory map
* @param data Pointer on resulting data
* @return 0 or -1 on error * @return 0 or -1 on error
* @note Data from position fecth is done by generic @ref rpn_if_setres_default * @note Data from position fecth is done by generic @ref rpn_if_setres_default
* function * 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 /**@brief Set the 1st & 2nd argument from position
* @param rif Expressions * @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 /**@brief Transform 1st and 2nd result into a memory map's offset
* @param rif Expressions * @param rif Expressions
* @param pos Memory map offset pointer * @param pos Memory map offset pointer
* @param data Pointer on resulting data
* @return 0 or -1 on error * @return 0 or -1 on error
* @note Data from position fetch is done by generic @ref rpn_if_setres_default * @note Data from position fetch is done by generic @ref rpn_if_setres_default
* function * 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 /**@brief Set X first arguments from position
* @param rif Expressions * @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 /**@brief Transform X arguments into a memory map's offset
* @param rif Expressions * @param rif Expressions
* @param pos Memory map offset pointer * @param pos Memory map offset pointer
* @param data Pointer on resulting data
* @return 0 or -1 on error * @return 0 or -1 on error
* @note Data from position fetch is done by generic @ref rpn_if_setres_default * @note Data from position fetch is done by generic @ref rpn_if_setres_default
* function * 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 #endif

View file

@ -124,6 +124,29 @@ class TestRPNIterInit(unittest.TestCase):
self.assertEqual(params.size_lim, arg[2]) self.assertEqual(params.size_lim, arg[2])
self.assertIsNone(params.const_values) 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() @skipIfNoNumpy()
def test_buffer(self): def test_buffer(self):
""" Test the len on buffer interface using numpy """ """ Test the len on buffer interface using numpy """
@ -445,6 +468,60 @@ class TestRPNIterConst(unittest.TestCase):
expt_pos = rif.to_pos(*expt) expt_pos = rif.to_pos(*expt)
self.assertEqual(pos, expt_pos) 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__': if __name__ == '__main__':