| #include "Python.h" |
| #include <ffi.h> |
| #ifdef MS_WIN32 |
| #include <windows.h> |
| #include <malloc.h> |
| #endif |
| #include "ctypes.h" |
| |
| /******************************************************************/ |
| /* |
| StdDict - a dictionary subclass, containing additional C accessible fields |
| |
| XXX blabla more |
| */ |
| |
| /* Seems we need this, otherwise we get problems when calling |
| * PyDict_SetItem() (ma_lookup is NULL) |
| */ |
| static int |
| PyCStgDict_init(StgDictObject *self, PyObject *args, PyObject *kwds) |
| { |
| if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0) |
| return -1; |
| self->format = NULL; |
| self->ndim = 0; |
| self->shape = NULL; |
| return 0; |
| } |
| |
| static int |
| PyCStgDict_clear(StgDictObject *self) |
| { |
| Py_CLEAR(self->proto); |
| Py_CLEAR(self->argtypes); |
| Py_CLEAR(self->converters); |
| Py_CLEAR(self->restype); |
| Py_CLEAR(self->checker); |
| return 0; |
| } |
| |
| static void |
| PyCStgDict_dealloc(StgDictObject *self) |
| { |
| PyCStgDict_clear(self); |
| PyMem_Free(self->format); |
| PyMem_Free(self->shape); |
| PyMem_Free(self->ffi_type_pointer.elements); |
| PyDict_Type.tp_dealloc((PyObject *)self); |
| } |
| |
| static PyObject * |
| PyCStgDict_sizeof(StgDictObject *self, void *unused) |
| { |
| Py_ssize_t res; |
| |
| res = _PyDict_SizeOf((PyDictObject *)self); |
| res += sizeof(StgDictObject) - sizeof(PyDictObject); |
| if (self->format) |
| res += strlen(self->format) + 1; |
| res += self->ndim * sizeof(Py_ssize_t); |
| if (self->ffi_type_pointer.elements) |
| res += (self->length + 1) * sizeof(ffi_type *); |
| return PyLong_FromSsize_t(res); |
| } |
| |
| int |
| PyCStgDict_clone(StgDictObject *dst, StgDictObject *src) |
| { |
| char *d, *s; |
| Py_ssize_t size; |
| |
| PyCStgDict_clear(dst); |
| PyMem_Free(dst->ffi_type_pointer.elements); |
| PyMem_Free(dst->format); |
| dst->format = NULL; |
| PyMem_Free(dst->shape); |
| dst->shape = NULL; |
| dst->ffi_type_pointer.elements = NULL; |
| |
| d = (char *)dst; |
| s = (char *)src; |
| memcpy(d + sizeof(PyDictObject), |
| s + sizeof(PyDictObject), |
| sizeof(StgDictObject) - sizeof(PyDictObject)); |
| |
| Py_XINCREF(dst->proto); |
| Py_XINCREF(dst->argtypes); |
| Py_XINCREF(dst->converters); |
| Py_XINCREF(dst->restype); |
| Py_XINCREF(dst->checker); |
| |
| if (src->format) { |
| dst->format = PyMem_Malloc(strlen(src->format) + 1); |
| if (dst->format == NULL) { |
| PyErr_NoMemory(); |
| return -1; |
| } |
| strcpy(dst->format, src->format); |
| } |
| if (src->shape) { |
| dst->shape = PyMem_Malloc(sizeof(Py_ssize_t) * src->ndim); |
| if (dst->shape == NULL) { |
| PyErr_NoMemory(); |
| return -1; |
| } |
| memcpy(dst->shape, src->shape, |
| sizeof(Py_ssize_t) * src->ndim); |
| } |
| |
| if (src->ffi_type_pointer.elements == NULL) |
| return 0; |
| size = sizeof(ffi_type *) * (src->length + 1); |
| dst->ffi_type_pointer.elements = PyMem_Malloc(size); |
| if (dst->ffi_type_pointer.elements == NULL) { |
| PyErr_NoMemory(); |
| return -1; |
| } |
| memcpy(dst->ffi_type_pointer.elements, |
| src->ffi_type_pointer.elements, |
| size); |
| return 0; |
| } |
| |
| static struct PyMethodDef PyCStgDict_methods[] = { |
| {"__sizeof__", (PyCFunction)PyCStgDict_sizeof, METH_NOARGS}, |
| {NULL, NULL} /* sentinel */ |
| }; |
| |
| PyTypeObject PyCStgDict_Type = { |
| PyVarObject_HEAD_INIT(NULL, 0) |
| "StgDict", |
| sizeof(StgDictObject), |
| 0, |
| (destructor)PyCStgDict_dealloc, /* tp_dealloc */ |
| 0, /* tp_vectorcall_offset */ |
| 0, /* tp_getattr */ |
| 0, /* tp_setattr */ |
| 0, /* tp_as_async */ |
| 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 */ |
| 0, /* tp_doc */ |
| 0, /* tp_traverse */ |
| 0, /* tp_clear */ |
| 0, /* tp_richcompare */ |
| 0, /* tp_weaklistoffset */ |
| 0, /* tp_iter */ |
| 0, /* tp_iternext */ |
| PyCStgDict_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 */ |
| (initproc)PyCStgDict_init, /* tp_init */ |
| 0, /* tp_alloc */ |
| 0, /* tp_new */ |
| 0, /* tp_free */ |
| }; |
| |
| /* May return NULL, but does not set an exception! */ |
| StgDictObject * |
| PyType_stgdict(PyObject *obj) |
| { |
| PyTypeObject *type; |
| |
| if (!PyType_Check(obj)) |
| return NULL; |
| type = (PyTypeObject *)obj; |
| if (!type->tp_dict || !PyCStgDict_CheckExact(type->tp_dict)) |
| return NULL; |
| return (StgDictObject *)type->tp_dict; |
| } |
| |
| /* May return NULL, but does not set an exception! */ |
| /* |
| This function should be as fast as possible, so we don't call PyType_stgdict |
| above but inline the code, and avoid the PyType_Check(). |
| */ |
| StgDictObject * |
| PyObject_stgdict(PyObject *self) |
| { |
| PyTypeObject *type = self->ob_type; |
| if (!type->tp_dict || !PyCStgDict_CheckExact(type->tp_dict)) |
| return NULL; |
| return (StgDictObject *)type->tp_dict; |
| } |
| |
| /* descr is the descriptor for a field marked as anonymous. Get all the |
| _fields_ descriptors from descr->proto, create new descriptors with offset |
| and index adjusted, and stuff them into type. |
| */ |
| static int |
| MakeFields(PyObject *type, CFieldObject *descr, |
| Py_ssize_t index, Py_ssize_t offset) |
| { |
| Py_ssize_t i; |
| PyObject *fields; |
| PyObject *fieldlist; |
| |
| fields = PyObject_GetAttrString(descr->proto, "_fields_"); |
| if (fields == NULL) |
| return -1; |
| fieldlist = PySequence_Fast(fields, "_fields_ must be a sequence"); |
| Py_DECREF(fields); |
| if (fieldlist == NULL) |
| return -1; |
| |
| for (i = 0; i < PySequence_Fast_GET_SIZE(fieldlist); ++i) { |
| PyObject *pair = PySequence_Fast_GET_ITEM(fieldlist, i); /* borrowed */ |
| PyObject *fname, *ftype, *bits; |
| CFieldObject *fdescr; |
| CFieldObject *new_descr; |
| /* Convert to PyArg_UnpackTuple... */ |
| if (!PyArg_ParseTuple(pair, "OO|O", &fname, &ftype, &bits)) { |
| Py_DECREF(fieldlist); |
| return -1; |
| } |
| fdescr = (CFieldObject *)PyObject_GetAttr(descr->proto, fname); |
| if (fdescr == NULL) { |
| Py_DECREF(fieldlist); |
| return -1; |
| } |
| if (Py_TYPE(fdescr) != &PyCField_Type) { |
| PyErr_SetString(PyExc_TypeError, "unexpected type"); |
| Py_DECREF(fdescr); |
| Py_DECREF(fieldlist); |
| return -1; |
| } |
| if (fdescr->anonymous) { |
| int rc = MakeFields(type, fdescr, |
| index + fdescr->index, |
| offset + fdescr->offset); |
| Py_DECREF(fdescr); |
| if (rc == -1) { |
| Py_DECREF(fieldlist); |
| return -1; |
| } |
| continue; |
| } |
| new_descr = (CFieldObject *)_PyObject_CallNoArg((PyObject *)&PyCField_Type); |
| if (new_descr == NULL) { |
| Py_DECREF(fdescr); |
| Py_DECREF(fieldlist); |
| return -1; |
| } |
| assert(Py_TYPE(new_descr) == &PyCField_Type); |
| new_descr->size = fdescr->size; |
| new_descr->offset = fdescr->offset + offset; |
| new_descr->index = fdescr->index + index; |
| new_descr->proto = fdescr->proto; |
| Py_XINCREF(new_descr->proto); |
| new_descr->getfunc = fdescr->getfunc; |
| new_descr->setfunc = fdescr->setfunc; |
| |
| Py_DECREF(fdescr); |
| |
| if (-1 == PyObject_SetAttr(type, fname, (PyObject *)new_descr)) { |
| Py_DECREF(fieldlist); |
| Py_DECREF(new_descr); |
| return -1; |
| } |
| Py_DECREF(new_descr); |
| } |
| Py_DECREF(fieldlist); |
| return 0; |
| } |
| |
| /* Iterate over the names in the type's _anonymous_ attribute, if present, |
| */ |
| static int |
| MakeAnonFields(PyObject *type) |
| { |
| _Py_IDENTIFIER(_anonymous_); |
| PyObject *anon; |
| PyObject *anon_names; |
| Py_ssize_t i; |
| |
| if (_PyObject_LookupAttrId(type, &PyId__anonymous_, &anon) < 0) { |
| return -1; |
| } |
| if (anon == NULL) { |
| return 0; |
| } |
| anon_names = PySequence_Fast(anon, "_anonymous_ must be a sequence"); |
| Py_DECREF(anon); |
| if (anon_names == NULL) |
| return -1; |
| |
| for (i = 0; i < PySequence_Fast_GET_SIZE(anon_names); ++i) { |
| PyObject *fname = PySequence_Fast_GET_ITEM(anon_names, i); /* borrowed */ |
| CFieldObject *descr = (CFieldObject *)PyObject_GetAttr(type, fname); |
| if (descr == NULL) { |
| Py_DECREF(anon_names); |
| return -1; |
| } |
| if (Py_TYPE(descr) != &PyCField_Type) { |
| PyErr_Format(PyExc_AttributeError, |
| "'%U' is specified in _anonymous_ but not in " |
| "_fields_", |
| fname); |
| Py_DECREF(anon_names); |
| Py_DECREF(descr); |
| return -1; |
| } |
| descr->anonymous = 1; |
| |
| /* descr is in the field descriptor. */ |
| if (-1 == MakeFields(type, (CFieldObject *)descr, |
| ((CFieldObject *)descr)->index, |
| ((CFieldObject *)descr)->offset)) { |
| Py_DECREF(descr); |
| Py_DECREF(anon_names); |
| return -1; |
| } |
| Py_DECREF(descr); |
| } |
| |
| Py_DECREF(anon_names); |
| return 0; |
| } |
| |
| /* |
| Retrieve the (optional) _pack_ attribute from a type, the _fields_ attribute, |
| and create an StgDictObject. Used for Structure and Union subclasses. |
| */ |
| int |
| PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct) |
| { |
| _Py_IDENTIFIER(_swappedbytes_); |
| _Py_IDENTIFIER(_use_broken_old_ctypes_structure_semantics_); |
| _Py_IDENTIFIER(_pack_); |
| StgDictObject *stgdict, *basedict; |
| Py_ssize_t len, offset, size, align, i; |
| Py_ssize_t union_size, total_align; |
| Py_ssize_t field_size = 0; |
| int bitofs; |
| PyObject *tmp; |
| int isPacked; |
| int pack; |
| Py_ssize_t ffi_ofs; |
| int big_endian; |
| int arrays_seen = 0; |
| |
| /* HACK Alert: I cannot be bothered to fix ctypes.com, so there has to |
| be a way to use the old, broken semantics: _fields_ are not extended |
| but replaced in subclasses. |
| |
| XXX Remove this in ctypes 1.0! |
| */ |
| int use_broken_old_ctypes_semantics; |
| |
| if (fields == NULL) |
| return 0; |
| |
| if (_PyObject_LookupAttrId(type, &PyId__swappedbytes_, &tmp) < 0) { |
| return -1; |
| } |
| if (tmp) { |
| Py_DECREF(tmp); |
| big_endian = !PY_BIG_ENDIAN; |
| } |
| else { |
| big_endian = PY_BIG_ENDIAN; |
| } |
| |
| if (_PyObject_LookupAttrId(type, |
| &PyId__use_broken_old_ctypes_structure_semantics_, &tmp) < 0) |
| { |
| return -1; |
| } |
| if (tmp) { |
| Py_DECREF(tmp); |
| use_broken_old_ctypes_semantics = 1; |
| } |
| else { |
| use_broken_old_ctypes_semantics = 0; |
| } |
| |
| if (_PyObject_LookupAttrId(type, &PyId__pack_, &tmp) < 0) { |
| return -1; |
| } |
| if (tmp) { |
| isPacked = 1; |
| pack = _PyLong_AsInt(tmp); |
| Py_DECREF(tmp); |
| if (pack < 0) { |
| if (!PyErr_Occurred() || |
| PyErr_ExceptionMatches(PyExc_TypeError) || |
| PyErr_ExceptionMatches(PyExc_OverflowError)) |
| { |
| PyErr_SetString(PyExc_ValueError, |
| "_pack_ must be a non-negative integer"); |
| } |
| return -1; |
| } |
| } |
| else { |
| isPacked = 0; |
| pack = 0; |
| } |
| |
| len = PySequence_Size(fields); |
| if (len == -1) { |
| if (PyErr_ExceptionMatches(PyExc_TypeError)) { |
| PyErr_SetString(PyExc_TypeError, |
| "'_fields_' must be a sequence of pairs"); |
| } |
| return -1; |
| } |
| |
| stgdict = PyType_stgdict(type); |
| if (!stgdict) |
| return -1; |
| /* If this structure/union is already marked final we cannot assign |
| _fields_ anymore. */ |
| |
| if (stgdict->flags & DICTFLAG_FINAL) {/* is final ? */ |
| PyErr_SetString(PyExc_AttributeError, |
| "_fields_ is final"); |
| return -1; |
| } |
| |
| if (stgdict->format) { |
| PyMem_Free(stgdict->format); |
| stgdict->format = NULL; |
| } |
| |
| if (stgdict->ffi_type_pointer.elements) |
| PyMem_Free(stgdict->ffi_type_pointer.elements); |
| |
| basedict = PyType_stgdict((PyObject *)((PyTypeObject *)type)->tp_base); |
| if (basedict) { |
| stgdict->flags |= (basedict->flags & |
| (TYPEFLAG_HASUNION | TYPEFLAG_HASBITFIELD)); |
| } |
| if (!isStruct) { |
| stgdict->flags |= TYPEFLAG_HASUNION; |
| } |
| if (basedict && !use_broken_old_ctypes_semantics) { |
| size = offset = basedict->size; |
| align = basedict->align; |
| union_size = 0; |
| total_align = align ? align : 1; |
| stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT; |
| stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *, basedict->length + len + 1); |
| if (stgdict->ffi_type_pointer.elements == NULL) { |
| PyErr_NoMemory(); |
| return -1; |
| } |
| memset(stgdict->ffi_type_pointer.elements, 0, |
| sizeof(ffi_type *) * (basedict->length + len + 1)); |
| if (basedict->length > 0) { |
| memcpy(stgdict->ffi_type_pointer.elements, |
| basedict->ffi_type_pointer.elements, |
| sizeof(ffi_type *) * (basedict->length)); |
| } |
| ffi_ofs = basedict->length; |
| } else { |
| offset = 0; |
| size = 0; |
| align = 0; |
| union_size = 0; |
| total_align = 1; |
| stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT; |
| stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *, len + 1); |
| if (stgdict->ffi_type_pointer.elements == NULL) { |
| PyErr_NoMemory(); |
| return -1; |
| } |
| memset(stgdict->ffi_type_pointer.elements, 0, |
| sizeof(ffi_type *) * (len + 1)); |
| ffi_ofs = 0; |
| } |
| |
| assert(stgdict->format == NULL); |
| if (isStruct && !isPacked) { |
| stgdict->format = _ctypes_alloc_format_string(NULL, "T{"); |
| } else { |
| /* PEP3118 doesn't support union, or packed structures (well, |
| only standard packing, but we don't support the pep for |
| that). Use 'B' for bytes. */ |
| stgdict->format = _ctypes_alloc_format_string(NULL, "B"); |
| } |
| if (stgdict->format == NULL) |
| return -1; |
| |
| #define realdict ((PyObject *)&stgdict->dict) |
| for (i = 0; i < len; ++i) { |
| PyObject *name = NULL, *desc = NULL; |
| PyObject *pair = PySequence_GetItem(fields, i); |
| PyObject *prop; |
| StgDictObject *dict; |
| int bitsize = 0; |
| |
| if (!pair || !PyArg_ParseTuple(pair, "UO|i", &name, &desc, &bitsize)) { |
| PyErr_SetString(PyExc_TypeError, |
| "'_fields_' must be a sequence of (name, C type) pairs"); |
| Py_XDECREF(pair); |
| return -1; |
| } |
| if (PyCArrayTypeObject_Check(desc)) |
| arrays_seen = 1; |
| dict = PyType_stgdict(desc); |
| if (dict == NULL) { |
| Py_DECREF(pair); |
| PyErr_Format(PyExc_TypeError, |
| "second item in _fields_ tuple (index %zd) must be a C type", |
| i); |
| return -1; |
| } |
| stgdict->ffi_type_pointer.elements[ffi_ofs + i] = &dict->ffi_type_pointer; |
| if (dict->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER)) |
| stgdict->flags |= TYPEFLAG_HASPOINTER; |
| stgdict->flags |= dict->flags & (TYPEFLAG_HASUNION | TYPEFLAG_HASBITFIELD); |
| dict->flags |= DICTFLAG_FINAL; /* mark field type final */ |
| if (PyTuple_Size(pair) == 3) { /* bits specified */ |
| stgdict->flags |= TYPEFLAG_HASBITFIELD; |
| switch(dict->ffi_type_pointer.type) { |
| case FFI_TYPE_UINT8: |
| case FFI_TYPE_UINT16: |
| case FFI_TYPE_UINT32: |
| case FFI_TYPE_SINT64: |
| case FFI_TYPE_UINT64: |
| break; |
| |
| case FFI_TYPE_SINT8: |
| case FFI_TYPE_SINT16: |
| case FFI_TYPE_SINT32: |
| if (dict->getfunc != _ctypes_get_fielddesc("c")->getfunc |
| #ifdef CTYPES_UNICODE |
| && dict->getfunc != _ctypes_get_fielddesc("u")->getfunc |
| #endif |
| ) |
| break; |
| /* else fall through */ |
| default: |
| PyErr_Format(PyExc_TypeError, |
| "bit fields not allowed for type %s", |
| ((PyTypeObject *)desc)->tp_name); |
| Py_DECREF(pair); |
| return -1; |
| } |
| if (bitsize <= 0 || bitsize > dict->size * 8) { |
| PyErr_SetString(PyExc_ValueError, |
| "number of bits invalid for bit field"); |
| Py_DECREF(pair); |
| return -1; |
| } |
| } else |
| bitsize = 0; |
| |
| if (isStruct && !isPacked) { |
| const char *fieldfmt = dict->format ? dict->format : "B"; |
| const char *fieldname = PyUnicode_AsUTF8(name); |
| char *ptr; |
| Py_ssize_t len; |
| char *buf; |
| |
| if (fieldname == NULL) |
| { |
| Py_DECREF(pair); |
| return -1; |
| } |
| |
| len = strlen(fieldname) + strlen(fieldfmt); |
| |
| buf = PyMem_Malloc(len + 2 + 1); |
| if (buf == NULL) { |
| Py_DECREF(pair); |
| PyErr_NoMemory(); |
| return -1; |
| } |
| sprintf(buf, "%s:%s:", fieldfmt, fieldname); |
| |
| ptr = stgdict->format; |
| if (dict->shape != NULL) { |
| stgdict->format = _ctypes_alloc_format_string_with_shape( |
| dict->ndim, dict->shape, stgdict->format, buf); |
| } else { |
| stgdict->format = _ctypes_alloc_format_string(stgdict->format, buf); |
| } |
| PyMem_Free(ptr); |
| PyMem_Free(buf); |
| |
| if (stgdict->format == NULL) { |
| Py_DECREF(pair); |
| return -1; |
| } |
| } |
| |
| if (isStruct) { |
| prop = PyCField_FromDesc(desc, i, |
| &field_size, bitsize, &bitofs, |
| &size, &offset, &align, |
| pack, big_endian); |
| } else /* union */ { |
| size = 0; |
| offset = 0; |
| align = 0; |
| prop = PyCField_FromDesc(desc, i, |
| &field_size, bitsize, &bitofs, |
| &size, &offset, &align, |
| pack, big_endian); |
| union_size = max(size, union_size); |
| } |
| total_align = max(align, total_align); |
| |
| if (!prop) { |
| Py_DECREF(pair); |
| return -1; |
| } |
| if (-1 == PyObject_SetAttr(type, name, prop)) { |
| Py_DECREF(prop); |
| Py_DECREF(pair); |
| return -1; |
| } |
| Py_DECREF(pair); |
| Py_DECREF(prop); |
| } |
| #undef realdict |
| |
| if (isStruct && !isPacked) { |
| char *ptr = stgdict->format; |
| stgdict->format = _ctypes_alloc_format_string(stgdict->format, "}"); |
| PyMem_Free(ptr); |
| if (stgdict->format == NULL) |
| return -1; |
| } |
| |
| if (!isStruct) |
| size = union_size; |
| |
| /* Adjust the size according to the alignment requirements */ |
| size = ((size + total_align - 1) / total_align) * total_align; |
| |
| stgdict->ffi_type_pointer.alignment = Py_SAFE_DOWNCAST(total_align, |
| Py_ssize_t, |
| unsigned short); |
| stgdict->ffi_type_pointer.size = size; |
| |
| stgdict->size = size; |
| stgdict->align = total_align; |
| stgdict->length = len; /* ADD ffi_ofs? */ |
| |
| #define MAX_STRUCT_SIZE 16 |
| |
| if (arrays_seen && (size <= MAX_STRUCT_SIZE)) { |
| /* |
| * See bpo-22273. Arrays are normally treated as pointers, which is |
| * fine when an array name is being passed as parameter, but not when |
| * passing structures by value that contain arrays. On 64-bit Linux, |
| * small structures passed by value are passed in registers, and in |
| * order to do this, libffi needs to know the true type of the array |
| * members of structs. Treating them as pointers breaks things. |
| * |
| * By small structures, we mean ones that are 16 bytes or less. In that |
| * case, there can't be more than 16 elements after unrolling arrays, |
| * as we (will) disallow bitfields. So we can collect the true ffi_type |
| * values in a fixed-size local array on the stack and, if any arrays |
| * were seen, replace the ffi_type_pointer.elements with a more |
| * accurate set, to allow libffi to marshal them into registers |
| * correctly. It means one more loop over the fields, but if we got |
| * here, the structure is small, so there aren't too many of those. |
| * |
| * Although the passing in registers is specific to 64-bit Linux, the |
| * array-in-struct vs. pointer problem is general. But we restrict the |
| * type transformation to small structs nonetheless. |
| * |
| * Note that although a union may be small in terms of memory usage, it |
| * could contain many overlapping declarations of arrays, e.g. |
| * |
| * union { |
| * unsigned int_8 foo [16]; |
| * unsigned uint_8 bar [16]; |
| * unsigned int_16 baz[8]; |
| * unsigned uint_16 bozz[8]; |
| * unsigned int_32 fizz[4]; |
| * unsigned uint_32 buzz[4]; |
| * } |
| * |
| * which is still only 16 bytes in size. We need to convert this into |
| * the following equivalent for libffi: |
| * |
| * union { |
| * struct { int_8 e1; int_8 e2; ... int_8 e_16; } f1; |
| * struct { uint_8 e1; uint_8 e2; ... uint_8 e_16; } f2; |
| * struct { int_16 e1; int_16 e2; ... int_16 e_8; } f3; |
| * struct { uint_16 e1; uint_16 e2; ... uint_16 e_8; } f4; |
| * struct { int_32 e1; int_32 e2; ... int_32 e_4; } f5; |
| * struct { uint_32 e1; uint_32 e2; ... uint_32 e_4; } f6; |
| * } |
| * |
| * So the struct/union needs setting up as follows: all non-array |
| * elements copied across as is, and all array elements replaced with |
| * an equivalent struct which has as many fields as the array has |
| * elements, plus one NULL pointer. |
| */ |
| |
| Py_ssize_t num_ffi_type_pointers = 0; /* for the dummy fields */ |
| Py_ssize_t num_ffi_types = 0; /* for the dummy structures */ |
| size_t alloc_size; /* total bytes to allocate */ |
| void *type_block; /* to hold all the type information needed */ |
| ffi_type **element_types; /* of this struct/union */ |
| ffi_type **dummy_types; /* of the dummy struct elements */ |
| ffi_type *structs; /* point to struct aliases of arrays */ |
| Py_ssize_t element_index; /* index into element_types for this */ |
| Py_ssize_t dummy_index = 0; /* index into dummy field pointers */ |
| Py_ssize_t struct_index = 0; /* index into dummy structs */ |
| |
| /* first pass to see how much memory to allocate */ |
| for (i = 0; i < len; ++i) { |
| PyObject *name, *desc; |
| PyObject *pair = PySequence_GetItem(fields, i); |
| StgDictObject *dict; |
| int bitsize = 0; |
| |
| if (pair == NULL) { |
| return -1; |
| } |
| if (!PyArg_ParseTuple(pair, "UO|i", &name, &desc, &bitsize)) { |
| PyErr_SetString(PyExc_TypeError, |
| "'_fields_' must be a sequence of (name, C type) pairs"); |
| Py_DECREF(pair); |
| return -1; |
| } |
| dict = PyType_stgdict(desc); |
| if (dict == NULL) { |
| Py_DECREF(pair); |
| PyErr_Format(PyExc_TypeError, |
| "second item in _fields_ tuple (index %zd) must be a C type", |
| i); |
| return -1; |
| } |
| if (!PyCArrayTypeObject_Check(desc)) { |
| /* Not an array. Just need an ffi_type pointer. */ |
| num_ffi_type_pointers++; |
| } |
| else { |
| /* It's an array. */ |
| Py_ssize_t length = dict->length; |
| StgDictObject *edict; |
| |
| edict = PyType_stgdict(dict->proto); |
| if (edict == NULL) { |
| Py_DECREF(pair); |
| PyErr_Format(PyExc_TypeError, |
| "second item in _fields_ tuple (index %zd) must be a C type", |
| i); |
| return -1; |
| } |
| /* |
| * We need one extra ffi_type to hold the struct, and one |
| * ffi_type pointer per array element + one for a NULL to |
| * mark the end. |
| */ |
| num_ffi_types++; |
| num_ffi_type_pointers += length + 1; |
| } |
| Py_DECREF(pair); |
| } |
| |
| /* |
| * At this point, we know we need storage for some ffi_types and some |
| * ffi_type pointers. We'll allocate these in one block. |
| * There are three sub-blocks of information: the ffi_type pointers to |
| * this structure/union's elements, the ffi_type_pointers to the |
| * dummy fields standing in for array elements, and the |
| * ffi_types representing the dummy structures. |
| */ |
| alloc_size = (ffi_ofs + 1 + len + num_ffi_type_pointers) * sizeof(ffi_type *) + |
| num_ffi_types * sizeof(ffi_type); |
| type_block = PyMem_Malloc(alloc_size); |
| |
| if (type_block == NULL) { |
| PyErr_NoMemory(); |
| return -1; |
| } |
| /* |
| * the first block takes up ffi_ofs + len + 1 which is the pointers * |
| * for this struct/union. The second block takes up |
| * num_ffi_type_pointers, so the sum of these is ffi_ofs + len + 1 + |
| * num_ffi_type_pointers as allocated above. The last bit is the |
| * num_ffi_types structs. |
| */ |
| element_types = (ffi_type **) type_block; |
| dummy_types = &element_types[ffi_ofs + len + 1]; |
| structs = (ffi_type *) &dummy_types[num_ffi_type_pointers]; |
| |
| if (num_ffi_types > 0) { |
| memset(structs, 0, num_ffi_types * sizeof(ffi_type)); |
| } |
| if (ffi_ofs && (basedict != NULL)) { |
| memcpy(element_types, |
| basedict->ffi_type_pointer.elements, |
| ffi_ofs * sizeof(ffi_type *)); |
| } |
| element_index = ffi_ofs; |
| |
| /* second pass to actually set the type pointers */ |
| for (i = 0; i < len; ++i) { |
| PyObject *name, *desc; |
| PyObject *pair = PySequence_GetItem(fields, i); |
| StgDictObject *dict; |
| int bitsize = 0; |
| |
| if (pair == NULL) { |
| PyMem_Free(type_block); |
| return -1; |
| } |
| /* In theory, we made this call in the first pass, so it *shouldn't* |
| * fail. However, you never know, and the code above might change |
| * later - keeping the check in here is a tad defensive but it |
| * will affect program size only slightly and performance hardly at |
| * all. |
| */ |
| if (!PyArg_ParseTuple(pair, "UO|i", &name, &desc, &bitsize)) { |
| PyErr_SetString(PyExc_TypeError, |
| "'_fields_' must be a sequence of (name, C type) pairs"); |
| Py_DECREF(pair); |
| PyMem_Free(type_block); |
| return -1; |
| } |
| dict = PyType_stgdict(desc); |
| /* Possibly this check could be avoided, but see above comment. */ |
| if (dict == NULL) { |
| Py_DECREF(pair); |
| PyMem_Free(type_block); |
| PyErr_Format(PyExc_TypeError, |
| "second item in _fields_ tuple (index %zd) must be a C type", |
| i); |
| return -1; |
| } |
| assert(element_index < (ffi_ofs + len)); /* will be used below */ |
| if (!PyCArrayTypeObject_Check(desc)) { |
| /* Not an array. Just copy over the element ffi_type. */ |
| element_types[element_index++] = &dict->ffi_type_pointer; |
| } |
| else { |
| Py_ssize_t length = dict->length; |
| StgDictObject *edict; |
| |
| edict = PyType_stgdict(dict->proto); |
| if (edict == NULL) { |
| Py_DECREF(pair); |
| PyMem_Free(type_block); |
| PyErr_Format(PyExc_TypeError, |
| "second item in _fields_ tuple (index %zd) must be a C type", |
| i); |
| return -1; |
| } |
| element_types[element_index++] = &structs[struct_index]; |
| structs[struct_index].size = length * edict->ffi_type_pointer.size; |
| structs[struct_index].alignment = edict->ffi_type_pointer.alignment; |
| structs[struct_index].type = FFI_TYPE_STRUCT; |
| structs[struct_index].elements = &dummy_types[dummy_index]; |
| ++struct_index; |
| /* Copy over the element's type, length times. */ |
| while (length > 0) { |
| assert(dummy_index < (num_ffi_type_pointers)); |
| dummy_types[dummy_index++] = &edict->ffi_type_pointer; |
| length--; |
| } |
| assert(dummy_index < (num_ffi_type_pointers)); |
| dummy_types[dummy_index++] = NULL; |
| } |
| Py_DECREF(pair); |
| } |
| |
| element_types[element_index] = NULL; |
| /* |
| * Replace the old elements with the new, taking into account |
| * base class elements where necessary. |
| */ |
| assert(stgdict->ffi_type_pointer.elements); |
| PyMem_Free(stgdict->ffi_type_pointer.elements); |
| stgdict->ffi_type_pointer.elements = element_types; |
| } |
| |
| /* We did check that this flag was NOT set above, it must not |
| have been set until now. */ |
| if (stgdict->flags & DICTFLAG_FINAL) { |
| PyErr_SetString(PyExc_AttributeError, |
| "Structure or union cannot contain itself"); |
| return -1; |
| } |
| stgdict->flags |= DICTFLAG_FINAL; |
| |
| return MakeAnonFields(type); |
| } |