1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
|
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "_arraycore.h"
static PyTypeObject Array_Type;
static void
Array_dealloc(PyObject *self)
{
PyTypeObject* basetype = Array_Type.tp_base;
Fields* fields = (Fields*)((intptr_t)self + basetype->tp_basicsize);
/* fields->alphabet may be NULL if this instance was created by numpy
* and __array_finalize__ somehow failed.
*/
Py_XDECREF(fields->alphabet);
/* PyBuffer_Release won't do anything if fields->mapping.obj is NULL. */
PyMem_Free(fields->mapping.buf);
basetype->tp_dealloc(self);
}
static PyObject *
Array_finalize(PyObject *self, PyObject *obj)
{
if (obj == Py_None) Py_RETURN_NONE;
if (Py_TYPE(self) != Py_TYPE(obj)) {
PyErr_SetString(PyExc_RuntimeError,
"__array_finalize__ argument is not an Array object");
return NULL;
}
PyTypeObject* basetype = Array_Type.tp_base;
Fields* self_fields = (Fields*)((intptr_t)self + basetype->tp_basicsize);
const Fields* obj_fields = (Fields*)((intptr_t)obj + basetype->tp_basicsize);
PyObject* alphabet = obj_fields->alphabet;
if (alphabet) {
Py_INCREF(alphabet);
self_fields->alphabet = obj_fields->alphabet;
}
Py_RETURN_NONE;
}
static PyMethodDef Array_methods[] = {
{"__array_finalize__", (PyCFunction)Array_finalize, METH_O,
"Called by NumPy to finalize new views or copies"},
{NULL} /* Sentinel */
};
static PyObject *Array_get_alphabet(PyObject *self, void *closure) {
PyTypeObject* basetype = Array_Type.tp_base;
Fields* fields = (Fields*)((intptr_t)self + basetype->tp_basicsize);
PyObject* alphabet = fields->alphabet;
if (!alphabet) Py_RETURN_NONE;
Py_INCREF(alphabet);
return alphabet;
}
static int Array_set_alphabet(PyObject *self, PyObject *arg, void *closure) {
Py_buffer view;
const Py_ssize_t length = PySequence_Size(arg);
PyTypeObject* basetype = Array_Type.tp_base;
Fields* fields = (Fields*)((intptr_t)self + basetype->tp_basicsize);
if (fields->alphabet) {
PyErr_SetString(PyExc_ValueError, "the alphabet has already been set.");
return -1;
}
if (!PySequence_Check(arg)) {
PyErr_SetString(PyExc_TypeError,
"alphabet must support the sequence protocol (e.g.,\n"
"strings, lists, and tuples can be valid alphabets).");
return -1;
}
if (PyObject_GetBuffer(self, &view, PyBUF_STRIDES) != 0) {
PyErr_SetString(PyExc_RuntimeError, "failed to access matrix buffer");
return -1;
}
switch (view.ndim) {
case 1:
if (view.shape[0] == length) break;
PyErr_Format(PyExc_ValueError,
"alphabet length %zd is inconsistent with array size "
"%zd", length, view.shape[0]);
PyBuffer_Release(&view);
return -1;
case 2:
if ((view.shape[0] == length && view.shape[1] == length)
|| (view.shape[0] == length && view.shape[1] == 1)
|| (view.shape[0] == 1 && view.shape[1] == length)) break;
PyErr_Format(PyExc_ValueError,
"alphabet length %zd is inconsistent with array size "
"(%zd, %zd)", length, view.shape[0], view.shape[1]);
PyBuffer_Release(&view);
return -1;
default:
PyErr_Format(PyExc_ValueError,
"substitution matrix has incorrect rank %d "
"(expected 1 or 2)", view.ndim);
PyBuffer_Release(&view);
return -1;
}
PyBuffer_Release(&view);
if (PyUnicode_Check(arg)) {
/* initialize mapping if alphabet is a string: */
Py_ssize_t mapping_size;
void* characters = PyUnicode_DATA(arg);
int kind = PyUnicode_KIND(arg);
int* mapping;
Py_ssize_t i;
switch (kind) {
case PyUnicode_1BYTE_KIND: {
mapping_size = 1 << 8 * sizeof(Py_UCS1);
break;
}
case PyUnicode_2BYTE_KIND: {
mapping_size = 1 << 8 * sizeof(Py_UCS2);
break;
}
case PyUnicode_4BYTE_KIND: {
mapping_size = 0x110000; /* Maximum code point in Unicode 6.0
* is 0x10ffff = 1114111 */
break;
}
default:
PyErr_SetString(PyExc_ValueError, "could not interpret alphabet");
return -1;
}
mapping = PyMem_Malloc(mapping_size*sizeof(int));
if (!mapping) return -1;
for (i = 0; i < mapping_size; i++) mapping[i] = MISSING_LETTER;
for (i = 0; i < length; i++) {
Py_UCS4 character = PyUnicode_READ(kind, characters, i);
if (mapping[character] != MISSING_LETTER) {
PyObject* c = PyUnicode_FromKindAndData(kind, &character, 1);
PyErr_Format(PyExc_ValueError,
"alphabet contains '%S' more than once", c);
Py_XDECREF(c);
PyMem_Free(mapping);
return -1;
}
mapping[character] = i;
}
if (PyBuffer_FillInfo(&fields->mapping,
NULL,
mapping,
mapping_size * sizeof(int),
0,
PyBUF_SIMPLE) == -1) {
PyMem_Free(mapping);
return -1;
}
fields->mapping.itemsize = sizeof(int);
}
Py_INCREF(arg);
fields->alphabet = arg;
return 0;
}
static PyGetSetDef Array_getset[] = {
{"alphabet", (getter)Array_get_alphabet, (setter)Array_set_alphabet, "alphabet", NULL},
{NULL}
};
static PyTypeObject Array_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "_arraycore.Array",
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_dealloc = (destructor)Array_dealloc,
.tp_methods = Array_methods,
.tp_getset = Array_getset,
};
static struct PyModuleDef module = {
PyModuleDef_HEAD_INIT,
.m_name = "_arraycore",
.m_doc = "Base module defining the Array base class",
.m_size = -1,
};
PyMODINIT_FUNC
PyInit__arraycore(void)
{
int result;
PyObject *basemodule;
PyObject *baseclass;
PyTypeObject* basetype;
PyObject *mod = PyModule_Create(&module);
if (!mod) return NULL;
// Import the module containing the base class
basemodule = PyImport_ImportModule("numpy");
if (!basemodule)
return NULL;
// Get the base class
baseclass = PyObject_GetAttrString(basemodule, "ndarray");
Py_DECREF(basemodule);
if (!baseclass || !PyType_Check(baseclass)) {
Py_XDECREF(basemodule);
PyErr_SetString(PyExc_RuntimeError, "Failed to get numpy.ndarray");
return NULL;
}
basetype = (PyTypeObject *)baseclass;
if (!(basetype->tp_flags & Py_TPFLAGS_BASETYPE)) {
PyErr_SetString(PyExc_RuntimeError,
"numpy ndarray class is not subclassable");
return NULL;
}
if (basetype->tp_itemsize != 0) {
PyErr_Format(PyExc_RuntimeError,
"expected numpy arrays to have tp_itemsize 0 (found %zd)",
basetype->tp_itemsize);
return NULL;
}
if (!basetype->tp_new) {
PyErr_SetString(PyExc_RuntimeError,
"numpy ndarray class does not have tp_new");
return NULL;
}
Array_Type.tp_basicsize = basetype->tp_basicsize + sizeof(Fields);
Array_Type.tp_base = (PyTypeObject *)baseclass;
if (PyType_Ready(&Array_Type) < 0)
return NULL;
result = PyModule_AddObjectRef(mod, "Array", (PyObject*)&Array_Type);
Py_DECREF(&Array_Type);
if (result == -1) {
Py_DECREF(mod);
return NULL;
}
return mod;
}
|