/********************************************************************* Numexpr - Fast numerical array expression evaluator for NumPy. License: MIT Author: See AUTHORS.txt See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "module.hpp" #include #include "numexpr_config.hpp" #include "interpreter.hpp" #include "numexpr_object.hpp" static int size_from_char(char c) { switch (c) { case 'b': return sizeof(char); case 'i': return sizeof(int); case 'l': return sizeof(long long); case 'f': return sizeof(float); case 'd': return sizeof(double); case 'c': return 2*sizeof(double); case 's': return 0; /* strings are ok but size must be computed */ default: PyErr_SetString(PyExc_TypeError, "signature value not in 'bilfdcs'"); return -1; } } static void NumExpr_dealloc(NumExprObject *self) { Py_XDECREF(self->signature); Py_XDECREF(self->tempsig); Py_XDECREF(self->constsig); Py_XDECREF(self->fullsig); Py_XDECREF(self->program); Py_XDECREF(self->constants); Py_XDECREF(self->input_names); PyMem_Del(self->mem); PyMem_Del(self->rawmem); PyMem_Del(self->memsteps); PyMem_Del(self->memsizes); Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject * NumExpr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { NumExprObject *self = (NumExprObject *)type->tp_alloc(type, 0); if (self != NULL) { #define INIT_WITH(name, object) \ self->name = object; \ if (!self->name) { \ Py_DECREF(self); \ return NULL; \ } INIT_WITH(signature, PyBytes_FromString("")); INIT_WITH(tempsig, PyBytes_FromString("")); INIT_WITH(constsig, PyBytes_FromString("")); INIT_WITH(fullsig, PyBytes_FromString("")); INIT_WITH(program, PyBytes_FromString("")); INIT_WITH(constants, PyTuple_New(0)); Py_INCREF(Py_None); self->input_names = Py_None; self->mem = NULL; self->rawmem = NULL; self->memsteps = NULL; self->memsizes = NULL; self->rawmemsize = 0; self->n_inputs = 0; self->n_constants = 0; self->n_temps = 0; #undef INIT_WITH } return (PyObject *)self; } static int NumExpr_init(NumExprObject *self, PyObject *args, PyObject *kwds) { int i, j, mem_offset; int n_inputs, n_constants, n_temps; PyObject *signature = NULL, *tempsig = NULL, *constsig = NULL; PyObject *fullsig = NULL, *program = NULL, *constants = NULL; PyObject *input_names = NULL, *o_constants = NULL; int *itemsizes = NULL; char **mem = NULL, *rawmem = NULL; npy_intp *memsteps; npy_intp *memsizes; int rawmemsize; static char *kwlist[] = {"signature", "tempsig", "program", "constants", "input_names", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "SSS|OO", kwlist, &signature, &tempsig, &program, &o_constants, &input_names)) { return -1; } n_inputs = (int)PyBytes_Size(signature); n_temps = (int)PyBytes_Size(tempsig); if (o_constants) { if (!PySequence_Check(o_constants) ) { PyErr_SetString(PyExc_TypeError, "constants must be a sequence"); return -1; } n_constants = (int)PySequence_Length(o_constants); if (!(constants = PyTuple_New(n_constants))) return -1; if (!(constsig = PyBytes_FromStringAndSize(NULL, n_constants))) { Py_DECREF(constants); return -1; } if (!(itemsizes = PyMem_New(int, n_constants))) { Py_DECREF(constants); return -1; } for (i = 0; i < n_constants; i++) { PyObject *o; if (!(o = PySequence_GetItem(o_constants, i))) { /* new reference */ Py_DECREF(constants); Py_DECREF(constsig); PyMem_Del(itemsizes); return -1; } PyTuple_SET_ITEM(constants, i, o); /* steals reference */ if (PyBool_Check(o)) { PyBytes_AS_STRING(constsig)[i] = 'b'; itemsizes[i] = size_from_char('b'); continue; } #if PY_MAJOR_VERSION < 3 if (PyInt_Check(o)) { #else if (PyArray_IsScalar(o, Int32)) { #endif PyBytes_AS_STRING(constsig)[i] = 'i'; itemsizes[i] = size_from_char('i'); continue; } #if PY_MAJOR_VERSION < 3 if (PyLong_Check(o)) { #else if (PyArray_IsScalar(o, Int64)) { #endif PyBytes_AS_STRING(constsig)[i] = 'l'; itemsizes[i] = size_from_char('l'); continue; } /* The Float32 scalars are the only ones that should reach here */ if (PyArray_IsScalar(o, Float32)) { PyBytes_AS_STRING(constsig)[i] = 'f'; itemsizes[i] = size_from_char('f'); continue; } if (PyFloat_Check(o)) { /* Python float constants are double precision by default */ PyBytes_AS_STRING(constsig)[i] = 'd'; itemsizes[i] = size_from_char('d'); continue; } if (PyComplex_Check(o)) { PyBytes_AS_STRING(constsig)[i] = 'c'; itemsizes[i] = size_from_char('c'); continue; } if (PyBytes_Check(o)) { PyBytes_AS_STRING(constsig)[i] = 's'; itemsizes[i] = (int)PyBytes_GET_SIZE(o); continue; } PyErr_SetString(PyExc_TypeError, "constants must be of type bool/int/long/float/double/complex/bytes"); Py_DECREF(constsig); Py_DECREF(constants); PyMem_Del(itemsizes); return -1; } } else { n_constants = 0; if (!(constants = PyTuple_New(0))) return -1; if (!(constsig = PyBytes_FromString(""))) { Py_DECREF(constants); return -1; } } fullsig = PyBytes_FromFormat("%c%s%s%s", get_return_sig(program), PyBytes_AS_STRING(signature), PyBytes_AS_STRING(constsig), PyBytes_AS_STRING(tempsig)); if (!fullsig) { Py_DECREF(constants); Py_DECREF(constsig); PyMem_Del(itemsizes); return -1; } if (!input_names) { input_names = Py_None; } /* Compute the size of registers. We leave temps out (will be malloc'ed later on). */ rawmemsize = 0; for (i = 0; i < n_constants; i++) rawmemsize += itemsizes[i]; rawmemsize *= BLOCK_SIZE1; mem = PyMem_New(char *, 1 + n_inputs + n_constants + n_temps); rawmem = PyMem_New(char, rawmemsize); memsteps = PyMem_New(npy_intp, 1 + n_inputs + n_constants + n_temps); memsizes = PyMem_New(npy_intp, 1 + n_inputs + n_constants + n_temps); if (!mem || !rawmem || !memsteps || !memsizes) { Py_DECREF(constants); Py_DECREF(constsig); Py_DECREF(fullsig); PyMem_Del(itemsizes); PyMem_Del(mem); PyMem_Del(rawmem); PyMem_Del(memsteps); PyMem_Del(memsizes); return -1; } /* 0 -> output [1, n_inputs+1) -> inputs [n_inputs+1, n_inputs+n_consts+1) -> constants [n_inputs+n_consts+1, n_inputs+n_consts+n_temps+1) -> temps */ /* Fill in 'mem' and 'rawmem' for constants */ mem_offset = 0; for (i = 0; i < n_constants; i++) { char c = PyBytes_AS_STRING(constsig)[i]; int size = itemsizes[i]; mem[i+n_inputs+1] = rawmem + mem_offset; mem_offset += BLOCK_SIZE1 * size; memsteps[i+n_inputs+1] = memsizes[i+n_inputs+1] = size; /* fill in the constants */ if (c == 'b') { char *bmem = (char*)mem[i+n_inputs+1]; char value = (char)PyLong_AsLong(PyTuple_GET_ITEM(constants, i)); for (j = 0; j < BLOCK_SIZE1; j++) { bmem[j] = value; } } else if (c == 'i') { int *imem = (int*)mem[i+n_inputs+1]; int value = (int)PyLong_AsLong(PyTuple_GET_ITEM(constants, i)); for (j = 0; j < BLOCK_SIZE1; j++) { imem[j] = value; } } else if (c == 'l') { long long *lmem = (long long*)mem[i+n_inputs+1]; long long value = PyLong_AsLongLong(PyTuple_GET_ITEM(constants, i)); for (j = 0; j < BLOCK_SIZE1; j++) { lmem[j] = value; } } else if (c == 'f') { /* In this particular case the constant is in a NumPy scalar and in a regular Python object */ float *fmem = (float*)mem[i+n_inputs+1]; float value = PyArrayScalar_VAL(PyTuple_GET_ITEM(constants, i), Float); for (j = 0; j < BLOCK_SIZE1; j++) { fmem[j] = value; } } else if (c == 'd') { double *dmem = (double*)mem[i+n_inputs+1]; double value = PyFloat_AS_DOUBLE(PyTuple_GET_ITEM(constants, i)); for (j = 0; j < BLOCK_SIZE1; j++) { dmem[j] = value; } } else if (c == 'c') { double *cmem = (double*)mem[i+n_inputs+1]; Py_complex value = PyComplex_AsCComplex(PyTuple_GET_ITEM(constants, i)); for (j = 0; j < 2*BLOCK_SIZE1; j+=2) { cmem[j] = value.real; cmem[j+1] = value.imag; } } else if (c == 's') { char *smem = (char*)mem[i+n_inputs+1]; char *value = PyBytes_AS_STRING(PyTuple_GET_ITEM(constants, i)); for (j = 0; j < size*BLOCK_SIZE1; j+=size) { memcpy(smem + j, value, size); } } } /* This is no longer needed since no unusual item sizes appear in temporaries (there are no string temporaries). */ PyMem_Del(itemsizes); /* Fill in 'memsteps' and 'memsizes' for temps */ for (i = 0; i < n_temps; i++) { char c = PyBytes_AS_STRING(tempsig)[i]; int size = size_from_char(c); memsteps[i+n_inputs+n_constants+1] = size; memsizes[i+n_inputs+n_constants+1] = size; } /* See if any errors occured (e.g., in size_from_char) or if mem_offset is wrong */ if (PyErr_Occurred() || mem_offset != rawmemsize) { if (mem_offset != rawmemsize) { PyErr_Format(PyExc_RuntimeError, "mem_offset does not match rawmemsize"); } Py_DECREF(constants); Py_DECREF(constsig); Py_DECREF(fullsig); PyMem_Del(mem); PyMem_Del(rawmem); PyMem_Del(memsteps); PyMem_Del(memsizes); return -1; } #define REPLACE_OBJ(arg) \ {PyObject *tmp = self->arg; \ self->arg = arg; \ Py_XDECREF(tmp);} #define INCREF_REPLACE_OBJ(arg) {Py_INCREF(arg); REPLACE_OBJ(arg);} #define REPLACE_MEM(arg) {PyMem_Del(self->arg); self->arg=arg;} INCREF_REPLACE_OBJ(signature); INCREF_REPLACE_OBJ(tempsig); REPLACE_OBJ(constsig); REPLACE_OBJ(fullsig); INCREF_REPLACE_OBJ(program); REPLACE_OBJ(constants); INCREF_REPLACE_OBJ(input_names); REPLACE_MEM(mem); REPLACE_MEM(rawmem); REPLACE_MEM(memsteps); REPLACE_MEM(memsizes); self->rawmemsize = rawmemsize; self->n_inputs = n_inputs; self->n_constants = n_constants; self->n_temps = n_temps; #undef REPLACE_OBJ #undef INCREF_REPLACE_OBJ #undef REPLACE_MEM return check_program(self); } static PyMethodDef NumExpr_methods[] = { {"run", (PyCFunction) NumExpr_run, METH_VARARGS|METH_KEYWORDS, NULL}, {NULL, NULL} }; static PyMemberDef NumExpr_members[] = { {"signature", T_OBJECT_EX, offsetof(NumExprObject, signature), READONLY, NULL}, {"constsig", T_OBJECT_EX, offsetof(NumExprObject, constsig), READONLY, NULL}, {"tempsig", T_OBJECT_EX, offsetof(NumExprObject, tempsig), READONLY, NULL}, {"fullsig", T_OBJECT_EX, offsetof(NumExprObject, fullsig), READONLY, NULL}, {"program", T_OBJECT_EX, offsetof(NumExprObject, program), READONLY, NULL}, {"constants", T_OBJECT_EX, offsetof(NumExprObject, constants), READONLY, NULL}, {"input_names", T_OBJECT, offsetof(NumExprObject, input_names), 0, NULL}, {NULL}, }; PyTypeObject NumExprType = { PyVarObject_HEAD_INIT(NULL, 0) "numexpr.NumExpr", /*tp_name*/ sizeof(NumExprObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)NumExpr_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ (ternaryfunc)NumExpr_run, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ "NumExpr objects", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ NumExpr_methods, /* tp_methods */ NumExpr_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)NumExpr_init, /* tp_init */ 0, /* tp_alloc */ NumExpr_new, /* tp_new */ };