#define PY_SSIZE_T_CLEAN #include #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE #include #include "npy_config.h" #include "npy_pycompat.h" #include "hashdescr.h" /* * How does this work ? The hash is computed from a list which contains all the * information specific to a type. The hard work is to build the list * (_array_descr_walk). The list is built as follows: * * If the dtype is builtin (no fields, no subarray), then the list * contains 6 items which uniquely define one dtype (_array_descr_builtin) * * If the dtype is a compound array, one walk on each field. For each * field, we append title, names, offset to the final list used for * hashing, and then append the list recursively built for each * corresponding dtype (_array_descr_walk_fields) * * If the dtype is a subarray, one adds the shape tuple to the list, and * then append the list recursively built for each corresponding dtype * (_array_descr_walk_subarray) * */ static int _is_array_descr_builtin(PyArray_Descr* descr); static int _array_descr_walk(PyArray_Descr* descr, PyObject *l); static int _array_descr_walk_fields(PyObject *names, PyObject* fields, PyObject* l); static int _array_descr_builtin(PyArray_Descr* descr, PyObject *l); /* * normalize endian character: always return 'I', '<' or '>' */ static char _normalize_byteorder(char byteorder) { switch(byteorder) { case '=': if (PyArray_GetEndianness() == NPY_CPU_BIG) { return '>'; } else { return '<'; } default: return byteorder; } } /* * Return true if descr is a builtin type */ static int _is_array_descr_builtin(PyArray_Descr* descr) { if (descr->fields != NULL && descr->fields != Py_None) { return 0; } if (PyDataType_HASSUBARRAY(descr)) { return 0; } return 1; } /* * Add to l all the items which uniquely define a builtin type */ static int _array_descr_builtin(PyArray_Descr* descr, PyObject *l) { Py_ssize_t i; PyObject *t, *item; char nbyteorder = _normalize_byteorder(descr->byteorder); /* * For builtin type, hash relies on : kind + byteorder + flags + * type_num + elsize + alignment */ t = Py_BuildValue("(cccii)", descr->kind, nbyteorder, descr->flags, descr->elsize, descr->alignment); for(i = 0; i < PyTuple_Size(t); ++i) { item = PyTuple_GetItem(t, i); if (item == NULL) { PyErr_SetString(PyExc_SystemError, "(Hash) Error while computing builting hash"); goto clean_t; } PyList_Append(l, item); } Py_DECREF(t); return 0; clean_t: Py_DECREF(t); return -1; } /* * Walk inside the fields and add every item which will be used for hashing * into the list l * * Return 0 on success */ static int _array_descr_walk_fields(PyObject *names, PyObject* fields, PyObject* l) { PyObject *key, *value, *foffset, *fdescr, *ftitle; Py_ssize_t pos = 0; int st; if (!PyTuple_Check(names)) { PyErr_SetString(PyExc_SystemError, "(Hash) names is not a tuple ???"); return -1; } if (!PyDict_Check(fields)) { PyErr_SetString(PyExc_SystemError, "(Hash) fields is not a dict ???"); return -1; } for (pos = 0; pos < PyTuple_GET_SIZE(names); pos++) { /* * For each field, add the key + descr + offset to l */ key = PyTuple_GET_ITEM(names, pos); value = PyDict_GetItem(fields, key); /* XXX: are those checks necessary ? */ if (value == NULL) { PyErr_SetString(PyExc_SystemError, "(Hash) names and fields inconsistent ???"); return -1; } if (!PyUString_Check(key)) { PyErr_SetString(PyExc_SystemError, "(Hash) key of dtype dict not a string ???"); return -1; } if (!PyTuple_Check(value)) { PyErr_SetString(PyExc_SystemError, "(Hash) value of dtype dict not a dtype ???"); return -1; } if (PyTuple_GET_SIZE(value) < 2) { PyErr_SetString(PyExc_SystemError, "(Hash) Less than 2 items in dtype dict ???"); return -1; } PyList_Append(l, key); fdescr = PyTuple_GET_ITEM(value, 0); if (!PyArray_DescrCheck(fdescr)) { PyErr_SetString(PyExc_SystemError, "(Hash) First item in compound dtype tuple not a descr ???"); return -1; } else { Py_INCREF(fdescr); st = _array_descr_walk((PyArray_Descr*)fdescr, l); Py_DECREF(fdescr); if (st) { return -1; } } foffset = PyTuple_GET_ITEM(value, 1); if (!PyInt_Check(foffset)) { PyErr_SetString(PyExc_SystemError, "(Hash) Second item in compound dtype tuple not an int ???"); return -1; } else { PyList_Append(l, foffset); } if (PyTuple_GET_SIZE(value) > 2) { ftitle = PyTuple_GET_ITEM(value, 2); PyList_Append(l, ftitle); } } return 0; } /* * Walk into subarray, and add items for hashing in l * * Return 0 on success */ static int _array_descr_walk_subarray(PyArray_ArrayDescr* adescr, PyObject *l) { PyObject *item; Py_ssize_t i; int st; /* * Add shape and descr itself to the list of object to hash */ if (PyTuple_Check(adescr->shape)) { for(i = 0; i < PyTuple_Size(adescr->shape); ++i) { item = PyTuple_GetItem(adescr->shape, i); if (item == NULL) { PyErr_SetString(PyExc_SystemError, "(Hash) Error while getting shape item of subarray dtype ???"); return -1; } PyList_Append(l, item); } } else if (PyInt_Check(adescr->shape)) { PyList_Append(l, adescr->shape); } else { PyErr_SetString(PyExc_SystemError, "(Hash) Shape of subarray dtype neither a tuple or int ???"); return -1; } Py_INCREF(adescr->base); st = _array_descr_walk(adescr->base, l); Py_DECREF(adescr->base); return st; } /* * 'Root' function to walk into a dtype. May be called recursively */ static int _array_descr_walk(PyArray_Descr* descr, PyObject *l) { int st; if (_is_array_descr_builtin(descr)) { return _array_descr_builtin(descr, l); } else { if(descr->fields != NULL && descr->fields != Py_None) { st = _array_descr_walk_fields(descr->names, descr->fields, l); if (st) { return -1; } } if(PyDataType_HASSUBARRAY(descr)) { st = _array_descr_walk_subarray(descr->subarray, l); if (st) { return -1; } } } return 0; } /* * Return 0 if successful */ static int _PyArray_DescrHashImp(PyArray_Descr *descr, npy_hash_t *hash) { PyObject *l, *tl; int st; l = PyList_New(0); if (l == NULL) { return -1; } st = _array_descr_walk(descr, l); if (st) { Py_DECREF(l); return -1; } /* * Convert the list to tuple and compute the tuple hash using python * builtin function */ tl = PyList_AsTuple(l); Py_DECREF(l); if (tl == NULL) return -1; *hash = PyObject_Hash(tl); Py_DECREF(tl); if (*hash == -1) { /* XXX: does PyObject_Hash set an exception on failure ? */ #if 0 PyErr_SetString(PyExc_SystemError, "(Hash) Error while hashing final tuple"); #endif return -1; } return 0; } NPY_NO_EXPORT npy_hash_t PyArray_DescrHash(PyObject* odescr) { PyArray_Descr *descr; int st; if (!PyArray_DescrCheck(odescr)) { PyErr_SetString(PyExc_ValueError, "PyArray_DescrHash argument must be a type descriptor"); return -1; } descr = (PyArray_Descr*)odescr; if (descr->hash == -1) { st = _PyArray_DescrHashImp(descr, &descr->hash); if (st) { return -1; } } return descr->hash; }