gh-97588: Move ctypes struct/union layout logic to Python (GH-123352)

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
This commit is contained in:
Petr Viktorin 2024-09-05 11:20:07 +02:00 committed by GitHub
parent 1fdfce9452
commit ce9f84a47b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 809 additions and 671 deletions

View File

@ -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));

View File

@ -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)

View File

@ -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), \

View File

@ -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));

337
Lib/ctypes/_layout.py Normal file
View File

@ -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"

View File

@ -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()

View File

@ -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):

View File

@ -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);
/*************************************************
*

View File

@ -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.
/*[clinic input]
@classmethod
_ctypes.CField.__new__ as PyCField_new
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.
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
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 start generated code]*/
See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-mms-bitfields for details.
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.
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
)
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]*/
{
// We don't use poffset here, so clear it, if it has been set.
*pbitofs += *poffset * 8;
*poffset = 0;
CFieldObject* self = NULL;
if (size < 0) {
PyErr_Format(PyExc_ValueError,
"size of field %R must not be negative, got %zd",
name, size);
goto error;
}
// 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;
}
*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);
PyTypeObject *tp = type;
ctypes_state *st = get_module_state_by_class(tp);
self = (CFieldObject *)tp->tp_alloc(tp, 0);
if (!self) {
return NULL;
}
if (PyUnicode_CheckExact(name)) {
self->name = Py_NewRef(name);
} else {
self->name = PyObject_Str(name);
if (!self->name) {
goto error;
}
}
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;
}
*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;
}
// *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;
}
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);
} else {
self->size = info->size;
}
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;
/* Field descriptors for 'c_char * n' are be scpecial cased to
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 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,
};

113
Modules/_ctypes/clinic/cfield.c.h generated Normal file
View File

@ -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]*/

View File

@ -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);

View File

@ -210,29 +210,6 @@ MakeAnonFields(PyObject *type)
return 0;
}
/*
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.
@ -240,125 +217,35 @@ _ctypes_alloc_format_padding(const char *prefix, Py_ssize_t padding)
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)
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;
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;
}
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;
}
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)
PyObject *layout_fields_obj = PyObject_GetAttr(layout, &_Py_ID(fields));
if (!layout_fields_obj) {
goto error;
}
layout_fields = PySequence_Tuple(layout_fields_obj);
Py_DECREF(layout_fields_obj);
if (!layout_fields) {
goto error;
}
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;
}
StgInfo *baseinfo;
if (PyStgInfo_FromType(st, (PyObject *)((PyTypeObject *)type)->tp_base,
&baseinfo) < 0) {
return -1;
}
if (baseinfo) {
stginfo->flags |= (baseinfo->flags &
(TYPEFLAG_HASUNION | TYPEFLAG_HASBITFIELD));
}
if (!isStruct) {
stginfo->flags |= TYPEFLAG_HASUNION;
}
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;
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 (PyCArrayTypeObject_Check(st, desc)) {
CFieldObject *prop = (CFieldObject *)prop_obj; // borrow from prop_obj
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, 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);
if (-1 == PyObject_SetAttr(type, prop->name, prop_obj)) {
goto error;
}
total_align = max(align, total_align);
if (-1 == PyObject_SetAttr(type, name, prop)) {
Py_DECREF(prop);
Py_DECREF(pair);
return -1;
}
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);
if (PyStgInfo_FromType(st, prop->proto, &info) < 0) {
PyMem_Free(type_block);
return -1;
}
/* Possibly this check could be avoided, but see above comment. */
if (info == 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;
}
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;
}