/* Defines a generic base array type that implements the array interface, and little else. The object and associated C-API allows the array interface to be accessed on the C-level. */ #include #include "basearray.h" /* Multiply a List. */ static intp _MultiplyList(register intp *l1, register int n) { register intp s=1; while (n--) s *= (*l1++); return s; } /* Destructor for PyDataType. */ static void datatype_dealloc(PyDataTypeObject *self) { Py_XDECREF(self->typeobj); Py_XDECREF(self->fields); if (self->subarray) { Py_DECREF(self->subarray->shape); Py_DECREF(self->subarray->base); _pybasearray_free(self->subarray); } self->ob_type->tp_free((PyObject *)self); } /* PyDataType methods. */ static PyMethodDef datatype_methods[] = { {NULL, NULL} }; static PyGetSetDef datatype_getsetlist[] = { {NULL, NULL, NULL, NULL} }; /* Create a DataType object. */ static PyDataTypeObject * PyDataType_New(PyDataTypeObject *base) { PyDataTypeObject *new; new = PyObject_New(PyDataTypeObject, &PyDataType_Type); if (new == NULL) return NULL; /* Don't copy the PyObject_HEAD part. */ memcpy((char *)new+sizeof(PyObject), (char *)base+sizeof(PyObject), sizeof(PyDataTypeObject)-sizeof(PyObject)); if (new->fields == Py_None) new->fields = NULL; Py_XINCREF(new->fields); Py_XINCREF(new->names); if (new->subarray) { new->subarray = _pybasearray_malloc(sizeof(PyDataType_Array)); memcpy(new->subarray, base->subarray, sizeof(PyDataType_Array)); Py_INCREF(new->subarray->shape); Py_INCREF(new->subarray->base); } Py_INCREF(new->typeobj); return new; } /* Return a new DataType object based on the kind given. */ static PyDataTypeObject * PyDataType_NewFromKind(char kind) { PyDataTypeObject *old; PyDataTypeObject *new; old = PyDataType_FromKind(kind); new = PyDataType_New(old); Py_DECREF(old); return new; } /* Build a DataType from a Python dictionary object. A dictionary specifying a data-type must have at least two and up to four keys. All must be sequences of the same length. "names" --- Field names. "formats" --- Data-type descriptors for the field names. Optional: "offsets" --- Integers indicating the offset into the record of the start of the field. If not given, then "consecutive offsets" will be assumed. "titles" --- Allows an additional keys for the fields dictionary. Attribute-lookup-based field names merely has to query the fields dictionary of the data-descriptor. Any result present can be used to return the correct field. So, the notion of what is a name andwhat what is a title is really quite arbitrary. What does distinguish a title, however, is that if it is not None, it will be placed at the end of the tuple inserted into the fields dictionary. If the dictionary does not have "names" and "formats" entries, then it will be checked for conformity and used directly. */ static PyDataTypeObject * PyDataType_FromDict(PyObject *obj, int align) { PyDataTypeObject *new; PyObject *fields=NULL; PyObject *names, *offsets, *descrs, *titles; int n, i, totalsize, maxalign=0, hasobject=0; fields = PyDict_New(); if (fields == NULL) return (PyDataTypeObject *)PyErr_NoMemory(); names = PyDict_GetItemString(obj, "names"); descrs = PyDict_GetItemString(obj, "formats"); if (!names || !descrs) { Py_DECREF(fields); PyErr_SetString(PyExc_ValueError, "The dictionary must have \ the names and formats keys."); goto fail; } n = PyObject_Length(names); offsets = PyDict_GetItemString(obj, "offsets"); titles = PyDict_GetItemString(obj, "titles"); if ((n > PyObject_Length(descrs)) || (offsets && (n > PyObject_Length(offsets))) || (titles && (n > PyObject_Length(titles)))) { PyErr_SetString(PyExc_ValueError, "All items in the dictionary \ must have the same length."); goto fail; } totalsize = 0; for(i=0; i totalsize) totalsize = offset; } else { if (align) { int _align = newdescr->alignment; if (_align > 1) totalsize = ((totalsize + _align - 1)/_align)*_align; maxalign = _MAX(maxalign,_align); } PyTuple_SET_ITEM(tup, 1, PyInt_FromLong(totalsize)); } if (len == 3) PyTuple_SET_ITEM(tup, 2, item); name = PyObject_GetItem(names, index); Py_DECREF(index); /* Insert into dictionary */ if (PyDict_GetItem(fields, name) != NULL) { PyErr_SetString(PyExc_ValueError, "Name already used as a name or title."); ret = PY_FAIL; } PyDict_SetItem(fields, name, tup); Py_DECREF(name); if (len == 3) { if (PyDict_GetItem(fields, item) != NULL) { PyErr_SetString(PyExc_ValueError, "Title already used as a name or title."); ret=PY_FAIL; } else { PyDict_SetItem(fields, item, tup); } } Py_DECREF(tup); if ((ret == PY_FAIL) || (newdescr->elsize == 0)) goto fail; if (!hasobject && newdescr->hasobject) hasobject = 1; totalsize += newdescr->elsize; } new = PyDataType_FromKind(PyDataType_VOID); if (new == NULL) goto fail; if (maxalign > 1) totalsize = ((totalsize + maxalign - 1)/maxalign)*maxalign; if (align) new->alignment = maxalign; new->elsize = totalsize; if (!PyTuple_Check(names)) { names = PySequence_Tuple(names); } else { Py_INCREF(names); } new->names = names; new->fields = fields; new->hasobject = hasobject; return new; fail: Py_XDECREF(fields); return NULL; } /* Build a datatype from a description in a list. Each item in obj is a tuple with a field name, datatype (either a list or a string), and an optional shape parameter. */ static PyDataTypeObject * PyDataType_FromDescription(PyObject *obj) { int n, i, totalsize, ret, hasobject=0; PyObject *fields, *item, *newobj, *name, *tup, *title, *nameslist; PyDataTypeObject *new, *conv; n = PyList_GET_SIZE(obj); nameslist = PyTuple_New(n); if (!nameslist) return NULL; totalsize = 0; fields = PyDict_New(); for (i=0; ihasobject) hasobject = 1; tup = PyTuple_New((title == NULL ? 2 : 3)); PyTuple_SET_ITEM(tup, 0, (PyObject *)conv); PyTuple_SET_ITEM(tup, 1, PyInt_FromLong((long) totalsize)); if (title != NULL) { Py_INCREF(title); PyTuple_SET_ITEM(tup, 2, title); PyDict_SetItem(fields, title, tup); } PyDict_SetItem(fields, name, tup); totalsize += conv->elsize; Py_DECREF(tup); } new = PyDataType_NewFromKind(PyDataType_VOID); new->hasobject = hasobject; new->fields = fields; new->names = nameslist; new->elsize = totalsize; return new; fail: Py_DECREF(fields); Py_DECREF(nameslist); return NULL; } /* Build PyDataType object from a Python list. The list specifying a data-type can just be a list of formats. The names for the fields will default to f1, f2, f3, and so forth, or it can be an array_descr format string, in which case align must be 0. */ static PyDataTypeObject * PyDataType_FromList(PyObject *obj, int align, int try_descr) { int n, i, totalsize, ret, maxalign=0, hasobject=0; PyObject *fields, *key, *tup, *nameslist=NULL; PyDataTypeObject *conv=NULL, *new; n = PyList_GET_SIZE(obj); totalsize = 0; if (n==0) return NULL; nameslist = PyTuple_New(n); if (!nameslist) return NULL; fields = PyDict_New(); for (i=0; ihasobject) hasobject=1; PyTuple_SET_ITEM(tup, 0, (PyObject *)conv); if (align) { int _align; _align = conv->alignment; if (_align > 1) totalsize = ((totalsize + _align - 1)/_align)*_align; maxalign = _MAX(maxalign, _align); } PyTuple_SET_ITEM(tup, 1, PyInt_FromLong((long) totalsize)); PyDict_SetItem(fields, key, tup); Py_DECREF(tup); PyTuple_SET_ITEM(nameslist, i, key); totalsize += conv->elsize; } new = PyDataType_NewFromKind(PyDataType_VOID); new->fields = fields; new->names = nameslist; new->hasobject=hasobject; if (maxalign > 1) totalsize = ((totalsize+maxalign-1)/maxalign)*maxalign; if (align) new->alignment = maxalign; new->elsize = totalsize; return new; fail: Py_DECREF(nameslist); Py_DECREF(fields); if (!try_descr) return NULL; if (align) { PyErr_SetString(PyExc_ValueError, "Failed to convert from list of formats, "\ "and align cannot be 1 for conversion from "\ "a PyDataTypeObject structure"); return NULL; } PyErr_Clear(); return PyDataType_FromDescription(obj); } /* Constructor for PyDataType. */ static PyObject * datatype_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) { PyObject *odescr; PyDataTypeObject *descr, *conv; int align=0; Bool copy=FALSE; static char *kwlist[] = {"dtype", "align", "copy", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iO&", kwlist, &odescr, &align, _BoolConverter, ©)) return NULL; if (align) { conv = NULL; if PyDict_Check(odescr) conv = PyDataType_FromDict(odescr, 1); else if PyList_Check(odescr) { conv = PyDataType_FromList(odescr, 1, 0); if ((conv == NULL) && (!PyErr_Occurred())) { PyErr_SetString(PyExc_ValueError, "Cannot specify align=1 "\ "with array_descriptor "\ "specification of the data-type."); } } else if PyString_Check(odescr) { /* Need to implement some python code for this: conv = _convert_from_commastring(odescr, 1); */ } else { PyErr_SetString(PyExc_ValueError, "Align can only be non-zero for " \ "dictionary, list, and string objects."); } if (conv) return (PyObject *)conv; if (!PyErr_Occurred()) PyErr_SetString(PyExc_ValueError, "Data-type-descriptor not understood."); return NULL; } if PyList_Check(odescr) { conv = PyDataType_FromDescription(odescr); if (!conv) { PyErr_Clear(); conv = PyDataType_FromList(odescr, 0, 0); } return (PyObject *)conv; } if (!PyBaseArray_DataTypeConverter(odescr, &conv)) return NULL; /* Get a new copy of it unless it's already a copy */ if (copy && conv->fields == Py_None) { descr = PyDataType_New(conv); Py_DECREF(conv); conv = descr; } return (PyObject *)conv; } /* Data-type type definition. */ static PyTypeObject PyDataType_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "datatype", /* tp_name */ sizeof(PyDataTypeObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)datatype_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ (cmpfunc)0, /* tp_compare */ (reprfunc)NULL, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ NULL, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ (reprfunc)NULL, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ 0, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ datatype_methods, /* tp_methods */ 0, /* tp_members */ datatype_getsetlist, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ datatype_new, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ 0, /* tp_subclasses */ 0 /* tp_weaklist */ }; /* XDECREF all objects found at this record. */ static void PyBaseArray_Item_XDECREF(char *data, PyDataTypeObject *descr) { PyObject **temp; if (descr->hasobject == 0) return; if (descr->kind == PyDataType_OBJECT) { temp = (PyObject **)data; Py_XDECREF(*temp); } else if (descr->fields) { PyObject *key, *value, *title=NULL; PyDataTypeObject *new; int offset, pos=0; while (PyDict_Next(descr->fields, &pos, &key, &value)) { if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset, &title)) return; PyBaseArray_Item_XDECREF(data + offset, new); } } return; } /* Decrement all internal references for object arrays. */ static int PyBaseArray_XDECREF(PyBaseArrayObject *a) { intp i, n; PyObject **data, **temp; PyBaseArrayIterObject *it; if (a->datatype->hasobject == 0) return 0; if (a->datatype->kind != PyDataType_OBJECT) { it = (PyBaseArrayIterObject *)PyBaseArrayIter_New((PyObject *)a); if (it == NULL) return -1; while(it->index < it->size) { PyBaseArray_Item_XDECREF(it->dataptr, a->datatype); PyBaseArrayIter_Next(it); } Py_DECREF(it); return 0; } if (PyBaseArray_ISONESEGMENT(a)) { data = (PyObject **)a->data; n = _MultiplyList(a->dimensions, a->ndim); if (a->flags & ALIGNED) { for(i=0; iindex < it->size) { temp = (PyObject **)it->dataptr; Py_XDECREF(*temp); PyBaseArrayIter_Next(it); } Py_DECREF(it); } return 0; } /* Destructor for PyBaseArray. */ static void basearray_dealloc(PyBaseArrayObject *self) { if (self->weakreflist != NULL) PyObject_ClearWeakRefs((PyObject *)self); if (self->base) { Py_DECREF(self->base); } if ((self->flags & OWNDATA) && self->data) { /* Free internal references if an Object array */ if (self->datatype->hasobject) { /*hold on to self */ Py_INCREF(self); PyBaseArray_XDECREF(self); /* Don't need to DECREF -- because we are deleting self. */ } PyBaseArray_FREE(self->data); } PyBaseArrayDim_FREE(self->dimensions); Py_DECREF(self->datatype); self->ob_type->tp_free((PyObject *)self); }; /* Return a Python iterator object for a PyBaseArray object. */ static PyObject * basearray_iter(PyBaseArrayObject *arr) { if (arr->ndim == 0) { PyErr_SetString(PyExc_TypeError, "Iteration over a scalar (0-dim array)"); return NULL; } return PySeqIter_New((PyObject *)arr); } /* just .ravel() for now */ static PyMethodDef basearray_methods[] = { {NULL, NULL} }; /* (array interface methods) */ static PyGetSetDef basearray_getsetlist[] = { {NULL, NULL, NULL, NULL} }; /* Returns the number of dimensions or -1 if an error occurred. */ /* Argument vals must be large enough to hold maxvals. */ static int PyBaseArray_IntpFromSequence(PyObject *seq, intp *vals, int maxvals) { int nd, i; PyObject *op; /* Check to see if the sequence is a single integer or can be made into one. */ if ((nd=PySequence_Length(seq)) == -1) { if (PyErr_Occurred()) PyErr_Clear(); if (sizeof(long) >= sizeof(intp)) { if (!(op = PyNumber_Int(seq))) return -1; vals[0] = (intp ) PyInt_AsLong(op); } else { if (!(op = PyNumber_Long(seq))) return -1; vals[0] = (intp ) PyLong_AsLongLong(op); } nd = 1; Py_DECREF(op); } else { for(i=0; i < _MIN(nd,maxvals); i++) { op = PySequence_GetItem(seq, i); if (op == NULL) return -1; if (sizeof(long) >= sizeof(intp)) vals[i]=(intp )PyInt_AsLong(op); else vals[i]=(intp )PyLong_AsLongLong(op); Py_DECREF(op); if(PyErr_Occurred()) return -1; } } return nd; } /* Takes a Python sequence object and allocates and fills in an intp array with the converted values. Remember to free the pointer seq.ptrwhen when done using PyDimMem_FREE(seq.ptr)! */ static int PyBaseArray_IntpConverter(PyObject *obj, PyBaseArray_Dims *seq) { int len, nd; seq->ptr = NULL; if (obj == Py_None) return PY_SUCCEED; len = PySequence_Size(obj); if (len == -1) { /* Check to see if it is a number */ if (PyNumber_Check(obj)) len = 1; } if (len < 0) { PyErr_SetString(PyExc_TypeError, "Expected sequence object with len >= 0."); return PY_FAIL; } if (len > MAX_DIMS) { PyErr_Format(PyExc_ValueError, "Sequence too long must be smaller than %d.", MAX_DIMS); return PY_FAIL; } if (len > 0) { seq->ptr = PyBaseArrayDim_MALLOC(len); if (seq->ptr == NULL) { PyErr_NoMemory(); return PY_FAIL; } } seq->len = len; nd = PyBaseArray_IntpFromSequence(obj, (intp *)seq->ptr, len); if (nd == -1 || nd != len) { PyBaseArrayDim_FREE(seq->ptr); seq->ptr=NULL; return PY_FAIL; } return PY_SUCCEED; } /* This function takes a Python object representing a type and converts it to a the correct PyDataTypeObject structure to describe the type. */ static int PyBaseArray_DataTypeConverter(PyObject *obj, PyDataTypeObject **at) { char *type, endian = '='; char check_num=PyDataType_NOTYPE; int len, elsize = 0; *at=NULL; /* Default. */ if (obj == Py_None) { *at = PyDataType_FromKind(PyDataType_DEFAULT); return PY_SUCCEED; } /* PyDataTypeObject already. */ else if (PyDataType_CHECK(obj)) { *at = (PyDataTypeObject *)obj; Py_INCREF(*at); return PY_SUCCEED; } /* Some other type object. */ else if (PyType_Check(obj)) { if (obj == (PyObject *)(&PyInt_Type)) check_num = PyDataType_INT; else if (obj == (PyObject *)(&PyLong_Type)) check_num = PyDataType_INT; else if (obj == (PyObject *)(&PyFloat_Type)) check_num = PyDataType_FLOAT; else if (obj == (PyObject *)(&PyComplex_Type)) check_num = PyDataType_COMPLEX; else if (obj == (PyObject *)(&PyBool_Type)) check_num = PyDataType_BOOL; else if (obj == (PyObject *)(&PyString_Type)) check_num = PyDataType_STRING; else if (obj == (PyObject *)(&PyUnicode_Type)) check_num = PyDataType_UNICODE; else if (obj == (PyObject *)(&PyBuffer_Type)) check_num = PyDataType_VOID; else check_num = PyDataType_OBJECT; *at = PyDataType_FromKind(check_num); return PY_SUCCEED; } /* Typecode string. */ else if (PyString_Check(obj)) { type = PyString_AS_STRING(obj); len = PyString_GET_SIZE(obj); if (len <= 0) goto fail; check_num = (char) type[0]; if ((char) check_num == '>' || (char) check_num == '<' || \ (char) check_num == '|' || (char) check_num == '=') { if (len <= 1) goto fail; endian = check_num; type++; len--; check_num = (char) type[0]; if (endian == '|') endian = '='; } if (len > 1) { elsize = atoi(type+1); /* See if it can be converted from a comma-separated string. */ /*if (_could_be_commastring(type, len)) { *at = _convert_from_commastring(obj, 0); if (*at) return PY_SUCCEED; else return PY_FAIL; }*/ if (elsize == 0) { check_num = PyDataType_NOTYPE; } /* When specifying length of UNICODE the number of characters is given to match the STRING interface. Each character can be more than one byte and itemsize must be the number of bytes. */ else if (check_num == PyDataType_UNICODE) { elsize <<= 2; } /* Support for generic processing c4, i4, f8, etc... */ /*else if ((check_num != PyDataType_STRING) && \ (check_num != PyDataType_VOIDLTR) { check_num = PyArray_TypestrConvert(elsize, check_num); elsize = 0; } */ } *at = PyDataType_FromKind(check_num); if (*at == NULL) goto fail; if (((*at)->elsize == 0) && (elsize != 0)) { PyDataTypeObject * dummy; dummy = PyDataType_New(*at); Py_XDECREF(*at); *at = dummy; (*at)->elsize = elsize; } if (endian != '=' && PyDataType_ISNBO(endian)) endian = '='; if (endian != '=' && (*at)->byteorder != '|' && (*at)->byteorder != endian) { PyDataTypeObject * dummy; dummy = PyDataType_New(*at); Py_XDECREF(*at); *at = dummy; (*at)->byteorder = endian; } return PY_SUCCEED; } /* Or a tuple. */ /* Tuples disabled for now. else if (PyTuple_Check(obj)) { *at = PyDataType_FromTuple(obj); if (*at == NULL){ if (PyErr_Occurred()) return PY_FAIL; goto fail; } return PY_SUCCEED; } */ /* Or a list. */ else if (PyList_Check(obj)) { *at = PyDataType_FromList(obj,0,1); if (*at == NULL) goto fail; return PY_SUCCEED; } /* Or a dictionary. */ else if (PyDict_Check(obj)) { *at = PyDataType_FromDict(obj,0); if (*at == NULL) goto fail; return PY_SUCCEED; } fail: /*if (typeDict != NULL) { item = PyDict_GetItem(typeDict, obj); if (item) return PyArray_DescrConverter(item, at); } */ if (PyErr_Occurred()) return PY_FAIL; PyErr_SetString(PyExc_TypeError, "Data type not understood."); return PY_FAIL; } /* Takes a Python object that exposes the (single-segment) buffer interface and returns a pointer to the data segment. You should increment the reference count by one of buf->base if you will hang on to a reference. You only get a borrowed reference to the object. Do not free the memory. */ static int PyBaseArray_BufferConverter(PyObject *obj, PyBaseArray_Chunk *buf) { int buflen; buf->ptr = NULL; buf->flags = BEHAVED_FLAGS; buf->base = NULL; if (obj == Py_None) return PY_SUCCEED; if (PyObject_AsWriteBuffer(obj, &(buf->ptr), &buflen) < 0) { PyErr_Clear(); buf->flags &= ~WRITEABLE; if (PyObject_AsReadBuffer(obj, (const void **)&(buf->ptr), &buflen) < 0) return PY_FAIL; } buf->len = (intp) buflen; /* Point to the base of the buffer object, if present. */ if (PyBuffer_Check(obj)) buf->base = ((PyBaseArray_Chunk *)obj)->base; if (buf->base == NULL) buf->base = obj; return PY_SUCCEED; } /* Converts a Python object to FORTRAN / C / ANY. */ static int PyBaseArray_OrderConverter(PyObject *object, PyBaseArray_ORDER *val) { char *str; if (object == Py_None) { *val = PyBaseArray_ANYORDER; } else if (!PyString_Check(object) || PyString_GET_SIZE(object) < 1) { if (PyObject_IsTrue(object)) *val = PyBaseArray_FORTRANORDER; else *val = PyBaseArray_CORDER; if (PyErr_Occurred()) return PY_FAIL; return PY_SUCCEED; } else { str = PyString_AS_STRING(object); if (str[0] == 'C' || str[0] == 'c') *val = PyBaseArray_CORDER; if (str[0] == 'F' || str[0] == 'f') *val = PyBaseArray_FORTRANORDER; if (str[0] == 'A' || str[0] == 'a') *val = PyBaseArray_ANYORDER; } return PY_SUCCEED; } /* This routine checks to see if newstrides (of length nd) will not ever be able to walk outside of the memory implied numbytes and offset. The available memory is assumed to start at -offset and proceed to numbytes-offset. The strides are checked to ensure that accessing memory using striding will not try to reach beyond this memory for any of the axes. If numbytes is 0 it will be calculated using the dimensions and element-size. This function checks for walking beyond the beginning and right-end of the buffer and therefore works for any integer stride (positive or negative). */ static Bool PyBaseArray_CheckStrides(int elsize, int nd, intp numbytes, intp offset, intp *dims, intp *newstrides) { int i; intp byte_begin, begin, end; if (numbytes == 0) numbytes = _MultiplyList(dims, nd) * elsize; begin = -offset; end = numbytes - offset - elsize; for (i=0; i end)) return FALSE; } return TRUE; } /* Change a sub-array field to the base descriptor */ /* and update the dimensions and strides appropriately. Dimensions and strides are added to the end unless we have a FORTRAN array and then they are added to the beginning Strides are only added if given (because data is given). */ static int PyBaseArray_UpdateDescrAndDimensions(PyDataTypeObject **des, intp *newdims, intp *newstrides, int oldnd, int isfortran) { PyDataTypeObject *old; int newnd, numnew, i, tuple; intp *mydim; old = *des; *des = old->subarray->base; mydim = newdims + oldnd; tuple = PyTuple_Check(old->subarray->shape); if (tuple) { numnew = PyTuple_GET_SIZE(old->subarray->shape); } else { numnew = 1; } newnd = oldnd + numnew; if (newnd > MAX_DIMS) goto finish; if (isfortran) { memmove(newdims+numnew, newdims, oldnd*sizeof(intp)); mydim = newdims; } if (tuple) { for (i=0; isubarray->shape, i)); } } else { mydim[0] = (intp) PyInt_AsLong(old->subarray->shape); } if (newstrides) { intp tempsize; intp *mystrides; mystrides = newstrides + oldnd; if (isfortran) { memmove(newstrides+numnew, newstrides, oldnd*sizeof(intp)); mystrides = newstrides; } /* Make new strides -- alwasy C-contiguous */ tempsize = (*des)->elsize; for (i=numnew-1; i>=0; i--) { mystrides[i] = tempsize; tempsize *= mydim[i] ? mydim[i] : 1; } } finish: Py_INCREF(*des); Py_DECREF(old); return newnd; } /* This is the main array creation routine. */ /* Flags argument has multiple related meanings depending on data and strides: If data is given, then flags is flags associated with data. If strides is not given, then a contiguous strides array will be created and the CONTIGUOUS bit will be set. If the flags argument has the FORTRAN bit set, then a FORTRAN-style strides array will be created (and of course the FORTRAN flag bit will be set). If data is not given but created here, then flags will be DEFAULT and a non-zero flags argument can be used to indicate a FORTRAN style array is desired. */ static size_t PyBaseArray_Fill(intp *strides, intp *dims, int nd, size_t itemsize, int inflag, int *objflags) { int i; /* Only make Fortran strides if not contiguous as well. */ if ((inflag & FORTRAN) && !(inflag & CONTIGUOUS)) { for (i=0; i 1) *objflags &= ~CONTIGUOUS; else *objflags |= CONTIGUOUS; } else { for (i=nd-1;i>=0;i--) { strides[i] = itemsize; itemsize *= dims[i] ? dims[i] : 1; } *objflags |= CONTIGUOUS; if (nd > 1) *objflags &= ~FORTRAN; else *objflags |= FORTRAN; } return itemsize; } /* Steals a reference to a data-type (even on failure). */ static PyObject * PyBaseArray_NewFromDataType(PyTypeObject *subtype, PyDataTypeObject *descr, int nd, intp *dims, intp *strides, void *data, int flags, PyObject *obj) { PyBaseArrayObject *self; register int i; size_t sd; intp largest, size; if (descr->subarray) { PyObject *ret; intp newdims[2*MAX_DIMS]; intp *newstrides=NULL; int isfortran=0; isfortran = (data && (flags & FORTRAN) && !(flags & CONTIGUOUS)) || (!data && flags); memcpy(newdims, dims, nd*sizeof(intp)); if (strides) { newstrides = newdims + MAX_DIMS; memcpy(newstrides, strides, nd*sizeof(intp)); } nd = PyBaseArray_UpdateDescrAndDimensions(&descr, newdims, newstrides, nd, isfortran); ret = PyBaseArray_NewFromDataType(subtype, descr, nd, newdims, newstrides, data, flags, obj); return ret; } if (nd < 0) { PyErr_SetString(PyExc_ValueError, "Number of dimensions must be >=0."); Py_DECREF(descr); return NULL; } if (nd > MAX_DIMS) { PyErr_Format(PyExc_ValueError, "Maximum number of dimensions is %d.", MAX_DIMS); Py_DECREF(descr); return NULL; } /* Check dimensions */ size = 1; sd = (size_t) descr->elsize; if (sd == 0) { PyErr_SetString(PyExc_ValueError, "Empty data-type."); Py_DECREF(descr); return NULL; } largest = INTP_MAX / sd; for (i=0;i largest) { PyErr_SetString(PyExc_ValueError, "Dimensions too large."); Py_DECREF(descr); return NULL; } } self = (PyBaseArrayObject *) subtype->tp_alloc(subtype, 0); if (self == NULL) { Py_DECREF(descr); return NULL; } self->ndim = nd; self->dimensions = NULL; self->data = NULL; if (data == NULL) { self->flags = DEFAULT_FLAGS; if (flags) { self->flags |= FORTRAN; if (nd > 1) self->flags &= ~CONTIGUOUS; flags = FORTRAN; } } self->datatype = descr; self->base = (PyObject *)NULL; self->weakreflist = (PyObject *)NULL; if (nd > 0) { self->dimensions = PyBaseArrayDim_MALLOC(2*nd); if (self->dimensions == NULL) { PyErr_NoMemory(); goto fail; } self->strides = self->dimensions + nd; memcpy(self->dimensions, dims, sizeof(intp)*nd); if (strides == NULL) { /* fill it in */ sd = PyBaseArray_Fill(self->strides, dims, nd, sd, flags, &(self->flags)); } else { /* We allow strides even when we create the memory, but be careful with this. */ memcpy(self->strides, strides, sizeof(intp)*nd); sd *= size; } } else { self->dimensions = self->strides = NULL; } if (data == NULL) { /* Allocate something even for zero-space arrays, e.g. shape=(0,) -- otherwise buffer exposure (a.data) doesn't work as it should. */ if (sd==0) sd = descr->elsize; if ((data = PyBaseArray_MALLOC(sd))==NULL) { PyErr_NoMemory(); goto fail; } self->flags |= OWNDATA; /* It is bad to have unitialized OBJECT pointers, */ /* which could also be sub-fields of a VOID array. */ if (descr->hasobject) { memset(data, 0, sd); } } else { /* If data is passed in, this object won't own it by default. The cller must arrange for this to be reset if truly desired */ self->flags &= ~OWNDATA; } self->data = data; return (PyObject *)self; fail: Py_DECREF(self); return NULL; } static void PyBaseArray_FillObject(char *optr, PyObject *obj, PyDataTypeObject *dtype) { if (!dtype->hasobject) { if ((obj == Py_None) || (PyInt_Check(obj) && PyInt_AsLong(obj)==0)) return; else { PyObject *arr; Py_INCREF(dtype); arr = PyBaseArray_NewFromDataType(&PyBaseArray_Type, dtype, 0, NULL, NULL, NULL, 0, NULL); if (arr!=NULL) dtype->funcs->setitem(obj, optr, arr); Py_XDECREF(arr); } } if (dtype->kind == PyDataType_OBJECT) { PyObject **temp; Py_XINCREF(obj); temp = (PyObject **)optr; *temp = obj; return; } if (dtype->fields) { PyObject *key, *value, *title=NULL; PyDataTypeObject *new; int offset, pos=0; while (PyDict_Next(dtype->fields, &pos, &key, &value)) { if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset, &title)) return; PyBaseArray_FillObject(optr + offset, obj, new); } } } /* Assumes contiguous. */ static void PyArray_FillObjectArray(PyBaseArrayObject *arr, PyObject *obj) { intp i,n; n = _MultiplyList(arr->dimensions, arr->ndim); if (arr->datatype->kind == PyDataType_OBJECT) { PyObject **optr; optr = (PyObject **)(arr->data); n = _MultiplyList(arr->dimensions, arr->ndim); if (obj == NULL) { for (i=0; idata; for (i=0; idatatype); optr += arr->datatype->elsize; } } } /* Check whether an array is stored fortran-contiguously (column-wise) in memory. Arrays that are 0-strided are not contiguous (even if their dimension == 1). */ static int PyBaseArray_IsFortranContiguous(PyBaseArrayObject *ap) { register intp sd; register intp dim; register int i; if (ap->ndim == 0) return 1; sd = ap->datatype->elsize; if (ap->ndim == 1) return (ap->dimensions[0] == 1 || sd == ap->strides[0]); for (i=0; i< ap->ndim; ++i) { dim = ap->dimensions[i]; /* Fortran contiguous by definition. */ if (dim == 0) return 1; if (ap->strides[i] != sd) return 0; sd *= dim; } return 1; } /* Check whether an array is stored contiguously (row-wise) in memory. Arrays that are 0-strided are not contiguous (even if dimension == 1). */ static int PyBaseArray_IsContiguous(PyBaseArrayObject *ap) { register intp sd; register intp dim; register int i; if (ap->ndim == 0) return 1; sd = ap->datatype->elsize; if (ap->ndim == 1) return (ap->dimensions[0] == 1 || sd == ap->strides[0]); for (i = ap->ndim-1; i >= 0; --i) { dim = ap->dimensions[i]; /* Contiguous by definition. */ if (dim == 0) return 1; if (ap->strides[i] != sd) return 0; sd *= dim; } return 1; } static int PyBaseArray_IsAligned(PyBaseArrayObject *ap) { int i, alignment, aligned=1; intp ptr; char type = ap->datatype->kind; if ((type == PyDataType_STRING) || (type == PyDataType_VOID)) return 1; alignment = ap->datatype->alignment; if (alignment == 1) return 1; ptr = (intp) ap->data; aligned = (ptr % alignment) == 0; for (i=0; i < ap->ndim; i++) aligned &= ((ap->strides[i] % alignment) == 0); return aligned != 0; } static Bool PyBaseArray_IsWriteable(PyBaseArrayObject *ap) { PyObject *base=ap->base; void *dummy; int n; /* If we own our own data, then no-problem. */ if ((base == NULL) || (ap->flags & OWNDATA)) return TRUE; /* Get to the final base object. If it is a writeable array, then return TRUE, or if we can find an array object or a writeable buffer object as the final base object or a string object (for pickling support memory savings). This last could be removed if a proper pickleable buffer was added to Python. */ while(PyBaseArray_CHECK(base)) { n = ((PyBaseArrayObject *)(base))->flags; if (n & OWNDATA) return (Bool) (n & WRITEABLE); base = ((PyBaseArrayObject *)(base))->base; } /* Here pickle support works seamlessly and unpickled arrays can be setitem and reset writeable. Warning: could be abused. */ if PyString_Check(base) return TRUE; if (PyObject_AsWriteBuffer(base, &dummy, &n) < 0) return FALSE; return TRUE; } /* Update Several Flags at once. */ static void PyBaseArray_UpdateFlags(PyBaseArrayObject *ret, int flagmask) { if (flagmask & FORTRAN) { if (PyBaseArray_IsFortranContiguous(ret)) { ret->flags |= FORTRAN; if (ret->ndim > 1) ret->flags &= ~CONTIGUOUS; } else ret->flags &= ~FORTRAN; } if (flagmask & CONTIGUOUS) { if (PyBaseArray_IsContiguous(ret)) { ret->flags |= CONTIGUOUS; if (ret->ndim > 1) ret->flags &= ~FORTRAN; } else ret->flags &= ~CONTIGUOUS; } if (flagmask & ALIGNED) { if (PyBaseArray_IsAligned(ret)) ret->flags |= ALIGNED; else ret->flags &= ~ALIGNED; } /* This is not checked by default, WRITEABLE is not part of UPDATE_ALL. */ if (flagmask & WRITEABLE) { if (PyBaseArray_IsWriteable(ret)) ret->flags |= WRITEABLE; else ret->flags &= ~WRITEABLE; } return; } /* Constructor for PyBaseArray. */ static PyObject * basearray_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"shape", "dtype", "buffer", "offset", "strides", "order", NULL}; PyDataTypeObject *descr=NULL; int type_num, itemsize, fortran=0; PyBaseArray_Dims dims = {NULL, 0}, strides = {NULL, 0}; PyBaseArray_Chunk buffer; longlong offset=0; PyBaseArray_ORDER order=PyBaseArray_CORDER; PyBaseArrayObject *ret; /* This function is usually called with a shape and type, but can also be called with buffer, strides, and swapped info. */ buffer.ptr = NULL; /* For now, let's just use this to create an empty, contiguous array of a specific type and shape. */ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&O&LO&O&", kwlist, PyBaseArray_IntpConverter, &dims, PyBaseArray_DataTypeConverter, &descr, PyBaseArray_BufferConverter, &buffer, &offset, &PyBaseArray_IntpConverter, &strides, &PyBaseArray_OrderConverter, &order)) goto fail; if (order == PyBaseArray_FORTRANORDER) fortran = 1; if (descr == NULL) descr = PyDataType_FromKind(PyDataType_DEFAULT); type_num = descr->kind; itemsize = descr->elsize; if (itemsize == 0) { PyErr_SetString(PyExc_ValueError, "Data-type with unspecified variable length."); goto fail; } if (strides.ptr != NULL) { intp nb, off; if (strides.len != dims.len) { PyErr_SetString(PyExc_ValueError, "Strides, if given, must be the same length as shape."); goto fail; } if (buffer.ptr == NULL) { nb = 0; off = 0; } else { nb = buffer.len; off = (intp) offset; } if (!PyBaseArray_CheckStrides(itemsize, dims.len, nb, off, dims.ptr, strides.ptr)) { PyErr_SetString(PyExc_ValueError, "Strides is incompatible with shape of \ requested array and size of buffer."); goto fail; } } if (buffer.ptr == NULL) { ret = (PyBaseArrayObject *) PyBaseArray_NewFromDataType(subtype, descr, (int)dims.len, dims.ptr, strides.ptr, NULL, fortran, NULL); if (ret == NULL) { descr=NULL; goto fail; } if (descr->hasobject) { PyArray_FillObjectArray(ret, Py_None); if (PyErr_Occurred()) { descr=NULL; goto fail; } } } else { if (dims.len == 1 && dims.ptr[0] == -1) { dims.ptr[0] = (buffer.len-(intp)offset) / itemsize; } else if ((strides.ptr == NULL) && \ buffer.len < itemsize* _MultiplyList(dims.ptr, dims.len)) { PyErr_SetString(PyExc_TypeError, "Buffer is too small for requested array."); goto fail; } if (descr->hasobject) { PyErr_SetString(PyExc_TypeError, "Cannot construct an object array" \ "from this buffer data"); goto fail; } /* get writeable and aligned */ if (fortran) buffer.flags |= FORTRAN; ret = (PyBaseArrayObject *) PyBaseArray_NewFromDataType(subtype, descr, dims.len, dims.ptr, strides.ptr, offset + (char *)buffer.ptr, buffer.flags, NULL); if (ret == NULL) { descr=NULL; goto fail; } PyBaseArray_UpdateFlags(ret, UPDATE_ALL_FLAGS); ret->base = buffer.base; Py_INCREF(buffer.base); } PyBaseArrayDim_FREE(dims.ptr); if (strides.ptr) PyBaseArrayDim_FREE(strides.ptr); return (PyObject *)ret; fail: Py_XDECREF(descr); if (dims.ptr) PyBaseArrayDim_FREE(dims.ptr); if (strides.ptr) PyBaseArrayDim_FREE(strides.ptr); return NULL; } /* Basearray type definition. */ static char PyBaseArray__doc__[] = "The basearray does nothing beyond \ allocating and interpreting memory for multidimensional arrays. \ It can serve as a base type for other, more specialized types, and as a \ means to understand a chunk of memory."; static PyTypeObject PyBaseArray_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "basearray", /*tp_name*/ sizeof(PyBaseArrayObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)basearray_dealloc, /*tp_dealloc */ (printfunc)NULL, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ (cmpfunc)0, /*tp_compare*/ (reprfunc)NULL, /*tp_repr*/ NULL, /*tp_as_number*/ NULL, /*tp_as_sequence*/ NULL, /*tp_as_mapping*/ (hashfunc)0, /*tp_hash*/ (ternaryfunc)0, /*tp_call*/ (reprfunc)NULL, /*tp_str*/ (getattrofunc)0, /*tp_getattro*/ (setattrofunc)0, /*tp_setattro*/ NULL, /*tp_as_buffer*/ (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES), /*tp_flags*/ PyBaseArray__doc__, /*tp_doc*/ (traverseproc)0, /*tp_traverse */ (inquiry)0, /*tp_clear */ (richcmpfunc)NULL, offsetof(PyBaseArrayObject, weakreflist), /*tp_weaklistoffset */ (getiterfunc)basearray_iter, /* tp_iter */ (iternextfunc)0, /* tp_iternext */ basearray_methods, /* tp_methods */ 0, /* tp_members */ basearray_getsetlist, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)0, /* tp_init */ 0, /* tp_alloc */ (newfunc)basearray_new, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ 0, /* tp_subclasses */ 0 /* tp_weaklist */ }; static void PyBaseArrayIter_Reset(PyBaseArrayIterObject *it) { it->index = 0; it->dataptr = it->ao->data; memset(it->coordinates, 0, (it->nd_m1+1)*sizeof(intp)); } /* Get new iterator. */ static PyObject * PyBaseArrayIter_New(PyObject *obj) { PyBaseArrayIterObject *it; int i, nd; PyBaseArrayObject *ao = (PyBaseArrayObject *)obj; if (!PyBaseArray_CHECK(ao)) { PyErr_BadInternalCall(); return NULL; } it = (PyBaseArrayIterObject*) _pybasearray_malloc(sizeof(PyBaseArrayIterObject)); PyObject_Init((PyObject *)it, &PyBaseArrayIter_Type); if (it == NULL) return NULL; nd = ao->ndim; PyBaseArray_UpdateFlags(ao, CONTIGUOUS); it->contiguous = 0; if (ao->flags & CONTIGUOUS) it->contiguous = 1; Py_INCREF(ao); it->ao = ao; it->size = _MultiplyList(ao->dimensions, ao->ndim); it->nd_m1 = nd - 1; it->factors[nd-1] = 1; for (i=0; i < nd; i++) { it->dims_m1[i] = it->ao->dimensions[i] - 1; it->strides[i] = it->ao->strides[i]; it->backstrides[i] = it->strides[i] * \ it->dims_m1[i]; if (i > 0) it->factors[nd-i-1] = it->factors[nd-i] * \ it->ao->dimensions[nd-i]; } PyBaseArrayIter_Reset(it); return (PyObject *)it; } static void PyBaseArrayIter_Next(PyBaseArrayIterObject *it) { it->index++; if (it->nd_m1 == 0) { it->dataptr += it->strides[0]; it->coordinates[0]++; } else if (it->contiguous) it->dataptr += it->ao->datatype->elsize; else if (it->nd_m1 == 1) { if (it->coordinates[1] < it->dims_m1[1]) { it->coordinates[1]++; it->dataptr += it->strides[1]; } else { it->coordinates[1] = 0; it->coordinates[0]++; it->dataptr += it->strides[0] - it->backstrides[1]; } } else { int _i_; for (_i_ = it->nd_m1; _i_ >= 0; _i_--) { if (it->coordinates[_i_] < it->dims_m1[_i_]) { it->coordinates[_i_]++; it->dataptr += it->strides[_i_]; break; } else { it->coordinates[_i_] = 0; it->dataptr -= it->backstrides[_i_]; } } } } static void basearrayiter_dealloc(PyBaseArrayIterObject *it) { Py_XDECREF(it->ao); _pybasearray_free(it); } /* Returns an array scalar holding the element desired */ static PyObject * basearrayiter_next(PyBaseArrayIterObject *it) { PyObject *ret; PyDataType_GetItemFunc *get; if (!(it->ao->datatype->funcs && (get=it->ao->datatype->funcs->getitem))) { PyErr_SetString(PyExc_ValueError, "Not supported."); return NULL; } if (it->index < it->size) { ret = get(it->dataptr, it->ao); PyBaseArrayIter_Next(it); return ret; } return NULL; } static PyTypeObject PyBaseArrayIter_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "basearrayiter", /* tp_name */ sizeof(PyBaseArrayIterObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)basearrayiter_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 */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ 0, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ (iternextfunc)basearrayiter_next, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ }; /* Init function for the Python module. */ static PyMethodDef module_methods[] = { {NULL} /* Sentinel */ }; PyMODINIT_FUNC initbasearray(void) { PyObject* m; if (PyType_Ready(&PyBaseArray_Type) < 0) return; if (PyType_Ready(&PyDataType_Type) < 0) return; m = Py_InitModule3("basearray", module_methods, "Basearray is alive!"); if (m == NULL)return; Py_INCREF(&PyBaseArray_Type); Py_INCREF(&PyDataType_Type); PyModule_AddObject(m, "basearray", (PyObject *)&PyBaseArray_Type); PyModule_AddObject(m, "datatype", (PyObject *)&PyDataType_Type); }