1
#define PY_SSIZE_T_CLEAN
2
#include <Python.h>
3
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
4
#define _MULTIARRAYMODULE
5
#include <numpy/arrayobject.h>
6

7
#include "npy_config.h"
8

9
#include "npy_pycompat.h"
10

11
#include "hashdescr.h"
12

13
/*
14
 * How does this work ? The hash is computed from a list which contains all the
15
 * information specific to a type. The hard work is to build the list
16
 * (_array_descr_walk). The list is built as follows:
17
 *      * If the dtype is builtin (no fields, no subarray), then the list
18
 *      contains 6 items which uniquely define one dtype (_array_descr_builtin)
19
 *      * If the dtype is a compound array, one walk on each field. For each
20
 *      field, we append title, names, offset to the final list used for
21
 *      hashing, and then append the list recursively built for each
22
 *      corresponding dtype (_array_descr_walk_fields)
23
 *      * If the dtype is a subarray, one adds the shape tuple to the list, and
24
 *      then append the list recursively built for each corresponding dtype
25
 *      (_array_descr_walk_subarray)
26
 *
27
 */
28

29
static int _is_array_descr_builtin(PyArray_Descr* descr);
30
static int _array_descr_walk(PyArray_Descr* descr, PyObject *l);
31
static int _array_descr_walk_fields(PyObject *names, PyObject* fields, PyObject* l);
32
static int _array_descr_builtin(PyArray_Descr* descr, PyObject *l);
33

34
/*
35
 * normalize endian character: always return 'I', '<' or '>'
36
 */
37
static char _normalize_byteorder(char byteorder)
38
{
39 1
    switch(byteorder) {
40 1
        case '=':
41 1
            if (PyArray_GetEndianness() == NPY_CPU_BIG) {
42
                return '>';
43
            }
44
            else {
45
                return '<';
46
            }
47
        default:
48
            return byteorder;
49
    }
50
}
51

52
/*
53
 * Return true if descr is a builtin type
54
 */
55
static int _is_array_descr_builtin(PyArray_Descr* descr)
56
{
57 1
    if (descr->fields != NULL && descr->fields != Py_None) {
58
        return 0;
59
    }
60 1
    if (PyDataType_HASSUBARRAY(descr)) {
61
        return 0;
62
    }
63
    return 1;
64
}
65

66
/*
67
 * Add to l all the items which uniquely define a builtin type
68
 */
69 1
static int _array_descr_builtin(PyArray_Descr* descr, PyObject *l)
70
{
71
    Py_ssize_t i;
72
    PyObject *t, *item;
73 1
    char nbyteorder = _normalize_byteorder(descr->byteorder);
74

75
    /*
76
     * For builtin type, hash relies on : kind + byteorder + flags +
77
     * type_num + elsize + alignment
78
     */
79 1
    t = Py_BuildValue("(cccii)", descr->kind, nbyteorder,
80 1
            descr->flags, descr->elsize, descr->alignment);
81

82 1
    for(i = 0; i < PyTuple_Size(t); ++i) {
83 1
        item = PyTuple_GetItem(t, i);
84 1
        if (item == NULL) {
85 0
            PyErr_SetString(PyExc_SystemError,
86
                    "(Hash) Error while computing builting hash");
87
            goto clean_t;
88
        }
89 1
        PyList_Append(l, item);
90
    }
91

92 1
    Py_DECREF(t);
93
    return 0;
94

95 0
clean_t:
96 0
    Py_DECREF(t);
97
    return -1;
98
}
99

100
/*
101
 * Walk inside the fields and add every item which will be used for hashing
102
 * into the list l
103
 *
104
 * Return 0 on success
105
 */
106 1
static int _array_descr_walk_fields(PyObject *names, PyObject* fields, PyObject* l)
107
{
108
    PyObject *key, *value, *foffset, *fdescr, *ftitle;
109 1
    Py_ssize_t pos = 0;
110
    int st;
111

112 1
    if (!PyTuple_Check(names)) {
113 0
        PyErr_SetString(PyExc_SystemError,
114
                "(Hash) names is not a tuple ???");
115 0
        return -1;
116
    }
117 1
    if (!PyDict_Check(fields)) {
118 0
        PyErr_SetString(PyExc_SystemError,
119
                "(Hash) fields is not a dict ???");
120 0
        return -1;
121
    }
122

123 1
    for (pos = 0; pos < PyTuple_GET_SIZE(names); pos++) {
124
        /*
125
         * For each field, add the key + descr + offset to l
126
         */
127 1
        key = PyTuple_GET_ITEM(names, pos);
128 1
        value = PyDict_GetItem(fields, key);
129
        /* XXX: are those checks necessary ? */
130 1
        if (value == NULL) {
131 0
            PyErr_SetString(PyExc_SystemError,
132
                    "(Hash) names and fields inconsistent ???");
133 0
            return -1;
134
        }
135 1
        if (!PyUnicode_Check(key)) {
136 0
            PyErr_SetString(PyExc_SystemError,
137
                    "(Hash) key of dtype dict not a string ???");
138 0
            return -1;
139
        }
140 1
        if (!PyTuple_Check(value)) {
141 0
            PyErr_SetString(PyExc_SystemError,
142
                    "(Hash) value of dtype dict not a dtype ???");
143 0
            return -1;
144
        }
145 1
        if (PyTuple_GET_SIZE(value) < 2) {
146 0
            PyErr_SetString(PyExc_SystemError,
147
                    "(Hash) Less than 2 items in dtype dict ???");
148 0
            return -1;
149
        }
150 1
        PyList_Append(l, key);
151

152 1
        fdescr = PyTuple_GET_ITEM(value, 0);
153 1
        if (!PyArray_DescrCheck(fdescr)) {
154 0
            PyErr_SetString(PyExc_SystemError,
155
                    "(Hash) First item in compound dtype tuple not a descr ???");
156 0
            return -1;
157
        }
158
        else {
159 1
            Py_INCREF(fdescr);
160 1
            st = _array_descr_walk((PyArray_Descr*)fdescr, l);
161 1
            Py_DECREF(fdescr);
162 1
            if (st) {
163
                return -1;
164
            }
165
        }
166

167 1
        foffset = PyTuple_GET_ITEM(value, 1);
168 1
        if (!PyInt_Check(foffset)) {
169 0
            PyErr_SetString(PyExc_SystemError,
170
                    "(Hash) Second item in compound dtype tuple not an int ???");
171 0
            return -1;
172
        }
173
        else {
174 1
            PyList_Append(l, foffset);
175
        }
176

177 1
        if (PyTuple_GET_SIZE(value) > 2) {
178 1
            ftitle = PyTuple_GET_ITEM(value, 2);
179 1
            PyList_Append(l, ftitle);
180
        }
181
    }
182

183
    return 0;
184
}
185

