/*
|
ToDo:
|
|
Get rid of the checker (and also the converters) field in PyCFuncPtrObject and
|
StgDictObject, and replace them by slot functions in StgDictObject.
|
|
think about a buffer-like object (memory? bytes?)
|
|
Should POINTER(c_char) and POINTER(c_wchar) have a .value property?
|
What about c_char and c_wchar arrays then?
|
|
Add from_mmap, from_file, from_string metaclass methods.
|
|
Maybe we can get away with from_file (calls read) and with a from_buffer
|
method?
|
|
And what about the to_mmap, to_file, to_str(?) methods? They would clobber
|
the namespace, probably. So, functions instead? And we already have memmove...
|
*/
|
|
/*
|
|
Name methods, members, getsets
|
==============================================================================
|
|
PyCStructType_Type __new__(), from_address(), __mul__(), from_param()
|
UnionType_Type __new__(), from_address(), __mul__(), from_param()
|
PyCPointerType_Type __new__(), from_address(), __mul__(), from_param(), set_type()
|
PyCArrayType_Type __new__(), from_address(), __mul__(), from_param()
|
PyCSimpleType_Type __new__(), from_address(), __mul__(), from_param()
|
|
PyCData_Type
|
Struct_Type __new__(), __init__()
|
PyCPointer_Type __new__(), __init__(), _as_parameter_, contents
|
PyCArray_Type __new__(), __init__(), _as_parameter_, __get/setitem__(), __len__()
|
Simple_Type __new__(), __init__(), _as_parameter_
|
|
PyCField_Type
|
PyCStgDict_Type
|
|
==============================================================================
|
|
class methods
|
-------------
|
|
It has some similarity to the byref() construct compared to pointer()
|
from_address(addr)
|
- construct an instance from a given memory block (sharing this memory block)
|
|
from_param(obj)
|
- typecheck and convert a Python object into a C function call parameter
|
The result may be an instance of the type, or an integer or tuple
|
(typecode, value[, obj])
|
|
instance methods/properties
|
---------------------------
|
|
_as_parameter_
|
- convert self into a C function call parameter
|
This is either an integer, or a 3-tuple (typecode, value, obj)
|
|
functions
|
---------
|
|
sizeof(cdata)
|
- return the number of bytes the buffer contains
|
|
sizeof(ctype)
|
- return the number of bytes the buffer of an instance would contain
|
|
byref(cdata)
|
|
addressof(cdata)
|
|
pointer(cdata)
|
|
POINTER(ctype)
|
|
bytes(cdata)
|
- return the buffer contents as a sequence of bytes (which is currently a string)
|
|
*/
|
|
/*
|
* PyCStgDict_Type
|
* PyCStructType_Type
|
* UnionType_Type
|
* PyCPointerType_Type
|
* PyCArrayType_Type
|
* PyCSimpleType_Type
|
*
|
* PyCData_Type
|
* Struct_Type
|
* Union_Type
|
* PyCArray_Type
|
* Simple_Type
|
* PyCPointer_Type
|
* PyCField_Type
|
*
|
*/
|
|
#define PY_SSIZE_T_CLEAN
|
|
#include "Python.h"
|
#include "structmember.h"
|
|
#include <ffi.h>
|
#ifdef MS_WIN32
|
#include <windows.h>
|
#include <malloc.h>
|
#ifndef IS_INTRESOURCE
|
#define IS_INTRESOURCE(x) (((size_t)(x) >> 16) == 0)
|
#endif
|
#else
|
#include "ctypes_dlfcn.h"
|
#endif
|
#include "ctypes.h"
|
|
PyObject *PyExc_ArgError = NULL;
|
|
/* This dict maps ctypes types to POINTER types */
|
PyObject *_ctypes_ptrtype_cache = NULL;
|
|
static PyTypeObject Simple_Type;
|
|
/* a callable object used for unpickling */
|
static PyObject *_unpickle;
|
|
|
|
/****************************************************************/
|
|
typedef struct {
|
PyObject_HEAD
|
PyObject *key;
|
PyObject *dict;
|
} DictRemoverObject;
|
|
static void
|
_DictRemover_dealloc(PyObject *myself)
|
{
|
DictRemoverObject *self = (DictRemoverObject *)myself;
|
Py_XDECREF(self->key);
|
Py_XDECREF(self->dict);
|
Py_TYPE(self)->tp_free(myself);
|
}
|
|
static PyObject *
|
_DictRemover_call(PyObject *myself, PyObject *args, PyObject *kw)
|
{
|
DictRemoverObject *self = (DictRemoverObject *)myself;
|
if (self->key && self->dict) {
|
if (-1 == PyDict_DelItem(self->dict, self->key))
|
/* XXX Error context */
|
PyErr_WriteUnraisable(Py_None);
|
Py_CLEAR(self->key);
|
Py_CLEAR(self->dict);
|
}
|
Py_RETURN_NONE;
|
}
|
|
static PyTypeObject DictRemover_Type = {
|
PyVarObject_HEAD_INIT(NULL, 0)
|
"_ctypes.DictRemover", /* tp_name */
|
sizeof(DictRemoverObject), /* tp_basicsize */
|
0, /* tp_itemsize */
|
_DictRemover_dealloc, /* tp_dealloc */
|
0, /* tp_print */
|
0, /* tp_getattr */
|
0, /* tp_setattr */
|
0, /* tp_reserved */
|
0, /* tp_repr */
|
0, /* tp_as_number */
|
0, /* tp_as_sequence */
|
0, /* tp_as_mapping */
|
0, /* tp_hash */
|
_DictRemover_call, /* tp_call */
|
0, /* tp_str */
|
0, /* tp_getattro */
|
0, /* tp_setattro */
|
0, /* tp_as_buffer */
|
/* XXX should participate in GC? */
|
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
"deletes a key from a dictionary", /* 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 */
|
};
|
|
int
|
PyDict_SetItemProxy(PyObject *dict, PyObject *key, PyObject *item)
|
{
|
PyObject *obj;
|
DictRemoverObject *remover;
|
PyObject *proxy;
|
int result;
|
|
obj = _PyObject_CallNoArg((PyObject *)&DictRemover_Type);
|
if (obj == NULL)
|
return -1;
|
|
remover = (DictRemoverObject *)obj;
|
assert(remover->key == NULL);
|
assert(remover->dict == NULL);
|
Py_INCREF(key);
|
remover->key = key;
|
Py_INCREF(dict);
|
remover->dict = dict;
|
|
proxy = PyWeakref_NewProxy(item, obj);
|
Py_DECREF(obj);
|
if (proxy == NULL)
|
return -1;
|
|
result = PyDict_SetItem(dict, key, proxy);
|
Py_DECREF(proxy);
|
return result;
|
}
|
|
PyObject *
|
PyDict_GetItemProxy(PyObject *dict, PyObject *key)
|
{
|
PyObject *result;
|
PyObject *item = PyDict_GetItem(dict, key);
|
|
if (item == NULL)
|
return NULL;
|
if (!PyWeakref_CheckProxy(item))
|
return item;
|
result = PyWeakref_GET_OBJECT(item);
|
if (result == Py_None)
|
return NULL;
|
return result;
|
}
|
|
/******************************************************************/
|
|
/*
|
Allocate a memory block for a pep3118 format string, filled with
|
a suitable PEP 3118 type code corresponding to the given ctypes
|
type. Returns NULL on failure, with the error indicator set.
|
|
This produces type codes in the standard size mode (cf. struct module),
|
since the endianness may need to be swapped to a non-native one
|
later on.
|
*/
|
static char *
|
_ctypes_alloc_format_string_for_type(char code, int big_endian)
|
{
|
char *result;
|
char pep_code = '\0';
|
|
switch (code) {
|
#if SIZEOF_INT == 2
|
case 'i': pep_code = 'h'; break;
|
case 'I': pep_code = 'H'; break;
|
#elif SIZEOF_INT == 4
|
case 'i': pep_code = 'i'; break;
|
case 'I': pep_code = 'I'; break;
|
#elif SIZEOF_INT == 8
|
case 'i': pep_code = 'q'; break;
|
case 'I': pep_code = 'Q'; break;
|
#else
|
# error SIZEOF_INT has an unexpected value
|
#endif /* SIZEOF_INT */
|
#if SIZEOF_LONG == 4
|
case 'l': pep_code = 'l'; break;
|
case 'L': pep_code = 'L'; break;
|
#elif SIZEOF_LONG == 8
|
case 'l': pep_code = 'q'; break;
|
case 'L': pep_code = 'Q'; break;
|
#else
|
# error SIZEOF_LONG has an unexpected value
|
#endif /* SIZEOF_LONG */
|
#if SIZEOF__BOOL == 1
|
case '?': pep_code = '?'; break;
|
#elif SIZEOF__BOOL == 2
|
case '?': pep_code = 'H'; break;
|
#elif SIZEOF__BOOL == 4
|
case '?': pep_code = 'L'; break;
|
#elif SIZEOF__BOOL == 8
|
case '?': pep_code = 'Q'; break;
|
#else
|
# error SIZEOF__BOOL has an unexpected value
|
#endif /* SIZEOF__BOOL */
|
default:
|
/* The standard-size code is the same as the ctypes one */
|
pep_code = code;
|
break;
|
}
|
|
result = PyMem_Malloc(3);
|
if (result == NULL) {
|
PyErr_NoMemory();
|
return NULL;
|
}
|
|
result[0] = big_endian ? '>' : '<';
|
result[1] = pep_code;
|
result[2] = '\0';
|
return result;
|
}
|
|
/*
|
Allocate a memory block for a pep3118 format string, copy prefix (if
|
non-null) and suffix into it. Returns NULL on failure, with the error
|
indicator set. If called with a suffix of NULL the error indicator must
|
already be set.
|
*/
|
char *
|
_ctypes_alloc_format_string(const char *prefix, const char *suffix)
|
{
|
size_t len;
|
char *result;
|
|
if (suffix == NULL) {
|
assert(PyErr_Occurred());
|
return NULL;
|
}
|
len = strlen(suffix);
|
if (prefix)
|
len += strlen(prefix);
|
result = PyMem_Malloc(len + 1);
|
if (result == NULL) {
|
PyErr_NoMemory();
|
return NULL;
|
}
|
if (prefix)
|
strcpy(result, prefix);
|
else
|
result[0] = '\0';
|
strcat(result, suffix);
|
return result;
|
}
|
|
/*
|
Allocate a memory block for a pep3118 format string, adding
|
the given prefix (if non-null), an additional shape prefix, and a suffix.
|
Returns NULL on failure, with the error indicator set. If called with
|
a suffix of NULL the error indicator must already be set.
|
*/
|
char *
|
_ctypes_alloc_format_string_with_shape(int ndim, const Py_ssize_t *shape,
|
const char *prefix, const char *suffix)
|
{
|
char *new_prefix;
|
char *result;
|
char buf[32];
|
Py_ssize_t prefix_len;
|
int k;
|
|
prefix_len = 32 * ndim + 3;
|
if (prefix)
|
prefix_len += strlen(prefix);
|
new_prefix = PyMem_Malloc(prefix_len);
|
if (new_prefix == NULL) {
|
PyErr_NoMemory();
|
return NULL;
|
}
|
new_prefix[0] = '\0';
|
if (prefix)
|
strcpy(new_prefix, prefix);
|
if (ndim > 0) {
|
/* Add the prefix "(shape[0],shape[1],...,shape[ndim-1])" */
|
strcat(new_prefix, "(");
|
for (k = 0; k < ndim; ++k) {
|
if (k < ndim-1) {
|
sprintf(buf, "%"PY_FORMAT_SIZE_T"d,", shape[k]);
|
} else {
|
sprintf(buf, "%"PY_FORMAT_SIZE_T"d)", shape[k]);
|
}
|
strcat(new_prefix, buf);
|
}
|
}
|
result = _ctypes_alloc_format_string(new_prefix, suffix);
|
PyMem_Free(new_prefix);
|
return result;
|
}
|
|
/*
|
PyCStructType_Type - a meta type/class. Creating a new class using this one as
|
__metaclass__ will call the constructor StructUnionType_new. It replaces the
|
tp_dict member with a new instance of StgDict, and initializes the C
|
accessible fields somehow.
|
*/
|
|
static PyCArgObject *
|
StructUnionType_paramfunc(CDataObject *self)
|
{
|
PyCArgObject *parg;
|
StgDictObject *stgdict;
|
|
parg = PyCArgObject_new();
|
if (parg == NULL)
|
return NULL;
|
|
parg->tag = 'V';
|
stgdict = PyObject_stgdict((PyObject *)self);
|
assert(stgdict); /* Cannot be NULL for structure/union instances */
|
parg->pffi_type = &stgdict->ffi_type_pointer;
|
/* For structure parameters (by value), parg->value doesn't contain the structure
|
data itself, instead parg->value.p *points* to the structure's data
|
See also _ctypes.c, function _call_function_pointer().
|
*/
|
parg->value.p = self->b_ptr;
|
parg->size = self->b_size;
|
Py_INCREF(self);
|
parg->obj = (PyObject *)self;
|
return parg;
|
}
|
|
static PyObject *
|
StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isStruct)
|
{
|
PyTypeObject *result;
|
PyObject *fields;
|
StgDictObject *dict;
|
|
/* create the new instance (which is a class,
|
since we are a metatype!) */
|
result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds);
|
if (!result)
|
return NULL;
|
|
/* keep this for bw compatibility */
|
if (PyDict_GetItemString(result->tp_dict, "_abstract_"))
|
return (PyObject *)result;
|
|
dict = (StgDictObject *)_PyObject_CallNoArg((PyObject *)&PyCStgDict_Type);
|
if (!dict) {
|
Py_DECREF(result);
|
return NULL;
|
}
|
/* replace the class dict by our updated stgdict, which holds info
|
about storage requirements of the instances */
|
if (-1 == PyDict_Update((PyObject *)dict, result->tp_dict)) {
|
Py_DECREF(result);
|
Py_DECREF((PyObject *)dict);
|
return NULL;
|
}
|
Py_SETREF(result->tp_dict, (PyObject *)dict);
|
dict->format = _ctypes_alloc_format_string(NULL, "B");
|
if (dict->format == NULL) {
|
Py_DECREF(result);
|
return NULL;
|
}
|
|
dict->paramfunc = StructUnionType_paramfunc;
|
|
fields = PyDict_GetItemString((PyObject *)dict, "_fields_");
|
if (!fields) {
|
StgDictObject *basedict = PyType_stgdict((PyObject *)result->tp_base);
|
|
if (basedict == NULL)
|
return (PyObject *)result;
|
/* copy base dict */
|
if (-1 == PyCStgDict_clone(dict, basedict)) {
|
Py_DECREF(result);
|
return NULL;
|
}
|
dict->flags &= ~DICTFLAG_FINAL; /* clear the 'final' flag in the subclass dict */
|
basedict->flags |= DICTFLAG_FINAL; /* set the 'final' flag in the baseclass dict */
|
return (PyObject *)result;
|
}
|
|
if (-1 == PyObject_SetAttrString((PyObject *)result, "_fields_", fields)) {
|
Py_DECREF(result);
|
return NULL;
|
}
|
return (PyObject *)result;
|
}
|
|
static PyObject *
|
PyCStructType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
{
|
return StructUnionType_new(type, args, kwds, 1);
|
}
|
|
static PyObject *
|
UnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
{
|
return StructUnionType_new(type, args, kwds, 0);
|
}
|
|
static const char from_address_doc[] =
|
"C.from_address(integer) -> C instance\naccess a C instance at the specified address";
|
|
static PyObject *
|
CDataType_from_address(PyObject *type, PyObject *value)
|
{
|
void *buf;
|
if (!PyLong_Check(value)) {
|
PyErr_SetString(PyExc_TypeError,
|
"integer expected");
|
return NULL;
|
}
|
buf = (void *)PyLong_AsVoidPtr(value);
|
if (PyErr_Occurred())
|
return NULL;
|
return PyCData_AtAddress(type, buf);
|
}
|
|
static const char from_buffer_doc[] =
|
"C.from_buffer(object, offset=0) -> C instance\ncreate a C instance from a writeable buffer";
|
|
static int
|
KeepRef(CDataObject *target, Py_ssize_t index, PyObject *keep);
|
|
static PyObject *
|
CDataType_from_buffer(PyObject *type, PyObject *args)
|
{
|
PyObject *obj;
|
PyObject *mv;
|
PyObject *result;
|
Py_buffer *buffer;
|
Py_ssize_t offset = 0;
|
|
StgDictObject *dict = PyType_stgdict(type);
|
if (!dict) {
|
PyErr_SetString(PyExc_TypeError, "abstract class");
|
return NULL;
|
}
|
|
if (!PyArg_ParseTuple(args, "O|n:from_buffer", &obj, &offset))
|
return NULL;
|
|
mv = PyMemoryView_FromObject(obj);
|
if (mv == NULL)
|
return NULL;
|
|
buffer = PyMemoryView_GET_BUFFER(mv);
|
|
if (buffer->readonly) {
|
PyErr_SetString(PyExc_TypeError,
|
"underlying buffer is not writable");
|
Py_DECREF(mv);
|
return NULL;
|
}
|
|
if (!PyBuffer_IsContiguous(buffer, 'C')) {
|
PyErr_SetString(PyExc_TypeError,
|
"underlying buffer is not C contiguous");
|
Py_DECREF(mv);
|
return NULL;
|
}
|
|
if (offset < 0) {
|
PyErr_SetString(PyExc_ValueError,
|
"offset cannot be negative");
|
Py_DECREF(mv);
|
return NULL;
|
}
|
|
if (dict->size > buffer->len - offset) {
|
PyErr_Format(PyExc_ValueError,
|
"Buffer size too small "
|
"(%zd instead of at least %zd bytes)",
|
buffer->len, dict->size + offset);
|
Py_DECREF(mv);
|
return NULL;
|
}
|
|
result = PyCData_AtAddress(type, (char *)buffer->buf + offset);
|
if (result == NULL) {
|
Py_DECREF(mv);
|
return NULL;
|
}
|
|
if (-1 == KeepRef((CDataObject *)result, -1, mv)) {
|
Py_DECREF(result);
|
return NULL;
|
}
|
|
return result;
|
}
|
|
static const char from_buffer_copy_doc[] =
|
"C.from_buffer_copy(object, offset=0) -> C instance\ncreate a C instance from a readable buffer";
|
|
static PyObject *
|
GenericPyCData_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
|
|
static PyObject *
|
CDataType_from_buffer_copy(PyObject *type, PyObject *args)
|
{
|
Py_buffer buffer;
|
Py_ssize_t offset = 0;
|
PyObject *result;
|
StgDictObject *dict = PyType_stgdict(type);
|
if (!dict) {
|
PyErr_SetString(PyExc_TypeError, "abstract class");
|
return NULL;
|
}
|
|
if (!PyArg_ParseTuple(args, "y*|n:from_buffer_copy", &buffer, &offset))
|
return NULL;
|
|
if (offset < 0) {
|
PyErr_SetString(PyExc_ValueError,
|
"offset cannot be negative");
|
PyBuffer_Release(&buffer);
|
return NULL;
|
}
|
|
if (dict->size > buffer.len - offset) {
|
PyErr_Format(PyExc_ValueError,
|
"Buffer size too small (%zd instead of at least %zd bytes)",
|
buffer.len, dict->size + offset);
|
PyBuffer_Release(&buffer);
|
return NULL;
|
}
|
|
result = GenericPyCData_new((PyTypeObject *)type, NULL, NULL);
|
if (result != NULL) {
|
memcpy(((CDataObject *)result)->b_ptr,
|
(char *)buffer.buf + offset, dict->size);
|
}
|
PyBuffer_Release(&buffer);
|
return result;
|
}
|
|
static const char in_dll_doc[] =
|
"C.in_dll(dll, name) -> C instance\naccess a C instance in a dll";
|
|
static PyObject *
|
CDataType_in_dll(PyObject *type, PyObject *args)
|
{
|
PyObject *dll;
|
char *name;
|
PyObject *obj;
|
void *handle;
|
void *address;
|
|
if (!PyArg_ParseTuple(args, "Os:in_dll", &dll, &name))
|
return NULL;
|
|
obj = PyObject_GetAttrString(dll, "_handle");
|
if (!obj)
|
return NULL;
|
if (!PyLong_Check(obj)) {
|
PyErr_SetString(PyExc_TypeError,
|
"the _handle attribute of the second argument must be an integer");
|
Py_DECREF(obj);
|
return NULL;
|
}
|
handle = (void *)PyLong_AsVoidPtr(obj);
|
Py_DECREF(obj);
|
if (PyErr_Occurred()) {
|
PyErr_SetString(PyExc_ValueError,
|
"could not convert the _handle attribute to a pointer");
|
return NULL;
|
}
|
|
#ifdef MS_WIN32
|
address = (void *)GetProcAddress(handle, name);
|
if (!address) {
|
PyErr_Format(PyExc_ValueError,
|
"symbol '%s' not found",
|
name);
|
return NULL;
|
}
|
#else
|
address = (void *)ctypes_dlsym(handle, name);
|
if (!address) {
|
#ifdef __CYGWIN__
|
/* dlerror() isn't very helpful on cygwin */
|
PyErr_Format(PyExc_ValueError,
|
"symbol '%s' not found",
|
name);
|
#else
|
PyErr_SetString(PyExc_ValueError, ctypes_dlerror());
|
#endif
|
return NULL;
|
}
|
#endif
|
return PyCData_AtAddress(type, address);
|
}
|
|
static const char from_param_doc[] =
|
"Convert a Python object into a function call parameter.";
|
|
static PyObject *
|
CDataType_from_param(PyObject *type, PyObject *value)
|
{
|
PyObject *as_parameter;
|
int res = PyObject_IsInstance(value, type);
|
if (res == -1)
|
return NULL;
|
if (res) {
|
Py_INCREF(value);
|
return value;
|
}
|
if (PyCArg_CheckExact(value)) {
|
PyCArgObject *p = (PyCArgObject *)value;
|
PyObject *ob = p->obj;
|
const char *ob_name;
|
StgDictObject *dict;
|
dict = PyType_stgdict(type);
|
|
/* If we got a PyCArgObject, we must check if the object packed in it
|
is an instance of the type's dict->proto */
|
if(dict && ob) {
|
res = PyObject_IsInstance(ob, dict->proto);
|
if (res == -1)
|
return NULL;
|
if (res) {
|
Py_INCREF(value);
|
return value;
|
}
|
}
|
ob_name = (ob) ? Py_TYPE(ob)->tp_name : "???";
|
PyErr_Format(PyExc_TypeError,
|
"expected %s instance instead of pointer to %s",
|
((PyTypeObject *)type)->tp_name, ob_name);
|
return NULL;
|
}
|
|
as_parameter = PyObject_GetAttrString(value, "_as_parameter_");
|
if (as_parameter) {
|
value = CDataType_from_param(type, as_parameter);
|
Py_DECREF(as_parameter);
|
return value;
|
}
|
PyErr_Format(PyExc_TypeError,
|
"expected %s instance instead of %s",
|
((PyTypeObject *)type)->tp_name,
|
Py_TYPE(value)->tp_name);
|
return NULL;
|
}
|
|
static PyMethodDef CDataType_methods[] = {
|
{ "from_param", CDataType_from_param, METH_O, from_param_doc },
|
{ "from_address", CDataType_from_address, METH_O, from_address_doc },
|
{ "from_buffer", CDataType_from_buffer, METH_VARARGS, from_buffer_doc, },
|
{ "from_buffer_copy", CDataType_from_buffer_copy, METH_VARARGS, from_buffer_copy_doc, },
|
{ "in_dll", CDataType_in_dll, METH_VARARGS, in_dll_doc },
|
{ NULL, NULL },
|
};
|
|
static PyObject *
|
CDataType_repeat(PyObject *self, Py_ssize_t length)
|
{
|
if (length < 0)
|
return PyErr_Format(PyExc_ValueError,
|
"Array length must be >= 0, not %zd",
|
length);
|
return PyCArrayType_from_ctype(self, length);
|
}
|
|
static PySequenceMethods CDataType_as_sequence = {
|
0, /* inquiry sq_length; */
|
0, /* binaryfunc sq_concat; */
|
CDataType_repeat, /* intargfunc sq_repeat; */
|
0, /* intargfunc sq_item; */
|
0, /* intintargfunc sq_slice; */
|
0, /* intobjargproc sq_ass_item; */
|
0, /* intintobjargproc sq_ass_slice; */
|
0, /* objobjproc sq_contains; */
|
|
0, /* binaryfunc sq_inplace_concat; */
|
0, /* intargfunc sq_inplace_repeat; */
|
};
|
|
static int
|
CDataType_clear(PyTypeObject *self)
|
{
|
StgDictObject *dict = PyType_stgdict((PyObject *)self);
|
if (dict)
|
Py_CLEAR(dict->proto);
|
return PyType_Type.tp_clear((PyObject *)self);
|
}
|
|
static int
|
CDataType_traverse(PyTypeObject *self, visitproc visit, void *arg)
|
{
|
StgDictObject *dict = PyType_stgdict((PyObject *)self);
|
if (dict)
|
Py_VISIT(dict->proto);
|
return PyType_Type.tp_traverse((PyObject *)self, visit, arg);
|
}
|
|
static int
|
PyCStructType_setattro(PyObject *self, PyObject *key, PyObject *value)
|
{
|
/* XXX Should we disallow deleting _fields_? */
|
if (-1 == PyType_Type.tp_setattro(self, key, value))
|
return -1;
|
|
if (value && PyUnicode_Check(key) &&
|
_PyUnicode_EqualToASCIIString(key, "_fields_"))
|
return PyCStructUnionType_update_stgdict(self, value, 1);
|
return 0;
|
}
|
|
|
static int
|
UnionType_setattro(PyObject *self, PyObject *key, PyObject *value)
|
{
|
/* XXX Should we disallow deleting _fields_? */
|
if (-1 == PyObject_GenericSetAttr(self, key, value))
|
return -1;
|
|
if (PyUnicode_Check(key) &&
|
_PyUnicode_EqualToASCIIString(key, "_fields_"))
|
return PyCStructUnionType_update_stgdict(self, value, 0);
|
return 0;
|
}
|
|
|
PyTypeObject PyCStructType_Type = {
|
PyVarObject_HEAD_INIT(NULL, 0)
|
"_ctypes.PyCStructType", /* tp_name */
|
0, /* tp_basicsize */
|
0, /* tp_itemsize */
|
0, /* tp_dealloc */
|
0, /* tp_print */
|
0, /* tp_getattr */
|
0, /* tp_setattr */
|
0, /* tp_reserved */
|
0, /* tp_repr */
|
0, /* tp_as_number */
|
&CDataType_as_sequence, /* tp_as_sequence */
|
0, /* tp_as_mapping */
|
0, /* tp_hash */
|
0, /* tp_call */
|
0, /* tp_str */
|
0, /* tp_getattro */
|
PyCStructType_setattro, /* tp_setattro */
|
0, /* tp_as_buffer */
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
"metatype for the CData Objects", /* tp_doc */
|
(traverseproc)CDataType_traverse, /* tp_traverse */
|
(inquiry)CDataType_clear, /* tp_clear */
|
0, /* tp_richcompare */
|
0, /* tp_weaklistoffset */
|
0, /* tp_iter */
|
0, /* tp_iternext */
|
CDataType_methods, /* 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 */
|
PyCStructType_new, /* tp_new */
|
0, /* tp_free */
|
};
|
|
static PyTypeObject UnionType_Type = {
|
PyVarObject_HEAD_INIT(NULL, 0)
|
"_ctypes.UnionType", /* tp_name */
|
0, /* tp_basicsize */
|
0, /* tp_itemsize */
|
0, /* tp_dealloc */
|
0, /* tp_print */
|
0, /* tp_getattr */
|
0, /* tp_setattr */
|
0, /* tp_reserved */
|
0, /* tp_repr */
|
0, /* tp_as_number */
|
&CDataType_as_sequence, /* tp_as_sequence */
|
0, /* tp_as_mapping */
|
0, /* tp_hash */
|
0, /* tp_call */
|
0, /* tp_str */
|
0, /* tp_getattro */
|
UnionType_setattro, /* tp_setattro */
|
0, /* tp_as_buffer */
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
"metatype for the CData Objects", /* tp_doc */
|
(traverseproc)CDataType_traverse, /* tp_traverse */
|
(inquiry)CDataType_clear, /* tp_clear */
|
0, /* tp_richcompare */
|
0, /* tp_weaklistoffset */
|
0, /* tp_iter */
|
0, /* tp_iternext */
|
CDataType_methods, /* 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 */
|
UnionType_new, /* tp_new */
|
0, /* tp_free */
|
};
|
|
|
/******************************************************************/
|
|
/*
|
|
The PyCPointerType_Type metaclass must ensure that the subclass of Pointer can be
|
created. It must check for a _type_ attribute in the class. Since are no
|
runtime created properties, a CField is probably *not* needed ?
|
|
class IntPointer(Pointer):
|
_type_ = "i"
|
|
The PyCPointer_Type provides the functionality: a contents method/property, a
|
size property/method, and the sequence protocol.
|
|
*/
|
|
static int
|
PyCPointerType_SetProto(StgDictObject *stgdict, PyObject *proto)
|
{
|
if (!proto || !PyType_Check(proto)) {
|
PyErr_SetString(PyExc_TypeError,
|
"_type_ must be a type");
|
return -1;
|
}
|
if (!PyType_stgdict(proto)) {
|
PyErr_SetString(PyExc_TypeError,
|
"_type_ must have storage info");
|
return -1;
|
}
|
Py_INCREF(proto);
|
Py_XSETREF(stgdict->proto, proto);
|
return 0;
|
}
|
|
static PyCArgObject *
|
PyCPointerType_paramfunc(CDataObject *self)
|
{
|
PyCArgObject *parg;
|
|
parg = PyCArgObject_new();
|
if (parg == NULL)
|
return NULL;
|
|
parg->tag = 'P';
|
parg->pffi_type = &ffi_type_pointer;
|
Py_INCREF(self);
|
parg->obj = (PyObject *)self;
|
parg->value.p = *(void **)self->b_ptr;
|
return parg;
|
}
|
|
static PyObject *
|
PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
{
|
PyTypeObject *result;
|
StgDictObject *stgdict;
|
PyObject *proto;
|
PyObject *typedict;
|
|
typedict = PyTuple_GetItem(args, 2);
|
if (!typedict)
|
return NULL;
|
/*
|
stgdict items size, align, length contain info about pointers itself,
|
stgdict->proto has info about the pointed to type!
|
*/
|
stgdict = (StgDictObject *)PyObject_CallObject(
|
(PyObject *)&PyCStgDict_Type, NULL);
|
if (!stgdict)
|
return NULL;
|
stgdict->size = sizeof(void *);
|
stgdict->align = _ctypes_get_fielddesc("P")->pffi_type->alignment;
|
stgdict->length = 1;
|
stgdict->ffi_type_pointer = ffi_type_pointer;
|
stgdict->paramfunc = PyCPointerType_paramfunc;
|
stgdict->flags |= TYPEFLAG_ISPOINTER;
|
|
proto = PyDict_GetItemString(typedict, "_type_"); /* Borrowed ref */
|
if (proto && -1 == PyCPointerType_SetProto(stgdict, proto)) {
|
Py_DECREF((PyObject *)stgdict);
|
return NULL;
|
}
|
|
if (proto) {
|
StgDictObject *itemdict = PyType_stgdict(proto);
|
const char *current_format;
|
/* PyCPointerType_SetProto has verified proto has a stgdict. */
|
assert(itemdict);
|
/* If itemdict->format is NULL, then this is a pointer to an
|
incomplete type. We create a generic format string
|
'pointer to bytes' in this case. XXX Better would be to
|
fix the format string later...
|
*/
|
current_format = itemdict->format ? itemdict->format : "B";
|
if (itemdict->shape != NULL) {
|
/* pointer to an array: the shape needs to be prefixed */
|
stgdict->format = _ctypes_alloc_format_string_with_shape(
|
itemdict->ndim, itemdict->shape, "&", current_format);
|
} else {
|
stgdict->format = _ctypes_alloc_format_string("&", current_format);
|
}
|
if (stgdict->format == NULL) {
|
Py_DECREF((PyObject *)stgdict);
|
return NULL;
|
}
|
}
|
|
/* create the new instance (which is a class,
|
since we are a metatype!) */
|
result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds);
|
if (result == NULL) {
|
Py_DECREF((PyObject *)stgdict);
|
return NULL;
|
}
|
|
/* replace the class dict by our updated spam dict */
|
if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) {
|
Py_DECREF(result);
|
Py_DECREF((PyObject *)stgdict);
|
return NULL;
|
}
|
Py_SETREF(result->tp_dict, (PyObject *)stgdict);
|
|
return (PyObject *)result;
|
}
|
|
|
static PyObject *
|
PyCPointerType_set_type(PyTypeObject *self, PyObject *type)
|
{
|
StgDictObject *dict;
|
|
dict = PyType_stgdict((PyObject *)self);
|
if (!dict) {
|
PyErr_SetString(PyExc_TypeError,
|
"abstract class");
|
return NULL;
|
}
|
|
if (-1 == PyCPointerType_SetProto(dict, type))
|
return NULL;
|
|
if (-1 == PyDict_SetItemString((PyObject *)dict, "_type_", type))
|
return NULL;
|
|
Py_RETURN_NONE;
|
}
|
|
static PyObject *_byref(PyObject *);
|
|
static PyObject *
|
PyCPointerType_from_param(PyObject *type, PyObject *value)
|
{
|
StgDictObject *typedict;
|
|
if (value == Py_None) {
|
/* ConvParam will convert to a NULL pointer later */
|
Py_INCREF(value);
|
return value;
|
}
|
|
typedict = PyType_stgdict(type);
|
if (!typedict) {
|
PyErr_SetString(PyExc_TypeError,
|
"abstract class");
|
return NULL;
|
}
|
|
/* If we expect POINTER(<type>), but receive a <type> instance, accept
|
it by calling byref(<type>).
|
*/
|
switch (PyObject_IsInstance(value, typedict->proto)) {
|
case 1:
|
Py_INCREF(value); /* _byref steals a refcount */
|
return _byref(value);
|
case -1:
|
return NULL;
|
default:
|
break;
|
}
|
|
if (PointerObject_Check(value) || ArrayObject_Check(value)) {
|
/* Array instances are also pointers when
|
the item types are the same.
|
*/
|
StgDictObject *v = PyObject_stgdict(value);
|
assert(v); /* Cannot be NULL for pointer or array objects */
|
if (PyObject_IsSubclass(v->proto, typedict->proto)) {
|
Py_INCREF(value);
|
return value;
|
}
|
}
|
return CDataType_from_param(type, value);
|
}
|
|
static PyMethodDef PyCPointerType_methods[] = {
|
{ "from_address", CDataType_from_address, METH_O, from_address_doc },
|
{ "from_buffer", CDataType_from_buffer, METH_VARARGS, from_buffer_doc, },
|
{ "from_buffer_copy", CDataType_from_buffer_copy, METH_VARARGS, from_buffer_copy_doc, },
|
{ "in_dll", CDataType_in_dll, METH_VARARGS, in_dll_doc},
|
{ "from_param", (PyCFunction)PyCPointerType_from_param, METH_O, from_param_doc},
|
{ "set_type", (PyCFunction)PyCPointerType_set_type, METH_O },
|
{ NULL, NULL },
|
};
|
|
PyTypeObject PyCPointerType_Type = {
|
PyVarObject_HEAD_INIT(NULL, 0)
|
"_ctypes.PyCPointerType", /* tp_name */
|
0, /* tp_basicsize */
|
0, /* tp_itemsize */
|
0, /* tp_dealloc */
|
0, /* tp_print */
|
0, /* tp_getattr */
|
0, /* tp_setattr */
|
0, /* tp_reserved */
|
0, /* tp_repr */
|
0, /* tp_as_number */
|
&CDataType_as_sequence, /* 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 | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
"metatype for the Pointer Objects", /* tp_doc */
|
(traverseproc)CDataType_traverse, /* tp_traverse */
|
(inquiry)CDataType_clear, /* tp_clear */
|
0, /* tp_richcompare */
|
0, /* tp_weaklistoffset */
|
0, /* tp_iter */
|
0, /* tp_iternext */
|
PyCPointerType_methods, /* 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 */
|
PyCPointerType_new, /* tp_new */
|
0, /* tp_free */
|
};
|
|
|
/******************************************************************/
|
/*
|
PyCArrayType_Type
|
*/
|
/*
|
PyCArrayType_new ensures that the new Array subclass created has a _length_
|
attribute, and a _type_ attribute.
|
*/
|
|
static int
|
CharArray_set_raw(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored))
|
{
|
char *ptr;
|
Py_ssize_t size;
|
Py_buffer view;
|
|
if (value == NULL) {
|
PyErr_SetString(PyExc_AttributeError, "cannot delete attribute");
|
return -1;
|
}
|
if (PyObject_GetBuffer(value, &view, PyBUF_SIMPLE) < 0)
|
return -1;
|
size = view.len;
|
ptr = view.buf;
|
if (size > self->b_size) {
|
PyErr_SetString(PyExc_ValueError,
|
"byte string too long");
|
goto fail;
|
}
|
|
memcpy(self->b_ptr, ptr, size);
|
|
PyBuffer_Release(&view);
|
return 0;
|
fail:
|
PyBuffer_Release(&view);
|
return -1;
|
}
|
|
static PyObject *
|
CharArray_get_raw(CDataObject *self, void *Py_UNUSED(ignored))
|
{
|
return PyBytes_FromStringAndSize(self->b_ptr, self->b_size);
|
}
|
|
static PyObject *
|
CharArray_get_value(CDataObject *self, void *Py_UNUSED(ignored))
|
{
|
Py_ssize_t i;
|
char *ptr = self->b_ptr;
|
for (i = 0; i < self->b_size; ++i)
|
if (*ptr++ == '\0')
|
break;
|
return PyBytes_FromStringAndSize(self->b_ptr, i);
|
}
|
|
static int
|
CharArray_set_value(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored))
|
{
|
char *ptr;
|
Py_ssize_t size;
|
|
if (value == NULL) {
|
PyErr_SetString(PyExc_TypeError,
|
"can't delete attribute");
|
return -1;
|
}
|
|
if (!PyBytes_Check(value)) {
|
PyErr_Format(PyExc_TypeError,
|
"bytes expected instead of %s instance",
|
Py_TYPE(value)->tp_name);
|
return -1;
|
} else
|
Py_INCREF(value);
|
size = PyBytes_GET_SIZE(value);
|
if (size > self->b_size) {
|
PyErr_SetString(PyExc_ValueError,
|
"byte string too long");
|
Py_DECREF(value);
|
return -1;
|
}
|
|
ptr = PyBytes_AS_STRING(value);
|
memcpy(self->b_ptr, ptr, size);
|
if (size < self->b_size)
|
self->b_ptr[size] = '\0';
|
Py_DECREF(value);
|
|
return 0;
|
}
|
|
static PyGetSetDef CharArray_getsets[] = {
|
{ "raw", (getter)CharArray_get_raw, (setter)CharArray_set_raw,
|
"value", NULL },
|
{ "value", (getter)CharArray_get_value, (setter)CharArray_set_value,
|
"string value"},
|
{ NULL, NULL }
|
};
|
|
#ifdef CTYPES_UNICODE
|
static PyObject *
|
WCharArray_get_value(CDataObject *self, void *Py_UNUSED(ignored))
|
{
|
Py_ssize_t i;
|
wchar_t *ptr = (wchar_t *)self->b_ptr;
|
for (i = 0; i < self->b_size/(Py_ssize_t)sizeof(wchar_t); ++i)
|
if (*ptr++ == (wchar_t)0)
|
break;
|
return PyUnicode_FromWideChar((wchar_t *)self->b_ptr, i);
|
}
|
|
static int
|
WCharArray_set_value(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored))
|
{
|
Py_ssize_t result = 0;
|
Py_UNICODE *wstr;
|
Py_ssize_t len;
|
|
if (value == NULL) {
|
PyErr_SetString(PyExc_TypeError,
|
"can't delete attribute");
|
return -1;
|
}
|
if (!PyUnicode_Check(value)) {
|
PyErr_Format(PyExc_TypeError,
|
"unicode string expected instead of %s instance",
|
Py_TYPE(value)->tp_name);
|
return -1;
|
} else
|
Py_INCREF(value);
|
|
wstr = PyUnicode_AsUnicodeAndSize(value, &len);
|
if (wstr == NULL)
|
return -1;
|
if ((size_t)len > self->b_size/sizeof(wchar_t)) {
|
PyErr_SetString(PyExc_ValueError,
|
"string too long");
|
result = -1;
|
goto done;
|
}
|
result = PyUnicode_AsWideChar(value,
|
(wchar_t *)self->b_ptr,
|
self->b_size/sizeof(wchar_t));
|
if (result >= 0 && (size_t)result < self->b_size/sizeof(wchar_t))
|
((wchar_t *)self->b_ptr)[result] = (wchar_t)0;
|
done:
|
Py_DECREF(value);
|
|
return result >= 0 ? 0 : -1;
|
}
|
|
static PyGetSetDef WCharArray_getsets[] = {
|
{ "value", (getter)WCharArray_get_value, (setter)WCharArray_set_value,
|
"string value"},
|
{ NULL, NULL }
|
};
|
#endif
|
|
/*
|
The next three functions copied from Python's typeobject.c.
|
|
They are used to attach methods, members, or getsets to a type *after* it
|
has been created: Arrays of characters have additional getsets to treat them
|
as strings.
|
*/
|
/*
|
static int
|
add_methods(PyTypeObject *type, PyMethodDef *meth)
|
{
|
PyObject *dict = type->tp_dict;
|
for (; meth->ml_name != NULL; meth++) {
|
PyObject *descr;
|
descr = PyDescr_NewMethod(type, meth);
|
if (descr == NULL)
|
return -1;
|
if (PyDict_SetItemString(dict, meth->ml_name, descr) < 0) {
|
Py_DECREF(descr);
|
return -1;
|
}
|
Py_DECREF(descr);
|
}
|
return 0;
|
}
|
|
static int
|
add_members(PyTypeObject *type, PyMemberDef *memb)
|
{
|
PyObject *dict = type->tp_dict;
|
for (; memb->name != NULL; memb++) {
|
PyObject *descr;
|
descr = PyDescr_NewMember(type, memb);
|
if (descr == NULL)
|
return -1;
|
if (PyDict_SetItemString(dict, memb->name, descr) < 0) {
|
Py_DECREF(descr);
|
return -1;
|
}
|
Py_DECREF(descr);
|
}
|
return 0;
|
}
|
*/
|
|
static int
|
add_getset(PyTypeObject *type, PyGetSetDef *gsp)
|
{
|
PyObject *dict = type->tp_dict;
|
for (; gsp->name != NULL; gsp++) {
|
PyObject *descr;
|
descr = PyDescr_NewGetSet(type, gsp);
|
if (descr == NULL)
|
return -1;
|
if (PyDict_SetItemString(dict, gsp->name, descr) < 0) {
|
Py_DECREF(descr);
|
return -1;
|
}
|
Py_DECREF(descr);
|
}
|
return 0;
|
}
|
|
static PyCArgObject *
|
PyCArrayType_paramfunc(CDataObject *self)
|
{
|
PyCArgObject *p = PyCArgObject_new();
|
if (p == NULL)
|
return NULL;
|
p->tag = 'P';
|
p->pffi_type = &ffi_type_pointer;
|
p->value.p = (char *)self->b_ptr;
|
Py_INCREF(self);
|
p->obj = (PyObject *)self;
|
return p;
|
}
|
|
static PyObject *
|
PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
{
|
PyTypeObject *result;
|
StgDictObject *stgdict;
|
StgDictObject *itemdict;
|
PyObject *length_attr, *type_attr;
|
Py_ssize_t length;
|
Py_ssize_t itemsize, itemalign;
|
|
/* create the new instance (which is a class,
|
since we are a metatype!) */
|
result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds);
|
if (result == NULL)
|
return NULL;
|
|
/* Initialize these variables to NULL so that we can simplify error
|
handling by using Py_XDECREF. */
|
stgdict = NULL;
|
type_attr = NULL;
|
|
length_attr = PyObject_GetAttrString((PyObject *)result, "_length_");
|
if (!length_attr || !PyLong_Check(length_attr)) {
|
PyErr_SetString(PyExc_AttributeError,
|
"class must define a '_length_' attribute, "
|
"which must be a positive integer");
|
Py_XDECREF(length_attr);
|
goto error;
|
}
|
length = PyLong_AsSsize_t(length_attr);
|
Py_DECREF(length_attr);
|
if (length == -1 && PyErr_Occurred()) {
|
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
|
PyErr_SetString(PyExc_OverflowError,
|
"The '_length_' attribute is too large");
|
}
|
goto error;
|
}
|
|
type_attr = PyObject_GetAttrString((PyObject *)result, "_type_");
|
if (!type_attr) {
|
PyErr_SetString(PyExc_AttributeError,
|
"class must define a '_type_' attribute");
|
goto error;
|
}
|
|
stgdict = (StgDictObject *)PyObject_CallObject(
|
(PyObject *)&PyCStgDict_Type, NULL);
|
if (!stgdict)
|
goto error;
|
|
itemdict = PyType_stgdict(type_attr);
|
if (!itemdict) {
|
PyErr_SetString(PyExc_TypeError,
|
"_type_ must have storage info");
|
goto error;
|
}
|
|
assert(itemdict->format);
|
stgdict->format = _ctypes_alloc_format_string(NULL, itemdict->format);
|
if (stgdict->format == NULL)
|
goto error;
|
stgdict->ndim = itemdict->ndim + 1;
|
stgdict->shape = PyMem_Malloc(sizeof(Py_ssize_t) * stgdict->ndim);
|
if (stgdict->shape == NULL) {
|
PyErr_NoMemory();
|
goto error;
|
}
|
stgdict->shape[0] = length;
|
if (stgdict->ndim > 1) {
|
memmove(&stgdict->shape[1], itemdict->shape,
|
sizeof(Py_ssize_t) * (stgdict->ndim - 1));
|
}
|
|
itemsize = itemdict->size;
|
if (length * itemsize < 0) {
|
PyErr_SetString(PyExc_OverflowError,
|
"array too large");
|
goto error;
|
}
|
|
itemalign = itemdict->align;
|
|
if (itemdict->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER))
|
stgdict->flags |= TYPEFLAG_HASPOINTER;
|
|
stgdict->size = itemsize * length;
|
stgdict->align = itemalign;
|
stgdict->length = length;
|
stgdict->proto = type_attr;
|
|
stgdict->paramfunc = &PyCArrayType_paramfunc;
|
|
/* Arrays are passed as pointers to function calls. */
|
stgdict->ffi_type_pointer = ffi_type_pointer;
|
|
/* replace the class dict by our updated spam dict */
|
if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict))
|
goto error;
|
Py_SETREF(result->tp_dict, (PyObject *)stgdict); /* steal the reference */
|
stgdict = NULL;
|
|
/* Special case for character arrays.
|
A permanent annoyance: char arrays are also strings!
|
*/
|
if (itemdict->getfunc == _ctypes_get_fielddesc("c")->getfunc) {
|
if (-1 == add_getset(result, CharArray_getsets))
|
goto error;
|
#ifdef CTYPES_UNICODE
|
} else if (itemdict->getfunc == _ctypes_get_fielddesc("u")->getfunc) {
|
if (-1 == add_getset(result, WCharArray_getsets))
|
goto error;
|
#endif
|
}
|
|
return (PyObject *)result;
|
error:
|
Py_XDECREF((PyObject*)stgdict);
|
Py_XDECREF(type_attr);
|
Py_DECREF(result);
|
return NULL;
|
}
|
|
PyTypeObject PyCArrayType_Type = {
|
PyVarObject_HEAD_INIT(NULL, 0)
|
"_ctypes.PyCArrayType", /* tp_name */
|
0, /* tp_basicsize */
|
0, /* tp_itemsize */
|
0, /* tp_dealloc */
|
0, /* tp_print */
|
0, /* tp_getattr */
|
0, /* tp_setattr */
|
0, /* tp_reserved */
|
0, /* tp_repr */
|
0, /* tp_as_number */
|
&CDataType_as_sequence, /* 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 | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
"metatype for the Array Objects", /* tp_doc */
|
0, /* tp_traverse */
|
0, /* tp_clear */
|
0, /* tp_richcompare */
|
0, /* tp_weaklistoffset */
|
0, /* tp_iter */
|
0, /* tp_iternext */
|
CDataType_methods, /* 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 */
|
PyCArrayType_new, /* tp_new */
|
0, /* tp_free */
|
};
|
|
|
/******************************************************************/
|
/*
|
PyCSimpleType_Type
|
*/
|
/*
|
|
PyCSimpleType_new ensures that the new Simple_Type subclass created has a valid
|
_type_ attribute.
|
|
*/
|
|
static const char SIMPLE_TYPE_CHARS[] = "cbBhHiIlLdfuzZqQPXOv?g";
|
|
static PyObject *
|
c_wchar_p_from_param(PyObject *type, PyObject *value)
|
{
|
PyObject *as_parameter;
|
int res;
|
if (value == Py_None) {
|
Py_RETURN_NONE;
|
}
|
if (PyUnicode_Check(value)) {
|
PyCArgObject *parg;
|
struct fielddesc *fd = _ctypes_get_fielddesc("Z");
|
|
parg = PyCArgObject_new();
|
if (parg == NULL)
|
return NULL;
|
parg->pffi_type = &ffi_type_pointer;
|
parg->tag = 'Z';
|
parg->obj = fd->setfunc(&parg->value, value, 0);
|
if (parg->obj == NULL) {
|
Py_DECREF(parg);
|
return NULL;
|
}
|
return (PyObject *)parg;
|
}
|
res = PyObject_IsInstance(value, type);
|
if (res == -1)
|
return NULL;
|
if (res) {
|
Py_INCREF(value);
|
return value;
|
}
|
if (ArrayObject_Check(value) || PointerObject_Check(value)) {
|
/* c_wchar array instance or pointer(c_wchar(...)) */
|
StgDictObject *dt = PyObject_stgdict(value);
|
StgDictObject *dict;
|
assert(dt); /* Cannot be NULL for pointer or array objects */
|
dict = dt && dt->proto ? PyType_stgdict(dt->proto) : NULL;
|
if (dict && (dict->setfunc == _ctypes_get_fielddesc("u")->setfunc)) {
|
Py_INCREF(value);
|
return value;
|
}
|
}
|
if (PyCArg_CheckExact(value)) {
|
/* byref(c_char(...)) */
|
PyCArgObject *a = (PyCArgObject *)value;
|
StgDictObject *dict = PyObject_stgdict(a->obj);
|
if (dict && (dict->setfunc == _ctypes_get_fielddesc("u")->setfunc)) {
|
Py_INCREF(value);
|
return value;
|
}
|
}
|
|
as_parameter = PyObject_GetAttrString(value, "_as_parameter_");
|
if (as_parameter) {
|
value = c_wchar_p_from_param(type, as_parameter);
|
Py_DECREF(as_parameter);
|
return value;
|
}
|
/* XXX better message */
|
PyErr_SetString(PyExc_TypeError,
|
"wrong type");
|
return NULL;
|
}
|
|
static PyObject *
|
c_char_p_from_param(PyObject *type, PyObject *value)
|
{
|
PyObject *as_parameter;
|
int res;
|
if (value == Py_None) {
|
Py_RETURN_NONE;
|
}
|
if (PyBytes_Check(value)) {
|
PyCArgObject *parg;
|
struct fielddesc *fd = _ctypes_get_fielddesc("z");
|
|
parg = PyCArgObject_new();
|
if (parg == NULL)
|
return NULL;
|
parg->pffi_type = &ffi_type_pointer;
|
parg->tag = 'z';
|
parg->obj = fd->setfunc(&parg->value, value, 0);
|
if (parg->obj == NULL) {
|
Py_DECREF(parg);
|
return NULL;
|
}
|
return (PyObject *)parg;
|
}
|
res = PyObject_IsInstance(value, type);
|
if (res == -1)
|
return NULL;
|
if (res) {
|
Py_INCREF(value);
|
return value;
|
}
|
if (ArrayObject_Check(value) || PointerObject_Check(value)) {
|
/* c_char array instance or pointer(c_char(...)) */
|
StgDictObject *dt = PyObject_stgdict(value);
|
StgDictObject *dict;
|
assert(dt); /* Cannot be NULL for pointer or array objects */
|
dict = dt && dt->proto ? PyType_stgdict(dt->proto) : NULL;
|
if (dict && (dict->setfunc == _ctypes_get_fielddesc("c")->setfunc)) {
|
Py_INCREF(value);
|
return value;
|
}
|
}
|
if (PyCArg_CheckExact(value)) {
|
/* byref(c_char(...)) */
|
PyCArgObject *a = (PyCArgObject *)value;
|
StgDictObject *dict = PyObject_stgdict(a->obj);
|
if (dict && (dict->setfunc == _ctypes_get_fielddesc("c")->setfunc)) {
|
Py_INCREF(value);
|
return value;
|
}
|
}
|
|
as_parameter = PyObject_GetAttrString(value, "_as_parameter_");
|
if (as_parameter) {
|
value = c_char_p_from_param(type, as_parameter);
|
Py_DECREF(as_parameter);
|
return value;
|
}
|
/* XXX better message */
|
PyErr_SetString(PyExc_TypeError,
|
"wrong type");
|
return NULL;
|
}
|
|
static PyObject *
|
c_void_p_from_param(PyObject *type, PyObject *value)
|
{
|
StgDictObject *stgd;
|
PyObject *as_parameter;
|
int res;
|
|
/* None */
|
if (value == Py_None) {
|
Py_RETURN_NONE;
|
}
|
/* Should probably allow buffer interface as well */
|
/* int, long */
|
if (PyLong_Check(value)) {
|
PyCArgObject *parg;
|
struct fielddesc *fd = _ctypes_get_fielddesc("P");
|
|
parg = PyCArgObject_new();
|
if (parg == NULL)
|
return NULL;
|
parg->pffi_type = &ffi_type_pointer;
|
parg->tag = 'P';
|
parg->obj = fd->setfunc(&parg->value, value, 0);
|
if (parg->obj == NULL) {
|
Py_DECREF(parg);
|
return NULL;
|
}
|
return (PyObject *)parg;
|
}
|
/* XXX struni: remove later */
|
/* bytes */
|
if (PyBytes_Check(value)) {
|
PyCArgObject *parg;
|
struct fielddesc *fd = _ctypes_get_fielddesc("z");
|
|
parg = PyCArgObject_new();
|
if (parg == NULL)
|
return NULL;
|
parg->pffi_type = &ffi_type_pointer;
|
parg->tag = 'z';
|
parg->obj = fd->setfunc(&parg->value, value, 0);
|
if (parg->obj == NULL) {
|
Py_DECREF(parg);
|
return NULL;
|
}
|
return (PyObject *)parg;
|
}
|
/* unicode */
|
if (PyUnicode_Check(value)) {
|
PyCArgObject *parg;
|
struct fielddesc *fd = _ctypes_get_fielddesc("Z");
|
|
parg = PyCArgObject_new();
|
if (parg == NULL)
|
return NULL;
|
parg->pffi_type = &ffi_type_pointer;
|
parg->tag = 'Z';
|
parg->obj = fd->setfunc(&parg->value, value, 0);
|
if (parg->obj == NULL) {
|
Py_DECREF(parg);
|
return NULL;
|
}
|
return (PyObject *)parg;
|
}
|
/* c_void_p instance (or subclass) */
|
res = PyObject_IsInstance(value, type);
|
if (res == -1)
|
return NULL;
|
if (res) {
|
/* c_void_p instances */
|
Py_INCREF(value);
|
return value;
|
}
|
/* ctypes array or pointer instance */
|
if (ArrayObject_Check(value) || PointerObject_Check(value)) {
|
/* Any array or pointer is accepted */
|
Py_INCREF(value);
|
return value;
|
}
|
/* byref(...) */
|
if (PyCArg_CheckExact(value)) {
|
/* byref(c_xxx()) */
|
PyCArgObject *a = (PyCArgObject *)value;
|
if (a->tag == 'P') {
|
Py_INCREF(value);
|
return value;
|
}
|
}
|
/* function pointer */
|
if (PyCFuncPtrObject_Check(value)) {
|
PyCArgObject *parg;
|
PyCFuncPtrObject *func;
|
func = (PyCFuncPtrObject *)value;
|
parg = PyCArgObject_new();
|
if (parg == NULL)
|
return NULL;
|
parg->pffi_type = &ffi_type_pointer;
|
parg->tag = 'P';
|
Py_INCREF(value);
|
parg->value.p = *(void **)func->b_ptr;
|
parg->obj = value;
|
return (PyObject *)parg;
|
}
|
/* c_char_p, c_wchar_p */
|
stgd = PyObject_stgdict(value);
|
if (stgd && CDataObject_Check(value) && stgd->proto && PyUnicode_Check(stgd->proto)) {
|
PyCArgObject *parg;
|
|
switch (PyUnicode_AsUTF8(stgd->proto)[0]) {
|
case 'z': /* c_char_p */
|
case 'Z': /* c_wchar_p */
|
parg = PyCArgObject_new();
|
if (parg == NULL)
|
return NULL;
|
parg->pffi_type = &ffi_type_pointer;
|
parg->tag = 'Z';
|
Py_INCREF(value);
|
parg->obj = value;
|
/* Remember: b_ptr points to where the pointer is stored! */
|
parg->value.p = *(void **)(((CDataObject *)value)->b_ptr);
|
return (PyObject *)parg;
|
}
|
}
|
|
as_parameter = PyObject_GetAttrString(value, "_as_parameter_");
|
if (as_parameter) {
|
value = c_void_p_from_param(type, as_parameter);
|
Py_DECREF(as_parameter);
|
return value;
|
}
|
/* XXX better message */
|
PyErr_SetString(PyExc_TypeError,
|
"wrong type");
|
return NULL;
|
}
|
|
static PyMethodDef c_void_p_method = { "from_param", c_void_p_from_param, METH_O };
|
static PyMethodDef c_char_p_method = { "from_param", c_char_p_from_param, METH_O };
|
static PyMethodDef c_wchar_p_method = { "from_param", c_wchar_p_from_param, METH_O };
|
|
static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject *kwds,
|
PyObject *proto, struct fielddesc *fmt)
|
{
|
PyTypeObject *result;
|
StgDictObject *stgdict;
|
PyObject *name = PyTuple_GET_ITEM(args, 0);
|
PyObject *newname;
|
PyObject *swapped_args;
|
static PyObject *suffix;
|
Py_ssize_t i;
|
|
swapped_args = PyTuple_New(PyTuple_GET_SIZE(args));
|
if (!swapped_args)
|
return NULL;
|
|
if (suffix == NULL)
|
#ifdef WORDS_BIGENDIAN
|
suffix = PyUnicode_InternFromString("_le");
|
#else
|
suffix = PyUnicode_InternFromString("_be");
|
#endif
|
if (suffix == NULL) {
|
Py_DECREF(swapped_args);
|
return NULL;
|
}
|
|
newname = PyUnicode_Concat(name, suffix);
|
if (newname == NULL) {
|
Py_DECREF(swapped_args);
|
return NULL;
|
}
|
|
PyTuple_SET_ITEM(swapped_args, 0, newname);
|
for (i=1; i<PyTuple_GET_SIZE(args); ++i) {
|
PyObject *v = PyTuple_GET_ITEM(args, i);
|
Py_INCREF(v);
|
PyTuple_SET_ITEM(swapped_args, i, v);
|
}
|
|
/* create the new instance (which is a class,
|
since we are a metatype!) */
|
result = (PyTypeObject *)PyType_Type.tp_new(type, swapped_args, kwds);
|
Py_DECREF(swapped_args);
|
if (result == NULL)
|
return NULL;
|
|
stgdict = (StgDictObject *)PyObject_CallObject(
|
(PyObject *)&PyCStgDict_Type, NULL);
|
if (!stgdict) {
|
Py_DECREF(result);
|
return NULL;
|
}
|
|
stgdict->ffi_type_pointer = *fmt->pffi_type;
|
stgdict->align = fmt->pffi_type->alignment;
|
stgdict->length = 0;
|
stgdict->size = fmt->pffi_type->size;
|
stgdict->setfunc = fmt->setfunc_swapped;
|
stgdict->getfunc = fmt->getfunc_swapped;
|
|
Py_INCREF(proto);
|
stgdict->proto = proto;
|
|
/* replace the class dict by our updated spam dict */
|
if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) {
|
Py_DECREF(result);
|
Py_DECREF((PyObject *)stgdict);
|
return NULL;
|
}
|
Py_SETREF(result->tp_dict, (PyObject *)stgdict);
|
|
return (PyObject *)result;
|
}
|
|
static PyCArgObject *
|
PyCSimpleType_paramfunc(CDataObject *self)
|
{
|
StgDictObject *dict;
|
const char *fmt;
|
PyCArgObject *parg;
|
struct fielddesc *fd;
|
|
dict = PyObject_stgdict((PyObject *)self);
|
assert(dict); /* Cannot be NULL for CDataObject instances */
|
fmt = PyUnicode_AsUTF8(dict->proto);
|
assert(fmt);
|
|
fd = _ctypes_get_fielddesc(fmt);
|
assert(fd);
|
|
parg = PyCArgObject_new();
|
if (parg == NULL)
|
return NULL;
|
|
parg->tag = fmt[0];
|
parg->pffi_type = fd->pffi_type;
|
Py_INCREF(self);
|
parg->obj = (PyObject *)self;
|
memcpy(&parg->value, self->b_ptr, self->b_size);
|
return parg;
|
}
|
|
static PyObject *
|
PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
{
|
PyTypeObject *result;
|
StgDictObject *stgdict;
|
PyObject *proto;
|
const char *proto_str;
|
Py_ssize_t proto_len;
|
PyMethodDef *ml;
|
struct fielddesc *fmt;
|
|
/* create the new instance (which is a class,
|
since we are a metatype!) */
|
result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds);
|
if (result == NULL)
|
return NULL;
|
|
proto = PyObject_GetAttrString((PyObject *)result, "_type_"); /* new ref */
|
if (!proto) {
|
PyErr_SetString(PyExc_AttributeError,
|
"class must define a '_type_' attribute");
|
error:
|
Py_XDECREF(proto);
|
Py_XDECREF(result);
|
return NULL;
|
}
|
if (PyUnicode_Check(proto)) {
|
proto_str = PyUnicode_AsUTF8AndSize(proto, &proto_len);
|
if (!proto_str)
|
goto error;
|
} else {
|
PyErr_SetString(PyExc_TypeError,
|
"class must define a '_type_' string attribute");
|
goto error;
|
}
|
if (proto_len != 1) {
|
PyErr_SetString(PyExc_ValueError,
|
"class must define a '_type_' attribute "
|
"which must be a string of length 1");
|
goto error;
|
}
|
if (!strchr(SIMPLE_TYPE_CHARS, *proto_str)) {
|
PyErr_Format(PyExc_AttributeError,
|
"class must define a '_type_' attribute which must be\n"
|
"a single character string containing one of '%s'.",
|
SIMPLE_TYPE_CHARS);
|
goto error;
|
}
|
fmt = _ctypes_get_fielddesc(proto_str);
|
if (fmt == NULL) {
|
PyErr_Format(PyExc_ValueError,
|
"_type_ '%s' not supported", proto_str);
|
goto error;
|
}
|
|
stgdict = (StgDictObject *)PyObject_CallObject(
|
(PyObject *)&PyCStgDict_Type, NULL);
|
if (!stgdict)
|
goto error;
|
|
stgdict->ffi_type_pointer = *fmt->pffi_type;
|
stgdict->align = fmt->pffi_type->alignment;
|
stgdict->length = 0;
|
stgdict->size = fmt->pffi_type->size;
|
stgdict->setfunc = fmt->setfunc;
|
stgdict->getfunc = fmt->getfunc;
|
#ifdef WORDS_BIGENDIAN
|
stgdict->format = _ctypes_alloc_format_string_for_type(proto_str[0], 1);
|
#else
|
stgdict->format = _ctypes_alloc_format_string_for_type(proto_str[0], 0);
|
#endif
|
if (stgdict->format == NULL) {
|
Py_DECREF(result);
|
Py_DECREF(proto);
|
Py_DECREF((PyObject *)stgdict);
|
return NULL;
|
}
|
|
stgdict->paramfunc = PyCSimpleType_paramfunc;
|
/*
|
if (result->tp_base != &Simple_Type) {
|
stgdict->setfunc = NULL;
|
stgdict->getfunc = NULL;
|
}
|
*/
|
|
/* This consumes the refcount on proto which we have */
|
stgdict->proto = proto;
|
|
/* replace the class dict by our updated spam dict */
|
if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) {
|
Py_DECREF(result);
|
Py_DECREF((PyObject *)stgdict);
|
return NULL;
|
}
|
Py_SETREF(result->tp_dict, (PyObject *)stgdict);
|
|
/* Install from_param class methods in ctypes base classes.
|
Overrides the PyCSimpleType_from_param generic method.
|
*/
|
if (result->tp_base == &Simple_Type) {
|
switch (*proto_str) {
|
case 'z': /* c_char_p */
|
ml = &c_char_p_method;
|
stgdict->flags |= TYPEFLAG_ISPOINTER;
|
break;
|
case 'Z': /* c_wchar_p */
|
ml = &c_wchar_p_method;
|
stgdict->flags |= TYPEFLAG_ISPOINTER;
|
break;
|
case 'P': /* c_void_p */
|
ml = &c_void_p_method;
|
stgdict->flags |= TYPEFLAG_ISPOINTER;
|
break;
|
case 's':
|
case 'X':
|
case 'O':
|
ml = NULL;
|
stgdict->flags |= TYPEFLAG_ISPOINTER;
|
break;
|
default:
|
ml = NULL;
|
break;
|
}
|
|
if (ml) {
|
PyObject *meth;
|
int x;
|
meth = PyDescr_NewClassMethod(result, ml);
|
if (!meth) {
|
Py_DECREF(result);
|
return NULL;
|
}
|
x = PyDict_SetItemString(result->tp_dict,
|
ml->ml_name,
|
meth);
|
Py_DECREF(meth);
|
if (x == -1) {
|
Py_DECREF(result);
|
return NULL;
|
}
|
}
|
}
|
|
if (type == &PyCSimpleType_Type && fmt->setfunc_swapped && fmt->getfunc_swapped) {
|
PyObject *swapped = CreateSwappedType(type, args, kwds,
|
proto, fmt);
|
StgDictObject *sw_dict;
|
if (swapped == NULL) {
|
Py_DECREF(result);
|
return NULL;
|
}
|
sw_dict = PyType_stgdict(swapped);
|
#ifdef WORDS_BIGENDIAN
|
PyObject_SetAttrString((PyObject *)result, "__ctype_le__", swapped);
|
PyObject_SetAttrString((PyObject *)result, "__ctype_be__", (PyObject *)result);
|
PyObject_SetAttrString(swapped, "__ctype_be__", (PyObject *)result);
|
PyObject_SetAttrString(swapped, "__ctype_le__", swapped);
|
/* We are creating the type for the OTHER endian */
|
sw_dict->format = _ctypes_alloc_format_string("<", stgdict->format+1);
|
#else
|
PyObject_SetAttrString((PyObject *)result, "__ctype_be__", swapped);
|
PyObject_SetAttrString((PyObject *)result, "__ctype_le__", (PyObject *)result);
|
PyObject_SetAttrString(swapped, "__ctype_le__", (PyObject *)result);
|
PyObject_SetAttrString(swapped, "__ctype_be__", swapped);
|
/* We are creating the type for the OTHER endian */
|
sw_dict->format = _ctypes_alloc_format_string(">", stgdict->format+1);
|
#endif
|
Py_DECREF(swapped);
|
if (PyErr_Occurred()) {
|
Py_DECREF(result);
|
return NULL;
|
}
|
};
|
|
return (PyObject *)result;
|
}
|
|
/*
|
* This is a *class method*.
|
* Convert a parameter into something that ConvParam can handle.
|
*/
|
static PyObject *
|
PyCSimpleType_from_param(PyObject *type, PyObject *value)
|
{
|
StgDictObject *dict;
|
const char *fmt;
|
PyCArgObject *parg;
|
struct fielddesc *fd;
|
PyObject *as_parameter;
|
int res;
|
|
/* If the value is already an instance of the requested type,
|
we can use it as is */
|
res = PyObject_IsInstance(value, type);
|
if (res == -1)
|
return NULL;
|
if (res) {
|
Py_INCREF(value);
|
return value;
|
}
|
|
dict = PyType_stgdict(type);
|
if (!dict) {
|
PyErr_SetString(PyExc_TypeError,
|
"abstract class");
|
return NULL;
|
}
|
|
/* I think we can rely on this being a one-character string */
|
fmt = PyUnicode_AsUTF8(dict->proto);
|
assert(fmt);
|
|
fd = _ctypes_get_fielddesc(fmt);
|
assert(fd);
|
|
parg = PyCArgObject_new();
|
if (parg == NULL)
|
return NULL;
|
|
parg->tag = fmt[0];
|
parg->pffi_type = fd->pffi_type;
|
parg->obj = fd->setfunc(&parg->value, value, 0);
|
if (parg->obj)
|
return (PyObject *)parg;
|
PyErr_Clear();
|
Py_DECREF(parg);
|
|
as_parameter = PyObject_GetAttrString(value, "_as_parameter_");
|
if (as_parameter) {
|
if (Py_EnterRecursiveCall("while processing _as_parameter_")) {
|
Py_DECREF(as_parameter);
|
return NULL;
|
}
|
value = PyCSimpleType_from_param(type, as_parameter);
|
Py_LeaveRecursiveCall();
|
Py_DECREF(as_parameter);
|
return value;
|
}
|
PyErr_SetString(PyExc_TypeError,
|
"wrong type");
|
return NULL;
|
}
|
|
static PyMethodDef PyCSimpleType_methods[] = {
|
{ "from_param", PyCSimpleType_from_param, METH_O, from_param_doc },
|
{ "from_address", CDataType_from_address, METH_O, from_address_doc },
|
{ "from_buffer", CDataType_from_buffer, METH_VARARGS, from_buffer_doc, },
|
{ "from_buffer_copy", CDataType_from_buffer_copy, METH_VARARGS, from_buffer_copy_doc, },
|
{ "in_dll", CDataType_in_dll, METH_VARARGS, in_dll_doc},
|
{ NULL, NULL },
|
};
|
|
PyTypeObject PyCSimpleType_Type = {
|
PyVarObject_HEAD_INIT(NULL, 0)
|
"_ctypes.PyCSimpleType", /* tp_name */
|
0, /* tp_basicsize */
|
0, /* tp_itemsize */
|
0, /* tp_dealloc */
|
0, /* tp_print */
|
0, /* tp_getattr */
|
0, /* tp_setattr */
|
0, /* tp_reserved */
|
0, /* tp_repr */
|
0, /* tp_as_number */
|
&CDataType_as_sequence, /* 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 | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
"metatype for the PyCSimpleType Objects", /* tp_doc */
|
0, /* tp_traverse */
|
0, /* tp_clear */
|
0, /* tp_richcompare */
|
0, /* tp_weaklistoffset */
|
0, /* tp_iter */
|
0, /* tp_iternext */
|
PyCSimpleType_methods, /* 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 */
|
PyCSimpleType_new, /* tp_new */
|
0, /* tp_free */
|
};
|
|
/******************************************************************/
|
/*
|
PyCFuncPtrType_Type
|
*/
|
|
static PyObject *
|
converters_from_argtypes(PyObject *ob)
|
{
|
PyObject *converters;
|
Py_ssize_t i;
|
Py_ssize_t nArgs;
|
|
ob = PySequence_Tuple(ob); /* new reference */
|
if (!ob) {
|
PyErr_SetString(PyExc_TypeError,
|
"_argtypes_ must be a sequence of types");
|
return NULL;
|
}
|
|
nArgs = PyTuple_GET_SIZE(ob);
|
converters = PyTuple_New(nArgs);
|
if (!converters) {
|
Py_DECREF(ob);
|
return NULL;
|
}
|
|
/* I have to check if this is correct. Using c_char, which has a size
|
of 1, will be assumed to be pushed as only one byte!
|
Aren't these promoted to integers by the C compiler and pushed as 4 bytes?
|
*/
|
|
for (i = 0; i < nArgs; ++i) {
|
PyObject *tp = PyTuple_GET_ITEM(ob, i);
|
PyObject *cnv = PyObject_GetAttrString(tp, "from_param");
|
if (!cnv)
|
goto argtypes_error_1;
|
PyTuple_SET_ITEM(converters, i, cnv);
|
}
|
Py_DECREF(ob);
|
return converters;
|
|
argtypes_error_1:
|
Py_XDECREF(converters);
|
Py_DECREF(ob);
|
PyErr_Format(PyExc_TypeError,
|
"item %zd in _argtypes_ has no from_param method",
|
i+1);
|
return NULL;
|
}
|
|
static int
|
make_funcptrtype_dict(StgDictObject *stgdict)
|
{
|
PyObject *ob;
|
PyObject *converters = NULL;
|
|
stgdict->align = _ctypes_get_fielddesc("P")->pffi_type->alignment;
|
stgdict->length = 1;
|
stgdict->size = sizeof(void *);
|
stgdict->setfunc = NULL;
|
stgdict->getfunc = NULL;
|
stgdict->ffi_type_pointer = ffi_type_pointer;
|
|
ob = PyDict_GetItemString((PyObject *)stgdict, "_flags_");
|
if (!ob || !PyLong_Check(ob)) {
|
PyErr_SetString(PyExc_TypeError,
|
"class must define _flags_ which must be an integer");
|
return -1;
|
}
|
stgdict->flags = PyLong_AS_LONG(ob) | TYPEFLAG_ISPOINTER;
|
|
/* _argtypes_ is optional... */
|
ob = PyDict_GetItemString((PyObject *)stgdict, "_argtypes_");
|
if (ob) {
|
converters = converters_from_argtypes(ob);
|
if (!converters)
|
goto error;
|
Py_INCREF(ob);
|
stgdict->argtypes = ob;
|
stgdict->converters = converters;
|
}
|
|
ob = PyDict_GetItemString((PyObject *)stgdict, "_restype_");
|
if (ob) {
|
if (ob != Py_None && !PyType_stgdict(ob) && !PyCallable_Check(ob)) {
|
PyErr_SetString(PyExc_TypeError,
|
"_restype_ must be a type, a callable, or None");
|
return -1;
|
}
|
Py_INCREF(ob);
|
stgdict->restype = ob;
|
stgdict->checker = PyObject_GetAttrString(ob, "_check_retval_");
|
if (stgdict->checker == NULL)
|
PyErr_Clear();
|
}
|
/* XXX later, maybe.
|
ob = PyDict_GetItemString((PyObject *)stgdict, "_errcheck_");
|
if (ob) {
|
if (!PyCallable_Check(ob)) {
|
PyErr_SetString(PyExc_TypeError,
|
"_errcheck_ must be callable");
|
return -1;
|
}
|
Py_INCREF(ob);
|
stgdict->errcheck = ob;
|
}
|
*/
|
return 0;
|
|
error:
|
Py_XDECREF(converters);
|
return -1;
|
|
}
|
|
static PyCArgObject *
|
PyCFuncPtrType_paramfunc(CDataObject *self)
|
{
|
PyCArgObject *parg;
|
|
parg = PyCArgObject_new();
|
if (parg == NULL)
|
return NULL;
|
|
parg->tag = 'P';
|
parg->pffi_type = &ffi_type_pointer;
|
Py_INCREF(self);
|
parg->obj = (PyObject *)self;
|
parg->value.p = *(void **)self->b_ptr;
|
return parg;
|
}
|
|
static PyObject *
|
PyCFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
{
|
PyTypeObject *result;
|
StgDictObject *stgdict;
|
|
stgdict = (StgDictObject *)PyObject_CallObject(
|
(PyObject *)&PyCStgDict_Type, NULL);
|
if (!stgdict)
|
return NULL;
|
|
stgdict->paramfunc = PyCFuncPtrType_paramfunc;
|
/* We do NOT expose the function signature in the format string. It
|
is impossible, generally, because the only requirement for the
|
argtypes items is that they have a .from_param method - we do not
|
know the types of the arguments (although, in practice, most
|
argtypes would be a ctypes type).
|
*/
|
stgdict->format = _ctypes_alloc_format_string(NULL, "X{}");
|
if (stgdict->format == NULL) {
|
Py_DECREF((PyObject *)stgdict);
|
return NULL;
|
}
|
stgdict->flags |= TYPEFLAG_ISPOINTER;
|
|
/* create the new instance (which is a class,
|
since we are a metatype!) */
|
result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds);
|
if (result == NULL) {
|
Py_DECREF((PyObject *)stgdict);
|
return NULL;
|
}
|
|
/* replace the class dict by our updated storage dict */
|
if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) {
|
Py_DECREF(result);
|
Py_DECREF((PyObject *)stgdict);
|
return NULL;
|
}
|
Py_SETREF(result->tp_dict, (PyObject *)stgdict);
|
|
if (-1 == make_funcptrtype_dict(stgdict)) {
|
Py_DECREF(result);
|
return NULL;
|
}
|
|
return (PyObject *)result;
|
}
|
|
PyTypeObject PyCFuncPtrType_Type = {
|
PyVarObject_HEAD_INIT(NULL, 0)
|
"_ctypes.PyCFuncPtrType", /* tp_name */
|
0, /* tp_basicsize */
|
0, /* tp_itemsize */
|
0, /* tp_dealloc */
|
0, /* tp_print */
|
0, /* tp_getattr */
|
0, /* tp_setattr */
|
0, /* tp_reserved */
|
0, /* tp_repr */
|
0, /* tp_as_number */
|
&CDataType_as_sequence, /* 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 | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
"metatype for C function pointers", /* tp_doc */
|
(traverseproc)CDataType_traverse, /* tp_traverse */
|
(inquiry)CDataType_clear, /* tp_clear */
|
0, /* tp_richcompare */
|
0, /* tp_weaklistoffset */
|
0, /* tp_iter */
|
0, /* tp_iternext */
|
CDataType_methods, /* 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 */
|
PyCFuncPtrType_new, /* tp_new */
|
0, /* tp_free */
|
};
|
|
|
/*****************************************************************
|
* Code to keep needed objects alive
|
*/
|
|
static CDataObject *
|
PyCData_GetContainer(CDataObject *self)
|
{
|
while (self->b_base)
|
self = self->b_base;
|
if (self->b_objects == NULL) {
|
if (self->b_length) {
|
self->b_objects = PyDict_New();
|
if (self->b_objects == NULL)
|
return NULL;
|
} else {
|
Py_INCREF(Py_None);
|
self->b_objects = Py_None;
|
}
|
}
|
return self;
|
}
|
|
static PyObject *
|
GetKeepedObjects(CDataObject *target)
|
{
|
CDataObject *container;
|
container = PyCData_GetContainer(target);
|
if (container == NULL)
|
return NULL;
|
return container->b_objects;
|
}
|
|
static PyObject *
|
unique_key(CDataObject *target, Py_ssize_t index)
|
{
|
char string[256];
|
char *cp = string;
|
size_t bytes_left;
|
|
Py_BUILD_ASSERT(sizeof(string) - 1 > sizeof(Py_ssize_t) * 2);
|
cp += sprintf(cp, "%x", Py_SAFE_DOWNCAST(index, Py_ssize_t, int));
|
while (target->b_base) {
|
bytes_left = sizeof(string) - (cp - string) - 1;
|
/* Hex format needs 2 characters per byte */
|
if (bytes_left < sizeof(Py_ssize_t) * 2) {
|
PyErr_SetString(PyExc_ValueError,
|
"ctypes object structure too deep");
|
return NULL;
|
}
|
cp += sprintf(cp, ":%x", Py_SAFE_DOWNCAST(target->b_index, Py_ssize_t, int));
|
target = target->b_base;
|
}
|
return PyUnicode_FromStringAndSize(string, cp-string);
|
}
|
|
/*
|
* Keep a reference to 'keep' in the 'target', at index 'index'.
|
*
|
* If 'keep' is None, do nothing.
|
*
|
* Otherwise create a dictionary (if it does not yet exist) id the root
|
* objects 'b_objects' item, which will store the 'keep' object under a unique
|
* key.
|
*
|
* The unique_key helper travels the target's b_base pointer down to the root,
|
* building a string containing hex-formatted indexes found during traversal,
|
* separated by colons.
|
*
|
* The index tuple is used as a key into the root object's b_objects dict.
|
*
|
* Note: This function steals a refcount of the third argument, even if it
|
* fails!
|
*/
|
static int
|
KeepRef(CDataObject *target, Py_ssize_t index, PyObject *keep)
|
{
|
int result;
|
CDataObject *ob;
|
PyObject *key;
|
|
/* Optimization: no need to store None */
|
if (keep == Py_None) {
|
Py_DECREF(Py_None);
|
return 0;
|
}
|
ob = PyCData_GetContainer(target);
|
if (ob == NULL) {
|
Py_DECREF(keep);
|
return -1;
|
}
|
if (ob->b_objects == NULL || !PyDict_CheckExact(ob->b_objects)) {
|
Py_XSETREF(ob->b_objects, keep); /* refcount consumed */
|
return 0;
|
}
|
key = unique_key(target, index);
|
if (key == NULL) {
|
Py_DECREF(keep);
|
return -1;
|
}
|
result = PyDict_SetItem(ob->b_objects, key, keep);
|
Py_DECREF(key);
|
Py_DECREF(keep);
|
return result;
|
}
|
|
/******************************************************************/
|
/*
|
PyCData_Type
|
*/
|
static int
|
PyCData_traverse(CDataObject *self, visitproc visit, void *arg)
|
{
|
Py_VISIT(self->b_objects);
|
Py_VISIT((PyObject *)self->b_base);
|
return 0;
|
}
|
|
static int
|
PyCData_clear(CDataObject *self)
|
{
|
Py_CLEAR(self->b_objects);
|
if ((self->b_needsfree)
|
&& _CDataObject_HasExternalBuffer(self))
|
PyMem_Free(self->b_ptr);
|
self->b_ptr = NULL;
|
Py_CLEAR(self->b_base);
|
return 0;
|
}
|
|
static void
|
PyCData_dealloc(PyObject *self)
|
{
|
PyCData_clear((CDataObject *)self);
|
Py_TYPE(self)->tp_free(self);
|
}
|
|
static PyMemberDef PyCData_members[] = {
|
{ "_b_base_", T_OBJECT,
|
offsetof(CDataObject, b_base), READONLY,
|
"the base object" },
|
{ "_b_needsfree_", T_INT,
|
offsetof(CDataObject, b_needsfree), READONLY,
|
"whether the object owns the memory or not" },
|
{ "_objects", T_OBJECT,
|
offsetof(CDataObject, b_objects), READONLY,
|
"internal objects tree (NEVER CHANGE THIS OBJECT!)"},
|
{ NULL },
|
};
|
|
static int PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags)
|
{
|
CDataObject *self = (CDataObject *)myself;
|
StgDictObject *dict = PyObject_stgdict(myself);
|
Py_ssize_t i;
|
|
if (view == NULL) return 0;
|
|
view->buf = self->b_ptr;
|
view->obj = myself;
|
Py_INCREF(myself);
|
view->len = self->b_size;
|
view->readonly = 0;
|
/* use default format character if not set */
|
view->format = dict->format ? dict->format : "B";
|
view->ndim = dict->ndim;
|
view->shape = dict->shape;
|
view->itemsize = self->b_size;
|
if (view->itemsize) {
|
for (i = 0; i < view->ndim; ++i) {
|
view->itemsize /= dict->shape[i];
|
}
|
}
|
view->strides = NULL;
|
view->suboffsets = NULL;
|
view->internal = NULL;
|
return 0;
|
}
|
|
static PyBufferProcs PyCData_as_buffer = {
|
PyCData_NewGetBuffer,
|
NULL,
|
};
|
|
/*
|
* CData objects are mutable, so they cannot be hashable!
|
*/
|
static Py_hash_t
|
PyCData_nohash(PyObject *self)
|
{
|
PyErr_SetString(PyExc_TypeError, "unhashable type");
|
return -1;
|
}
|
|
static PyObject *
|
PyCData_reduce(PyObject *myself, PyObject *args)
|
{
|
CDataObject *self = (CDataObject *)myself;
|
|
if (PyObject_stgdict(myself)->flags & (TYPEFLAG_ISPOINTER|TYPEFLAG_HASPOINTER)) {
|
PyErr_SetString(PyExc_ValueError,
|
"ctypes objects containing pointers cannot be pickled");
|
return NULL;
|
}
|
return Py_BuildValue("O(O(NN))",
|
_unpickle,
|
Py_TYPE(myself),
|
PyObject_GetAttrString(myself, "__dict__"),
|
PyBytes_FromStringAndSize(self->b_ptr, self->b_size));
|
}
|
|
static PyObject *
|
PyCData_setstate(PyObject *myself, PyObject *args)
|
{
|
void *data;
|
Py_ssize_t len;
|
int res;
|
PyObject *dict, *mydict;
|
CDataObject *self = (CDataObject *)myself;
|
if (!PyArg_ParseTuple(args, "O!s#",
|
&PyDict_Type, &dict, &data, &len))
|
{
|
return NULL;
|
}
|
if (len > self->b_size)
|
len = self->b_size;
|
memmove(self->b_ptr, data, len);
|
mydict = PyObject_GetAttrString(myself, "__dict__");
|
if (mydict == NULL) {
|
return NULL;
|
}
|
if (!PyDict_Check(mydict)) {
|
PyErr_Format(PyExc_TypeError,
|
"%.200s.__dict__ must be a dictionary, not %.200s",
|
Py_TYPE(myself)->tp_name, Py_TYPE(mydict)->tp_name);
|
Py_DECREF(mydict);
|
return NULL;
|
}
|
res = PyDict_Update(mydict, dict);
|
Py_DECREF(mydict);
|
if (res == -1)
|
return NULL;
|
Py_RETURN_NONE;
|
}
|
|
/*
|
* default __ctypes_from_outparam__ method returns self.
|
*/
|
static PyObject *
|
PyCData_from_outparam(PyObject *self, PyObject *args)
|
{
|
Py_INCREF(self);
|
return self;
|
}
|
|
static PyMethodDef PyCData_methods[] = {
|
{ "__ctypes_from_outparam__", PyCData_from_outparam, METH_NOARGS, },
|
{ "__reduce__", PyCData_reduce, METH_NOARGS, },
|
{ "__setstate__", PyCData_setstate, METH_VARARGS, },
|
{ NULL, NULL },
|
};
|
|
PyTypeObject PyCData_Type = {
|
PyVarObject_HEAD_INIT(NULL, 0)
|
"_ctypes._CData",
|
sizeof(CDataObject), /* tp_basicsize */
|
0, /* tp_itemsize */
|
PyCData_dealloc, /* tp_dealloc */
|
0, /* tp_print */
|
0, /* tp_getattr */
|
0, /* tp_setattr */
|
0, /* tp_reserved */
|
0, /* tp_repr */
|
0, /* tp_as_number */
|
0, /* tp_as_sequence */
|
0, /* tp_as_mapping */
|
PyCData_nohash, /* tp_hash */
|
0, /* tp_call */
|
0, /* tp_str */
|
0, /* tp_getattro */
|
0, /* tp_setattro */
|
&PyCData_as_buffer, /* tp_as_buffer */
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
"XXX to be provided", /* tp_doc */
|
(traverseproc)PyCData_traverse, /* tp_traverse */
|
(inquiry)PyCData_clear, /* tp_clear */
|
0, /* tp_richcompare */
|
0, /* tp_weaklistoffset */
|
0, /* tp_iter */
|
0, /* tp_iternext */
|
PyCData_methods, /* tp_methods */
|
PyCData_members, /* 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 */
|
};
|
|
static int PyCData_MallocBuffer(CDataObject *obj, StgDictObject *dict)
|
{
|
if ((size_t)dict->size <= sizeof(obj->b_value)) {
|
/* No need to call malloc, can use the default buffer */
|
obj->b_ptr = (char *)&obj->b_value;
|
/* The b_needsfree flag does not mean that we actually did
|
call PyMem_Malloc to allocate the memory block; instead it
|
means we are the *owner* of the memory and are responsible
|
for freeing resources associated with the memory. This is
|
also the reason that b_needsfree is exposed to Python.
|
*/
|
obj->b_needsfree = 1;
|
} else {
|
/* In python 2.4, and ctypes 0.9.6, the malloc call took about
|
33% of the creation time for c_int().
|
*/
|
obj->b_ptr = (char *)PyMem_Malloc(dict->size);
|
if (obj->b_ptr == NULL) {
|
PyErr_NoMemory();
|
return -1;
|
}
|
obj->b_needsfree = 1;
|
memset(obj->b_ptr, 0, dict->size);
|
}
|
obj->b_size = dict->size;
|
return 0;
|
}
|
|
PyObject *
|
PyCData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr)
|
{
|
CDataObject *cmem;
|
StgDictObject *dict;
|
|
assert(PyType_Check(type));
|
dict = PyType_stgdict(type);
|
if (!dict) {
|
PyErr_SetString(PyExc_TypeError,
|
"abstract class");
|
return NULL;
|
}
|
dict->flags |= DICTFLAG_FINAL;
|
cmem = (CDataObject *)((PyTypeObject *)type)->tp_alloc((PyTypeObject *)type, 0);
|
if (cmem == NULL)
|
return NULL;
|
assert(CDataObject_Check(cmem));
|
|
cmem->b_length = dict->length;
|
cmem->b_size = dict->size;
|
if (base) { /* use base's buffer */
|
assert(CDataObject_Check(base));
|
cmem->b_ptr = adr;
|
cmem->b_needsfree = 0;
|
Py_INCREF(base);
|
cmem->b_base = (CDataObject *)base;
|
cmem->b_index = index;
|
} else { /* copy contents of adr */
|
if (-1 == PyCData_MallocBuffer(cmem, dict)) {
|
Py_DECREF(cmem);
|
return NULL;
|
}
|
memcpy(cmem->b_ptr, adr, dict->size);
|
cmem->b_index = index;
|
}
|
return (PyObject *)cmem;
|
}
|
|
/*
|
Box a memory block into a CData instance.
|
*/
|
PyObject *
|
PyCData_AtAddress(PyObject *type, void *buf)
|
{
|
CDataObject *pd;
|
StgDictObject *dict;
|
|
assert(PyType_Check(type));
|
dict = PyType_stgdict(type);
|
if (!dict) {
|
PyErr_SetString(PyExc_TypeError,
|
"abstract class");
|
return NULL;
|
}
|
dict->flags |= DICTFLAG_FINAL;
|
|
pd = (CDataObject *)((PyTypeObject *)type)->tp_alloc((PyTypeObject *)type, 0);
|
if (!pd)
|
return NULL;
|
assert(CDataObject_Check(pd));
|
pd->b_ptr = (char *)buf;
|
pd->b_length = dict->length;
|
pd->b_size = dict->size;
|
return (PyObject *)pd;
|
}
|
|
/*
|
This function returns TRUE for c_int, c_void_p, and these kind of
|
classes. FALSE otherwise FALSE also for subclasses of c_int and
|
such.
|
*/
|
int _ctypes_simple_instance(PyObject *obj)
|
{
|
PyTypeObject *type = (PyTypeObject *)obj;
|
|
if (PyCSimpleTypeObject_Check(type))
|
return type->tp_base != &Simple_Type;
|
return 0;
|
}
|
|
PyObject *
|
PyCData_get(PyObject *type, GETFUNC getfunc, PyObject *src,
|
Py_ssize_t index, Py_ssize_t size, char *adr)
|
{
|
StgDictObject *dict;
|
if (getfunc)
|
return getfunc(adr, size);
|
assert(type);
|
dict = PyType_stgdict(type);
|
if (dict && dict->getfunc && !_ctypes_simple_instance(type))
|
return dict->getfunc(adr, size);
|
return PyCData_FromBaseObj(type, src, index, adr);
|
}
|
|
/*
|
Helper function for PyCData_set below.
|
*/
|
static PyObject *
|
_PyCData_set(CDataObject *dst, PyObject *type, SETFUNC setfunc, PyObject *value,
|
Py_ssize_t size, char *ptr)
|
{
|
CDataObject *src;
|
int err;
|
|
if (setfunc)
|
return setfunc(ptr, value, size);
|
|
if (!CDataObject_Check(value)) {
|
StgDictObject *dict = PyType_stgdict(type);
|
if (dict && dict->setfunc)
|
return dict->setfunc(ptr, value, size);
|
/*
|
If value is a tuple, we try to call the type with the tuple
|
and use the result!
|
*/
|
assert(PyType_Check(type));
|
if (PyTuple_Check(value)) {
|
PyObject *ob;
|
PyObject *result;
|
ob = PyObject_CallObject(type, value);
|
if (ob == NULL) {
|
_ctypes_extend_error(PyExc_RuntimeError, "(%s) ",
|
((PyTypeObject *)type)->tp_name);
|
return NULL;
|
}
|
result = _PyCData_set(dst, type, setfunc, ob,
|
size, ptr);
|
Py_DECREF(ob);
|
return result;
|
} else if (value == Py_None && PyCPointerTypeObject_Check(type)) {
|
*(void **)ptr = NULL;
|
Py_RETURN_NONE;
|
} else {
|
PyErr_Format(PyExc_TypeError,
|
"expected %s instance, got %s",
|
((PyTypeObject *)type)->tp_name,
|
Py_TYPE(value)->tp_name);
|
return NULL;
|
}
|
}
|
src = (CDataObject *)value;
|
|
err = PyObject_IsInstance(value, type);
|
if (err == -1)
|
return NULL;
|
if (err) {
|
memcpy(ptr,
|
src->b_ptr,
|
size);
|
|
if (PyCPointerTypeObject_Check(type)) {
|
/* XXX */
|
}
|
|
value = GetKeepedObjects(src);
|
if (value == NULL)
|
return NULL;
|
|
Py_INCREF(value);
|
return value;
|
}
|
|
if (PyCPointerTypeObject_Check(type)
|
&& ArrayObject_Check(value)) {
|
StgDictObject *p1, *p2;
|
PyObject *keep;
|
p1 = PyObject_stgdict(value);
|
assert(p1); /* Cannot be NULL for array instances */
|
p2 = PyType_stgdict(type);
|
assert(p2); /* Cannot be NULL for pointer types */
|
|
if (p1->proto != p2->proto) {
|
PyErr_Format(PyExc_TypeError,
|
"incompatible types, %s instance instead of %s instance",
|
Py_TYPE(value)->tp_name,
|
((PyTypeObject *)type)->tp_name);
|
return NULL;
|
}
|
*(void **)ptr = src->b_ptr;
|
|
keep = GetKeepedObjects(src);
|
if (keep == NULL)
|
return NULL;
|
|
/*
|
We are assigning an array object to a field which represents
|
a pointer. This has the same effect as converting an array
|
into a pointer. So, again, we have to keep the whole object
|
pointed to (which is the array in this case) alive, and not
|
only it's object list. So we create a tuple, containing
|
b_objects list PLUS the array itself, and return that!
|
*/
|
return PyTuple_Pack(2, keep, value);
|
}
|
PyErr_Format(PyExc_TypeError,
|
"incompatible types, %s instance instead of %s instance",
|
Py_TYPE(value)->tp_name,
|
((PyTypeObject *)type)->tp_name);
|
return NULL;
|
}
|
|
/*
|
* Set a slice in object 'dst', which has the type 'type',
|
* to the value 'value'.
|
*/
|
int
|
PyCData_set(PyObject *dst, PyObject *type, SETFUNC setfunc, PyObject *value,
|
Py_ssize_t index, Py_ssize_t size, char *ptr)
|
{
|
CDataObject *mem = (CDataObject *)dst;
|
PyObject *result;
|
|
if (!CDataObject_Check(dst)) {
|
PyErr_SetString(PyExc_TypeError,
|
"not a ctype instance");
|
return -1;
|
}
|
|
result = _PyCData_set(mem, type, setfunc, value,
|
size, ptr);
|
if (result == NULL)
|
return -1;
|
|
/* KeepRef steals a refcount from it's last argument */
|
/* If KeepRef fails, we are stumped. The dst memory block has already
|
been changed */
|
return KeepRef(mem, index, result);
|
}
|
|
|
/******************************************************************/
|
static PyObject *
|
GenericPyCData_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
{
|
CDataObject *obj;
|
StgDictObject *dict;
|
|
dict = PyType_stgdict((PyObject *)type);
|
if (!dict) {
|
PyErr_SetString(PyExc_TypeError,
|
"abstract class");
|
return NULL;
|
}
|
dict->flags |= DICTFLAG_FINAL;
|
|
obj = (CDataObject *)type->tp_alloc(type, 0);
|
if (!obj)
|
return NULL;
|
|
obj->b_base = NULL;
|
obj->b_index = 0;
|
obj->b_objects = NULL;
|
obj->b_length = dict->length;
|
|
if (-1 == PyCData_MallocBuffer(obj, dict)) {
|
Py_DECREF(obj);
|
return NULL;
|
}
|
return (PyObject *)obj;
|
}
|
/*****************************************************************/
|
/*
|
PyCFuncPtr_Type
|
*/
|
|
static int
|
PyCFuncPtr_set_errcheck(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored))
|
{
|
if (ob && !PyCallable_Check(ob)) {
|
PyErr_SetString(PyExc_TypeError,
|
"the errcheck attribute must be callable");
|
return -1;
|
}
|
Py_XINCREF(ob);
|
Py_XSETREF(self->errcheck, ob);
|
return 0;
|
}
|
|
static PyObject *
|
PyCFuncPtr_get_errcheck(PyCFuncPtrObject *self, void *Py_UNUSED(ignored))
|
{
|
if (self->errcheck) {
|
Py_INCREF(self->errcheck);
|
return self->errcheck;
|
}
|
Py_RETURN_NONE;
|
}
|
|
static int
|
PyCFuncPtr_set_restype(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored))
|
{
|
if (ob == NULL) {
|
Py_CLEAR(self->restype);
|
Py_CLEAR(self->checker);
|
return 0;
|
}
|
if (ob != Py_None && !PyType_stgdict(ob) && !PyCallable_Check(ob)) {
|
PyErr_SetString(PyExc_TypeError,
|
"restype must be a type, a callable, or None");
|
return -1;
|
}
|
Py_INCREF(ob);
|
Py_XSETREF(self->restype, ob);
|
Py_XSETREF(self->checker, PyObject_GetAttrString(ob, "_check_retval_"));
|
if (self->checker == NULL)
|
PyErr_Clear();
|
return 0;
|
}
|
|
static PyObject *
|
PyCFuncPtr_get_restype(PyCFuncPtrObject *self, void *Py_UNUSED(ignored))
|
{
|
StgDictObject *dict;
|
if (self->restype) {
|
Py_INCREF(self->restype);
|
return self->restype;
|
}
|
dict = PyObject_stgdict((PyObject *)self);
|
assert(dict); /* Cannot be NULL for PyCFuncPtrObject instances */
|
if (dict->restype) {
|
Py_INCREF(dict->restype);
|
return dict->restype;
|
} else {
|
Py_RETURN_NONE;
|
}
|
}
|
|
static int
|
PyCFuncPtr_set_argtypes(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored))
|
{
|
PyObject *converters;
|
|
if (ob == NULL || ob == Py_None) {
|
Py_CLEAR(self->converters);
|
Py_CLEAR(self->argtypes);
|
} else {
|
converters = converters_from_argtypes(ob);
|
if (!converters)
|
return -1;
|
Py_XSETREF(self->converters, converters);
|
Py_INCREF(ob);
|
Py_XSETREF(self->argtypes, ob);
|
}
|
return 0;
|
}
|
|
static PyObject *
|
PyCFuncPtr_get_argtypes(PyCFuncPtrObject *self, void *Py_UNUSED(ignored))
|
{
|
StgDictObject *dict;
|
if (self->argtypes) {
|
Py_INCREF(self->argtypes);
|
return self->argtypes;
|
}
|
dict = PyObject_stgdict((PyObject *)self);
|
assert(dict); /* Cannot be NULL for PyCFuncPtrObject instances */
|
if (dict->argtypes) {
|
Py_INCREF(dict->argtypes);
|
return dict->argtypes;
|
} else {
|
Py_RETURN_NONE;
|
}
|
}
|
|
static PyGetSetDef PyCFuncPtr_getsets[] = {
|
{ "errcheck", (getter)PyCFuncPtr_get_errcheck, (setter)PyCFuncPtr_set_errcheck,
|
"a function to check for errors", NULL },
|
{ "restype", (getter)PyCFuncPtr_get_restype, (setter)PyCFuncPtr_set_restype,
|
"specify the result type", NULL },
|
{ "argtypes", (getter)PyCFuncPtr_get_argtypes,
|
(setter)PyCFuncPtr_set_argtypes,
|
"specify the argument types", NULL },
|
{ NULL, NULL }
|
};
|
|
#ifdef MS_WIN32
|
static PPROC FindAddress(void *handle, const char *name, PyObject *type)
|
{
|
#ifdef MS_WIN64
|
/* win64 has no stdcall calling conv, so it should
|
also not have the name mangling of it.
|
*/
|
return (PPROC)GetProcAddress(handle, name);
|
#else
|
PPROC address;
|
char *mangled_name;
|
int i;
|
StgDictObject *dict;
|
|
address = (PPROC)GetProcAddress(handle, name);
|
if (address)
|
return address;
|
if (((size_t)name & ~0xFFFF) == 0) {
|
return NULL;
|
}
|
|
dict = PyType_stgdict((PyObject *)type);
|
/* It should not happen that dict is NULL, but better be safe */
|
if (dict==NULL || dict->flags & FUNCFLAG_CDECL)
|
return address;
|
|
/* for stdcall, try mangled names:
|
funcname -> _funcname@<n>
|
where n is 0, 4, 8, 12, ..., 128
|
*/
|
mangled_name = alloca(strlen(name) + 1 + 1 + 1 + 3); /* \0 _ @ %d */
|
if (!mangled_name)
|
return NULL;
|
for (i = 0; i < 32; ++i) {
|
sprintf(mangled_name, "_%s@%d", name, i*4);
|
address = (PPROC)GetProcAddress(handle, mangled_name);
|
if (address)
|
return address;
|
}
|
return NULL;
|
#endif
|
}
|
#endif
|
|
/* Return 1 if usable, 0 else and exception set. */
|
static int
|
_check_outarg_type(PyObject *arg, Py_ssize_t index)
|
{
|
StgDictObject *dict;
|
|
if (PyCPointerTypeObject_Check(arg))
|
return 1;
|
|
if (PyCArrayTypeObject_Check(arg))
|
return 1;
|
|
dict = PyType_stgdict(arg);
|
if (dict
|
/* simple pointer types, c_void_p, c_wchar_p, BSTR, ... */
|
&& PyUnicode_Check(dict->proto)
|
/* We only allow c_void_p, c_char_p and c_wchar_p as a simple output parameter type */
|
&& (strchr("PzZ", PyUnicode_AsUTF8(dict->proto)[0]))) {
|
return 1;
|
}
|
|
PyErr_Format(PyExc_TypeError,
|
"'out' parameter %d must be a pointer type, not %s",
|
Py_SAFE_DOWNCAST(index, Py_ssize_t, int),
|
PyType_Check(arg) ?
|
((PyTypeObject *)arg)->tp_name :
|
Py_TYPE(arg)->tp_name);
|
return 0;
|
}
|
|
/* Returns 1 on success, 0 on error */
|
static int
|
_validate_paramflags(PyTypeObject *type, PyObject *paramflags)
|
{
|
Py_ssize_t i, len;
|
StgDictObject *dict;
|
PyObject *argtypes;
|
|
dict = PyType_stgdict((PyObject *)type);
|
if (!dict) {
|
PyErr_SetString(PyExc_TypeError,
|
"abstract class");
|
return 0;
|
}
|
argtypes = dict->argtypes;
|
|
if (paramflags == NULL || dict->argtypes == NULL)
|
return 1;
|
|
if (!PyTuple_Check(paramflags)) {
|
PyErr_SetString(PyExc_TypeError,
|
"paramflags must be a tuple or None");
|
return 0;
|
}
|
|
len = PyTuple_GET_SIZE(paramflags);
|
if (len != PyTuple_GET_SIZE(dict->argtypes)) {
|
PyErr_SetString(PyExc_ValueError,
|
"paramflags must have the same length as argtypes");
|
return 0;
|
}
|
|
for (i = 0; i < len; ++i) {
|
PyObject *item = PyTuple_GET_ITEM(paramflags, i);
|
int flag;
|
char *name;
|
PyObject *defval;
|
PyObject *typ;
|
if (!PyArg_ParseTuple(item, "i|ZO", &flag, &name, &defval)) {
|
PyErr_SetString(PyExc_TypeError,
|
"paramflags must be a sequence of (int [,string [,value]]) tuples");
|
return 0;
|
}
|
typ = PyTuple_GET_ITEM(argtypes, i);
|
switch (flag & (PARAMFLAG_FIN | PARAMFLAG_FOUT | PARAMFLAG_FLCID)) {
|
case 0:
|
case PARAMFLAG_FIN:
|
case PARAMFLAG_FIN | PARAMFLAG_FLCID:
|
case PARAMFLAG_FIN | PARAMFLAG_FOUT:
|
break;
|
case PARAMFLAG_FOUT:
|
if (!_check_outarg_type(typ, i+1))
|
return 0;
|
break;
|
default:
|
PyErr_Format(PyExc_TypeError,
|
"paramflag value %d not supported",
|
flag);
|
return 0;
|
}
|
}
|
return 1;
|
}
|
|
static int
|
_get_name(PyObject *obj, const char **pname)
|
{
|
#ifdef MS_WIN32
|
if (PyLong_Check(obj)) {
|
/* We have to use MAKEINTRESOURCEA for Windows CE.
|
Works on Windows as well, of course.
|
*/
|
*pname = MAKEINTRESOURCEA(PyLong_AsUnsignedLongMask(obj) & 0xFFFF);
|
return 1;
|
}
|
#endif
|
if (PyBytes_Check(obj)) {
|
*pname = PyBytes_AS_STRING(obj);
|
return *pname ? 1 : 0;
|
}
|
if (PyUnicode_Check(obj)) {
|
*pname = PyUnicode_AsUTF8(obj);
|
return *pname ? 1 : 0;
|
}
|
PyErr_SetString(PyExc_TypeError,
|
"function name must be string, bytes object or integer");
|
return 0;
|
}
|
|
|
static PyObject *
|
PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
{
|
const char *name;
|
int (* address)(void);
|
PyObject *ftuple;
|
PyObject *dll;
|
PyObject *obj;
|
PyCFuncPtrObject *self;
|
void *handle;
|
PyObject *paramflags = NULL;
|
|
if (!PyArg_ParseTuple(args, "O|O", &ftuple, ¶mflags))
|
return NULL;
|
if (paramflags == Py_None)
|
paramflags = NULL;
|
|
ftuple = PySequence_Tuple(ftuple);
|
if (!ftuple)
|
/* Here ftuple is a borrowed reference */
|
return NULL;
|
|
if (!PyArg_ParseTuple(ftuple, "O&O;illegal func_spec argument",
|
_get_name, &name, &dll))
|
{
|
Py_DECREF(ftuple);
|
return NULL;
|
}
|
|
obj = PyObject_GetAttrString(dll, "_handle");
|
if (!obj) {
|
Py_DECREF(ftuple);
|
return NULL;
|
}
|
if (!PyLong_Check(obj)) {
|
PyErr_SetString(PyExc_TypeError,
|
"the _handle attribute of the second argument must be an integer");
|
Py_DECREF(ftuple);
|
Py_DECREF(obj);
|
return NULL;
|
}
|
handle = (void *)PyLong_AsVoidPtr(obj);
|
Py_DECREF(obj);
|
if (PyErr_Occurred()) {
|
PyErr_SetString(PyExc_ValueError,
|
"could not convert the _handle attribute to a pointer");
|
Py_DECREF(ftuple);
|
return NULL;
|
}
|
|
#ifdef MS_WIN32
|
address = FindAddress(handle, name, (PyObject *)type);
|
if (!address) {
|
if (!IS_INTRESOURCE(name))
|
PyErr_Format(PyExc_AttributeError,
|
"function '%s' not found",
|
name);
|
else
|
PyErr_Format(PyExc_AttributeError,
|
"function ordinal %d not found",
|
(WORD)(size_t)name);
|
Py_DECREF(ftuple);
|
return NULL;
|
}
|
#else
|
address = (PPROC)ctypes_dlsym(handle, name);
|
if (!address) {
|
#ifdef __CYGWIN__
|
/* dlerror() isn't very helpful on cygwin */
|
PyErr_Format(PyExc_AttributeError,
|
"function '%s' not found",
|
name);
|
#else
|
PyErr_SetString(PyExc_AttributeError, ctypes_dlerror());
|
#endif
|
Py_DECREF(ftuple);
|
return NULL;
|
}
|
#endif
|
if (!_validate_paramflags(type, paramflags)) {
|
Py_DECREF(ftuple);
|
return NULL;
|
}
|
|
self = (PyCFuncPtrObject *)GenericPyCData_new(type, args, kwds);
|
if (!self) {
|
Py_DECREF(ftuple);
|
return NULL;
|
}
|
|
Py_XINCREF(paramflags);
|
self->paramflags = paramflags;
|
|
*(void **)self->b_ptr = address;
|
Py_INCREF(dll);
|
Py_DECREF(ftuple);
|
if (-1 == KeepRef((CDataObject *)self, 0, dll)) {
|
Py_DECREF((PyObject *)self);
|
return NULL;
|
}
|
|
Py_INCREF(self);
|
self->callable = (PyObject *)self;
|
return (PyObject *)self;
|
}
|
|
#ifdef MS_WIN32
|
static PyObject *
|
PyCFuncPtr_FromVtblIndex(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
{
|
PyCFuncPtrObject *self;
|
int index;
|
char *name = NULL;
|
PyObject *paramflags = NULL;
|
GUID *iid = NULL;
|
Py_ssize_t iid_len = 0;
|
|
if (!PyArg_ParseTuple(args, "is|Oz#", &index, &name, ¶mflags, &iid, &iid_len))
|
return NULL;
|
if (paramflags == Py_None)
|
paramflags = NULL;
|
|
if (!_validate_paramflags(type, paramflags))
|
return NULL;
|
|
self = (PyCFuncPtrObject *)GenericPyCData_new(type, args, kwds);
|
self->index = index + 0x1000;
|
Py_XINCREF(paramflags);
|
self->paramflags = paramflags;
|
if (iid_len == sizeof(GUID))
|
self->iid = iid;
|
return (PyObject *)self;
|
}
|
#endif
|
|
/*
|
PyCFuncPtr_new accepts different argument lists in addition to the standard
|
_basespec_ keyword arg:
|
|
one argument form
|
"i" - function address
|
"O" - must be a callable, creates a C callable function
|
|
two or more argument forms (the third argument is a paramflags tuple)
|
"(sO)|..." - (function name, dll object (with an integer handle)), paramflags
|
"(iO)|..." - (function ordinal, dll object (with an integer handle)), paramflags
|
"is|..." - vtable index, method name, creates callable calling COM vtbl
|
*/
|
static PyObject *
|
PyCFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
{
|
PyCFuncPtrObject *self;
|
PyObject *callable;
|
StgDictObject *dict;
|
CThunkObject *thunk;
|
|
if (PyTuple_GET_SIZE(args) == 0)
|
return GenericPyCData_new(type, args, kwds);
|
|
if (1 <= PyTuple_GET_SIZE(args) && PyTuple_Check(PyTuple_GET_ITEM(args, 0)))
|
return PyCFuncPtr_FromDll(type, args, kwds);
|
|
#ifdef MS_WIN32
|
if (2 <= PyTuple_GET_SIZE(args) && PyLong_Check(PyTuple_GET_ITEM(args, 0)))
|
return PyCFuncPtr_FromVtblIndex(type, args, kwds);
|
#endif
|
|
if (1 == PyTuple_GET_SIZE(args)
|
&& (PyLong_Check(PyTuple_GET_ITEM(args, 0)))) {
|
CDataObject *ob;
|
void *ptr = PyLong_AsVoidPtr(PyTuple_GET_ITEM(args, 0));
|
if (ptr == NULL && PyErr_Occurred())
|
return NULL;
|
ob = (CDataObject *)GenericPyCData_new(type, args, kwds);
|
if (ob == NULL)
|
return NULL;
|
*(void **)ob->b_ptr = ptr;
|
return (PyObject *)ob;
|
}
|
|
if (!PyArg_ParseTuple(args, "O", &callable))
|
return NULL;
|
if (!PyCallable_Check(callable)) {
|
PyErr_SetString(PyExc_TypeError,
|
"argument must be callable or integer function address");
|
return NULL;
|
}
|
|
/* XXX XXX This would allow passing additional options. For COM
|
method *implementations*, we would probably want different
|
behaviour than in 'normal' callback functions: return a HRESULT if
|
an exception occurs in the callback, and print the traceback not
|
only on the console, but also to OutputDebugString() or something
|
like that.
|
*/
|
/*
|
if (kwds && PyDict_GetItemString(kwds, "options")) {
|
...
|
}
|
*/
|
|
dict = PyType_stgdict((PyObject *)type);
|
/* XXXX Fails if we do: 'PyCFuncPtr(lambda x: x)' */
|
if (!dict || !dict->argtypes) {
|
PyErr_SetString(PyExc_TypeError,
|
"cannot construct instance of this class:"
|
" no argtypes");
|
return NULL;
|
}
|
|
thunk = _ctypes_alloc_callback(callable,
|
dict->argtypes,
|
dict->restype,
|
dict->flags);
|
if (!thunk)
|
return NULL;
|
|
self = (PyCFuncPtrObject *)GenericPyCData_new(type, args, kwds);
|
if (self == NULL) {
|
Py_DECREF(thunk);
|
return NULL;
|
}
|
|
Py_INCREF(callable);
|
self->callable = callable;
|
|
self->thunk = thunk;
|
*(void **)self->b_ptr = (void *)thunk->pcl_exec;
|
|
Py_INCREF((PyObject *)thunk); /* for KeepRef */
|
if (-1 == KeepRef((CDataObject *)self, 0, (PyObject *)thunk)) {
|
Py_DECREF((PyObject *)self);
|
return NULL;
|
}
|
return (PyObject *)self;
|
}
|
|
|
/*
|
_byref consumes a refcount to its argument
|
*/
|
static PyObject *
|
_byref(PyObject *obj)
|
{
|
PyCArgObject *parg;
|
if (!CDataObject_Check(obj)) {
|
PyErr_SetString(PyExc_TypeError,
|
"expected CData instance");
|
return NULL;
|
}
|
|
parg = PyCArgObject_new();
|
if (parg == NULL) {
|
Py_DECREF(obj);
|
return NULL;
|
}
|
|
parg->tag = 'P';
|
parg->pffi_type = &ffi_type_pointer;
|
parg->obj = obj;
|
parg->value.p = ((CDataObject *)obj)->b_ptr;
|
return (PyObject *)parg;
|
}
|
|
static PyObject *
|
_get_arg(int *pindex, PyObject *name, PyObject *defval, PyObject *inargs, PyObject *kwds)
|
{
|
PyObject *v;
|
|
if (*pindex < PyTuple_GET_SIZE(inargs)) {
|
v = PyTuple_GET_ITEM(inargs, *pindex);
|
++*pindex;
|
Py_INCREF(v);
|
return v;
|
}
|
if (kwds && name && (v = PyDict_GetItem(kwds, name))) {
|
++*pindex;
|
Py_INCREF(v);
|
return v;
|
}
|
if (defval) {
|
Py_INCREF(defval);
|
return defval;
|
}
|
/* we can't currently emit a better error message */
|
if (name)
|
PyErr_Format(PyExc_TypeError,
|
"required argument '%S' missing", name);
|
else
|
PyErr_Format(PyExc_TypeError,
|
"not enough arguments");
|
return NULL;
|
}
|
|
/*
|
This function implements higher level functionality plus the ability to call
|
functions with keyword arguments by looking at parameter flags. parameter
|
flags is a tuple of 1, 2 or 3-tuples. The first entry in each is an integer
|
specifying the direction of the data transfer for this parameter - 'in',
|
'out' or 'inout' (zero means the same as 'in'). The second entry is the
|
parameter name, and the third is the default value if the parameter is
|
missing in the function call.
|
|
This function builds and returns a new tuple 'callargs' which contains the
|
parameters to use in the call. Items on this tuple are copied from the
|
'inargs' tuple for 'in' and 'in, out' parameters, and constructed from the
|
'argtypes' tuple for 'out' parameters. It also calculates numretvals which
|
is the number of return values for the function, outmask/inoutmask are
|
bitmasks containing indexes into the callargs tuple specifying which
|
parameters have to be returned. _build_result builds the return value of the
|
function.
|
*/
|
static PyObject *
|
_build_callargs(PyCFuncPtrObject *self, PyObject *argtypes,
|
PyObject *inargs, PyObject *kwds,
|
int *poutmask, int *pinoutmask, unsigned int *pnumretvals)
|
{
|
PyObject *paramflags = self->paramflags;
|
PyObject *callargs;
|
StgDictObject *dict;
|
Py_ssize_t i, len;
|
int inargs_index = 0;
|
/* It's a little bit difficult to determine how many arguments the
|
function call requires/accepts. For simplicity, we count the consumed
|
args and compare this to the number of supplied args. */
|
Py_ssize_t actual_args;
|
|
*poutmask = 0;
|
*pinoutmask = 0;
|
*pnumretvals = 0;
|
|
/* Trivial cases, where we either return inargs itself, or a slice of it. */
|
if (argtypes == NULL || paramflags == NULL || PyTuple_GET_SIZE(argtypes) == 0) {
|
#ifdef MS_WIN32
|
if (self->index)
|
return PyTuple_GetSlice(inargs, 1, PyTuple_GET_SIZE(inargs));
|
#endif
|
Py_INCREF(inargs);
|
return inargs;
|
}
|
|
len = PyTuple_GET_SIZE(argtypes);
|
callargs = PyTuple_New(len); /* the argument tuple we build */
|
if (callargs == NULL)
|
return NULL;
|
|
#ifdef MS_WIN32
|
/* For a COM method, skip the first arg */
|
if (self->index) {
|
inargs_index = 1;
|
}
|
#endif
|
for (i = 0; i < len; ++i) {
|
PyObject *item = PyTuple_GET_ITEM(paramflags, i);
|
PyObject *ob;
|
int flag;
|
PyObject *name = NULL;
|
PyObject *defval = NULL;
|
|
/* This way seems to be ~2 us faster than the PyArg_ParseTuple
|
calls below. */
|
/* We HAVE already checked that the tuple can be parsed with "i|ZO", so... */
|
Py_ssize_t tsize = PyTuple_GET_SIZE(item);
|
flag = PyLong_AS_LONG(PyTuple_GET_ITEM(item, 0));
|
name = tsize > 1 ? PyTuple_GET_ITEM(item, 1) : NULL;
|
defval = tsize > 2 ? PyTuple_GET_ITEM(item, 2) : NULL;
|
|
switch (flag & (PARAMFLAG_FIN | PARAMFLAG_FOUT | PARAMFLAG_FLCID)) {
|
case PARAMFLAG_FIN | PARAMFLAG_FLCID:
|
/* ['in', 'lcid'] parameter. Always taken from defval,
|
if given, else the integer 0. */
|
if (defval == NULL)
|
defval = _PyLong_Zero;
|
Py_INCREF(defval);
|
PyTuple_SET_ITEM(callargs, i, defval);
|
break;
|
case (PARAMFLAG_FIN | PARAMFLAG_FOUT):
|
*pinoutmask |= (1 << i); /* mark as inout arg */
|
(*pnumretvals)++;
|
/* fall through */
|
case 0:
|
case PARAMFLAG_FIN:
|
/* 'in' parameter. Copy it from inargs. */
|
ob =_get_arg(&inargs_index, name, defval, inargs, kwds);
|
if (ob == NULL)
|
goto error;
|
PyTuple_SET_ITEM(callargs, i, ob);
|
break;
|
case PARAMFLAG_FOUT:
|
/* XXX Refactor this code into a separate function. */
|
/* 'out' parameter.
|
argtypes[i] must be a POINTER to a c type.
|
|
Cannot by supplied in inargs, but a defval will be used
|
if available. XXX Should we support getting it from kwds?
|
*/
|
if (defval) {
|
/* XXX Using mutable objects as defval will
|
make the function non-threadsafe, unless we
|
copy the object in each invocation */
|
Py_INCREF(defval);
|
PyTuple_SET_ITEM(callargs, i, defval);
|
*poutmask |= (1 << i); /* mark as out arg */
|
(*pnumretvals)++;
|
break;
|
}
|
ob = PyTuple_GET_ITEM(argtypes, i);
|
dict = PyType_stgdict(ob);
|
if (dict == NULL) {
|
/* Cannot happen: _validate_paramflags()
|
would not accept such an object */
|
PyErr_Format(PyExc_RuntimeError,
|
"NULL stgdict unexpected");
|
goto error;
|
}
|
if (PyUnicode_Check(dict->proto)) {
|
PyErr_Format(
|
PyExc_TypeError,
|
"%s 'out' parameter must be passed as default value",
|
((PyTypeObject *)ob)->tp_name);
|
goto error;
|
}
|
if (PyCArrayTypeObject_Check(ob))
|
ob = _PyObject_CallNoArg(ob);
|
else
|
/* Create an instance of the pointed-to type */
|
ob = _PyObject_CallNoArg(dict->proto);
|
/*
|
XXX Is the following correct any longer?
|
We must not pass a byref() to the array then but
|
the array instance itself. Then, we cannot retrieve
|
the result from the PyCArgObject.
|
*/
|
if (ob == NULL)
|
goto error;
|
/* The .from_param call that will occur later will pass this
|
as a byref parameter. */
|
PyTuple_SET_ITEM(callargs, i, ob);
|
*poutmask |= (1 << i); /* mark as out arg */
|
(*pnumretvals)++;
|
break;
|
default:
|
PyErr_Format(PyExc_ValueError,
|
"paramflag %d not yet implemented", flag);
|
goto error;
|
break;
|
}
|
}
|
|
/* We have counted the arguments we have consumed in 'inargs_index'. This
|
must be the same as len(inargs) + len(kwds), otherwise we have
|
either too much or not enough arguments. */
|
|
actual_args = PyTuple_GET_SIZE(inargs) + (kwds ? PyDict_GET_SIZE(kwds) : 0);
|
if (actual_args != inargs_index) {
|
/* When we have default values or named parameters, this error
|
message is misleading. See unittests/test_paramflags.py
|
*/
|
PyErr_Format(PyExc_TypeError,
|
"call takes exactly %d arguments (%zd given)",
|
inargs_index, actual_args);
|
goto error;
|
}
|
|
/* outmask is a bitmask containing indexes into callargs. Items at
|
these indexes contain values to return.
|
*/
|
return callargs;
|
error:
|
Py_DECREF(callargs);
|
return NULL;
|
}
|
|
/* See also:
|
http://msdn.microsoft.com/library/en-us/com/html/769127a1-1a14-4ed4-9d38-7cf3e571b661.asp
|
*/
|
/*
|
Build return value of a function.
|
|
Consumes the refcount on result and callargs.
|
*/
|
static PyObject *
|
_build_result(PyObject *result, PyObject *callargs,
|
int outmask, int inoutmask, unsigned int numretvals)
|
{
|
unsigned int i, index;
|
int bit;
|
PyObject *tup = NULL;
|
|
if (callargs == NULL)
|
return result;
|
if (result == NULL || numretvals == 0) {
|
Py_DECREF(callargs);
|
return result;
|
}
|
Py_DECREF(result);
|
|
/* tup will not be allocated if numretvals == 1 */
|
/* allocate tuple to hold the result */
|
if (numretvals > 1) {
|
tup = PyTuple_New(numretvals);
|
if (tup == NULL) {
|
Py_DECREF(callargs);
|
return NULL;
|
}
|
}
|
|
index = 0;
|
for (bit = 1, i = 0; i < 32; ++i, bit <<= 1) {
|
PyObject *v;
|
if (bit & inoutmask) {
|
v = PyTuple_GET_ITEM(callargs, i);
|
Py_INCREF(v);
|
if (numretvals == 1) {
|
Py_DECREF(callargs);
|
return v;
|
}
|
PyTuple_SET_ITEM(tup, index, v);
|
index++;
|
} else if (bit & outmask) {
|
_Py_IDENTIFIER(__ctypes_from_outparam__);
|
|
v = PyTuple_GET_ITEM(callargs, i);
|
v = _PyObject_CallMethodId(v, &PyId___ctypes_from_outparam__, NULL);
|
if (v == NULL || numretvals == 1) {
|
Py_DECREF(callargs);
|
return v;
|
}
|
PyTuple_SET_ITEM(tup, index, v);
|
index++;
|
}
|
if (index == numretvals)
|
break;
|
}
|
|
Py_DECREF(callargs);
|
return tup;
|
}
|
|
static PyObject *
|
PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds)
|
{
|
PyObject *restype;
|
PyObject *converters;
|
PyObject *checker;
|
PyObject *argtypes;
|
StgDictObject *dict = PyObject_stgdict((PyObject *)self);
|
PyObject *result;
|
PyObject *callargs;
|
PyObject *errcheck;
|
#ifdef MS_WIN32
|
IUnknown *piunk = NULL;
|
#endif
|
void *pProc = NULL;
|
|
int inoutmask;
|
int outmask;
|
unsigned int numretvals;
|
|
assert(dict); /* Cannot be NULL for PyCFuncPtrObject instances */
|
restype = self->restype ? self->restype : dict->restype;
|
converters = self->converters ? self->converters : dict->converters;
|
checker = self->checker ? self->checker : dict->checker;
|
argtypes = self->argtypes ? self->argtypes : dict->argtypes;
|
/* later, we probably want to have an errcheck field in stgdict */
|
errcheck = self->errcheck /* ? self->errcheck : dict->errcheck */;
|
|
|
pProc = *(void **)self->b_ptr;
|
#ifdef MS_WIN32
|
if (self->index) {
|
/* It's a COM method */
|
CDataObject *this;
|
this = (CDataObject *)PyTuple_GetItem(inargs, 0); /* borrowed ref! */
|
if (!this) {
|
PyErr_SetString(PyExc_ValueError,
|
"native com method call without 'this' parameter");
|
return NULL;
|
}
|
if (!CDataObject_Check(this)) {
|
PyErr_SetString(PyExc_TypeError,
|
"Expected a COM this pointer as first argument");
|
return NULL;
|
}
|
/* there should be more checks? No, in Python */
|
/* First arg is a pointer to an interface instance */
|
if (!this->b_ptr || *(void **)this->b_ptr == NULL) {
|
PyErr_SetString(PyExc_ValueError,
|
"NULL COM pointer access");
|
return NULL;
|
}
|
piunk = *(IUnknown **)this->b_ptr;
|
if (NULL == piunk->lpVtbl) {
|
PyErr_SetString(PyExc_ValueError,
|
"COM method call without VTable");
|
return NULL;
|
}
|
pProc = ((void **)piunk->lpVtbl)[self->index - 0x1000];
|
}
|
#endif
|
callargs = _build_callargs(self, argtypes,
|
inargs, kwds,
|
&outmask, &inoutmask, &numretvals);
|
if (callargs == NULL)
|
return NULL;
|
|
if (converters) {
|
int required = Py_SAFE_DOWNCAST(PyTuple_GET_SIZE(converters),
|
Py_ssize_t, int);
|
int actual = Py_SAFE_DOWNCAST(PyTuple_GET_SIZE(callargs),
|
Py_ssize_t, int);
|
|
if ((dict->flags & FUNCFLAG_CDECL) == FUNCFLAG_CDECL) {
|
/* For cdecl functions, we allow more actual arguments
|
than the length of the argtypes tuple.
|
*/
|
if (required > actual) {
|
Py_DECREF(callargs);
|
PyErr_Format(PyExc_TypeError,
|
"this function takes at least %d argument%s (%d given)",
|
required,
|
required == 1 ? "" : "s",
|
actual);
|
return NULL;
|
}
|
} else if (required != actual) {
|
Py_DECREF(callargs);
|
PyErr_Format(PyExc_TypeError,
|
"this function takes %d argument%s (%d given)",
|
required,
|
required == 1 ? "" : "s",
|
actual);
|
return NULL;
|
}
|
}
|
|
result = _ctypes_callproc(pProc,
|
callargs,
|
#ifdef MS_WIN32
|
piunk,
|
self->iid,
|
#endif
|
dict->flags,
|
converters,
|
restype,
|
checker);
|
/* The 'errcheck' protocol */
|
if (result != NULL && errcheck) {
|
PyObject *v = PyObject_CallFunctionObjArgs(errcheck,
|
result,
|
self,
|
callargs,
|
NULL);
|
/* If the errcheck function failed, return NULL.
|
If the errcheck function returned callargs unchanged,
|
continue normal processing.
|
If the errcheck function returned something else,
|
use that as result.
|
*/
|
if (v == NULL || v != callargs) {
|
Py_DECREF(result);
|
Py_DECREF(callargs);
|
return v;
|
}
|
Py_DECREF(v);
|
}
|
|
return _build_result(result, callargs,
|
outmask, inoutmask, numretvals);
|
}
|
|
static int
|
PyCFuncPtr_traverse(PyCFuncPtrObject *self, visitproc visit, void *arg)
|
{
|
Py_VISIT(self->callable);
|
Py_VISIT(self->restype);
|
Py_VISIT(self->checker);
|
Py_VISIT(self->errcheck);
|
Py_VISIT(self->argtypes);
|
Py_VISIT(self->converters);
|
Py_VISIT(self->paramflags);
|
Py_VISIT(self->thunk);
|
return PyCData_traverse((CDataObject *)self, visit, arg);
|
}
|
|
static int
|
PyCFuncPtr_clear(PyCFuncPtrObject *self)
|
{
|
Py_CLEAR(self->callable);
|
Py_CLEAR(self->restype);
|
Py_CLEAR(self->checker);
|
Py_CLEAR(self->errcheck);
|
Py_CLEAR(self->argtypes);
|
Py_CLEAR(self->converters);
|
Py_CLEAR(self->paramflags);
|
Py_CLEAR(self->thunk);
|
return PyCData_clear((CDataObject *)self);
|
}
|
|
static void
|
PyCFuncPtr_dealloc(PyCFuncPtrObject *self)
|
{
|
PyCFuncPtr_clear(self);
|
Py_TYPE(self)->tp_free((PyObject *)self);
|
}
|
|
static PyObject *
|
PyCFuncPtr_repr(PyCFuncPtrObject *self)
|
{
|
#ifdef MS_WIN32
|
if (self->index)
|
return PyUnicode_FromFormat("<COM method offset %d: %s at %p>",
|
self->index - 0x1000,
|
Py_TYPE(self)->tp_name,
|
self);
|
#endif
|
return PyUnicode_FromFormat("<%s object at %p>",
|
Py_TYPE(self)->tp_name,
|
self);
|
}
|
|
static int
|
PyCFuncPtr_bool(PyCFuncPtrObject *self)
|
{
|
return ((*(void **)self->b_ptr != NULL)
|
#ifdef MS_WIN32
|
|| (self->index != 0)
|
#endif
|
);
|
}
|
|
static PyNumberMethods PyCFuncPtr_as_number = {
|
0, /* nb_add */
|
0, /* nb_subtract */
|
0, /* nb_multiply */
|
0, /* nb_remainder */
|
0, /* nb_divmod */
|
0, /* nb_power */
|
0, /* nb_negative */
|
0, /* nb_positive */
|
0, /* nb_absolute */
|
(inquiry)PyCFuncPtr_bool, /* nb_bool */
|
};
|
|
PyTypeObject PyCFuncPtr_Type = {
|
PyVarObject_HEAD_INIT(NULL, 0)
|
"_ctypes.PyCFuncPtr",
|
sizeof(PyCFuncPtrObject), /* tp_basicsize */
|
0, /* tp_itemsize */
|
(destructor)PyCFuncPtr_dealloc, /* tp_dealloc */
|
0, /* tp_print */
|
0, /* tp_getattr */
|
0, /* tp_setattr */
|
0, /* tp_reserved */
|
(reprfunc)PyCFuncPtr_repr, /* tp_repr */
|
&PyCFuncPtr_as_number, /* tp_as_number */
|
0, /* tp_as_sequence */
|
0, /* tp_as_mapping */
|
0, /* tp_hash */
|
(ternaryfunc)PyCFuncPtr_call, /* tp_call */
|
0, /* tp_str */
|
0, /* tp_getattro */
|
0, /* tp_setattro */
|
&PyCData_as_buffer, /* tp_as_buffer */
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
"Function Pointer", /* tp_doc */
|
(traverseproc)PyCFuncPtr_traverse, /* tp_traverse */
|
(inquiry)PyCFuncPtr_clear, /* tp_clear */
|
0, /* tp_richcompare */
|
0, /* tp_weaklistoffset */
|
0, /* tp_iter */
|
0, /* tp_iternext */
|
0, /* tp_methods */
|
0, /* tp_members */
|
PyCFuncPtr_getsets, /* 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 */
|
PyCFuncPtr_new, /* tp_new */
|
0, /* tp_free */
|
};
|
|
/*****************************************************************/
|
/*
|
Struct_Type
|
*/
|
/*
|
This function is called to initialize a Structure or Union with positional
|
arguments. It calls itself recursively for all Structure or Union base
|
classes, then retrieves the _fields_ member to associate the argument
|
position with the correct field name.
|
|
Returns -1 on error, or the index of next argument on success.
|
*/
|
static Py_ssize_t
|
_init_pos_args(PyObject *self, PyTypeObject *type,
|
PyObject *args, PyObject *kwds,
|
Py_ssize_t index)
|
{
|
StgDictObject *dict;
|
PyObject *fields;
|
Py_ssize_t i;
|
|
if (PyType_stgdict((PyObject *)type->tp_base)) {
|
index = _init_pos_args(self, type->tp_base,
|
args, kwds,
|
index);
|
if (index == -1)
|
return -1;
|
}
|
|
dict = PyType_stgdict((PyObject *)type);
|
fields = PyDict_GetItemString((PyObject *)dict, "_fields_");
|
if (fields == NULL)
|
return index;
|
|
for (i = 0;
|
i < dict->length && (i+index) < PyTuple_GET_SIZE(args);
|
++i) {
|
PyObject *pair = PySequence_GetItem(fields, i);
|
PyObject *name, *val;
|
int res;
|
if (!pair)
|
return -1;
|
name = PySequence_GetItem(pair, 0);
|
if (!name) {
|
Py_DECREF(pair);
|
return -1;
|
}
|
val = PyTuple_GET_ITEM(args, i + index);
|
if (kwds && PyDict_GetItem(kwds, name)) {
|
PyErr_Format(PyExc_TypeError,
|
"duplicate values for field %R",
|
name);
|
Py_DECREF(pair);
|
Py_DECREF(name);
|
return -1;
|
}
|
|
res = PyObject_SetAttr(self, name, val);
|
Py_DECREF(pair);
|
Py_DECREF(name);
|
if (res == -1)
|
return -1;
|
}
|
return index + dict->length;
|
}
|
|
static int
|
Struct_init(PyObject *self, PyObject *args, PyObject *kwds)
|
{
|
/* Optimization possible: Store the attribute names _fields_[x][0]
|
* in C accessible fields somewhere ?
|
*/
|
if (!PyTuple_Check(args)) {
|
PyErr_SetString(PyExc_TypeError,
|
"args not a tuple?");
|
return -1;
|
}
|
if (PyTuple_GET_SIZE(args)) {
|
Py_ssize_t res = _init_pos_args(self, Py_TYPE(self),
|
args, kwds, 0);
|
if (res == -1)
|
return -1;
|
if (res < PyTuple_GET_SIZE(args)) {
|
PyErr_SetString(PyExc_TypeError,
|
"too many initializers");
|
return -1;
|
}
|
}
|
|
if (kwds) {
|
PyObject *key, *value;
|
Py_ssize_t pos = 0;
|
while(PyDict_Next(kwds, &pos, &key, &value)) {
|
if (-1 == PyObject_SetAttr(self, key, value))
|
return -1;
|
}
|
}
|
return 0;
|
}
|
|
static PyTypeObject Struct_Type = {
|
PyVarObject_HEAD_INIT(NULL, 0)
|
"_ctypes.Structure",
|
sizeof(CDataObject), /* tp_basicsize */
|
0, /* tp_itemsize */
|
0, /* tp_dealloc */
|
0, /* tp_print */
|
0, /* tp_getattr */
|
0, /* tp_setattr */
|
0, /* tp_reserved */
|
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 */
|
&PyCData_as_buffer, /* tp_as_buffer */
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
"Structure base class", /* tp_doc */
|
(traverseproc)PyCData_traverse, /* tp_traverse */
|
(inquiry)PyCData_clear, /* 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 */
|
Struct_init, /* tp_init */
|
0, /* tp_alloc */
|
GenericPyCData_new, /* tp_new */
|
0, /* tp_free */
|
};
|
|
static PyTypeObject Union_Type = {
|
PyVarObject_HEAD_INIT(NULL, 0)
|
"_ctypes.Union",
|
sizeof(CDataObject), /* tp_basicsize */
|
0, /* tp_itemsize */
|
0, /* tp_dealloc */
|
0, /* tp_print */
|
0, /* tp_getattr */
|
0, /* tp_setattr */
|
0, /* tp_reserved */
|
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 */
|
&PyCData_as_buffer, /* tp_as_buffer */
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
"Union base class", /* tp_doc */
|
(traverseproc)PyCData_traverse, /* tp_traverse */
|
(inquiry)PyCData_clear, /* 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 */
|
Struct_init, /* tp_init */
|
0, /* tp_alloc */
|
GenericPyCData_new, /* tp_new */
|
0, /* tp_free */
|
};
|
|
|
/******************************************************************/
|
/*
|
PyCArray_Type
|
*/
|
static int
|
Array_init(CDataObject *self, PyObject *args, PyObject *kw)
|
{
|
Py_ssize_t i;
|
Py_ssize_t n;
|
|
if (!PyTuple_Check(args)) {
|
PyErr_SetString(PyExc_TypeError,
|
"args not a tuple?");
|
return -1;
|
}
|
n = PyTuple_GET_SIZE(args);
|
for (i = 0; i < n; ++i) {
|
PyObject *v;
|
v = PyTuple_GET_ITEM(args, i);
|
if (-1 == PySequence_SetItem((PyObject *)self, i, v))
|
return -1;
|
}
|
return 0;
|
}
|
|
static PyObject *
|
Array_item(PyObject *myself, Py_ssize_t index)
|
{
|
CDataObject *self = (CDataObject *)myself;
|
Py_ssize_t offset, size;
|
StgDictObject *stgdict;
|
|
|
if (index < 0 || index >= self->b_length) {
|
PyErr_SetString(PyExc_IndexError,
|
"invalid index");
|
return NULL;
|
}
|
|
stgdict = PyObject_stgdict((PyObject *)self);
|
assert(stgdict); /* Cannot be NULL for array instances */
|
/* Would it be clearer if we got the item size from
|
stgdict->proto's stgdict?
|
*/
|
size = stgdict->size / stgdict->length;
|
offset = index * size;
|
|
return PyCData_get(stgdict->proto, stgdict->getfunc, (PyObject *)self,
|
index, size, self->b_ptr + offset);
|
}
|
|
static PyObject *
|
Array_subscript(PyObject *myself, PyObject *item)
|
{
|
CDataObject *self = (CDataObject *)myself;
|
|
if (PyIndex_Check(item)) {
|
Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
|
|
if (i == -1 && PyErr_Occurred())
|
return NULL;
|
if (i < 0)
|
i += self->b_length;
|
return Array_item(myself, i);
|
}
|
else if (PySlice_Check(item)) {
|
StgDictObject *stgdict, *itemdict;
|
PyObject *proto;
|
PyObject *np;
|
Py_ssize_t start, stop, step, slicelen, cur, i;
|
|
if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
|
return NULL;
|
}
|
slicelen = PySlice_AdjustIndices(self->b_length, &start, &stop, step);
|
|
stgdict = PyObject_stgdict((PyObject *)self);
|
assert(stgdict); /* Cannot be NULL for array object instances */
|
proto = stgdict->proto;
|
itemdict = PyType_stgdict(proto);
|
assert(itemdict); /* proto is the item type of the array, a
|
ctypes type, so this cannot be NULL */
|
|
if (itemdict->getfunc == _ctypes_get_fielddesc("c")->getfunc) {
|
char *ptr = (char *)self->b_ptr;
|
char *dest;
|
|
if (slicelen <= 0)
|
return PyBytes_FromStringAndSize("", 0);
|
if (step == 1) {
|
return PyBytes_FromStringAndSize(ptr + start,
|
slicelen);
|
}
|
dest = (char *)PyMem_Malloc(slicelen);
|
|
if (dest == NULL)
|
return PyErr_NoMemory();
|
|
for (cur = start, i = 0; i < slicelen;
|
cur += step, i++) {
|
dest[i] = ptr[cur];
|
}
|
|
np = PyBytes_FromStringAndSize(dest, slicelen);
|
PyMem_Free(dest);
|
return np;
|
}
|
#ifdef CTYPES_UNICODE
|
if (itemdict->getfunc == _ctypes_get_fielddesc("u")->getfunc) {
|
wchar_t *ptr = (wchar_t *)self->b_ptr;
|
wchar_t *dest;
|
|
if (slicelen <= 0)
|
return PyUnicode_New(0, 0);
|
if (step == 1) {
|
return PyUnicode_FromWideChar(ptr + start,
|
slicelen);
|
}
|
|
dest = PyMem_New(wchar_t, slicelen);
|
if (dest == NULL) {
|
PyErr_NoMemory();
|
return NULL;
|
}
|
|
for (cur = start, i = 0; i < slicelen;
|
cur += step, i++) {
|
dest[i] = ptr[cur];
|
}
|
|
np = PyUnicode_FromWideChar(dest, slicelen);
|
PyMem_Free(dest);
|
return np;
|
}
|
#endif
|
|
np = PyList_New(slicelen);
|
if (np == NULL)
|
return NULL;
|
|
for (cur = start, i = 0; i < slicelen;
|
cur += step, i++) {
|
PyObject *v = Array_item(myself, cur);
|
if (v == NULL) {
|
Py_DECREF(np);
|
return NULL;
|
}
|
PyList_SET_ITEM(np, i, v);
|
}
|
return np;
|
}
|
else {
|
PyErr_SetString(PyExc_TypeError,
|
"indices must be integers");
|
return NULL;
|
}
|
|
}
|
|
static int
|
Array_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value)
|
{
|
CDataObject *self = (CDataObject *)myself;
|
Py_ssize_t size, offset;
|
StgDictObject *stgdict;
|
char *ptr;
|
|
if (value == NULL) {
|
PyErr_SetString(PyExc_TypeError,
|
"Array does not support item deletion");
|
return -1;
|
}
|
|
stgdict = PyObject_stgdict((PyObject *)self);
|
assert(stgdict); /* Cannot be NULL for array object instances */
|
if (index < 0 || index >= stgdict->length) {
|
PyErr_SetString(PyExc_IndexError,
|
"invalid index");
|
return -1;
|
}
|
size = stgdict->size / stgdict->length;
|
offset = index * size;
|
ptr = self->b_ptr + offset;
|
|
return PyCData_set((PyObject *)self, stgdict->proto, stgdict->setfunc, value,
|
index, size, ptr);
|
}
|
|
static int
|
Array_ass_subscript(PyObject *myself, PyObject *item, PyObject *value)
|
{
|
CDataObject *self = (CDataObject *)myself;
|
|
if (value == NULL) {
|
PyErr_SetString(PyExc_TypeError,
|
"Array does not support item deletion");
|
return -1;
|
}
|
|
if (PyIndex_Check(item)) {
|
Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
|
|
if (i == -1 && PyErr_Occurred())
|
return -1;
|
if (i < 0)
|
i += self->b_length;
|
return Array_ass_item(myself, i, value);
|
}
|
else if (PySlice_Check(item)) {
|
Py_ssize_t start, stop, step, slicelen, otherlen, i, cur;
|
|
if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
|
return -1;
|
}
|
slicelen = PySlice_AdjustIndices(self->b_length, &start, &stop, step);
|
if ((step < 0 && start < stop) ||
|
(step > 0 && start > stop))
|
stop = start;
|
|
otherlen = PySequence_Length(value);
|
if (otherlen != slicelen) {
|
PyErr_SetString(PyExc_ValueError,
|
"Can only assign sequence of same size");
|
return -1;
|
}
|
for (cur = start, i = 0; i < otherlen; cur += step, i++) {
|
PyObject *item = PySequence_GetItem(value, i);
|
int result;
|
if (item == NULL)
|
return -1;
|
result = Array_ass_item(myself, cur, item);
|
Py_DECREF(item);
|
if (result == -1)
|
return -1;
|
}
|
return 0;
|
}
|
else {
|
PyErr_SetString(PyExc_TypeError,
|
"indices must be integer");
|
return -1;
|
}
|
}
|
|
static Py_ssize_t
|
Array_length(PyObject *myself)
|
{
|
CDataObject *self = (CDataObject *)myself;
|
return self->b_length;
|
}
|
|
static PySequenceMethods Array_as_sequence = {
|
Array_length, /* sq_length; */
|
0, /* sq_concat; */
|
0, /* sq_repeat; */
|
Array_item, /* sq_item; */
|
0, /* sq_slice; */
|
Array_ass_item, /* sq_ass_item; */
|
0, /* sq_ass_slice; */
|
0, /* sq_contains; */
|
|
0, /* sq_inplace_concat; */
|
0, /* sq_inplace_repeat; */
|
};
|
|
static PyMappingMethods Array_as_mapping = {
|
Array_length,
|
Array_subscript,
|
Array_ass_subscript,
|
};
|
|
PyTypeObject PyCArray_Type = {
|
PyVarObject_HEAD_INIT(NULL, 0)
|
"_ctypes.Array",
|
sizeof(CDataObject), /* tp_basicsize */
|
0, /* tp_itemsize */
|
0, /* tp_dealloc */
|
0, /* tp_print */
|
0, /* tp_getattr */
|
0, /* tp_setattr */
|
0, /* tp_reserved */
|
0, /* tp_repr */
|
0, /* tp_as_number */
|
&Array_as_sequence, /* tp_as_sequence */
|
&Array_as_mapping, /* tp_as_mapping */
|
0, /* tp_hash */
|
0, /* tp_call */
|
0, /* tp_str */
|
0, /* tp_getattro */
|
0, /* tp_setattro */
|
&PyCData_as_buffer, /* tp_as_buffer */
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
"XXX to be provided", /* tp_doc */
|
(traverseproc)PyCData_traverse, /* tp_traverse */
|
(inquiry)PyCData_clear, /* 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 */
|
(initproc)Array_init, /* tp_init */
|
0, /* tp_alloc */
|
GenericPyCData_new, /* tp_new */
|
0, /* tp_free */
|
};
|
|
PyObject *
|
PyCArrayType_from_ctype(PyObject *itemtype, Py_ssize_t length)
|
{
|
static PyObject *cache;
|
PyObject *key;
|
PyObject *result;
|
char name[256];
|
PyObject *len;
|
|
if (cache == NULL) {
|
cache = PyDict_New();
|
if (cache == NULL)
|
return NULL;
|
}
|
len = PyLong_FromSsize_t(length);
|
if (len == NULL)
|
return NULL;
|
key = PyTuple_Pack(2, itemtype, len);
|
Py_DECREF(len);
|
if (!key)
|
return NULL;
|
result = PyDict_GetItemProxy(cache, key);
|
if (result) {
|
Py_INCREF(result);
|
Py_DECREF(key);
|
return result;
|
}
|
|
if (!PyType_Check(itemtype)) {
|
PyErr_SetString(PyExc_TypeError,
|
"Expected a type object");
|
Py_DECREF(key);
|
return NULL;
|
}
|
#ifdef MS_WIN64
|
sprintf(name, "%.200s_Array_%Id",
|
((PyTypeObject *)itemtype)->tp_name, length);
|
#else
|
sprintf(name, "%.200s_Array_%ld",
|
((PyTypeObject *)itemtype)->tp_name, (long)length);
|
#endif
|
|
result = PyObject_CallFunction((PyObject *)&PyCArrayType_Type,
|
"s(O){s:n,s:O}",
|
name,
|
&PyCArray_Type,
|
"_length_",
|
length,
|
"_type_",
|
itemtype
|
);
|
if (result == NULL) {
|
Py_DECREF(key);
|
return NULL;
|
}
|
if (-1 == PyDict_SetItemProxy(cache, key, result)) {
|
Py_DECREF(key);
|
Py_DECREF(result);
|
return NULL;
|
}
|
Py_DECREF(key);
|
return result;
|
}
|
|
|
/******************************************************************/
|
/*
|
Simple_Type
|
*/
|
|
static int
|
Simple_set_value(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored))
|
{
|
PyObject *result;
|
StgDictObject *dict = PyObject_stgdict((PyObject *)self);
|
|
if (value == NULL) {
|
PyErr_SetString(PyExc_TypeError,
|
"can't delete attribute");
|
return -1;
|
}
|
assert(dict); /* Cannot be NULL for CDataObject instances */
|
assert(dict->setfunc);
|
result = dict->setfunc(self->b_ptr, value, dict->size);
|
if (!result)
|
return -1;
|
|
/* consumes the refcount the setfunc returns */
|
return KeepRef(self, 0, result);
|
}
|
|
static int
|
Simple_init(CDataObject *self, PyObject *args, PyObject *kw)
|
{
|
PyObject *value = NULL;
|
if (!PyArg_UnpackTuple(args, "__init__", 0, 1, &value))
|
return -1;
|
if (value)
|
return Simple_set_value(self, value, NULL);
|
return 0;
|
}
|
|
static PyObject *
|
Simple_get_value(CDataObject *self, void *Py_UNUSED(ignored))
|
{
|
StgDictObject *dict;
|
dict = PyObject_stgdict((PyObject *)self);
|
assert(dict); /* Cannot be NULL for CDataObject instances */
|
assert(dict->getfunc);
|
return dict->getfunc(self->b_ptr, self->b_size);
|
}
|
|
static PyGetSetDef Simple_getsets[] = {
|
{ "value", (getter)Simple_get_value, (setter)Simple_set_value,
|
"current value", NULL },
|
{ NULL, NULL }
|
};
|
|
static PyObject *
|
Simple_from_outparm(PyObject *self, PyObject *args)
|
{
|
if (_ctypes_simple_instance((PyObject *)Py_TYPE(self))) {
|
Py_INCREF(self);
|
return self;
|
}
|
/* call stgdict->getfunc */
|
return Simple_get_value((CDataObject *)self, NULL);
|
}
|
|
static PyMethodDef Simple_methods[] = {
|
{ "__ctypes_from_outparam__", Simple_from_outparm, METH_NOARGS, },
|
{ NULL, NULL },
|
};
|
|
static int Simple_bool(CDataObject *self)
|
{
|
return memcmp(self->b_ptr, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", self->b_size);
|
}
|
|
static PyNumberMethods Simple_as_number = {
|
0, /* nb_add */
|
0, /* nb_subtract */
|
0, /* nb_multiply */
|
0, /* nb_remainder */
|
0, /* nb_divmod */
|
0, /* nb_power */
|
0, /* nb_negative */
|
0, /* nb_positive */
|
0, /* nb_absolute */
|
(inquiry)Simple_bool, /* nb_bool */
|
};
|
|
/* "%s(%s)" % (self.__class__.__name__, self.value) */
|
static PyObject *
|
Simple_repr(CDataObject *self)
|
{
|
PyObject *val, *result;
|
|
if (Py_TYPE(self)->tp_base != &Simple_Type) {
|
return PyUnicode_FromFormat("<%s object at %p>",
|
Py_TYPE(self)->tp_name, self);
|
}
|
|
val = Simple_get_value(self, NULL);
|
if (val == NULL)
|
return NULL;
|
|
result = PyUnicode_FromFormat("%s(%R)",
|
Py_TYPE(self)->tp_name, val);
|
Py_DECREF(val);
|
return result;
|
}
|
|
static PyTypeObject Simple_Type = {
|
PyVarObject_HEAD_INIT(NULL, 0)
|
"_ctypes._SimpleCData",
|
sizeof(CDataObject), /* tp_basicsize */
|
0, /* tp_itemsize */
|
0, /* tp_dealloc */
|
0, /* tp_print */
|
0, /* tp_getattr */
|
0, /* tp_setattr */
|
0, /* tp_reserved */
|
(reprfunc)&Simple_repr, /* tp_repr */
|
&Simple_as_number, /* 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 */
|
&PyCData_as_buffer, /* tp_as_buffer */
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
"XXX to be provided", /* tp_doc */
|
(traverseproc)PyCData_traverse, /* tp_traverse */
|
(inquiry)PyCData_clear, /* tp_clear */
|
0, /* tp_richcompare */
|
0, /* tp_weaklistoffset */
|
0, /* tp_iter */
|
0, /* tp_iternext */
|
Simple_methods, /* tp_methods */
|
0, /* tp_members */
|
Simple_getsets, /* tp_getset */
|
0, /* tp_base */
|
0, /* tp_dict */
|
0, /* tp_descr_get */
|
0, /* tp_descr_set */
|
0, /* tp_dictoffset */
|
(initproc)Simple_init, /* tp_init */
|
0, /* tp_alloc */
|
GenericPyCData_new, /* tp_new */
|
0, /* tp_free */
|
};
|
|
/******************************************************************/
|
/*
|
PyCPointer_Type
|
*/
|
static PyObject *
|
Pointer_item(PyObject *myself, Py_ssize_t index)
|
{
|
CDataObject *self = (CDataObject *)myself;
|
Py_ssize_t size;
|
Py_ssize_t offset;
|
StgDictObject *stgdict, *itemdict;
|
PyObject *proto;
|
|
if (*(void **)self->b_ptr == NULL) {
|
PyErr_SetString(PyExc_ValueError,
|
"NULL pointer access");
|
return NULL;
|
}
|
|
stgdict = PyObject_stgdict((PyObject *)self);
|
assert(stgdict); /* Cannot be NULL for pointer object instances */
|
|
proto = stgdict->proto;
|
assert(proto);
|
itemdict = PyType_stgdict(proto);
|
assert(itemdict); /* proto is the item type of the pointer, a ctypes
|
type, so this cannot be NULL */
|
|
size = itemdict->size;
|
offset = index * itemdict->size;
|
|
return PyCData_get(proto, stgdict->getfunc, (PyObject *)self,
|
index, size, (*(char **)self->b_ptr) + offset);
|
}
|
|
static int
|
Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value)
|
{
|
CDataObject *self = (CDataObject *)myself;
|
Py_ssize_t size;
|
Py_ssize_t offset;
|
StgDictObject *stgdict, *itemdict;
|
PyObject *proto;
|
|
if (value == NULL) {
|
PyErr_SetString(PyExc_TypeError,
|
"Pointer does not support item deletion");
|
return -1;
|
}
|
|
if (*(void **)self->b_ptr == NULL) {
|
PyErr_SetString(PyExc_ValueError,
|
"NULL pointer access");
|
return -1;
|
}
|
|
stgdict = PyObject_stgdict((PyObject *)self);
|
assert(stgdict); /* Cannot be NULL for pointer instances */
|
|
proto = stgdict->proto;
|
assert(proto);
|
|
itemdict = PyType_stgdict(proto);
|
assert(itemdict); /* Cannot be NULL because the itemtype of a pointer
|
is always a ctypes type */
|
|
size = itemdict->size;
|
offset = index * itemdict->size;
|
|
return PyCData_set((PyObject *)self, proto, stgdict->setfunc, value,
|
index, size, (*(char **)self->b_ptr) + offset);
|
}
|
|
static PyObject *
|
Pointer_get_contents(CDataObject *self, void *closure)
|
{
|
StgDictObject *stgdict;
|
|
if (*(void **)self->b_ptr == NULL) {
|
PyErr_SetString(PyExc_ValueError,
|
"NULL pointer access");
|
return NULL;
|
}
|
|
stgdict = PyObject_stgdict((PyObject *)self);
|
assert(stgdict); /* Cannot be NULL for pointer instances */
|
return PyCData_FromBaseObj(stgdict->proto,
|
(PyObject *)self, 0,
|
*(void **)self->b_ptr);
|
}
|
|
static int
|
Pointer_set_contents(CDataObject *self, PyObject *value, void *closure)
|
{
|
StgDictObject *stgdict;
|
CDataObject *dst;
|
PyObject *keep;
|
|
if (value == NULL) {
|
PyErr_SetString(PyExc_TypeError,
|
"Pointer does not support item deletion");
|
return -1;
|
}
|
stgdict = PyObject_stgdict((PyObject *)self);
|
assert(stgdict); /* Cannot be NULL for pointer instances */
|
assert(stgdict->proto);
|
if (!CDataObject_Check(value)) {
|
int res = PyObject_IsInstance(value, stgdict->proto);
|
if (res == -1)
|
return -1;
|
if (!res) {
|
PyErr_Format(PyExc_TypeError,
|
"expected %s instead of %s",
|
((PyTypeObject *)(stgdict->proto))->tp_name,
|
Py_TYPE(value)->tp_name);
|
return -1;
|
}
|
}
|
|
dst = (CDataObject *)value;
|
*(void **)self->b_ptr = dst->b_ptr;
|
|
/*
|
A Pointer instance must keep the value it points to alive. So, a
|
pointer instance has b_length set to 2 instead of 1, and we set
|
'value' itself as the second item of the b_objects list, additionally.
|
*/
|
Py_INCREF(value);
|
if (-1 == KeepRef(self, 1, value))
|
return -1;
|
|
keep = GetKeepedObjects(dst);
|
if (keep == NULL)
|
return -1;
|
|
Py_INCREF(keep);
|
return KeepRef(self, 0, keep);
|
}
|
|
static PyGetSetDef Pointer_getsets[] = {
|
{ "contents", (getter)Pointer_get_contents,
|
(setter)Pointer_set_contents,
|
"the object this pointer points to (read-write)", NULL },
|
{ NULL, NULL }
|
};
|
|
static int
|
Pointer_init(CDataObject *self, PyObject *args, PyObject *kw)
|
{
|
PyObject *value = NULL;
|
|
if (!PyArg_UnpackTuple(args, "POINTER", 0, 1, &value))
|
return -1;
|
if (value == NULL)
|
return 0;
|
return Pointer_set_contents(self, value, NULL);
|
}
|
|
static PyObject *
|
Pointer_new(PyTypeObject *type, PyObject *args, PyObject *kw)
|
{
|
StgDictObject *dict = PyType_stgdict((PyObject *)type);
|
if (!dict || !dict->proto) {
|
PyErr_SetString(PyExc_TypeError,
|
"Cannot create instance: has no _type_");
|
return NULL;
|
}
|
return GenericPyCData_new(type, args, kw);
|
}
|
|
static PyObject *
|
Pointer_subscript(PyObject *myself, PyObject *item)
|
{
|
CDataObject *self = (CDataObject *)myself;
|
if (PyIndex_Check(item)) {
|
Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
|
if (i == -1 && PyErr_Occurred())
|
return NULL;
|
return Pointer_item(myself, i);
|
}
|
else if (PySlice_Check(item)) {
|
PySliceObject *slice = (PySliceObject *)item;
|
Py_ssize_t start, stop, step;
|
PyObject *np;
|
StgDictObject *stgdict, *itemdict;
|
PyObject *proto;
|
Py_ssize_t i, len, cur;
|
|
/* Since pointers have no length, and we want to apply
|
different semantics to negative indices than normal
|
slicing, we have to dissect the slice object ourselves.*/
|
if (slice->step == Py_None) {
|
step = 1;
|
}
|
else {
|
step = PyNumber_AsSsize_t(slice->step,
|
PyExc_ValueError);
|
if (step == -1 && PyErr_Occurred())
|
return NULL;
|
if (step == 0) {
|
PyErr_SetString(PyExc_ValueError,
|
"slice step cannot be zero");
|
return NULL;
|
}
|
}
|
if (slice->start == Py_None) {
|
if (step < 0) {
|
PyErr_SetString(PyExc_ValueError,
|
"slice start is required "
|
"for step < 0");
|
return NULL;
|
}
|
start = 0;
|
}
|
else {
|
start = PyNumber_AsSsize_t(slice->start,
|
PyExc_ValueError);
|
if (start == -1 && PyErr_Occurred())
|
return NULL;
|
}
|
if (slice->stop == Py_None) {
|
PyErr_SetString(PyExc_ValueError,
|
"slice stop is required");
|
return NULL;
|
}
|
stop = PyNumber_AsSsize_t(slice->stop,
|
PyExc_ValueError);
|
if (stop == -1 && PyErr_Occurred())
|
return NULL;
|
if ((step > 0 && start > stop) ||
|
(step < 0 && start < stop))
|
len = 0;
|
else if (step > 0)
|
len = (stop - start - 1) / step + 1;
|
else
|
len = (stop - start + 1) / step + 1;
|
|
stgdict = PyObject_stgdict((PyObject *)self);
|
assert(stgdict); /* Cannot be NULL for pointer instances */
|
proto = stgdict->proto;
|
assert(proto);
|
itemdict = PyType_stgdict(proto);
|
assert(itemdict);
|
if (itemdict->getfunc == _ctypes_get_fielddesc("c")->getfunc) {
|
char *ptr = *(char **)self->b_ptr;
|
char *dest;
|
|
if (len <= 0)
|
return PyBytes_FromStringAndSize("", 0);
|
if (step == 1) {
|
return PyBytes_FromStringAndSize(ptr + start,
|
len);
|
}
|
dest = (char *)PyMem_Malloc(len);
|
if (dest == NULL)
|
return PyErr_NoMemory();
|
for (cur = start, i = 0; i < len; cur += step, i++) {
|
dest[i] = ptr[cur];
|
}
|
np = PyBytes_FromStringAndSize(dest, len);
|
PyMem_Free(dest);
|
return np;
|
}
|
#ifdef CTYPES_UNICODE
|
if (itemdict->getfunc == _ctypes_get_fielddesc("u")->getfunc) {
|
wchar_t *ptr = *(wchar_t **)self->b_ptr;
|
wchar_t *dest;
|
|
if (len <= 0)
|
return PyUnicode_New(0, 0);
|
if (step == 1) {
|
return PyUnicode_FromWideChar(ptr + start,
|
len);
|
}
|
dest = PyMem_New(wchar_t, len);
|
if (dest == NULL)
|
return PyErr_NoMemory();
|
for (cur = start, i = 0; i < len; cur += step, i++) {
|
dest[i] = ptr[cur];
|
}
|
np = PyUnicode_FromWideChar(dest, len);
|
PyMem_Free(dest);
|
return np;
|
}
|
#endif
|
|
np = PyList_New(len);
|
if (np == NULL)
|
return NULL;
|
|
for (cur = start, i = 0; i < len; cur += step, i++) {
|
PyObject *v = Pointer_item(myself, cur);
|
PyList_SET_ITEM(np, i, v);
|
}
|
return np;
|
}
|
else {
|
PyErr_SetString(PyExc_TypeError,
|
"Pointer indices must be integer");
|
return NULL;
|
}
|
}
|
|
static PySequenceMethods Pointer_as_sequence = {
|
0, /* inquiry sq_length; */
|
0, /* binaryfunc sq_concat; */
|
0, /* intargfunc sq_repeat; */
|
Pointer_item, /* intargfunc sq_item; */
|
0, /* intintargfunc sq_slice; */
|
Pointer_ass_item, /* intobjargproc sq_ass_item; */
|
0, /* intintobjargproc sq_ass_slice; */
|
0, /* objobjproc sq_contains; */
|
/* Added in release 2.0 */
|
0, /* binaryfunc sq_inplace_concat; */
|
0, /* intargfunc sq_inplace_repeat; */
|
};
|
|
static PyMappingMethods Pointer_as_mapping = {
|
0,
|
Pointer_subscript,
|
};
|
|
static int
|
Pointer_bool(CDataObject *self)
|
{
|
return (*(void **)self->b_ptr != NULL);
|
}
|
|
static PyNumberMethods Pointer_as_number = {
|
0, /* nb_add */
|
0, /* nb_subtract */
|
0, /* nb_multiply */
|
0, /* nb_remainder */
|
0, /* nb_divmod */
|
0, /* nb_power */
|
0, /* nb_negative */
|
0, /* nb_positive */
|
0, /* nb_absolute */
|
(inquiry)Pointer_bool, /* nb_bool */
|
};
|
|
PyTypeObject PyCPointer_Type = {
|
PyVarObject_HEAD_INIT(NULL, 0)
|
"_ctypes._Pointer",
|
sizeof(CDataObject), /* tp_basicsize */
|
0, /* tp_itemsize */
|
0, /* tp_dealloc */
|
0, /* tp_print */
|
0, /* tp_getattr */
|
0, /* tp_setattr */
|
0, /* tp_reserved */
|
0, /* tp_repr */
|
&Pointer_as_number, /* tp_as_number */
|
&Pointer_as_sequence, /* tp_as_sequence */
|
&Pointer_as_mapping, /* tp_as_mapping */
|
0, /* tp_hash */
|
0, /* tp_call */
|
0, /* tp_str */
|
0, /* tp_getattro */
|
0, /* tp_setattro */
|
&PyCData_as_buffer, /* tp_as_buffer */
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
"XXX to be provided", /* tp_doc */
|
(traverseproc)PyCData_traverse, /* tp_traverse */
|
(inquiry)PyCData_clear, /* tp_clear */
|
0, /* tp_richcompare */
|
0, /* tp_weaklistoffset */
|
0, /* tp_iter */
|
0, /* tp_iternext */
|
0, /* tp_methods */
|
0, /* tp_members */
|
Pointer_getsets, /* tp_getset */
|
0, /* tp_base */
|
0, /* tp_dict */
|
0, /* tp_descr_get */
|
0, /* tp_descr_set */
|
0, /* tp_dictoffset */
|
(initproc)Pointer_init, /* tp_init */
|
0, /* tp_alloc */
|
Pointer_new, /* tp_new */
|
0, /* tp_free */
|
};
|
|
|
/******************************************************************/
|
/*
|
* Module initialization.
|
*/
|
|
static const char module_docs[] =
|
"Create and manipulate C compatible data types in Python.";
|
|
#ifdef MS_WIN32
|
|
static const char comerror_doc[] = "Raised when a COM method call failed.";
|
|
int
|
comerror_init(PyObject *self, PyObject *args, PyObject *kwds)
|
{
|
PyObject *hresult, *text, *details;
|
PyObject *a;
|
int status;
|
|
if (!_PyArg_NoKeywords(Py_TYPE(self)->tp_name, kwds))
|
return -1;
|
|
if (!PyArg_ParseTuple(args, "OOO:COMError", &hresult, &text, &details))
|
return -1;
|
|
a = PySequence_GetSlice(args, 1, PyTuple_GET_SIZE(args));
|
if (!a)
|
return -1;
|
status = PyObject_SetAttrString(self, "args", a);
|
Py_DECREF(a);
|
if (status < 0)
|
return -1;
|
|
if (PyObject_SetAttrString(self, "hresult", hresult) < 0)
|
return -1;
|
|
if (PyObject_SetAttrString(self, "text", text) < 0)
|
return -1;
|
|
if (PyObject_SetAttrString(self, "details", details) < 0)
|
return -1;
|
|
Py_INCREF(args);
|
Py_SETREF(((PyBaseExceptionObject *)self)->args, args);
|
|
return 0;
|
}
|
|
static PyTypeObject PyComError_Type = {
|
PyVarObject_HEAD_INIT(NULL, 0)
|
"_ctypes.COMError", /* tp_name */
|
sizeof(PyBaseExceptionObject), /* tp_basicsize */
|
0, /* tp_itemsize */
|
0, /* tp_dealloc */
|
0, /* tp_print */
|
0, /* tp_getattr */
|
0, /* tp_setattr */
|
0, /* tp_reserved */
|
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 | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
PyDoc_STR(comerror_doc), /* 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 */
|
(initproc)comerror_init, /* tp_init */
|
0, /* tp_alloc */
|
0, /* tp_new */
|
};
|
|
|
static int
|
create_comerror(void)
|
{
|
PyComError_Type.tp_base = (PyTypeObject*)PyExc_Exception;
|
if (PyType_Ready(&PyComError_Type) < 0)
|
return -1;
|
Py_INCREF(&PyComError_Type);
|
ComError = (PyObject*)&PyComError_Type;
|
return 0;
|
}
|
|
#endif
|
|
static PyObject *
|
string_at(const char *ptr, int size)
|
{
|
if (size == -1)
|
return PyBytes_FromStringAndSize(ptr, strlen(ptr));
|
return PyBytes_FromStringAndSize(ptr, size);
|
}
|
|
static int
|
cast_check_pointertype(PyObject *arg)
|
{
|
StgDictObject *dict;
|
|
if (PyCPointerTypeObject_Check(arg))
|
return 1;
|
if (PyCFuncPtrTypeObject_Check(arg))
|
return 1;
|
dict = PyType_stgdict(arg);
|
if (dict != NULL && dict->proto != NULL) {
|
if (PyUnicode_Check(dict->proto)
|
&& (strchr("sPzUZXO", PyUnicode_AsUTF8(dict->proto)[0]))) {
|
/* simple pointer types, c_void_p, c_wchar_p, BSTR, ... */
|
return 1;
|
}
|
}
|
PyErr_Format(PyExc_TypeError,
|
"cast() argument 2 must be a pointer type, not %s",
|
PyType_Check(arg)
|
? ((PyTypeObject *)arg)->tp_name
|
: Py_TYPE(arg)->tp_name);
|
return 0;
|
}
|
|
static PyObject *
|
cast(void *ptr, PyObject *src, PyObject *ctype)
|
{
|
CDataObject *result;
|
if (0 == cast_check_pointertype(ctype))
|
return NULL;
|
result = (CDataObject *)_PyObject_CallNoArg(ctype);
|
if (result == NULL)
|
return NULL;
|
|
/*
|
The casted objects '_objects' member:
|
|
It must certainly contain the source objects one.
|
It must contain the source object itself.
|
*/
|
if (CDataObject_Check(src)) {
|
CDataObject *obj = (CDataObject *)src;
|
CDataObject *container;
|
|
/* PyCData_GetContainer will initialize src.b_objects, we need
|
this so it can be shared */
|
container = PyCData_GetContainer(obj);
|
if (container == NULL)
|
goto failed;
|
|
/* But we need a dictionary! */
|
if (obj->b_objects == Py_None) {
|
Py_DECREF(Py_None);
|
obj->b_objects = PyDict_New();
|
if (obj->b_objects == NULL)
|
goto failed;
|
}
|
Py_XINCREF(obj->b_objects);
|
result->b_objects = obj->b_objects;
|
if (result->b_objects && PyDict_CheckExact(result->b_objects)) {
|
PyObject *index;
|
int rc;
|
index = PyLong_FromVoidPtr((void *)src);
|
if (index == NULL)
|
goto failed;
|
rc = PyDict_SetItem(result->b_objects, index, src);
|
Py_DECREF(index);
|
if (rc == -1)
|
goto failed;
|
}
|
}
|
/* Should we assert that result is a pointer type? */
|
memcpy(result->b_ptr, &ptr, sizeof(void *));
|
return (PyObject *)result;
|
|
failed:
|
Py_DECREF(result);
|
return NULL;
|
}
|
|
#ifdef CTYPES_UNICODE
|
static PyObject *
|
wstring_at(const wchar_t *ptr, int size)
|
{
|
Py_ssize_t ssize = size;
|
if (ssize == -1)
|
ssize = wcslen(ptr);
|
return PyUnicode_FromWideChar(ptr, ssize);
|
}
|
#endif
|
|
|
static struct PyModuleDef _ctypesmodule = {
|
PyModuleDef_HEAD_INIT,
|
"_ctypes",
|
module_docs,
|
-1,
|
_ctypes_module_methods,
|
NULL,
|
NULL,
|
NULL,
|
NULL
|
};
|
|
PyMODINIT_FUNC
|
PyInit__ctypes(void)
|
{
|
PyObject *m;
|
|
/* Note:
|
ob_type is the metatype (the 'type'), defaults to PyType_Type,
|
tp_base is the base type, defaults to 'object' aka PyBaseObject_Type.
|
*/
|
PyEval_InitThreads();
|
m = PyModule_Create(&_ctypesmodule);
|
if (!m)
|
return NULL;
|
|
_ctypes_ptrtype_cache = PyDict_New();
|
if (_ctypes_ptrtype_cache == NULL)
|
return NULL;
|
|
PyModule_AddObject(m, "_pointer_type_cache", (PyObject *)_ctypes_ptrtype_cache);
|
|
_unpickle = PyObject_GetAttrString(m, "_unpickle");
|
if (_unpickle == NULL)
|
return NULL;
|
|
if (PyType_Ready(&PyCArg_Type) < 0)
|
return NULL;
|
|
if (PyType_Ready(&PyCThunk_Type) < 0)
|
return NULL;
|
|
/* StgDict is derived from PyDict_Type */
|
PyCStgDict_Type.tp_base = &PyDict_Type;
|
if (PyType_Ready(&PyCStgDict_Type) < 0)
|
return NULL;
|
|
/*************************************************
|
*
|
* Metaclasses
|
*/
|
|
PyCStructType_Type.tp_base = &PyType_Type;
|
if (PyType_Ready(&PyCStructType_Type) < 0)
|
return NULL;
|
|
UnionType_Type.tp_base = &PyType_Type;
|
if (PyType_Ready(&UnionType_Type) < 0)
|
return NULL;
|
|
PyCPointerType_Type.tp_base = &PyType_Type;
|
if (PyType_Ready(&PyCPointerType_Type) < 0)
|
return NULL;
|
|
PyCArrayType_Type.tp_base = &PyType_Type;
|
if (PyType_Ready(&PyCArrayType_Type) < 0)
|
return NULL;
|
|
PyCSimpleType_Type.tp_base = &PyType_Type;
|
if (PyType_Ready(&PyCSimpleType_Type) < 0)
|
return NULL;
|
|
PyCFuncPtrType_Type.tp_base = &PyType_Type;
|
if (PyType_Ready(&PyCFuncPtrType_Type) < 0)
|
return NULL;
|
|
/*************************************************
|
*
|
* Classes using a custom metaclass
|
*/
|
|
if (PyType_Ready(&PyCData_Type) < 0)
|
return NULL;
|
|
Py_TYPE(&Struct_Type) = &PyCStructType_Type;
|
Struct_Type.tp_base = &PyCData_Type;
|
if (PyType_Ready(&Struct_Type) < 0)
|
return NULL;
|
Py_INCREF(&Struct_Type);
|
PyModule_AddObject(m, "Structure", (PyObject *)&Struct_Type);
|
|
Py_TYPE(&Union_Type) = &UnionType_Type;
|
Union_Type.tp_base = &PyCData_Type;
|
if (PyType_Ready(&Union_Type) < 0)
|
return NULL;
|
Py_INCREF(&Union_Type);
|
PyModule_AddObject(m, "Union", (PyObject *)&Union_Type);
|
|
Py_TYPE(&PyCPointer_Type) = &PyCPointerType_Type;
|
PyCPointer_Type.tp_base = &PyCData_Type;
|
if (PyType_Ready(&PyCPointer_Type) < 0)
|
return NULL;
|
Py_INCREF(&PyCPointer_Type);
|
PyModule_AddObject(m, "_Pointer", (PyObject *)&PyCPointer_Type);
|
|
Py_TYPE(&PyCArray_Type) = &PyCArrayType_Type;
|
PyCArray_Type.tp_base = &PyCData_Type;
|
if (PyType_Ready(&PyCArray_Type) < 0)
|
return NULL;
|
Py_INCREF(&PyCArray_Type);
|
PyModule_AddObject(m, "Array", (PyObject *)&PyCArray_Type);
|
|
Py_TYPE(&Simple_Type) = &PyCSimpleType_Type;
|
Simple_Type.tp_base = &PyCData_Type;
|
if (PyType_Ready(&Simple_Type) < 0)
|
return NULL;
|
Py_INCREF(&Simple_Type);
|
PyModule_AddObject(m, "_SimpleCData", (PyObject *)&Simple_Type);
|
|
Py_TYPE(&PyCFuncPtr_Type) = &PyCFuncPtrType_Type;
|
PyCFuncPtr_Type.tp_base = &PyCData_Type;
|
if (PyType_Ready(&PyCFuncPtr_Type) < 0)
|
return NULL;
|
Py_INCREF(&PyCFuncPtr_Type);
|
PyModule_AddObject(m, "CFuncPtr", (PyObject *)&PyCFuncPtr_Type);
|
|
/*************************************************
|
*
|
* Simple classes
|
*/
|
|
/* PyCField_Type is derived from PyBaseObject_Type */
|
if (PyType_Ready(&PyCField_Type) < 0)
|
return NULL;
|
|
/*************************************************
|
*
|
* Other stuff
|
*/
|
|
DictRemover_Type.tp_new = PyType_GenericNew;
|
if (PyType_Ready(&DictRemover_Type) < 0)
|
return NULL;
|
|
#ifdef MS_WIN32
|
if (create_comerror() < 0)
|
return NULL;
|
PyModule_AddObject(m, "COMError", ComError);
|
|
PyModule_AddObject(m, "FUNCFLAG_HRESULT", PyLong_FromLong(FUNCFLAG_HRESULT));
|
PyModule_AddObject(m, "FUNCFLAG_STDCALL", PyLong_FromLong(FUNCFLAG_STDCALL));
|
#endif
|
PyModule_AddObject(m, "FUNCFLAG_CDECL", PyLong_FromLong(FUNCFLAG_CDECL));
|
PyModule_AddObject(m, "FUNCFLAG_USE_ERRNO", PyLong_FromLong(FUNCFLAG_USE_ERRNO));
|
PyModule_AddObject(m, "FUNCFLAG_USE_LASTERROR", PyLong_FromLong(FUNCFLAG_USE_LASTERROR));
|
PyModule_AddObject(m, "FUNCFLAG_PYTHONAPI", PyLong_FromLong(FUNCFLAG_PYTHONAPI));
|
PyModule_AddStringConstant(m, "__version__", "1.1.0");
|
|
PyModule_AddObject(m, "_memmove_addr", PyLong_FromVoidPtr(memmove));
|
PyModule_AddObject(m, "_memset_addr", PyLong_FromVoidPtr(memset));
|
PyModule_AddObject(m, "_string_at_addr", PyLong_FromVoidPtr(string_at));
|
PyModule_AddObject(m, "_cast_addr", PyLong_FromVoidPtr(cast));
|
#ifdef CTYPES_UNICODE
|
PyModule_AddObject(m, "_wstring_at_addr", PyLong_FromVoidPtr(wstring_at));
|
#endif
|
|
/* If RTLD_LOCAL is not defined (Windows!), set it to zero. */
|
#if !HAVE_DECL_RTLD_LOCAL
|
#define RTLD_LOCAL 0
|
#endif
|
|
/* If RTLD_GLOBAL is not defined (cygwin), set it to the same value as
|
RTLD_LOCAL.
|
*/
|
#if !HAVE_DECL_RTLD_GLOBAL
|
#define RTLD_GLOBAL RTLD_LOCAL
|
#endif
|
|
PyModule_AddObject(m, "RTLD_LOCAL", PyLong_FromLong(RTLD_LOCAL));
|
PyModule_AddObject(m, "RTLD_GLOBAL", PyLong_FromLong(RTLD_GLOBAL));
|
|
PyExc_ArgError = PyErr_NewException("ctypes.ArgumentError", NULL, NULL);
|
if (PyExc_ArgError) {
|
Py_INCREF(PyExc_ArgError);
|
PyModule_AddObject(m, "ArgumentError", PyExc_ArgError);
|
}
|
return m;
|
}
|
|
/*
|
Local Variables:
|
compile-command: "cd .. && python setup.py -q build -g && python setup.py -q build install --home ~"
|
End:
|
*/
|