diff options
-rw-r--r-- | Include/internal/pycore_global_objects_fini_generated.h | 10 | ||||
-rw-r--r-- | Include/internal/pycore_global_strings.h | 10 | ||||
-rw-r--r-- | Include/internal/pycore_runtime_init_generated.h | 10 | ||||
-rw-r--r-- | Include/internal/pycore_unicodeobject_generated.h | 40 | ||||
-rw-r--r-- | Lib/ctypes/_layout.py | 337 | ||||
-rw-r--r-- | Lib/test/test_ctypes/test_bitfields.py | 33 | ||||
-rw-r--r-- | Lib/test/test_ctypes/test_struct_fields.py | 1 | ||||
-rw-r--r-- | Modules/_ctypes/_ctypes.c | 13 | ||||
-rw-r--r-- | Modules/_ctypes/cfield.c | 302 | ||||
-rw-r--r-- | Modules/_ctypes/clinic/cfield.c.h | 113 | ||||
-rw-r--r-- | Modules/_ctypes/ctypes.h | 24 | ||||
-rw-r--r-- | Modules/_ctypes/stgdict.c | 579 |
12 files changed, 805 insertions, 667 deletions
diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index d9b46df..6e948e1 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -738,7 +738,6 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_abc_impl)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_abstract_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_active)); - _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_align_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_anonymous_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_argtypes_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_as_parameter_)); @@ -759,21 +758,18 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_initializing)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_io)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_is_text_encoding)); - _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_layout_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_length_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_limbo)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_lock_unlock_module)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_loop)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_needs_com_addref_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_only_immortal)); - _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_pack_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_restype_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_showwarnmsg)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_shutdown)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_slotnames)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_strptime)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_strptime_datetime)); - _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_swappedbytes_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_type_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_uninitialized_submodules)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_warn_unawaited_coroutine)); @@ -787,6 +783,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(after_in_parent)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(aggregate_class)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(alias)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(align)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(allow_code)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(append)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(arg)); @@ -806,6 +803,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(before)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(big)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(binary_form)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(bit_size)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(block)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(bound)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(buffer)); @@ -934,6 +932,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(fd2)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(fdel)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(fget)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(fields)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(file)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(file_actions)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(filename)); @@ -950,6 +949,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(fold)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(follow_symlinks)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(format)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(format_spec)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(from_param)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(fromlist)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(fromtimestamp)); @@ -986,6 +986,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(importlib)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(in_fd)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(incoming)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(index)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(indexgroup)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(inf)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(infer_variance)); @@ -1006,6 +1007,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(intersection)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(interval)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(is_running)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(is_struct)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(isatty)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(isinstance)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(isoformat)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 10773d7..5c63a6e 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -227,7 +227,6 @@ struct _Py_global_strings { STRUCT_FOR_ID(_abc_impl) STRUCT_FOR_ID(_abstract_) STRUCT_FOR_ID(_active) - STRUCT_FOR_ID(_align_) STRUCT_FOR_ID(_anonymous_) STRUCT_FOR_ID(_argtypes_) STRUCT_FOR_ID(_as_parameter_) @@ -248,21 +247,18 @@ struct _Py_global_strings { STRUCT_FOR_ID(_initializing) STRUCT_FOR_ID(_io) STRUCT_FOR_ID(_is_text_encoding) - STRUCT_FOR_ID(_layout_) STRUCT_FOR_ID(_length_) STRUCT_FOR_ID(_limbo) STRUCT_FOR_ID(_lock_unlock_module) STRUCT_FOR_ID(_loop) STRUCT_FOR_ID(_needs_com_addref_) STRUCT_FOR_ID(_only_immortal) - STRUCT_FOR_ID(_pack_) STRUCT_FOR_ID(_restype_) STRUCT_FOR_ID(_showwarnmsg) STRUCT_FOR_ID(_shutdown) STRUCT_FOR_ID(_slotnames) STRUCT_FOR_ID(_strptime) STRUCT_FOR_ID(_strptime_datetime) - STRUCT_FOR_ID(_swappedbytes_) STRUCT_FOR_ID(_type_) STRUCT_FOR_ID(_uninitialized_submodules) STRUCT_FOR_ID(_warn_unawaited_coroutine) @@ -276,6 +272,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(after_in_parent) STRUCT_FOR_ID(aggregate_class) STRUCT_FOR_ID(alias) + STRUCT_FOR_ID(align) STRUCT_FOR_ID(allow_code) STRUCT_FOR_ID(append) STRUCT_FOR_ID(arg) @@ -295,6 +292,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(before) STRUCT_FOR_ID(big) STRUCT_FOR_ID(binary_form) + STRUCT_FOR_ID(bit_size) STRUCT_FOR_ID(block) STRUCT_FOR_ID(bound) STRUCT_FOR_ID(buffer) @@ -423,6 +421,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(fd2) STRUCT_FOR_ID(fdel) STRUCT_FOR_ID(fget) + STRUCT_FOR_ID(fields) STRUCT_FOR_ID(file) STRUCT_FOR_ID(file_actions) STRUCT_FOR_ID(filename) @@ -439,6 +438,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(fold) STRUCT_FOR_ID(follow_symlinks) STRUCT_FOR_ID(format) + STRUCT_FOR_ID(format_spec) STRUCT_FOR_ID(from_param) STRUCT_FOR_ID(fromlist) STRUCT_FOR_ID(fromtimestamp) @@ -475,6 +475,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(importlib) STRUCT_FOR_ID(in_fd) STRUCT_FOR_ID(incoming) + STRUCT_FOR_ID(index) STRUCT_FOR_ID(indexgroup) STRUCT_FOR_ID(inf) STRUCT_FOR_ID(infer_variance) @@ -495,6 +496,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(intersection) STRUCT_FOR_ID(interval) STRUCT_FOR_ID(is_running) + STRUCT_FOR_ID(is_struct) STRUCT_FOR_ID(isatty) STRUCT_FOR_ID(isinstance) STRUCT_FOR_ID(isoformat) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 618f8d0..bac6b5b 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -736,7 +736,6 @@ extern "C" { INIT_ID(_abc_impl), \ INIT_ID(_abstract_), \ INIT_ID(_active), \ - INIT_ID(_align_), \ INIT_ID(_anonymous_), \ INIT_ID(_argtypes_), \ INIT_ID(_as_parameter_), \ @@ -757,21 +756,18 @@ extern "C" { INIT_ID(_initializing), \ INIT_ID(_io), \ INIT_ID(_is_text_encoding), \ - INIT_ID(_layout_), \ INIT_ID(_length_), \ INIT_ID(_limbo), \ INIT_ID(_lock_unlock_module), \ INIT_ID(_loop), \ INIT_ID(_needs_com_addref_), \ INIT_ID(_only_immortal), \ - INIT_ID(_pack_), \ INIT_ID(_restype_), \ INIT_ID(_showwarnmsg), \ INIT_ID(_shutdown), \ INIT_ID(_slotnames), \ INIT_ID(_strptime), \ INIT_ID(_strptime_datetime), \ - INIT_ID(_swappedbytes_), \ INIT_ID(_type_), \ INIT_ID(_uninitialized_submodules), \ INIT_ID(_warn_unawaited_coroutine), \ @@ -785,6 +781,7 @@ extern "C" { INIT_ID(after_in_parent), \ INIT_ID(aggregate_class), \ INIT_ID(alias), \ + INIT_ID(align), \ INIT_ID(allow_code), \ INIT_ID(append), \ INIT_ID(arg), \ @@ -804,6 +801,7 @@ extern "C" { INIT_ID(before), \ INIT_ID(big), \ INIT_ID(binary_form), \ + INIT_ID(bit_size), \ INIT_ID(block), \ INIT_ID(bound), \ INIT_ID(buffer), \ @@ -932,6 +930,7 @@ extern "C" { INIT_ID(fd2), \ INIT_ID(fdel), \ INIT_ID(fget), \ + INIT_ID(fields), \ INIT_ID(file), \ INIT_ID(file_actions), \ INIT_ID(filename), \ @@ -948,6 +947,7 @@ extern "C" { INIT_ID(fold), \ INIT_ID(follow_symlinks), \ INIT_ID(format), \ + INIT_ID(format_spec), \ INIT_ID(from_param), \ INIT_ID(fromlist), \ INIT_ID(fromtimestamp), \ @@ -984,6 +984,7 @@ extern "C" { INIT_ID(importlib), \ INIT_ID(in_fd), \ INIT_ID(incoming), \ + INIT_ID(index), \ INIT_ID(indexgroup), \ INIT_ID(inf), \ INIT_ID(infer_variance), \ @@ -1004,6 +1005,7 @@ extern "C" { INIT_ID(intersection), \ INIT_ID(interval), \ INIT_ID(is_running), \ + INIT_ID(is_struct), \ INIT_ID(isatty), \ INIT_ID(isinstance), \ INIT_ID(isoformat), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index f848a00..efdbde4 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -708,10 +708,6 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); - string = &_Py_ID(_align_); - _PyUnicode_InternStatic(interp, &string); - assert(_PyUnicode_CheckConsistency(string, 1)); - assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(_anonymous_); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -792,10 +788,6 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); - string = &_Py_ID(_layout_); - _PyUnicode_InternStatic(interp, &string); - assert(_PyUnicode_CheckConsistency(string, 1)); - assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(_length_); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -820,10 +812,6 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); - string = &_Py_ID(_pack_); - _PyUnicode_InternStatic(interp, &string); - assert(_PyUnicode_CheckConsistency(string, 1)); - assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(_restype_); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -848,10 +836,6 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); - string = &_Py_ID(_swappedbytes_); - _PyUnicode_InternStatic(interp, &string); - assert(_PyUnicode_CheckConsistency(string, 1)); - assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(_type_); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -904,6 +888,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(align); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(allow_code); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -980,6 +968,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(bit_size); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(block); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -1492,6 +1484,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(fields); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(file); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -1556,6 +1552,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(format_spec); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(from_param); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -1700,6 +1700,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(index); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(indexgroup); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -1780,6 +1784,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(is_struct); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(isatty); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Lib/ctypes/_layout.py b/Lib/ctypes/_layout.py new file mode 100644 index 0000000..e30db59 --- /dev/null +++ b/Lib/ctypes/_layout.py @@ -0,0 +1,337 @@ +"""Python implementation of computing the layout of a struct/union + +This code is internal and tightly coupled to the C part. The interface +may change at any time. +""" + +import sys +import warnings +import struct + +from _ctypes import CField, buffer_info +import ctypes + +def round_down(n, multiple): + assert n >= 0 + assert multiple > 0 + return (n // multiple) * multiple + +def round_up(n, multiple): + assert n >= 0 + assert multiple > 0 + return ((n + multiple - 1) // multiple) * multiple + +def LOW_BIT(offset): + return offset & 0xFFFF + +def NUM_BITS(bitsize): + return bitsize >> 16 + +def BUILD_SIZE(bitsize, offset): + assert 0 <= offset, offset + assert offset <= 0xFFFF, offset + # We don't support zero length bitfields. + # And GET_BITFIELD uses NUM_BITS(size) == 0, + # to figure out whether we are handling a bitfield. + assert bitsize > 0, bitsize + result = (bitsize << 16) + offset + assert bitsize == NUM_BITS(result), (bitsize, result) + assert offset == LOW_BIT(result), (offset, result) + return result + +def build_size(bit_size, bit_offset, big_endian, type_size): + if big_endian: + return BUILD_SIZE(bit_size, 8 * type_size - bit_offset - bit_size) + return BUILD_SIZE(bit_size, bit_offset) + +_INT_MAX = (1 << (ctypes.sizeof(ctypes.c_int) * 8) - 1) - 1 + + +class StructUnionLayout: + def __init__(self, fields, size, align, format_spec): + # sequence of CField objects + self.fields = fields + + # total size of the aggregate (rounded up to alignment) + self.size = size + + # total alignment requirement of the aggregate + self.align = align + + # buffer format specification (as a string, UTF-8 but bes + # kept ASCII-only) + self.format_spec = format_spec + + +def get_layout(cls, input_fields, is_struct, base): + """Return a StructUnionLayout for the given class. + + Called by PyCStructUnionType_update_stginfo when _fields_ is assigned + to a class. + """ + # Currently there are two modes, selectable using the '_layout_' attribute: + # + # 'gcc-sysv' mode places fields one after another, bit by bit. + # But "each bit field must fit within a single object of its specified + # type" (GCC manual, section 15.8 "Bit Field Packing"). When it doesn't, + # we insert a few bits of padding to avoid that. + # + # 'ms' mode works similar except for bitfield packing. Adjacent + # bit-fields are packed into the same 1-, 2-, or 4-byte allocation unit + # if the integral types are the same size and if the next bit-field fits + # into the current allocation unit without crossing the boundary imposed + # by the common alignment requirements of the bit-fields. + # + # See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-mms-bitfields + # for details. + + # We do not support zero length bitfields (we use bitsize != 0 + # elsewhere to indicate a bitfield). Here, non-bitfields have bit_size + # set to size*8. + + # For clarity, variables that count bits have `bit` in their names. + + layout = getattr(cls, '_layout_', None) + if layout is None: + if sys.platform == 'win32' or getattr(cls, '_pack_', None): + gcc_layout = False + else: + gcc_layout = True + elif layout == 'ms': + gcc_layout = False + elif layout == 'gcc-sysv': + gcc_layout = True + else: + raise ValueError(f'unknown _layout_: {layout!r}') + + align = getattr(cls, '_align_', 1) + if align < 0: + raise ValueError('_align_ must be a non-negative integer') + elif align == 0: + # Setting `_align_ = 0` amounts to using the default alignment + align == 1 + + if base: + align = max(ctypes.alignment(base), align) + + swapped_bytes = hasattr(cls, '_swappedbytes_') + if swapped_bytes: + big_endian = sys.byteorder == 'little' + else: + big_endian = sys.byteorder == 'big' + + pack = getattr(cls, '_pack_', None) + if pack is not None: + try: + pack = int(pack) + except (TypeError, ValueError): + raise ValueError("_pack_ must be an integer") + if pack < 0: + raise ValueError("_pack_ must be a non-negative integer") + if pack > _INT_MAX: + raise ValueError("_pack_ too big") + if gcc_layout: + raise ValueError('_pack_ is not compatible with gcc-sysv layout') + + result_fields = [] + + if is_struct: + format_spec_parts = ["T{"] + else: + format_spec_parts = ["B"] + + last_field_bit_size = 0 # used in MS layout only + + # `8 * next_byte_offset + next_bit_offset` points to where the + # next field would start. + next_bit_offset = 0 + next_byte_offset = 0 + + # size if this was a struct (sum of field sizes, plus padding) + struct_size = 0 + # max of field sizes; only meaningful for unions + union_size = 0 + + if base: + struct_size = ctypes.sizeof(base) + if gcc_layout: + next_bit_offset = struct_size * 8 + else: + next_byte_offset = struct_size + + last_size = struct_size + for i, field in enumerate(input_fields): + if not is_struct: + # Unions start fresh each time + last_field_bit_size = 0 + next_bit_offset = 0 + next_byte_offset = 0 + + # Unpack the field + field = tuple(field) + try: + name, ctype = field + except (ValueError, TypeError): + try: + name, ctype, bit_size = field + except (ValueError, TypeError) as exc: + raise ValueError( + '_fields_ must be a sequence of (name, C type) pairs ' + + 'or (name, C type, bit size) triples') from exc + is_bitfield = True + if bit_size <= 0: + raise ValueError( + f'number of bits invalid for bit field {name!r}') + type_size = ctypes.sizeof(ctype) + if bit_size > type_size * 8: + raise ValueError( + f'number of bits invalid for bit field {name!r}') + else: + is_bitfield = False + type_size = ctypes.sizeof(ctype) + bit_size = type_size * 8 + + type_bit_size = type_size * 8 + type_align = ctypes.alignment(ctype) or 1 + type_bit_align = type_align * 8 + + if gcc_layout: + # We don't use next_byte_offset here + assert pack is None + assert next_byte_offset == 0 + + # Determine whether the bit field, if placed at the next + # free bit, fits within a single object of its specified type. + # That is: determine a "slot", sized & aligned for the + # specified type, which contains the bitfield's beginning: + slot_start_bit = round_down(next_bit_offset, type_bit_align) + slot_end_bit = slot_start_bit + type_bit_size + # And see if it also contains the bitfield's last bit: + field_end_bit = next_bit_offset + bit_size + if field_end_bit > slot_end_bit: + # It doesn't: add padding (bump up to the next + # alignment boundary) + next_bit_offset = round_up(next_bit_offset, type_bit_align) + + offset = round_down(next_bit_offset, type_bit_align) // 8 + if is_bitfield: + effective_bit_offset = next_bit_offset - 8 * offset + size = build_size(bit_size, effective_bit_offset, + big_endian, type_size) + assert effective_bit_offset <= type_bit_size + else: + assert offset == next_bit_offset / 8 + size = type_size + + next_bit_offset += bit_size + struct_size = round_up(next_bit_offset, 8) // 8 + else: + if pack: + type_align = min(pack, type_align) + + # next_byte_offset points to end of current bitfield. + # next_bit_offset is generally non-positive, + # and 8 * next_byte_offset + next_bit_offset points just behind + # the end of the last field we placed. + if ( + (0 < next_bit_offset + bit_size) + or (type_bit_size != last_field_bit_size) + ): + # Close the previous bitfield (if any) + # and start a new bitfield + next_byte_offset = round_up(next_byte_offset, type_align) + + next_byte_offset += type_size + + last_field_bit_size = type_bit_size + # Reminder: 8 * (next_byte_offset) + next_bit_offset + # points to where we would start a new field, namely + # just behind where we placed the last field plus an + # allowance for alignment. + next_bit_offset = -last_field_bit_size + + assert type_bit_size == last_field_bit_size + + offset = next_byte_offset - last_field_bit_size // 8 + if is_bitfield: + assert 0 <= (last_field_bit_size + next_bit_offset) + size = build_size(bit_size, + last_field_bit_size + next_bit_offset, + big_endian, type_size) + else: + size = type_size + if type_bit_size: + assert (last_field_bit_size + next_bit_offset) < type_bit_size + + next_bit_offset += bit_size + struct_size = next_byte_offset + + assert (not is_bitfield) or (LOW_BIT(size) <= size * 8) + + # Add the format spec parts + if is_struct: + padding = offset - last_size + format_spec_parts.append(padding_spec(padding)) + + fieldfmt, bf_ndim, bf_shape = buffer_info(ctype) + + if bf_shape: + format_spec_parts.extend(( + "(", + ','.join(str(n) for n in bf_shape), + ")", + )) + + if fieldfmt is None: + fieldfmt = "B" + if isinstance(name, bytes): + # a bytes name would be rejected later, but we check early + # to avoid a BytesWarning with `python -bb` + raise TypeError( + "field {name!r}: name must be a string, not bytes") + format_spec_parts.append(f"{fieldfmt}:{name}:") + + result_fields.append(CField( + name=name, + type=ctype, + size=size, + offset=offset, + bit_size=bit_size if is_bitfield else None, + index=i, + )) + if is_bitfield and not gcc_layout: + assert type_bit_size > 0 + + align = max(align, type_align) + last_size = struct_size + if not is_struct: + union_size = max(struct_size, union_size) + + if is_struct: + total_size = struct_size + else: + total_size = union_size + + # Adjust the size according to the alignment requirements + aligned_size = round_up(total_size, align) + + # Finish up the format spec + if is_struct: + padding = aligned_size - total_size + format_spec_parts.append(padding_spec(padding)) + format_spec_parts.append("}") + + return StructUnionLayout( + fields=result_fields, + size=aligned_size, + align=align, + format_spec="".join(format_spec_parts), + ) + + +def padding_spec(padding): + if padding <= 0: + return "" + if padding == 1: + return "x" + return f"{padding}x" diff --git a/Lib/test/test_ctypes/test_bitfields.py b/Lib/test/test_ctypes/test_bitfields.py index e6509e6..19ba2f4 100644 --- a/Lib/test/test_ctypes/test_bitfields.py +++ b/Lib/test/test_ctypes/test_bitfields.py @@ -5,7 +5,9 @@ from ctypes import (CDLL, Structure, sizeof, POINTER, byref, alignment, LittleEndianStructure, BigEndianStructure, c_byte, c_ubyte, c_char, c_char_p, c_void_p, c_wchar, c_uint8, c_uint16, c_uint32, c_uint64, - c_short, c_ushort, c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong) + c_short, c_ushort, c_int, c_uint, c_long, c_ulong, + c_longlong, c_ulonglong, + Union) from test import support from test.support import import_helper _ctypes_test = import_helper.import_module("_ctypes_test") @@ -186,8 +188,10 @@ class BitFieldTest(unittest.TestCase): self.assertEqual((c_typ, x.a, x.b, x.c), (c_typ, 0, 7, 0)) def fail_fields(self, *fields): - return self.get_except(type(Structure), "X", (), - {"_fields_": fields}) + for layout in "ms", "gcc-sysv": + with self.subTest(layout=layout): + return self.get_except(type(Structure), "X", (), + {"_fields_": fields, "layout": layout}) def test_nonint_types(self): # bit fields are not allowed on non-integer types. @@ -204,9 +208,15 @@ class BitFieldTest(unittest.TestCase): result = self.fail_fields(("a", c_char, 1)) self.assertEqual(result, (TypeError, 'bit fields not allowed for type c_char')) - class Dummy(Structure): + class Empty(Structure): _fields_ = [] + result = self.fail_fields(("a", Empty, 1)) + self.assertEqual(result, (ValueError, "number of bits invalid for bit field 'a'")) + + class Dummy(Structure): + _fields_ = [("x", c_int)] + result = self.fail_fields(("a", Dummy, 1)) self.assertEqual(result, (TypeError, 'bit fields not allowed for type Dummy')) @@ -518,6 +528,21 @@ class BitFieldTest(unittest.TestCase): x.c = 2 self.assertEqual(b, b'\xab\xcd\xef\x12') + def test_union_bitfield(self): + class BitfieldUnion(Union): + _fields_ = [("a", c_uint32, 1), + ("b", c_uint32, 2), + ("c", c_uint32, 3)] + self.assertEqual(sizeof(BitfieldUnion), 4) + b = bytearray(4) + x = BitfieldUnion.from_buffer(b) + x.a = 1 + self.assertEqual(int.from_bytes(b).bit_count(), 1) + x.b = 3 + self.assertEqual(int.from_bytes(b).bit_count(), 2) + x.c = 7 + self.assertEqual(int.from_bytes(b).bit_count(), 3) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_ctypes/test_struct_fields.py b/Lib/test/test_ctypes/test_struct_fields.py index 7adab79..7d7a518 100644 --- a/Lib/test/test_ctypes/test_struct_fields.py +++ b/Lib/test/test_ctypes/test_struct_fields.py @@ -60,7 +60,6 @@ class StructFieldsTestCase(unittest.TestCase): self.assertRaises(TypeError, CField) def test_cfield_type_flags(self): - self.assertTrue(CField.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION) self.assertTrue(CField.__flags__ & Py_TPFLAGS_IMMUTABLETYPE) def test_cfield_inheritance_hierarchy(self): diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index b551026..2b23be7 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -320,7 +320,7 @@ _ctypes_alloc_format_string_for_type(char code, int big_endian) indicator set. If called with a suffix of NULL the error indicator must already be set. */ -char * +static char * _ctypes_alloc_format_string(const char *prefix, const char *suffix) { size_t len; @@ -352,7 +352,7 @@ _ctypes_alloc_format_string(const char *prefix, const char *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 * +static char * _ctypes_alloc_format_string_with_shape(int ndim, const Py_ssize_t *shape, const char *prefix, const char *suffix) { @@ -664,9 +664,6 @@ StructUnionType_init(PyObject *self, PyObject *args, PyObject *kwds, int isStruc Py_DECREF(attrdict); return -1; } - if (!isStruct) { - info->flags |= TYPEFLAG_HASUNION; - } info->format = _ctypes_alloc_format_string(NULL, "B"); if (info->format == NULL) { @@ -2534,6 +2531,10 @@ converters_from_argtypes(ctypes_state *st, PyObject *ob) return -1; } + // TYPEFLAG_HASUNION and TYPEFLAG_HASBITFIELD used to be set + // if there were any unions/bitfields; + // if the check is re-enabled we either need to loop here or + // restore the flag if (stginfo != NULL) { if (stginfo->flags & TYPEFLAG_HASUNION) { Py_DECREF(converters); @@ -5780,7 +5781,7 @@ _ctypes_add_types(PyObject *mod) * Simple classes */ - CREATE_TYPE(st->PyCField_Type, &cfield_spec, NULL, NULL); + MOD_ADD_TYPE(st->PyCField_Type, &cfield_spec, NULL, NULL); /************************************************* * diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 2c1fb9b..abcab65 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -20,6 +20,13 @@ #define CTYPES_CFIELD_CAPSULE_NAME_PYMEM "_ctypes/cfield.c pymem" +/*[clinic input] +module _ctypes +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=476a19c49b31a75c]*/ + +#include "clinic/cfield.c.h" + static void pymem_destructor(PyObject *ptr) { void *p = PyCapsule_GetPointer(ptr, CTYPES_CFIELD_CAPSULE_NAME_PYMEM); @@ -33,6 +40,10 @@ static void pymem_destructor(PyObject *ptr) /* PyCField_Type */ +/*[clinic input] +class _ctypes.CField "PyObject *" "PyObject" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=602817ea3ffc709c]*/ static inline Py_ssize_t round_down(Py_ssize_t numToRound, Py_ssize_t multiple) @@ -61,238 +72,142 @@ Py_ssize_t LOW_BIT(Py_ssize_t offset); static inline Py_ssize_t BUILD_SIZE(Py_ssize_t bitsize, Py_ssize_t offset); -/* PyCField_FromDesc creates and returns a struct/union field descriptor. - -The function expects to be called repeatedly for all fields in a struct or -union. It uses helper functions PyCField_FromDesc_gcc and -PyCField_FromDesc_msvc to simulate the corresponding compilers. - -GCC mode places fields one after another, bit by bit. But "each bit field must -fit within a single object of its specified type" (GCC manual, section 15.8 -"Bit Field Packing"). When it doesn't, we insert a few bits of padding to -avoid that. -MSVC mode works similar except for bitfield packing. Adjacent bit-fields are -packed into the same 1-, 2-, or 4-byte allocation unit if the integral types -are the same size and if the next bit-field fits into the current allocation -unit without crossing the boundary imposed by the common alignment requirements -of the bit-fields. +/*[clinic input] +@classmethod +_ctypes.CField.__new__ as PyCField_new -See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-mms-bitfields for details. + name: object(subclass_of='&PyUnicode_Type') + type as proto: object + size: Py_ssize_t + offset: Py_ssize_t + index: Py_ssize_t + bit_size as bit_size_obj: object = None -We do not support zero length bitfields. In fact we use bitsize != 0 elsewhere -to indicate a bitfield. Here, non-bitfields need bitsize set to size*8. +[clinic start generated code]*/ -PyCField_FromDesc manages: -- *psize: the size of the structure / union so far. -- *poffset, *pbitofs: 8* (*poffset) + *pbitofs points to where the next field - would start. -- *palign: the alignment requirements of the last field we placed. -*/ - -static int -PyCField_FromDesc_gcc(Py_ssize_t bitsize, Py_ssize_t *pbitofs, - Py_ssize_t *psize, Py_ssize_t *poffset, Py_ssize_t *palign, - CFieldObject* self, StgInfo* info, - int is_bitfield - ) -{ - // We don't use poffset here, so clear it, if it has been set. - *pbitofs += *poffset * 8; - *poffset = 0; - - *palign = info->align; - - if (bitsize > 0) { - // Determine whether the bit field, if placed at the next free bit, - // fits within a single object of its specified type. - // That is: determine a "slot", sized & aligned for the specified type, - // which contains the bitfield's beginning: - Py_ssize_t slot_start_bit = round_down(*pbitofs, 8 * info->align); - Py_ssize_t slot_end_bit = slot_start_bit + 8 * info->size; - // And see if it also contains the bitfield's last bit: - Py_ssize_t field_end_bit = *pbitofs + bitsize; - if (field_end_bit > slot_end_bit) { - // It doesn't: add padding (bump up to the next alignment boundary) - *pbitofs = round_up(*pbitofs, 8*info->align); - } - } - assert(*poffset == 0); - - self->offset = round_down(*pbitofs, 8*info->align) / 8; - if(is_bitfield) { - Py_ssize_t effective_bitsof = *pbitofs - 8 * self->offset; - self->size = BUILD_SIZE(bitsize, effective_bitsof); - assert(effective_bitsof <= info->size * 8); - } else { - self->size = info->size; +static PyObject * +PyCField_new_impl(PyTypeObject *type, PyObject *name, PyObject *proto, + Py_ssize_t size, Py_ssize_t offset, Py_ssize_t index, + PyObject *bit_size_obj) +/*[clinic end generated code: output=43649ef9157c5f58 input=3d813f56373c4caa]*/ +{ + CFieldObject* self = NULL; + if (size < 0) { + PyErr_Format(PyExc_ValueError, + "size of field %R must not be negative, got %zd", + name, size); + goto error; } - - *pbitofs += bitsize; - *psize = round_up(*pbitofs, 8) / 8; - - return 0; -} - -static int -PyCField_FromDesc_msvc( - Py_ssize_t *pfield_size, Py_ssize_t bitsize, - Py_ssize_t *pbitofs, Py_ssize_t *psize, Py_ssize_t *poffset, - Py_ssize_t *palign, int pack, - CFieldObject* self, StgInfo* info, - int is_bitfield - ) -{ - if (pack) { - *palign = Py_MIN(pack, info->align); - } else { - *palign = info->align; + // assert: no overflow; + if ((unsigned long long int) size + >= (1ULL << (8*sizeof(Py_ssize_t)-1)) / 8) { + PyErr_Format(PyExc_ValueError, + "size of field %R is too big: %zd", name, size); + goto error; } - // *poffset points to end of current bitfield. - // *pbitofs is generally non-positive, - // and 8 * (*poffset) + *pbitofs points just behind - // the end of the last field we placed. - if (0 < *pbitofs + bitsize || 8 * info->size != *pfield_size) { - // Close the previous bitfield (if any). - // and start a new bitfield: - *poffset = round_up(*poffset, *palign); - - *poffset += info->size; - - *pfield_size = info->size * 8; - // Reminder: 8 * (*poffset) + *pbitofs points to where we would start a - // new field. Ie just behind where we placed the last field plus an - // allowance for alignment. - *pbitofs = - *pfield_size; + PyTypeObject *tp = type; + ctypes_state *st = get_module_state_by_class(tp); + self = (CFieldObject *)tp->tp_alloc(tp, 0); + if (!self) { + return NULL; } - - assert(8 * info->size == *pfield_size); - - self->offset = *poffset - (*pfield_size) / 8; - if(is_bitfield) { - assert(0 <= (*pfield_size + *pbitofs)); - assert((*pfield_size + *pbitofs) < info->size * 8); - self->size = BUILD_SIZE(bitsize, *pfield_size + *pbitofs); + if (PyUnicode_CheckExact(name)) { + self->name = Py_NewRef(name); } else { - self->size = info->size; + self->name = PyObject_Str(name); + if (!self->name) { + goto error; + } } - assert(*pfield_size + *pbitofs <= info->size * 8); - - *pbitofs += bitsize; - *psize = *poffset; - return 0; -} - -PyObject * -PyCField_FromDesc(ctypes_state *st, PyObject *desc, Py_ssize_t index, - Py_ssize_t *pfield_size, Py_ssize_t bitsize, - Py_ssize_t *pbitofs, Py_ssize_t *psize, Py_ssize_t *poffset, Py_ssize_t *palign, - int pack, int big_endian, LayoutMode layout_mode) -{ - PyTypeObject *tp = st->PyCField_Type; - CFieldObject* self = (CFieldObject *)tp->tp_alloc(tp, 0); - if (self == NULL) { - return NULL; - } StgInfo *info; - if (PyStgInfo_FromType(st, desc, &info) < 0) { - Py_DECREF(self); - return NULL; + if (PyStgInfo_FromType(st, proto, &info) < 0) { + goto error; } - if (!info) { - PyErr_SetString(PyExc_TypeError, - "has no _stginfo_"); - Py_DECREF(self); - return NULL; + if (info == NULL) { + PyErr_Format(PyExc_TypeError, + "type of field %R must be a C type", self->name); + goto error; } - PyObject* proto = desc; + Py_ssize_t bit_size = NUM_BITS(size); + if (bit_size) { + assert(bit_size > 0); + assert(bit_size <= info->size * 8); + switch(info->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 (info->getfunc != _ctypes_get_fielddesc("c")->getfunc + && info->getfunc != _ctypes_get_fielddesc("u")->getfunc) + { + break; + } + _Py_FALLTHROUGH; /* else fall through */ + default: + PyErr_Format(PyExc_TypeError, + "bit fields not allowed for type %s", + ((PyTypeObject*)proto)->tp_name); + goto error; + } + } + + self->proto = Py_NewRef(proto); + self->size = size; + self->offset = offset; + + self->index = index; - /* Field descriptors for 'c_char * n' are be scpecial cased to + /* Field descriptors for 'c_char * n' are be special cased to return a Python string instead of an Array object instance... */ - SETFUNC setfunc = NULL; - GETFUNC getfunc = NULL; + self->setfunc = NULL; + self->getfunc = NULL; if (PyCArrayTypeObject_Check(st, proto)) { StgInfo *ainfo; if (PyStgInfo_FromType(st, proto, &ainfo) < 0) { - Py_DECREF(self); - return NULL; + goto error; } if (ainfo && ainfo->proto) { StgInfo *iinfo; if (PyStgInfo_FromType(st, ainfo->proto, &iinfo) < 0) { - Py_DECREF(self); - return NULL; + goto error; } if (!iinfo) { PyErr_SetString(PyExc_TypeError, "has no _stginfo_"); - Py_DECREF(self); - return NULL; + goto error; } if (iinfo->getfunc == _ctypes_get_fielddesc("c")->getfunc) { struct fielddesc *fd = _ctypes_get_fielddesc("s"); - getfunc = fd->getfunc; - setfunc = fd->setfunc; + self->getfunc = fd->getfunc; + self->setfunc = fd->setfunc; } if (iinfo->getfunc == _ctypes_get_fielddesc("u")->getfunc) { struct fielddesc *fd = _ctypes_get_fielddesc("U"); - getfunc = fd->getfunc; - setfunc = fd->setfunc; + self->getfunc = fd->getfunc; + self->setfunc = fd->setfunc; } } } - self->setfunc = setfunc; - self->getfunc = getfunc; - self->index = index; - - self->proto = Py_NewRef(proto); - - int is_bitfield = !!bitsize; - if(!is_bitfield) { - assert(info->size >= 0); - // assert: no overflow; - assert((unsigned long long int) info->size - < (1ULL << (8*sizeof(Py_ssize_t)-1)) / 8); - bitsize = 8 * info->size; - // Caution: bitsize might still be 0 now. - } - assert(bitsize <= info->size * 8); - - int result; - if (layout_mode == LAYOUT_MODE_MS) { - result = PyCField_FromDesc_msvc( - pfield_size, bitsize, pbitofs, - psize, poffset, palign, - pack, - self, info, - is_bitfield - ); - } else { - assert(pack == 0); - result = PyCField_FromDesc_gcc( - bitsize, pbitofs, - psize, poffset, palign, - self, info, - is_bitfield - ); - } - if (result < 0) { - Py_DECREF(self); - return NULL; - } - assert(!is_bitfield || (LOW_BIT(self->size) <= self->size * 8)); - if(big_endian && is_bitfield) { - self->size = BUILD_SIZE(NUM_BITS(self->size), 8*info->size - LOW_BIT(self->size) - bitsize); - } return (PyObject *)self; +error: + Py_XDECREF(self); + return NULL; } + static int PyCField_set(CFieldObject *self, PyObject *inst, PyObject *value) { @@ -371,8 +286,10 @@ PyCField_dealloc(PyObject *self) { PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - (void)PyCField_clear((CFieldObject *)self); - Py_TYPE(self)->tp_free((PyObject *)self); + CFieldObject *self_cf = (CFieldObject *)self; + (void)PyCField_clear(self_cf); + Py_CLEAR(self_cf->name); + Py_TYPE(self)->tp_free(self); Py_DECREF(tp); } @@ -398,6 +315,7 @@ PyCField_repr(CFieldObject *self) } static PyType_Slot cfield_slots[] = { + {Py_tp_new, PyCField_new}, {Py_tp_dealloc, PyCField_dealloc}, {Py_tp_repr, PyCField_repr}, {Py_tp_doc, (void *)PyDoc_STR("Structure/Union member")}, @@ -413,7 +331,7 @@ PyType_Spec cfield_spec = { .name = "_ctypes.CField", .basicsize = sizeof(CFieldObject), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION), + Py_TPFLAGS_IMMUTABLETYPE), .slots = cfield_slots, }; diff --git a/Modules/_ctypes/clinic/cfield.c.h b/Modules/_ctypes/clinic/cfield.c.h new file mode 100644 index 0000000..df5da78 --- /dev/null +++ b/Modules/_ctypes/clinic/cfield.c.h @@ -0,0 +1,113 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) +# include "pycore_gc.h" // PyGC_Head +# include "pycore_runtime.h" // _Py_ID() +#endif +#include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_modsupport.h" // _PyArg_UnpackKeywords() + +static PyObject * +PyCField_new_impl(PyTypeObject *type, PyObject *name, PyObject *proto, + Py_ssize_t size, Py_ssize_t offset, Py_ssize_t index, + PyObject *bit_size_obj); + +static PyObject * +PyCField_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 6 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(name), &_Py_ID(type), &_Py_ID(size), &_Py_ID(offset), &_Py_ID(index), &_Py_ID(bit_size), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"name", "type", "size", "offset", "index", "bit_size", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "CField", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[6]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 5; + PyObject *name; + PyObject *proto; + Py_ssize_t size; + Py_ssize_t offset; + Py_ssize_t index; + PyObject *bit_size_obj = Py_None; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 5, 6, 0, argsbuf); + if (!fastargs) { + goto exit; + } + if (!PyUnicode_Check(fastargs[0])) { + _PyArg_BadArgument("CField", "argument 'name'", "str", fastargs[0]); + goto exit; + } + name = fastargs[0]; + proto = fastargs[1]; + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(fastargs[2]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + size = ival; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(fastargs[3]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + offset = ival; + } + { + Py_ssize_t ival = -1; + PyObject *iobj = _PyNumber_Index(fastargs[4]); + if (iobj != NULL) { + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + } + if (ival == -1 && PyErr_Occurred()) { + goto exit; + } + index = ival; + } + if (!noptargs) { + goto skip_optional_pos; + } + bit_size_obj = fastargs[5]; +skip_optional_pos: + return_value = PyCField_new_impl(type, name, proto, size, offset, index, bit_size_obj); + +exit: + return return_value; +} +/*[clinic end generated code: output=27c010bae9be7213 input=a9049054013a1b77]*/ diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index a794cfe..2eb1b6c 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -216,18 +216,6 @@ extern int PyObject_stginfo(PyObject *self, Py_ssize_t *psize, Py_ssize_t *palig extern struct fielddesc *_ctypes_get_fielddesc(const char *fmt); -typedef enum { - LAYOUT_MODE_MS, - LAYOUT_MODE_GCC_SYSV, -} LayoutMode; - -extern PyObject * -PyCField_FromDesc(ctypes_state *st, PyObject *desc, Py_ssize_t index, - Py_ssize_t *pfield_size, Py_ssize_t bitsize, - Py_ssize_t *pbitofs, Py_ssize_t *psize, Py_ssize_t *poffset, - Py_ssize_t *palign, - int pack, int is_big_endian, LayoutMode layout_mode); - extern PyObject *PyCData_AtAddress(ctypes_state *st, PyObject *type, void *buf); extern PyObject *PyCData_FromBytes(ctypes_state *st, PyObject *type, char *data, Py_ssize_t length); @@ -259,16 +247,18 @@ struct fielddesc { GETFUNC getfunc_swapped; }; -typedef struct { +typedef struct CFieldObject { PyObject_HEAD Py_ssize_t offset; Py_ssize_t size; Py_ssize_t index; /* Index into CDataObject's object array */ - PyObject *proto; /* a type or NULL */ + PyObject *proto; /* underlying ctype; must have StgInfo */ GETFUNC getfunc; /* getter function if proto is NULL */ SETFUNC setfunc; /* setter function if proto is NULL */ int anonymous; + + PyObject *name; /* exact PyUnicode */ } CFieldObject; /**************************************************************** @@ -379,8 +369,6 @@ PyObject *_ctypes_callproc(ctypes_state *st, #define TYPEFLAG_ISPOINTER 0x100 #define TYPEFLAG_HASPOINTER 0x200 -#define TYPEFLAG_HASUNION 0x400 -#define TYPEFLAG_HASBITFIELD 0x800 #define DICTFLAG_FINAL 0x1000 @@ -436,10 +424,6 @@ extern void *_ctypes_alloc_closure(void); extern PyObject *PyCData_FromBaseObj(ctypes_state *st, PyObject *type, PyObject *base, Py_ssize_t index, char *adr); -extern char *_ctypes_alloc_format_string(const char *prefix, const char *suffix); -extern char *_ctypes_alloc_format_string_with_shape(int ndim, - const Py_ssize_t *shape, - const char *prefix, const char *suffix); extern int _ctypes_simple_instance(ctypes_state *st, PyObject *obj); diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 970f0a0..c9cd1c6 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -211,154 +211,41 @@ MakeAnonFields(PyObject *type) } /* - Allocate a memory block for a pep3118 format string, copy prefix (if - non-null) into it and append `{padding}x` to the end. - Returns NULL on failure, with the error indicator set. -*/ -char * -_ctypes_alloc_format_padding(const char *prefix, Py_ssize_t padding) -{ - /* int64 decimal characters + x + null */ - char buf[19 + 1 + 1]; - - assert(padding > 0); - - if (padding == 1) { - /* Use x instead of 1x, for brevity */ - return _ctypes_alloc_format_string(prefix, "x"); - } - - int ret = PyOS_snprintf(buf, sizeof(buf), "%zdx", padding); (void)ret; - assert(0 <= ret && ret < (Py_ssize_t)sizeof(buf)); - return _ctypes_alloc_format_string(prefix, buf); -} - -/* Retrieve the (optional) _pack_ attribute from a type, the _fields_ attribute, and initialize StgInfo. Used for Structure and Union subclasses. */ int PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct) { - Py_ssize_t len, offset, size, align, i; - Py_ssize_t union_size, total_align, aligned_size; - Py_ssize_t field_size = 0; - Py_ssize_t bitofs = 0; PyObject *tmp; - int pack; - int forced_alignment = 1; Py_ssize_t ffi_ofs; - int big_endian; int arrays_seen = 0; - if (fields == NULL) - return 0; - - int rc = PyObject_HasAttrWithError(type, &_Py_ID(_swappedbytes_)); - if (rc < 0) { - return -1; - } - if (rc) { - big_endian = !PY_BIG_ENDIAN; - } - else { - big_endian = PY_BIG_ENDIAN; - } - - if (PyObject_GetOptionalAttr(type, &_Py_ID(_pack_), &tmp) < 0) { - return -1; - } - if (tmp) { - 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 { - /* Setting `_pack_ = 0` amounts to using the default alignment */ - pack = 0; - } - - #ifdef MS_WIN32 - LayoutMode layout_mode = LAYOUT_MODE_MS; - #else - LayoutMode layout_mode = (pack > 0) ? LAYOUT_MODE_MS : LAYOUT_MODE_GCC_SYSV; - #endif - - if (PyObject_GetOptionalAttr(type, &_Py_ID(_layout_), &tmp) < 0) { - return -1; - } - if (tmp) { - if (!PyUnicode_Check(tmp)) { - PyErr_SetString(PyExc_TypeError, - "_layout_ must be a string"); - return -1; - } - if (PyUnicode_CompareWithASCIIString(tmp, "ms") == 0) { - layout_mode = LAYOUT_MODE_MS; - } - else if (PyUnicode_CompareWithASCIIString(tmp, "gcc-sysv") == 0) { - layout_mode = LAYOUT_MODE_GCC_SYSV; - if (pack > 0) { - PyErr_SetString(PyExc_ValueError, - "_pack_ is not compatible with _layout_=\"gcc-sysv\""); - return -1; - } - } - else { - PyErr_Format(PyExc_ValueError, - "unknown _layout_ %R", tmp); - return -1; - } - } - if (PyObject_GetOptionalAttr(type, &_Py_ID(_align_), &tmp) < 0) { - return -1; - } - if (tmp) { - forced_alignment = PyLong_AsInt(tmp); - Py_DECREF(tmp); - if (forced_alignment < 0) { - if (!PyErr_Occurred() || - PyErr_ExceptionMatches(PyExc_TypeError) || - PyErr_ExceptionMatches(PyExc_OverflowError)) - { - PyErr_SetString(PyExc_ValueError, - "_align_ must be a non-negative integer"); - } - return -1; - } - } - else { - /* Setting `_align_ = 0` amounts to using the default alignment */ - forced_alignment = 1; - } + int retval = -1; + // The following are NULL or hold strong references. + // They're cleared on error. + PyObject *layout_fields = NULL; + PyObject *layout = NULL; + PyObject *format_spec_obj = NULL; - 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; + if (fields == NULL) { + return 0; } ctypes_state *st = get_module_state_by_def(Py_TYPE(type)); StgInfo *stginfo; if (PyStgInfo_FromType(st, type, &stginfo) < 0) { - return -1; + goto error; } if (!stginfo) { PyErr_SetString(PyExc_TypeError, "ctypes state is not initialized"); - return -1; + goto error; + } + PyObject *base = (PyObject *)((PyTypeObject *)type)->tp_base; + StgInfo *baseinfo; + if (PyStgInfo_FromType(st, base, &baseinfo) < 0) { + goto error; } /* If this structure/union is already marked final we cannot assign @@ -367,40 +254,114 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct if (stginfo->flags & DICTFLAG_FINAL) {/* is final ? */ PyErr_SetString(PyExc_AttributeError, "_fields_ is final"); - return -1; + goto error; + } + + PyObject *layout_func = _PyImport_GetModuleAttrString("ctypes._layout", + "get_layout"); + if (!layout_func) { + goto error; + } + PyObject *kwnames = PyTuple_Pack( + 2, + &_Py_ID(is_struct), + &_Py_ID(base)); + if (!kwnames) { + goto error; + } + layout = PyObject_Vectorcall( + layout_func, + 1 + (PyObject*[]){ + NULL, + /* positional args */ + type, + fields, + /* keyword args */ + isStruct ? Py_True : Py_False, + baseinfo ? base : Py_None}, + 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, + kwnames); + Py_DECREF(kwnames); + Py_DECREF(layout_func); + fields = NULL; // a borrowed reference we won't be using again + if (!layout) { + goto error; + } + + tmp = PyObject_GetAttr(layout, &_Py_ID(align)); + if (!tmp) { + goto error; + } + Py_ssize_t total_align = PyLong_AsInt(tmp); + Py_DECREF(tmp); + if (total_align < 0) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_ValueError, + "align must be a non-negative integer"); + } + goto error; + } + + tmp = PyObject_GetAttr(layout, &_Py_ID(size)); + if (!tmp) { + goto error; + } + Py_ssize_t total_size = PyLong_AsInt(tmp); + Py_DECREF(tmp); + if (total_size < 0) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_ValueError, + "size must be a non-negative integer"); + } + goto error; + } + + format_spec_obj = PyObject_GetAttr(layout, &_Py_ID(format_spec)); + if (!format_spec_obj) { + goto error; + } + Py_ssize_t format_spec_size; + const char *format_spec = PyUnicode_AsUTF8AndSize(format_spec_obj, + &format_spec_size); + if (!format_spec) { + goto error; } if (stginfo->format) { PyMem_Free(stginfo->format); stginfo->format = NULL; } + stginfo->format = PyMem_Malloc(format_spec_size + 1); + if (!stginfo->format) { + PyErr_NoMemory(); + goto error; + } + memcpy(stginfo->format, format_spec, format_spec_size + 1); - if (stginfo->ffi_type_pointer.elements) - PyMem_Free(stginfo->ffi_type_pointer.elements); - - StgInfo *baseinfo; - if (PyStgInfo_FromType(st, (PyObject *)((PyTypeObject *)type)->tp_base, - &baseinfo) < 0) { - return -1; + PyObject *layout_fields_obj = PyObject_GetAttr(layout, &_Py_ID(fields)); + if (!layout_fields_obj) { + goto error; } - if (baseinfo) { - stginfo->flags |= (baseinfo->flags & - (TYPEFLAG_HASUNION | TYPEFLAG_HASBITFIELD)); + layout_fields = PySequence_Tuple(layout_fields_obj); + Py_DECREF(layout_fields_obj); + if (!layout_fields) { + goto error; } - if (!isStruct) { - stginfo->flags |= TYPEFLAG_HASUNION; + Py_CLEAR(layout); + + Py_ssize_t len = PyTuple_GET_SIZE(layout_fields); + + if (stginfo->ffi_type_pointer.elements) { + PyMem_Free(stginfo->ffi_type_pointer.elements); + stginfo->ffi_type_pointer.elements = NULL; } + if (baseinfo) { - size = offset = baseinfo->size; - align = baseinfo->align; - union_size = 0; - total_align = align ? align : 1; - total_align = max(total_align, forced_alignment); stginfo->ffi_type_pointer.type = FFI_TYPE_STRUCT; stginfo->ffi_type_pointer.elements = PyMem_New(ffi_type *, baseinfo->length + len + 1); if (stginfo->ffi_type_pointer.elements == NULL) { PyErr_NoMemory(); - return -1; + goto error; } memset(stginfo->ffi_type_pointer.elements, 0, sizeof(ffi_type *) * (baseinfo->length + len + 1)); @@ -411,231 +372,61 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct } ffi_ofs = baseinfo->length; } else { - offset = 0; - size = 0; - align = 0; - union_size = 0; - total_align = forced_alignment; stginfo->ffi_type_pointer.type = FFI_TYPE_STRUCT; stginfo->ffi_type_pointer.elements = PyMem_New(ffi_type *, len + 1); if (stginfo->ffi_type_pointer.elements == NULL) { PyErr_NoMemory(); - return -1; + goto error; } memset(stginfo->ffi_type_pointer.elements, 0, sizeof(ffi_type *) * (len + 1)); ffi_ofs = 0; } - assert(stginfo->format == NULL); - if (isStruct) { - stginfo->format = _ctypes_alloc_format_string(NULL, "T{"); - } else { - /* PEP3118 doesn't support union. Use 'B' for bytes. */ - stginfo->format = _ctypes_alloc_format_string(NULL, "B"); - } - if (stginfo->format == NULL) - return -1; + for (Py_ssize_t i = 0; i < len; ++i) { + PyObject *prop_obj = PyTuple_GET_ITEM(layout_fields, i); + assert(prop_obj); + if (!PyType_IsSubtype(Py_TYPE(prop_obj), st->PyCField_Type)) { + PyErr_Format(PyExc_TypeError, + "fields must be of type CField, got %T", prop_obj); + goto error; - for (i = 0; i < len; ++i) { - PyObject *name = NULL, *desc = NULL; - PyObject *pair = PySequence_GetItem(fields, i); - PyObject *prop; - Py_ssize_t bitsize = 0; + } + CFieldObject *prop = (CFieldObject *)prop_obj; // borrow from prop_obj - if (!pair || !PyArg_ParseTuple(pair, "UO|n", &name, &desc, &bitsize)) { - PyErr_SetString(PyExc_TypeError, - "'_fields_' must be a sequence of (name, C type) pairs"); - Py_XDECREF(pair); - return -1; + if (prop->index != i) { + PyErr_Format(PyExc_ValueError, + "field %R index mismatch (expected %zd, got %zd)", + prop->name, i, prop->index); + goto error; } - if (PyCArrayTypeObject_Check(st, desc)) { + + if (PyCArrayTypeObject_Check(st, prop->proto)) { arrays_seen = 1; } StgInfo *info; - if (PyStgInfo_FromType(st, desc, &info) < 0) { - Py_DECREF(pair); - return -1; - } - if (info == NULL) { - Py_DECREF(pair); - PyErr_Format(PyExc_TypeError, - "second item in _fields_ tuple (index %zd) must be a C type", - i); - return -1; + if (PyStgInfo_FromType(st, prop->proto, &info) < 0) { + goto error; } + assert(info); stginfo->ffi_type_pointer.elements[ffi_ofs + i] = &info->ffi_type_pointer; if (info->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER)) stginfo->flags |= TYPEFLAG_HASPOINTER; - stginfo->flags |= info->flags & (TYPEFLAG_HASUNION | TYPEFLAG_HASBITFIELD); info->flags |= DICTFLAG_FINAL; /* mark field type final */ - if (PyTuple_Size(pair) == 3) { /* bits specified */ - stginfo->flags |= TYPEFLAG_HASBITFIELD; - switch(info->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 (info->getfunc != _ctypes_get_fielddesc("c")->getfunc - && info->getfunc != _ctypes_get_fielddesc("u")->getfunc) - { - break; - } - _Py_FALLTHROUGH; /* 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 > info->size * 8) { - PyErr_Format(PyExc_ValueError, - "number of bits invalid for bit field %R", - name); - Py_DECREF(pair); - return -1; - } - } else - bitsize = 0; - - if (isStruct) { - const char *fieldfmt = info->format ? info->format : "B"; - const char *fieldname = PyUnicode_AsUTF8(name); - char *ptr; - Py_ssize_t len; - char *buf; - Py_ssize_t last_size = size; - Py_ssize_t padding; - - if (fieldname == NULL) - { - Py_DECREF(pair); - return -1; - } - - /* construct the field now, as `prop->offset` is `offset` with - corrected alignment */ - prop = PyCField_FromDesc(st, desc, i, - &field_size, bitsize, &bitofs, - &size, &offset, &align, - pack, big_endian, layout_mode); - if (prop == NULL) { - Py_DECREF(pair); - return -1; - } - - /* number of bytes between the end of the last field and the start - of this one */ - padding = ((CFieldObject *)prop)->offset - last_size; - - if (padding > 0) { - ptr = stginfo->format; - stginfo->format = _ctypes_alloc_format_padding(ptr, padding); - PyMem_Free(ptr); - if (stginfo->format == NULL) { - Py_DECREF(pair); - Py_DECREF(prop); - return -1; - } - } - - len = strlen(fieldname) + strlen(fieldfmt); - - buf = PyMem_Malloc(len + 2 + 1); - if (buf == NULL) { - Py_DECREF(pair); - Py_DECREF(prop); - PyErr_NoMemory(); - return -1; - } - sprintf(buf, "%s:%s:", fieldfmt, fieldname); - - ptr = stginfo->format; - if (info->shape != NULL) { - stginfo->format = _ctypes_alloc_format_string_with_shape( - info->ndim, info->shape, stginfo->format, buf); - } else { - stginfo->format = _ctypes_alloc_format_string(stginfo->format, buf); - } - PyMem_Free(ptr); - PyMem_Free(buf); - - if (stginfo->format == NULL) { - Py_DECREF(pair); - Py_DECREF(prop); - return -1; - } - } else /* union */ { - field_size = 0; - size = 0; - bitofs = 0; - offset = 0; - align = 0; - prop = PyCField_FromDesc(st, desc, i, - &field_size, bitsize, &bitofs, - &size, &offset, &align, - pack, big_endian, layout_mode); - if (prop == NULL) { - Py_DECREF(pair); - return -1; - } - union_size = max(size, union_size); - } - total_align = max(align, total_align); - if (-1 == PyObject_SetAttr(type, name, prop)) { - Py_DECREF(prop); - Py_DECREF(pair); - return -1; + if (-1 == PyObject_SetAttr(type, prop->name, prop_obj)) { + goto error; } - Py_DECREF(pair); - Py_DECREF(prop); - } - - if (!isStruct) { - size = union_size; - } - - /* Adjust the size according to the alignment requirements */ - aligned_size = ((size + total_align - 1) / total_align) * total_align; - - if (isStruct) { - char *ptr; - Py_ssize_t padding; - - /* Pad up to the full size of the struct */ - padding = aligned_size - size; - if (padding > 0) { - ptr = stginfo->format; - stginfo->format = _ctypes_alloc_format_padding(ptr, padding); - PyMem_Free(ptr); - if (stginfo->format == NULL) { - return -1; - } - } - - ptr = stginfo->format; - stginfo->format = _ctypes_alloc_format_string(stginfo->format, "}"); - PyMem_Free(ptr); - if (stginfo->format == NULL) - return -1; } stginfo->ffi_type_pointer.alignment = Py_SAFE_DOWNCAST(total_align, Py_ssize_t, unsigned short); - stginfo->ffi_type_pointer.size = aligned_size; + stginfo->ffi_type_pointer.size = total_size; - stginfo->size = aligned_size; + stginfo->size = total_size; stginfo->align = total_align; stginfo->length = ffi_ofs + len; @@ -650,7 +441,7 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct # define MAX_STRUCT_SIZE 16 #endif - if (arrays_seen && (size <= MAX_STRUCT_SIZE)) { + if (arrays_seen && (total_size <= MAX_STRUCT_SIZE)) { /* * See bpo-22273 and gh-110190. Arrays are normally treated as * pointers, which is fine when an array name is being passed as @@ -725,35 +516,19 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct 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); - 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; - } + for (Py_ssize_t i = 0; i < len; ++i) { + PyObject *prop_obj = PyTuple_GET_ITEM(layout_fields, i); // borrowed + assert(prop_obj); + assert(PyType_IsSubtype(Py_TYPE(prop_obj), st->PyCField_Type)); + CFieldObject *prop = (CFieldObject *)prop_obj; // borrowed StgInfo *info; - if (PyStgInfo_FromType(st, desc, &info) < 0) { - Py_DECREF(pair); - return -1; - } - if (info == NULL) { - Py_DECREF(pair); - PyErr_Format(PyExc_TypeError, - "second item in _fields_ tuple (index %zd) must be a C type", - i); - return -1; + if (PyStgInfo_FromType(st, prop->proto, &info) < 0) { + goto error; } + assert(info); - if (!PyCArrayTypeObject_Check(st, desc)) { + if (!PyCArrayTypeObject_Check(st, prop->proto)) { /* Not an array. Just need an ffi_type pointer. */ num_ffi_type_pointers++; } @@ -763,15 +538,13 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct StgInfo *einfo; if (PyStgInfo_FromType(st, info->proto, &einfo) < 0) { - Py_DECREF(pair); - return -1; + goto error; } if (einfo == NULL) { - Py_DECREF(pair); PyErr_Format(PyExc_TypeError, "second item in _fields_ tuple (index %zd) must be a C type", i); - return -1; + goto error; } /* * We need one extra ffi_type to hold the struct, and one @@ -781,7 +554,6 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct num_ffi_types++; num_ffi_type_pointers += length + 1; } - Py_DECREF(pair); } /* @@ -798,7 +570,7 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct if (type_block == NULL) { PyErr_NoMemory(); - return -1; + goto error; } /* * the first block takes up ffi_ofs + len + 1 which is the pointers * @@ -822,48 +594,21 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct 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); - 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; - } + for (Py_ssize_t i = 0; i < len; ++i) { + PyObject *prop_obj = PyTuple_GET_ITEM(layout_fields, i); // borrowed + assert(prop_obj); + assert(PyType_IsSubtype(Py_TYPE(prop_obj), st->PyCField_Type)); + CFieldObject *prop = (CFieldObject *)prop_obj; // borrowed StgInfo *info; - if (PyStgInfo_FromType(st, desc, &info) < 0) { - Py_DECREF(pair); - PyMem_Free(type_block); - return -1; - } - - /* Possibly this check could be avoided, but see above comment. */ - if (info == NULL) { - Py_DECREF(pair); + if (PyStgInfo_FromType(st, prop->proto, &info) < 0) { PyMem_Free(type_block); - PyErr_Format(PyExc_TypeError, - "second item in _fields_ tuple (index %zd) must be a C type", - i); - return -1; + goto error; } + assert(info); assert(element_index < (ffi_ofs + len)); /* will be used below */ - if (!PyCArrayTypeObject_Check(st, desc)) { + if (!PyCArrayTypeObject_Check(st, prop->proto)) { /* Not an array. Just copy over the element ffi_type. */ element_types[element_index++] = &info->ffi_type_pointer; } @@ -871,17 +616,15 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct Py_ssize_t length = info->length; StgInfo *einfo; if (PyStgInfo_FromType(st, info->proto, &einfo) < 0) { - Py_DECREF(pair); PyMem_Free(type_block); - return -1; + goto error; } if (einfo == 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; + goto error; } element_types[element_index++] = &structs[struct_index]; structs[struct_index].size = length * einfo->ffi_type_pointer.size; @@ -898,7 +641,6 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct assert(dummy_index < (num_ffi_type_pointers)); dummy_types[dummy_index++] = NULL; } - Py_DECREF(pair); } element_types[element_index] = NULL; @@ -916,9 +658,14 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct if (stginfo->flags & DICTFLAG_FINAL) { PyErr_SetString(PyExc_AttributeError, "Structure or union cannot contain itself"); - return -1; + goto error; } stginfo->flags |= DICTFLAG_FINAL; - return MakeAnonFields(type); + retval = MakeAnonFields(type); +error: + Py_XDECREF(layout_fields); + Py_XDECREF(layout); + Py_XDECREF(format_spec_obj); + return retval; } |