186
/*
187
 * Walk into subarray, and add items for hashing in l
188
 *
189
 * Return 0 on success
190
 */
191 1
static int _array_descr_walk_subarray(PyArray_ArrayDescr* adescr, PyObject *l)
192
{
193
    PyObject *item;
194
    Py_ssize_t i;
195
    int st;
196

197
    /*
198
     * Add shape and descr itself to the list of object to hash
199
     */
200 1
    if (PyTuple_Check(adescr->shape)) {
201 1
        for(i = 0; i < PyTuple_Size(adescr->shape); ++i) {
202 1
            item = PyTuple_GetItem(adescr->shape, i);
203 1
            if (item == NULL) {
204 0
                PyErr_SetString(PyExc_SystemError,
205
                        "(Hash) Error while getting shape item of subarray dtype ???");
206 0
                return -1;
207
            }
208 1
            PyList_Append(l, item);
209
        }
210
    }
211 0
    else if (PyInt_Check(adescr->shape)) {
212 0
        PyList_Append(l, adescr->shape);
213
    }
214
    else {
215 0
        PyErr_SetString(PyExc_SystemError,
216
                "(Hash) Shape of subarray dtype neither a tuple or int ???");
217 0
        return -1;
218
    }
219

220 1
    Py_INCREF(adescr->base);
221 1
    st = _array_descr_walk(adescr->base, l);
222 1
    Py_DECREF(adescr->base);
223

224
    return st;
225
}
226

227
/*
228
 * 'Root' function to walk into a dtype. May be called recursively
229
 */
230 1
static int _array_descr_walk(PyArray_Descr* descr, PyObject *l)
231
{
232
    int st;
233

234 1
    if (_is_array_descr_builtin(descr)) {
235 1
        return _array_descr_builtin(descr, l);
236
    }
237
    else {
238 1
        if(descr->fields != NULL && descr->fields != Py_None) {
239 1
            st = _array_descr_walk_fields(descr->names, descr->fields, l);
240 1
            if (st) {
241
                return -1;
242
            }
243
        }
244 1
        if(PyDataType_HASSUBARRAY(descr)) {
245 1
            st = _array_descr_walk_subarray(descr->subarray, l);
246 1
            if (st) {
247
                return -1;
248
            }
249
        }
250
    }
251

252
    return 0;
253
}
254

255
/*
256
 * Return 0 if successful
257
 */
258 1
static int _PyArray_DescrHashImp(PyArray_Descr *descr, npy_hash_t *hash)
259
{
260
    PyObject *l, *tl;
261
    int st;
262

263 1
    l = PyList_New(0);
264 1
    if (l == NULL) {
265
        return -1;
266
    }
267

268 1
    st = _array_descr_walk(descr, l);
269 1
    if (st) {
270 0
        Py_DECREF(l);
271
        return -1;
272
    }
273

274
    /*
275
     * Convert the list to tuple and compute the tuple hash using python
276
     * builtin function
277
     */
278 1
    tl = PyList_AsTuple(l);
279 1
    Py_DECREF(l);
280 1
    if (tl == NULL)
281
        return -1;
282

283 1
    *hash = PyObject_Hash(tl);
284 1
    Py_DECREF(tl);
285 1
    if (*hash == -1) {
286
        /* XXX: does PyObject_Hash set an exception on failure ? */
287
#if 0
288
        PyErr_SetString(PyExc_SystemError,
289
                "(Hash) Error while hashing final tuple");
290
#endif
291
        return -1;
292
    }
293

294 1
    return 0;
295
}
296

297
NPY_NO_EXPORT npy_hash_t
298 1
PyArray_DescrHash(PyObject* odescr)
299
{
300
    PyArray_Descr *descr;
301
    int st;
302

303 1
    if (!PyArray_DescrCheck(odescr)) {
304 0
        PyErr_SetString(PyExc_ValueError,
305
                "PyArray_DescrHash argument must be a type descriptor");
306 0
        return -1;
307
    }
308 1
    descr = (PyArray_Descr*)odescr;
309

310 1
    if (descr->hash == -1) {
311 1
        st = _PyArray_DescrHashImp(descr, &descr->hash);
312 1
        if (st) {
313
            return -1;
314
        }
315
    }
316

317 1
    return descr->hash;
318
}

Read our documentation on viewing source code .

Loading