Files
Phoenix/sip/siplib/array.c
2021-01-04 15:36:14 -08:00

704 lines
17 KiB
C

/*
* This file implements the API for the array type.
*
* Copyright (c) 2019 Riverbank Computing Limited <info@riverbankcomputing.com>
*
* This file is part of SIP.
*
* This copy of SIP is licensed for use under the terms of the SIP License
* Agreement. See the file LICENSE for more details.
*
* This copy of SIP may also used under the terms of the GNU General Public
* License v2 or v3 as published by the Free Software Foundation which can be
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
*
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <Python.h>
#include <stddef.h>
#include <string.h>
#include "sipint.h"
#include "array.h"
/* The object data structure. */
typedef struct {
PyObject_HEAD
void *data;
const sipTypeDef *td;
const char *format;
size_t stride;
Py_ssize_t len;
int flags;
PyObject *owner;
} sipArrayObject;
static int check_writable(sipArrayObject *array);
static int check_index(sipArrayObject *array, Py_ssize_t idx);
static void *get_value(sipArrayObject *array, PyObject *value);
static void *get_slice(sipArrayObject *array, PyObject *value, Py_ssize_t len);
static void bad_key(PyObject *key);
static void *element(sipArrayObject *array, Py_ssize_t idx);
static PyObject *make_array(void *data, const sipTypeDef *td,
const char *format, size_t stride, Py_ssize_t len, int flags,
PyObject *owner);
/*
* Implement len() for the type.
*/
static Py_ssize_t sipArray_length(PyObject *self)
{
return ((sipArrayObject *)self)->len;
}
/*
* Implement sequence item sub-script for the type.
*/
static PyObject *sipArray_item(PyObject *self, Py_ssize_t idx)
{
sipArrayObject *array = (sipArrayObject *)self;
PyObject *py_item;
void *data;
if (check_index(array, idx) < 0)
return NULL;
data = element(array, idx);
if (array->td != NULL)
{
py_item = sip_api_convert_from_type(data, array->td, NULL);
}
else
{
switch (*array->format)
{
case 'b':
py_item = PyLong_FromLong(*(char *)data);
break;
case 'B':
py_item = PyLong_FromUnsignedLong(*(unsigned char *)data);
break;
case 'h':
py_item = PyLong_FromLong(*(short *)data);
break;
case 'H':
py_item = PyLong_FromUnsignedLong(*(unsigned short *)data);
break;
case 'i':
py_item = PyLong_FromLong(*(int *)data);
break;
case 'I':
py_item = PyLong_FromUnsignedLong(*(unsigned int *)data);
break;
case 'f':
py_item = PyFloat_FromDouble(*(float *)data);
break;
case 'd':
py_item = PyFloat_FromDouble(*(double *)data);
break;
default:
py_item = NULL;
}
}
return py_item;
}
/* The sequence methods data structure. */
static PySequenceMethods sipArray_SequenceMethods = {
sipArray_length, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
sipArray_item, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
0, /* sq_contains */
0, /* sq_inplace_concat */
0, /* sq_inplace_repeat */
};
/*
* Implement mapping sub-script for the type.
*/
static PyObject *sipArray_subscript(PyObject *self, PyObject *key)
{
sipArrayObject *array = (sipArrayObject *)self;
if (PyIndex_Check(key))
{
Py_ssize_t idx = PyNumber_AsSsize_t(key, PyExc_IndexError);
if (idx == -1 && PyErr_Occurred())
return NULL;
if (idx < 0)
idx += array->len;
return sipArray_item(self, idx);
}
if (PySlice_Check(key))
{
Py_ssize_t start, stop, step, slicelength;
if (sip_api_convert_from_slice_object(key, array->len, &start, &stop, &step, &slicelength) < 0)
return NULL;
if (step != 1)
{
PyErr_SetNone(PyExc_NotImplementedError);
return NULL;
}
return make_array(element(array->data, start), array->td,
array->format, array->stride, slicelength,
(array->flags & ~SIP_OWNS_MEMORY), array->owner);
}
bad_key(key);
return NULL;
}
/*
* Implement mapping assignment sub-script for the type.
*/
static int sipArray_ass_subscript(PyObject *self, PyObject *key,
PyObject *value)
{
sipArrayObject *array = (sipArrayObject *)self;
Py_ssize_t start, len;
void *value_data;
if (check_writable(array) < 0)
return -1;
if (PyIndex_Check(key))
{
start = PyNumber_AsSsize_t(key, PyExc_IndexError);
if (start == -1 && PyErr_Occurred())
return -1;
if (start < 0)
start += array->len;
if (check_index(array, start) < 0)
return -1;
if ((value_data = get_value(array, value)) == NULL)
return -1;
len = 1;
}
else if (PySlice_Check(key))
{
Py_ssize_t stop, step;
if (sip_api_convert_from_slice_object(key, array->len, &start, &stop, &step, &len) < 0)
return -1;
if (step != 1)
{
PyErr_SetNone(PyExc_NotImplementedError);
return -1;
}
if ((value_data = get_slice(array, value, len)) == NULL)
return -1;
}
else
{
bad_key(key);
return -1;
}
memmove(element(array, start), value_data, len * array->stride);
return 0;
}
/* The mapping methods data structure. */
static PyMappingMethods sipArray_MappingMethods = {
sipArray_length, /* mp_length */
sipArray_subscript, /* mp_subscript */
sipArray_ass_subscript, /* mp_ass_subscript */
};
/*
* The buffer implementation for Python v2.6.3 and later.
*/
static int sipArray_getbuffer(PyObject *self, Py_buffer *view, int flags)
{
sipArrayObject *array = (sipArrayObject *)self;
if (view == NULL)
return 0;
if (((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) && (array->flags & SIP_READ_ONLY))
{
PyErr_SetString(PyExc_BufferError, "object is not writable.");
return -1;
}
view->obj = self;
Py_INCREF(self);
view->buf = array->data;
view->len = array->len;
view->readonly = (array->flags & SIP_READ_ONLY);
view->itemsize = array->stride;
view->format = NULL;
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
view->format = (char *)array->format;
view->ndim = 1;
view->shape = NULL;
if ((flags & PyBUF_ND) == PyBUF_ND)
view->shape = &view->len;
view->strides = NULL;
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES)
view->strides = &view->itemsize;
view->suboffsets = NULL;
view->internal = NULL;
return 0;
}
/* The buffer methods data structure. */
static PyBufferProcs sipArray_BufferProcs = {
sipArray_getbuffer, /* bf_getbuffer */
0 /* bf_releasebuffer */
};
/* The instance deallocation function. */
static void sipArray_dealloc(PyObject *self)
{
sipArrayObject *array = (sipArrayObject *)self;
if (array->flags & SIP_OWNS_MEMORY)
sip_api_free(array->data);
else
Py_XDECREF(array->owner);
}
/* The type data structure. */
PyTypeObject sipArray_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"sip.array", /* tp_name */
sizeof (sipArrayObject), /* tp_basicsize */
0, /* tp_itemsize */
sipArray_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
&sipArray_SequenceMethods, /* tp_as_sequence */
&sipArray_MappingMethods, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
&sipArray_BufferProcs, /* tp_as_buffer */
#if defined(Py_TPFLAGS_HAVE_NEWBUFFER)
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_NEWBUFFER, /* tp_flags */
#else
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
#endif
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* 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 */
0, /* tp_new */
0, /* tp_free */
0, /* tp_is_gc */
0, /* tp_bases */
0, /* tp_mro */
0, /* tp_cache */
0, /* tp_subclasses */
0, /* tp_weaklist */
0, /* tp_del */
0, /* tp_version_tag */
0, /* tp_finalize */
#if PY_VERSION_HEX >= 0x03080000
0, /* tp_vectorcall */
#endif
};
/*
* Check that an array is writable.
*/
static int check_writable(sipArrayObject *array)
{
if (array->flags & SIP_READ_ONLY)
{
PyErr_SetString(PyExc_TypeError, "sip.array object is read-only");
return -1;
}
return 0;
}
/*
* Check that an index is valid for an array.
*/
static int check_index(sipArrayObject *array, Py_ssize_t idx)
{
if (idx >= 0 && idx < array->len)
return 0;
PyErr_SetString(PyExc_IndexError, "index out of bounds");
return -1;
}
/*
* Raise an exception about a bad sub-script key.
*/
static void bad_key(PyObject *key)
{
PyErr_Format(PyExc_TypeError,
"cannot index a sip.array object using '%s'",
Py_TYPE(key)->tp_name);
}
/*
* Get the address of an element of an array.
*/
static void *element(sipArrayObject *array, Py_ssize_t idx)
{
return (unsigned char *)(array->data) + idx * array->stride;
}
/*
* Get the address of a value that will be copied to an array.
*/
static void *get_value(sipArrayObject *array, PyObject *value)
{
static union {
signed char s_char_t;
unsigned char u_char_t;
signed short s_short_t;
unsigned short u_short_t;
signed int s_int_t;
unsigned int u_int_t;
float float_t;
double double_t;
} static_data;
void *data;
if (array->td != NULL)
{
int iserr = FALSE;
data = sip_api_force_convert_to_type(value, array->td, NULL,
SIP_NOT_NONE|SIP_NO_CONVERTORS, NULL, &iserr);
}
else
{
PyErr_Clear();
switch (*array->format)
{
case 'b':
static_data.s_char_t = sip_api_long_as_char(value);
data = &static_data.s_char_t;
break;
case 'B':
static_data.u_char_t = sip_api_long_as_unsigned_char(value);
data = &static_data.u_char_t;
break;
case 'h':
static_data.s_short_t = sip_api_long_as_short(value);
data = &static_data.s_short_t;
break;
case 'H':
static_data.u_short_t = sip_api_long_as_unsigned_short(value);
data = &static_data.u_short_t;
break;
case 'i':
static_data.s_int_t = sip_api_long_as_int(value);
data = &static_data.s_int_t;
break;
case 'I':
static_data.u_int_t = sip_api_long_as_unsigned_int(value);
data = &static_data.u_int_t;
break;
case 'f':
static_data.float_t = (float)PyFloat_AsDouble(value);
data = &static_data.float_t;
break;
case 'd':
static_data.double_t = PyFloat_AsDouble(value);
data = &static_data.double_t;
break;
default:
data = NULL;
}
if (PyErr_Occurred())
data = NULL;
}
return data;
}
/*
* Get the address of an value that will be copied to an array slice.
*/
static void *get_slice(sipArrayObject *array, PyObject *value, Py_ssize_t len)
{
sipArrayObject *other = (sipArrayObject *)value;
if (!PyObject_IsInstance(value, (PyObject *)&sipArray_Type) || array->td != other->td || strcmp(array->format, other->format) != 0)
{
const char *type;
if (array->td != NULL)
{
type = sipTypeName(array->td);
}
else
{
switch (*array->format)
{
case 'b':
type = "char";
break;
case 'B':
type = "unsigned char";
break;
case 'h':
type = "short";
break;
case 'H':
type = "unsigned short";
break;
case 'i':
type = "int";
break;
case 'I':
type = "unsigned int";
break;
case 'f':
type = "float";
break;
case 'd':
type = "double";
break;
default:
type = "";
}
}
PyErr_Format(PyExc_TypeError,
"can only assign another array of %s to the slice", type);
return NULL;
}
if (other->len != len)
{
PyErr_Format(PyExc_TypeError,
"the array being assigned must have length %zd", len);
return NULL;
}
if (other->stride == array->stride)
{
PyErr_Format(PyExc_TypeError,
"the array being assigned must have stride %zu",
array->stride);
return NULL;
}
return other->data;
}
/*
* Do the work of creating an array.
*/
static PyObject *make_array(void *data, const sipTypeDef *td,
const char *format, size_t stride, Py_ssize_t len, int flags,
PyObject *owner)
{
sipArrayObject *array;
if ((array = PyObject_NEW(sipArrayObject, &sipArray_Type)) == NULL)
return NULL;
array->data = data;
array->td = td;
array->format = format;
array->stride = stride;
array->len = len;
array->flags = flags;
if (flags & SIP_OWNS_MEMORY)
{
/* This is a borrowed reference to itself. */
array->owner = (PyObject *)array;
}
else
{
Py_XINCREF(owner);
array->owner = owner;
}
return (PyObject *)array;
}
/*
* Wrap an array of instances of a fundamental type. At the moment format must
* be either "b" (char), "B" (unsigned char), "h" (short), "H" (unsigned
* short), "i" (int), "I" (unsigned int), "f" (float) or "d" (double).
*/
PyObject *sip_api_convert_to_array(void *data, const char *format,
Py_ssize_t len, int flags)
{
size_t stride;
if (data == NULL)
{
Py_INCREF(Py_None);
return Py_None;
}
switch (*format)
{
case 'b':
stride = sizeof (char);
break;
case 'B':
stride = sizeof (unsigned char);
break;
case 'h':
stride = sizeof (short);
break;
case 'H':
stride = sizeof (unsigned short);
break;
case 'i':
stride = sizeof (int);
break;
case 'I':
stride = sizeof (unsigned int);
break;
case 'f':
stride = sizeof (float);
break;
case 'd':
stride = sizeof (double);
break;
default:
stride = 0;
}
assert(stride > 0);
assert(len >= 0);
return make_array(data, NULL, format, stride, len, flags, NULL);
}
/*
* Wrap an array of instances of a defined type.
*/
PyObject *sip_api_convert_to_typed_array(void *data, const sipTypeDef *td,
const char *format, size_t stride, Py_ssize_t len, int flags)
{
if (data == NULL)
{
Py_INCREF(Py_None);
return Py_None;
}
assert(stride > 0);
assert(len >= 0);
return make_array(data, td, format, stride, len, flags, NULL);
}