summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/internal/pycore_global_objects_fini_generated.h10
-rw-r--r--Include/internal/pycore_global_strings.h10
-rw-r--r--Include/internal/pycore_runtime_init_generated.h10
-rw-r--r--Include/internal/pycore_unicodeobject_generated.h40
-rw-r--r--Lib/ctypes/_layout.py337
-rw-r--r--Lib/test/test_ctypes/test_bitfields.py33
-rw-r--r--Lib/test/test_ctypes/test_struct_fields.py1
-rw-r--r--Modules/_ctypes/_ctypes.c13
-rw-r--r--Modules/_ctypes/cfield.c302
-rw-r--r--Modules/_ctypes/clinic/cfield.c.h113
-rw-r--r--Modules/_ctypes/ctypes.h24
-rw-r--r--Modules/_ctypes/stgdict.c579
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;
}