summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Viktorin <encukou@gmail.com>2024-12-20 13:28:18 (GMT)
committerGitHub <noreply@github.com>2024-12-20 13:28:18 (GMT)
commit78ffba4221dcb2e39fd5db80c297d1777588bb59 (patch)
tree1b5d16b0bac6d061035486b644f303c305935206
parentba45e5cdd41a39ce0b3de08bdcfa9d8e28e0e4f3 (diff)
downloadcpython-78ffba4221dcb2e39fd5db80c297d1777588bb59.zip
cpython-78ffba4221dcb2e39fd5db80c297d1777588bb59.tar.gz
cpython-78ffba4221dcb2e39fd5db80c297d1777588bb59.tar.bz2
gh-127295: ctypes: Switch field accessors to fixed-width integers (GH-127297)
This should be a pure refactoring, without user-visible behaviour changes. Before this change, ctypes uses traditional native C types, usually identified by [`struct` format characters][struct-chars] when a short (and identifier-friendly) name is needed: - `signed char` (`b`) / `unsigned char` (`B`) - `short` (`h`) / `unsigned short` (`h`) - `int` (`i`) / `unsigned int` (`i`) - `long` (`l`) / `unsigned long` (`l`) - `long long` (`q`) / `unsigned long long` (`q`) These map to C99 fixed-width types, which this PR switches to: - - `int8_t`/`uint8_t` - `int16_t`/`uint16_t` - `int32_t`/`uint32_t` - `int64_t`/`uint64_t` The C standard doesn't guarantee that the “traditional” types must map to the fixints. But, [`ctypes` currently requires it][swapdefs], so the assumption won't break anything. By “map” I mean that the *size* of the types matches. The *alignment* requirements might not. This needs to be kept in mind but is not an issue in `ctypes` accessors, which [explicitly handle unaligned memory][memcpy] for the integer types. Note that there are 5 “traditional” C type sizes, but 4 fixed-width ones. Two of the former are functionally identical to one another; which ones they are is platform-specific (e.g. `int`==`long`==`int32_t`.) This means that one of the [current][current-impls-1] [implementations][current-impls-2] is redundant on any given platform. The fixint types are parametrized by the number of bytes/bits, and one bit for signedness. This makes it easier to autogenerate code for them or to write generic macros (though generic API like [`PyLong_AsNativeBytes`][PyLong_AsNativeBytes] is problematic for performance reasons -- especially compared to a `memcpy` with compile-time-constant size). When one has a *different* integer type, determining the corresponding fixint means a `sizeof` and signedness check. This is easier and more robust than the current implementations (see [`wchar_t`][sizeof-wchar_t] or [`_Bool`][sizeof-bool]). [swapdefs]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L420-L444 [struct-chars]: https://docs.python.org/3/library/struct.html#format-characters [current-impls-1]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L470-L653 [current-impls-2]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L703-L944 [memcpy]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L613 [PyLong_AsNativeBytes]: https://docs.python.org/3/c-api/long.html#c.PyLong_AsNativeBytes [sizeof-wchar_t]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L1547-L1555 [sizeof-bool]: https://github.com/python/cpython/blob/v3.13.0/Modules/_ctypes/cfield.c#L1562-L1572 Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
-rw-r--r--Modules/_ctypes/_ctypes.c4
-rw-r--r--Modules/_ctypes/callbacks.c2
-rw-r--r--Modules/_ctypes/cfield.c1132
-rw-r--r--Modules/_ctypes/ctypes.h15
4 files changed, 501 insertions, 652 deletions
diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index dcdb7b2..ac520ff 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -1979,7 +1979,7 @@ c_void_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value)
return NULL;
parg->pffi_type = &ffi_type_pointer;
parg->tag = 'P';
- parg->obj = fd->setfunc(&parg->value, value, 0);
+ parg->obj = fd->setfunc(&parg->value, value, sizeof(void*));
if (parg->obj == NULL) {
Py_DECREF(parg);
return NULL;
@@ -2444,7 +2444,7 @@ PyCSimpleType_from_param_impl(PyObject *type, PyTypeObject *cls,
parg->tag = fmt[0];
parg->pffi_type = fd->pffi_type;
- parg->obj = fd->setfunc(&parg->value, value, 0);
+ parg->obj = fd->setfunc(&parg->value, value, info->size);
if (parg->obj)
return (PyObject *)parg;
PyObject *exc = PyErr_GetRaisedException();
diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c
index 7b9f643..89c0749 100644
--- a/Modules/_ctypes/callbacks.c
+++ b/Modules/_ctypes/callbacks.c
@@ -264,7 +264,7 @@ static void _CallPythonObject(ctypes_state *st,
be the result. EXCEPT when restype is py_object - Python
itself knows how to manage the refcount of these objects.
*/
- PyObject *keep = setfunc(mem, result, 0);
+ PyObject *keep = setfunc(mem, result, restype->size);
if (keep == NULL) {
/* Could not convert callback result. */
diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c
index 2b9e8a1..dcac9da 100644
--- a/Modules/_ctypes/cfield.c
+++ b/Modules/_ctypes/cfield.c
@@ -10,6 +10,7 @@
#include "pycore_bitutils.h" // _Py_bswap32()
#include "pycore_call.h" // _PyObject_CallNoArgs()
+#include <stdbool.h> // bool
#include <ffi.h>
#include "ctypes.h"
@@ -320,61 +321,6 @@ PyType_Spec cfield_spec = {
};
-/******************************************************************/
-/*
- Accessor functions
-*/
-
-/* Derived from Modules/structmodule.c:
- Helper routine to get a Python integer and raise the appropriate error
- if it isn't one */
-
-static int
-get_long(PyObject *v, long *p)
-{
- long x = PyLong_AsUnsignedLongMask(v);
- if (x == -1 && PyErr_Occurred())
- return -1;
- *p = x;
- return 0;
-}
-
-/* Same, but handling unsigned long */
-
-static int
-get_ulong(PyObject *v, unsigned long *p)
-{
- unsigned long x = PyLong_AsUnsignedLongMask(v);
- if (x == (unsigned long)-1 && PyErr_Occurred())
- return -1;
- *p = x;
- return 0;
-}
-
-/* Same, but handling native long long. */
-
-static int
-get_longlong(PyObject *v, long long *p)
-{
- long long x = PyLong_AsUnsignedLongLongMask(v);
- if (x == -1 && PyErr_Occurred())
- return -1;
- *p = x;
- return 0;
-}
-
-/* Same, but handling native unsigned long long. */
-
-static int
-get_ulonglong(PyObject *v, unsigned long long *p)
-{
- unsigned long long x = PyLong_AsUnsignedLongLongMask(v);
- if (x == (unsigned long long)-1 && PyErr_Occurred())
- return -1;
- *p = x;
- return 0;
-}
-
/*****************************************************************
* Integer fields, with bitfield support
*/
@@ -404,34 +350,8 @@ Py_ssize_t NUM_BITS(Py_ssize_t bitsize) {
/* This macro RETURNS the first parameter with the bit field CHANGED. */
#define SET(type, x, v, size) \
(NUM_BITS(size) ? \
- ( ( (type)x & ~(BIT_MASK(type, size) << LOW_BIT(size)) ) | ( ((type)v & BIT_MASK(type, size)) << LOW_BIT(size) ) ) \
- : (type)v)
-
-#if SIZEOF_SHORT == 2
-# define SWAP_SHORT _Py_bswap16
-#else
-# error "unsupported short size"
-#endif
-
-#if SIZEOF_INT == 4
-# define SWAP_INT _Py_bswap32
-#else
-# error "unsupported int size"
-#endif
-
-#if SIZEOF_LONG == 4
-# define SWAP_LONG _Py_bswap32
-#elif SIZEOF_LONG == 8
-# define SWAP_LONG _Py_bswap64
-#else
-# error "unsupported long size"
-#endif
-
-#if SIZEOF_LONG_LONG == 8
-# define SWAP_LONG_LONG _Py_bswap64
-#else
-# error "unsupported long long size"
-#endif
+ ( ( (type)(x) & ~(BIT_MASK(type, size) << LOW_BIT(size)) ) | ( ((type)(v) & BIT_MASK(type, size)) << LOW_BIT(size) ) ) \
+ : (type)(v))
/*****************************************************************
* The setter methods return an object which must be kept alive, to keep the
@@ -454,203 +374,145 @@ Py_ssize_t NUM_BITS(Py_ssize_t bitsize) {
#endif
/*****************************************************************
- * integer accessor methods, supporting bit fields
+ * accessor methods for fixed-width integers (e.g. int8_t, uint64_t),
+ * supporting bit fields.
+ * These are named e.g. `i8_set`/`i8_get` or `u64_set`/`u64_get`,
+ * and are all alike, so they're defined using a macro.
*/
-static PyObject *
-b_set(void *ptr, PyObject *value, Py_ssize_t size)
-{
- long val;
- if (get_long(value, &val) < 0)
- return NULL;
- *(signed char *)ptr = SET(signed char, *(signed char *)ptr, val, size);
- _RET(value);
-}
-
-
-static PyObject *
-b_get(void *ptr, Py_ssize_t size)
-{
- signed char val = *(signed char *)ptr;
- GET_BITFIELD(val, size);
- return PyLong_FromLong(val);
-}
-
-static PyObject *
-B_set(void *ptr, PyObject *value, Py_ssize_t size)
-{
- unsigned long val;
- if (get_ulong(value, &val) < 0)
- return NULL;
- *(unsigned char *)ptr = SET(unsigned char, *(unsigned char*)ptr, val, size);
- _RET(value);
-}
-
-
-static PyObject *
-B_get(void *ptr, Py_ssize_t size)
-{
- unsigned char val = *(unsigned char *)ptr;
- GET_BITFIELD(val, size);
- return PyLong_FromLong(val);
-}
-
-static PyObject *
-h_set(void *ptr, PyObject *value, Py_ssize_t size)
-{
- long val;
- short x;
- if (get_long(value, &val) < 0)
- return NULL;
- memcpy(&x, ptr, sizeof(x));
- x = SET(short, x, val, size);
- memcpy(ptr, &x, sizeof(x));
- _RET(value);
-}
-
-
-static PyObject *
-h_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
-{
- long val;
- short field;
- if (get_long(value, &val) < 0) {
- return NULL;
- }
- memcpy(&field, ptr, sizeof(field));
- field = SWAP_SHORT(field);
- field = SET(short, field, val, size);
- field = SWAP_SHORT(field);
- memcpy(ptr, &field, sizeof(field));
- _RET(value);
-}
-
-static PyObject *
-h_get(void *ptr, Py_ssize_t size)
-{
- short val;
- memcpy(&val, ptr, sizeof(val));
- GET_BITFIELD(val, size);
- return PyLong_FromLong((long)val);
-}
-
-static PyObject *
-h_get_sw(void *ptr, Py_ssize_t size)
-{
- short val;
- memcpy(&val, ptr, sizeof(val));
- val = SWAP_SHORT(val);
- GET_BITFIELD(val, size);
- return PyLong_FromLong(val);
-}
-
-static PyObject *
-H_set(void *ptr, PyObject *value, Py_ssize_t size)
-{
- unsigned long val;
- unsigned short x;
- if (get_ulong(value, &val) < 0)
- return NULL;
- memcpy(&x, ptr, sizeof(x));
- x = SET(unsigned short, x, val, size);
- memcpy(ptr, &x, sizeof(x));
- _RET(value);
-}
-
-static PyObject *
-H_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
-{
- unsigned long val;
- unsigned short field;
- if (get_ulong(value, &val) < 0) {
- return NULL;
- }
- memcpy(&field, ptr, sizeof(field));
- field = SWAP_SHORT(field);
- field = SET(unsigned short, field, val, size);
- field = SWAP_SHORT(field);
- memcpy(ptr, &field, sizeof(field));
- _RET(value);
-}
-
-
-static PyObject *
-H_get(void *ptr, Py_ssize_t size)
-{
- unsigned short val;
- memcpy(&val, ptr, sizeof(val));
- GET_BITFIELD(val, size);
- return PyLong_FromLong(val);
-}
-
-static PyObject *
-H_get_sw(void *ptr, Py_ssize_t size)
-{
- unsigned short val;
- memcpy(&val, ptr, sizeof(val));
- val = SWAP_SHORT(val);
- GET_BITFIELD(val, size);
- return PyLong_FromLong(val);
-}
-
-static PyObject *
-i_set(void *ptr, PyObject *value, Py_ssize_t size)
-{
- long val;
- int x;
- if (get_long(value, &val) < 0)
- return NULL;
- memcpy(&x, ptr, sizeof(x));
- x = SET(int, x, val, size);
- memcpy(ptr, &x, sizeof(x));
- _RET(value);
-}
-
-static PyObject *
-i_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
-{
- long val;
- int field;
- if (get_long(value, &val) < 0) {
- return NULL;
- }
- memcpy(&field, ptr, sizeof(field));
- field = SWAP_INT(field);
- field = SET(int, field, val, size);
- field = SWAP_INT(field);
- memcpy(ptr, &field, sizeof(field));
- _RET(value);
-}
+#define FIXINT_GETSET(TAG, CTYPE, NBITS, PYAPI_FROMFUNC) \
+ static PyObject * \
+ TAG ## _set(void *ptr, PyObject *value, Py_ssize_t size_arg) \
+ { \
+ assert(NUM_BITS(size_arg) || (size_arg == (NBITS) / 8)); \
+ CTYPE val; \
+ if (PyLong_Check(value) \
+ && PyUnstable_Long_IsCompact((PyLongObject *)value)) \
+ { \
+ val = (CTYPE)PyUnstable_Long_CompactValue( \
+ (PyLongObject *)value); \
+ } \
+ else { \
+ Py_ssize_t res = PyLong_AsNativeBytes( \
+ value, &val, (NBITS) / 8, \
+ Py_ASNATIVEBYTES_NATIVE_ENDIAN \
+ | Py_ASNATIVEBYTES_ALLOW_INDEX); \
+ if (res < 0) { \
+ return NULL; \
+ } \
+ } \
+ CTYPE prev; \
+ memcpy(&prev, ptr, (NBITS) / 8); \
+ val = SET(CTYPE, prev, val, size_arg); \
+ memcpy(ptr, &val, (NBITS) / 8); \
+ _RET(value); \
+ } \
+ \
+ static PyObject * \
+ TAG ## _get(void *ptr, Py_ssize_t size_arg) \
+ { \
+ assert(NUM_BITS(size_arg) || (size_arg == (NBITS) / 8)); \
+ CTYPE val; \
+ memcpy(&val, ptr, sizeof(val)); \
+ GET_BITFIELD(val, size_arg); \
+ return PYAPI_FROMFUNC(val); \
+ } \
+ ///////////////////////////////////////////////////////////////////////////
+
+/* Another macro for byte-swapped variants (e.g. `i8_set_sw`/`i8_get_sw`) */
+
+#define FIXINT_GETSET_SW(TAG, CTYPE, NBITS, PYAPI_FROMFUNC, PY_SWAPFUNC) \
+ static PyObject * \
+ TAG ## _set_sw(void *ptr, PyObject *value, Py_ssize_t size_arg) \
+ { \
+ CTYPE val; \
+ PyObject *res = TAG ## _set(&val, value, (NBITS) / 8); \
+ if (res == NULL) { \
+ return NULL; \
+ } \
+ Py_DECREF(res); \
+ CTYPE field; \
+ memcpy(&field, ptr, sizeof(field)); \
+ field = PY_SWAPFUNC(field); \
+ field = SET(CTYPE, field, val, size_arg); \
+ field = PY_SWAPFUNC(field); \
+ memcpy(ptr, &field, sizeof(field)); \
+ _RET(value); \
+ } \
+ \
+ static PyObject * \
+ TAG ## _get_sw(void *ptr, Py_ssize_t size_arg) \
+ { \
+ assert(NUM_BITS(size_arg) || (size_arg == (NBITS) / 8)); \
+ CTYPE val; \
+ memcpy(&val, ptr, sizeof(val)); \
+ val = PY_SWAPFUNC(val); \
+ GET_BITFIELD(val, size_arg); \
+ return PYAPI_FROMFUNC(val); \
+ } \
+ ///////////////////////////////////////////////////////////////////////////
+
+/* These macros are expanded for all supported combinations of byte sizes
+ * (1, 2, 4, 8), signed and unsigned, native and swapped byteorder.
+ * That's a lot, so generate the list with Argument Clinic (`make clinic`).
+ */
+/*[python input]
+for nbits in 8, 16, 32, 64:
+ for sgn in 'i', 'u':
+ u = 'u' if sgn == 'u' else ''
+ U = u.upper()
+ apibits = max(nbits, 32)
+ parts = [
+ f'{sgn}{nbits}',
+ f'{u}int{nbits}_t',
+ f'{nbits}',
+ f'PyLong_From{U}Int{apibits}',
+ ]
+ print(f'FIXINT_GETSET({", ".join(parts)})')
+ if nbits > 8:
+ parts.append(f'_Py_bswap{nbits}')
+ print(f'FIXINT_GETSET_SW({", ".join(parts)})')
+[python start generated code]*/
+FIXINT_GETSET(i8, int8_t, 8, PyLong_FromInt32)
+FIXINT_GETSET(u8, uint8_t, 8, PyLong_FromUInt32)
+FIXINT_GETSET(i16, int16_t, 16, PyLong_FromInt32)
+FIXINT_GETSET_SW(i16, int16_t, 16, PyLong_FromInt32, _Py_bswap16)
+FIXINT_GETSET(u16, uint16_t, 16, PyLong_FromUInt32)
+FIXINT_GETSET_SW(u16, uint16_t, 16, PyLong_FromUInt32, _Py_bswap16)
+FIXINT_GETSET(i32, int32_t, 32, PyLong_FromInt32)
+FIXINT_GETSET_SW(i32, int32_t, 32, PyLong_FromInt32, _Py_bswap32)
+FIXINT_GETSET(u32, uint32_t, 32, PyLong_FromUInt32)
+FIXINT_GETSET_SW(u32, uint32_t, 32, PyLong_FromUInt32, _Py_bswap32)
+FIXINT_GETSET(i64, int64_t, 64, PyLong_FromInt64)
+FIXINT_GETSET_SW(i64, int64_t, 64, PyLong_FromInt64, _Py_bswap64)
+FIXINT_GETSET(u64, uint64_t, 64, PyLong_FromUInt64)
+FIXINT_GETSET_SW(u64, uint64_t, 64, PyLong_FromUInt64, _Py_bswap64)
+/*[python end generated code: output=3d60c96fa58e07d5 input=0b7e166f2ea18e70]*/
+
+// For one-byte types, swapped variants are the same as native
+#define i8_set_sw i8_set
+#define i8_get_sw i8_get
+#define u8_set_sw u8_set
+#define u8_get_sw u8_get
+
+#undef FIXINT_GETSET
+#undef FIXINT_GETSET_SW
-static PyObject *
-i_get(void *ptr, Py_ssize_t size)
-{
- int val;
- memcpy(&val, ptr, sizeof(val));
- GET_BITFIELD(val, size);
- return PyLong_FromLong(val);
-}
-
-static PyObject *
-i_get_sw(void *ptr, Py_ssize_t size)
-{
- int val;
- memcpy(&val, ptr, sizeof(val));
- val = SWAP_INT(val);
- GET_BITFIELD(val, size);
- return PyLong_FromLong(val);
-}
+/*****************************************************************
+ * non-integer accessor methods, not supporting bit fields
+ */
#ifndef MS_WIN32
/* http://msdn.microsoft.com/en-us/library/cc237864.aspx */
#define VARIANT_FALSE 0x0000
#define VARIANT_TRUE 0xFFFF
#endif
-/* short BOOL - VARIANT_BOOL */
+/* v: short BOOL - VARIANT_BOOL */
static PyObject *
-vBOOL_set(void *ptr, PyObject *value, Py_ssize_t size)
+v_set(void *ptr, PyObject *value, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(short int)));
switch (PyObject_IsTrue(value)) {
case -1:
return NULL;
@@ -664,22 +526,25 @@ vBOOL_set(void *ptr, PyObject *value, Py_ssize_t size)
}
static PyObject *
-vBOOL_get(void *ptr, Py_ssize_t size)
+v_get(void *ptr, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(short int)));
return PyBool_FromLong((long)*(short int *)ptr);
}
+/* bool ('?'): bool (i.e. _Bool) */
static PyObject *
bool_set(void *ptr, PyObject *value, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(bool)));
switch (PyObject_IsTrue(value)) {
case -1:
return NULL;
case 0:
- *(_Bool *)ptr = 0;
+ *(bool *)ptr = 0;
_RET(value);
default:
- *(_Bool *)ptr = 1;
+ *(bool *)ptr = 1;
_RET(value);
}
}
@@ -687,260 +552,15 @@ bool_set(void *ptr, PyObject *value, Py_ssize_t size)
static PyObject *
bool_get(void *ptr, Py_ssize_t size)
{
- return PyBool_FromLong((long)*(_Bool *)ptr);
-}
-
-static PyObject *
-I_set(void *ptr, PyObject *value, Py_ssize_t size)
-{
- unsigned long val;
- unsigned int x;
- if (get_ulong(value, &val) < 0)
- return NULL;
- memcpy(&x, ptr, sizeof(x));
- x = SET(unsigned int, x, val, size);
- memcpy(ptr, &x, sizeof(x));
- _RET(value);
-}
-
-static PyObject *
-I_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
-{
- unsigned long val;
- unsigned int field;
- if (get_ulong(value, &val) < 0) {
- return NULL;
- }
- memcpy(&field, ptr, sizeof(field));
- field = SWAP_INT(field);
- field = SET(unsigned int, field, (unsigned int)val, size);
- field = SWAP_INT(field);
- memcpy(ptr, &field, sizeof(field));
- _RET(value);
-}
-
-
-static PyObject *
-I_get(void *ptr, Py_ssize_t size)
-{
- unsigned int val;
- memcpy(&val, ptr, sizeof(val));
- GET_BITFIELD(val, size);
- return PyLong_FromUnsignedLong(val);
-}
-
-static PyObject *
-I_get_sw(void *ptr, Py_ssize_t size)
-{
- unsigned int val;
- memcpy(&val, ptr, sizeof(val));
- val = SWAP_INT(val);
- GET_BITFIELD(val, size);
- return PyLong_FromUnsignedLong(val);
-}
-
-static PyObject *
-l_set(void *ptr, PyObject *value, Py_ssize_t size)
-{
- long val;
- long x;
- if (get_long(value, &val) < 0)
- return NULL;
- memcpy(&x, ptr, sizeof(x));
- x = SET(long, x, val, size);
- memcpy(ptr, &x, sizeof(x));
- _RET(value);
-}
-
-static PyObject *
-l_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
-{
- long val;
- long field;
- if (get_long(value, &val) < 0) {
- return NULL;
- }
- memcpy(&field, ptr, sizeof(field));
- field = SWAP_LONG(field);
- field = SET(long, field, val, size);
- field = SWAP_LONG(field);
- memcpy(ptr, &field, sizeof(field));
- _RET(value);
-}
-
-
-static PyObject *
-l_get(void *ptr, Py_ssize_t size)
-{
- long val;
- memcpy(&val, ptr, sizeof(val));
- GET_BITFIELD(val, size);
- return PyLong_FromLong(val);
-}
-
-static PyObject *
-l_get_sw(void *ptr, Py_ssize_t size)
-{
- long val;
- memcpy(&val, ptr, sizeof(val));
- val = SWAP_LONG(val);
- GET_BITFIELD(val, size);
- return PyLong_FromLong(val);
-}
-
-static PyObject *
-L_set(void *ptr, PyObject *value, Py_ssize_t size)
-{
- unsigned long val;
- unsigned long x;
- if (get_ulong(value, &val) < 0)
- return NULL;
- memcpy(&x, ptr, sizeof(x));
- x = SET(unsigned long, x, val, size);
- memcpy(ptr, &x, sizeof(x));
- _RET(value);
-}
-
-static PyObject *
-L_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
-{
- unsigned long val;
- unsigned long field;
- if (get_ulong(value, &val) < 0) {
- return NULL;
- }
- memcpy(&field, ptr, sizeof(field));
- field = SWAP_LONG(field);
- field = SET(unsigned long, field, val, size);
- field = SWAP_LONG(field);
- memcpy(ptr, &field, sizeof(field));
- _RET(value);
-}
-
-
-static PyObject *
-L_get(void *ptr, Py_ssize_t size)
-{
- unsigned long val;
- memcpy(&val, ptr, sizeof(val));
- GET_BITFIELD(val, size);
- return PyLong_FromUnsignedLong(val);
-}
-
-static PyObject *
-L_get_sw(void *ptr, Py_ssize_t size)
-{
- unsigned long val;
- memcpy(&val, ptr, sizeof(val));
- val = SWAP_LONG(val);
- GET_BITFIELD(val, size);
- return PyLong_FromUnsignedLong(val);
-}
-
-static PyObject *
-q_set(void *ptr, PyObject *value, Py_ssize_t size)
-{
- long long val;
- long long x;
- if (get_longlong(value, &val) < 0)
- return NULL;
- memcpy(&x, ptr, sizeof(x));
- x = SET(long long, x, val, size);
- memcpy(ptr, &x, sizeof(x));
- _RET(value);
-}
-
-static PyObject *
-q_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
-{
- long long val;
- long long field;
- if (get_longlong(value, &val) < 0) {
- return NULL;
- }
- memcpy(&field, ptr, sizeof(field));
- field = SWAP_LONG_LONG(field);
- field = SET(long long, field, val, size);
- field = SWAP_LONG_LONG(field);
- memcpy(ptr, &field, sizeof(field));
- _RET(value);
-}
-
-static PyObject *
-q_get(void *ptr, Py_ssize_t size)
-{
- long long val;
- memcpy(&val, ptr, sizeof(val));
- GET_BITFIELD(val, size);
- return PyLong_FromLongLong(val);
-}
-
-static PyObject *
-q_get_sw(void *ptr, Py_ssize_t size)
-{
- long long val;
- memcpy(&val, ptr, sizeof(val));
- val = SWAP_LONG_LONG(val);
- GET_BITFIELD(val, size);
- return PyLong_FromLongLong(val);
-}
-
-static PyObject *
-Q_set(void *ptr, PyObject *value, Py_ssize_t size)
-{
- unsigned long long val;
- unsigned long long x;
- if (get_ulonglong(value, &val) < 0)
- return NULL;
- memcpy(&x, ptr, sizeof(x));
- x = SET(long long, x, val, size);
- memcpy(ptr, &x, sizeof(x));
- _RET(value);
-}
-
-static PyObject *
-Q_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
-{
- unsigned long long val;
- unsigned long long field;
- if (get_ulonglong(value, &val) < 0) {
- return NULL;
- }
- memcpy(&field, ptr, sizeof(field));
- field = SWAP_LONG_LONG(field);
- field = SET(unsigned long long, field, val, size);
- field = SWAP_LONG_LONG(field);
- memcpy(ptr, &field, sizeof(field));
- _RET(value);
-}
-
-static PyObject *
-Q_get(void *ptr, Py_ssize_t size)
-{
- unsigned long long val;
- memcpy(&val, ptr, sizeof(val));
- GET_BITFIELD(val, size);
- return PyLong_FromUnsignedLongLong(val);
-}
-
-static PyObject *
-Q_get_sw(void *ptr, Py_ssize_t size)
-{
- unsigned long long val;
- memcpy(&val, ptr, sizeof(val));
- val = SWAP_LONG_LONG(val);
- GET_BITFIELD(val, size);
- return PyLong_FromUnsignedLongLong(val);
+ assert(NUM_BITS(size) || (size == sizeof(bool)));
+ return PyBool_FromLong((long)*(bool *)ptr);
}
-/*****************************************************************
- * non-integer accessor methods, not supporting bit fields
- */
-
-
+/* g: long double */
static PyObject *
g_set(void *ptr, PyObject *value, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(long double)));
long double x;
x = PyFloat_AsDouble(value);
@@ -953,14 +573,17 @@ g_set(void *ptr, PyObject *value, Py_ssize_t size)
static PyObject *
g_get(void *ptr, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(long double)));
long double val;
memcpy(&val, ptr, sizeof(long double));
return PyFloat_FromDouble(val);
}
+/* d: double */
static PyObject *
d_set(void *ptr, PyObject *value, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(double)));
double x;
x = PyFloat_AsDouble(value);
@@ -973,15 +596,18 @@ d_set(void *ptr, PyObject *value, Py_ssize_t size)
static PyObject *
d_get(void *ptr, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(double)));
double val;
memcpy(&val, ptr, sizeof(val));
return PyFloat_FromDouble(val);
}
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
+/* C: double complex */
static PyObject *
C_set(void *ptr, PyObject *value, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(double complex)));
Py_complex c = PyComplex_AsCComplex(value);
if (c.real == -1 && PyErr_Occurred()) {
@@ -995,15 +621,18 @@ C_set(void *ptr, PyObject *value, Py_ssize_t size)
static PyObject *
C_get(void *ptr, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(double complex)));
double complex x;
memcpy(&x, ptr, sizeof(x));
return PyComplex_FromDoubles(creal(x), cimag(x));
}
+/* E: float complex */
static PyObject *
E_set(void *ptr, PyObject *value, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(float complex)));
Py_complex c = PyComplex_AsCComplex(value);
if (c.real == -1 && PyErr_Occurred()) {
@@ -1017,15 +646,18 @@ E_set(void *ptr, PyObject *value, Py_ssize_t size)
static PyObject *
E_get(void *ptr, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(float complex)));
float complex x;
memcpy(&x, ptr, sizeof(x));
return PyComplex_FromDoubles(crealf(x), cimagf(x));
}
+/* F: long double complex */
static PyObject *
F_set(void *ptr, PyObject *value, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(long double complex)));
Py_complex c = PyComplex_AsCComplex(value);
if (c.real == -1 && PyErr_Occurred()) {
@@ -1039,6 +671,7 @@ F_set(void *ptr, PyObject *value, Py_ssize_t size)
static PyObject *
F_get(void *ptr, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(long double complex)));
long double complex x;
memcpy(&x, ptr, sizeof(x));
@@ -1046,9 +679,11 @@ F_get(void *ptr, Py_ssize_t size)
}
#endif
+/* d: double */
static PyObject *
d_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(double)));
double x;
x = PyFloat_AsDouble(value);
@@ -1067,6 +702,7 @@ d_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
static PyObject *
d_get_sw(void *ptr, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(double)));
#ifdef WORDS_BIGENDIAN
return PyFloat_FromDouble(PyFloat_Unpack8(ptr, 1));
#else
@@ -1074,9 +710,11 @@ d_get_sw(void *ptr, Py_ssize_t size)
#endif
}
+/* f: float */
static PyObject *
f_set(void *ptr, PyObject *value, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(float)));
float x;
x = (float)PyFloat_AsDouble(value);
@@ -1089,6 +727,7 @@ f_set(void *ptr, PyObject *value, Py_ssize_t size)
static PyObject *
f_get(void *ptr, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(float)));
float val;
memcpy(&val, ptr, sizeof(val));
return PyFloat_FromDouble(val);
@@ -1097,6 +736,7 @@ f_get(void *ptr, Py_ssize_t size)
static PyObject *
f_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(float)));
float x;
x = (float)PyFloat_AsDouble(value);
@@ -1115,6 +755,7 @@ f_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
static PyObject *
f_get_sw(void *ptr, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(float)));
#ifdef WORDS_BIGENDIAN
return PyFloat_FromDouble(PyFloat_Unpack4(ptr, 1));
#else
@@ -1122,6 +763,7 @@ f_get_sw(void *ptr, Py_ssize_t size)
#endif
}
+/* O: Python object */
/*
py_object refcounts:
@@ -1135,6 +777,7 @@ f_get_sw(void *ptr, Py_ssize_t size)
static PyObject *
O_get(void *ptr, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(PyObject *)));
PyObject *ob = *(PyObject **)ptr;
if (ob == NULL) {
if (!PyErr_Occurred())
@@ -1149,15 +792,18 @@ O_get(void *ptr, Py_ssize_t size)
static PyObject *
O_set(void *ptr, PyObject *value, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(PyObject *)));
/* Hm, does the memory block need it's own refcount or not? */
*(PyObject **)ptr = value;
return Py_NewRef(value);
}
+/* c: a single byte-character */
static PyObject *
c_set(void *ptr, PyObject *value, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(char)));
if (PyBytes_Check(value)) {
if (PyBytes_GET_SIZE(value) != 1) {
PyErr_Format(PyExc_TypeError,
@@ -1204,13 +850,15 @@ c_set(void *ptr, PyObject *value, Py_ssize_t size)
static PyObject *
c_get(void *ptr, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(char)));
return PyBytes_FromStringAndSize((char *)ptr, 1);
}
-/* u - a single wchar_t character */
+/* u: a single wchar_t character */
static PyObject *
u_set(void *ptr, PyObject *value, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(wchar_t)));
Py_ssize_t len;
wchar_t chars[2];
if (!PyUnicode_Check(value)) {
@@ -1244,10 +892,11 @@ u_set(void *ptr, PyObject *value, Py_ssize_t size)
static PyObject *
u_get(void *ptr, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(wchar_t)));
return PyUnicode_FromWideChar((wchar_t *)ptr, 1);
}
-/* U - a unicode string */
+/* U: a wchar_t* unicode string */
static PyObject *
U_get(void *ptr, Py_ssize_t size)
{
@@ -1306,6 +955,7 @@ U_set(void *ptr, PyObject *value, Py_ssize_t length)
}
+/* s: a byte string */
static PyObject *
s_get(void *ptr, Py_ssize_t size)
{
@@ -1355,6 +1005,7 @@ s_set(void *ptr, PyObject *value, Py_ssize_t length)
_RET(value);
}
+/* z: a byte string, can be set from integer pointer */
static PyObject *
z_set(void *ptr, PyObject *value, Py_ssize_t size)
{
@@ -1391,6 +1042,7 @@ z_get(void *ptr, Py_ssize_t size)
}
}
+/* Z: a wchar* string, can be set from integer pointer */
static PyObject *
Z_set(void *ptr, PyObject *value, Py_ssize_t size)
{
@@ -1445,8 +1097,9 @@ Z_get(void *ptr, Py_ssize_t size)
#ifdef MS_WIN32
+/* X: COM BSTR (wide-char string to be handled handled using Windows API) */
static PyObject *
-BSTR_set(void *ptr, PyObject *value, Py_ssize_t size)
+X_set(void *ptr, PyObject *value, Py_ssize_t size)
{
BSTR bstr;
@@ -1490,7 +1143,7 @@ BSTR_set(void *ptr, PyObject *value, Py_ssize_t size)
static PyObject *
-BSTR_get(void *ptr, Py_ssize_t size)
+X_get(void *ptr, Py_ssize_t size)
{
BSTR p;
p = *(BSTR *)ptr;
@@ -1505,9 +1158,11 @@ BSTR_get(void *ptr, Py_ssize_t size)
}
#endif
+/* P: generic pointer */
static PyObject *
P_set(void *ptr, PyObject *value, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(void *)));
void *v;
if (value == Py_None) {
*(void **)ptr = NULL;
@@ -1539,154 +1194,339 @@ P_set(void *ptr, PyObject *value, Py_ssize_t size)
static PyObject *
P_get(void *ptr, Py_ssize_t size)
{
+ assert(NUM_BITS(size) || (size == sizeof(void *)));
if (*(void **)ptr == NULL) {
Py_RETURN_NONE;
}
return PyLong_FromVoidPtr(*(void **)ptr);
}
-static struct fielddesc formattable[] = {
- { 's', s_set, s_get, NULL},
- { 'b', b_set, b_get, NULL},
- { 'B', B_set, B_get, NULL},
- { 'c', c_set, c_get, NULL},
- { 'd', d_set, d_get, NULL, d_set_sw, d_get_sw},
-#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
- { 'C', C_set, C_get, NULL},
- { 'E', E_set, E_get, NULL},
- { 'F', F_set, F_get, NULL},
-#endif
- { 'g', g_set, g_get, NULL},
- { 'f', f_set, f_get, NULL, f_set_sw, f_get_sw},
- { 'h', h_set, h_get, NULL, h_set_sw, h_get_sw},
- { 'H', H_set, H_get, NULL, H_set_sw, H_get_sw},
- { 'i', i_set, i_get, NULL, i_set_sw, i_get_sw},
- { 'I', I_set, I_get, NULL, I_set_sw, I_get_sw},
- { 'l', l_set, l_get, NULL, l_set_sw, l_get_sw},
- { 'L', L_set, L_get, NULL, L_set_sw, L_get_sw},
- { 'q', q_set, q_get, NULL, q_set_sw, q_get_sw},
- { 'Q', Q_set, Q_get, NULL, Q_set_sw, Q_get_sw},
- { 'P', P_set, P_get, NULL},
- { 'z', z_set, z_get, NULL},
- { 'u', u_set, u_get, NULL},
- { 'U', U_set, U_get, NULL},
- { 'Z', Z_set, Z_get, NULL},
-#ifdef MS_WIN32
- { 'X', BSTR_set, BSTR_get, NULL},
-#endif
- { 'v', vBOOL_set, vBOOL_get, NULL},
-#if SIZEOF__BOOL == SIZEOF_INT
- { '?', bool_set, bool_get, NULL, I_set_sw, I_get_sw},
-#elif SIZEOF__BOOL == SIZEOF_LONG
- { '?', bool_set, bool_get, NULL, L_set_sw, L_get_sw},
-#elif SIZEOF__BOOL == SIZEOF_LONG_LONG
- { '?', bool_set, bool_get, NULL, Q_set_sw, Q_get_sw},
-#else
- { '?', bool_set, bool_get, NULL},
-#endif /* SIZEOF__BOOL */
- { 'O', O_set, O_get, NULL},
- { 0, NULL, NULL, NULL},
+/* Table with info about all formats.
+ * Must be accessed via _ctypes_get_fielddesc, which initializes it on
+ * first use. After initialization it's treated as constant & read-only.
+ */
+
+struct formattable {
+/*[python input]
+for nbytes in 8, 16, 32, 64:
+ for sgn in 'i', 'u':
+ print(f' struct fielddesc fmt_{sgn}{nbytes};')
+for code in 'sbBcdCEFgfhHiIlLqQPzuUZXvO':
+ print(f' struct fielddesc fmt_{code};')
+[python start generated code]*/
+ struct fielddesc fmt_i8;
+ struct fielddesc fmt_u8;
+ struct fielddesc fmt_i16;
+ struct fielddesc fmt_u16;
+ struct fielddesc fmt_i32;
+ struct fielddesc fmt_u32;
+ struct fielddesc fmt_i64;
+ struct fielddesc fmt_u64;
+ struct fielddesc fmt_s;
+ struct fielddesc fmt_b;
+ struct fielddesc fmt_B;
+ struct fielddesc fmt_c;
+ struct fielddesc fmt_d;
+ struct fielddesc fmt_C;
+ struct fielddesc fmt_E;
+ struct fielddesc fmt_F;
+ struct fielddesc fmt_g;
+ struct fielddesc fmt_f;
+ struct fielddesc fmt_h;
+ struct fielddesc fmt_H;
+ struct fielddesc fmt_i;
+ struct fielddesc fmt_I;
+ struct fielddesc fmt_l;
+ struct fielddesc fmt_L;
+ struct fielddesc fmt_q;
+ struct fielddesc fmt_Q;
+ struct fielddesc fmt_P;
+ struct fielddesc fmt_z;
+ struct fielddesc fmt_u;
+ struct fielddesc fmt_U;
+ struct fielddesc fmt_Z;
+ struct fielddesc fmt_X;
+ struct fielddesc fmt_v;
+ struct fielddesc fmt_O;
+/*[python end generated code: output=fa648744ec7f919d input=087d58357d4bf2c5]*/
+
+ // bool has code '?':
+ struct fielddesc fmt_bool;
+
+ // always contains NULLs:
+ struct fielddesc fmt_nil;
};
-/*
- Ideas: Implement VARIANT in this table, using 'V' code.
- Use '?' as code for BOOL.
-*/
+static struct formattable formattable;
+
+
+/* Get fielddesc info for a fixed-width integer.
+ * N.B: - must be called after (or from) _ctypes_init_fielddesc!
+ * - nbytes must be one of the supported values
+ */
+
+static inline struct fielddesc *
+_ctypes_fixint_fielddesc(Py_ssize_t nbytes, bool is_signed)
+{
+#define _PACK(NBYTES, SGN) ((NBYTES<<2) + (SGN ? 1 : 0))
+ switch (_PACK(nbytes, is_signed)) {
+/*[python input]
+for nbytes in 8, 16, 32, 64:
+ for sgn in 'i', 'u':
+ is_signed = sgn == 'i'
+ print(f' case (_PACK({nbytes // 8}, {int(is_signed)})): '
+ + f'return &formattable.fmt_{sgn}{nbytes};')
+[python start generated code]*/
+ case (_PACK(1, 1)): return &formattable.fmt_i8;
+ case (_PACK(1, 0)): return &formattable.fmt_u8;
+ case (_PACK(2, 1)): return &formattable.fmt_i16;
+ case (_PACK(2, 0)): return &formattable.fmt_u16;
+ case (_PACK(4, 1)): return &formattable.fmt_i32;
+ case (_PACK(4, 0)): return &formattable.fmt_u32;
+ case (_PACK(8, 1)): return &formattable.fmt_i64;
+ case (_PACK(8, 0)): return &formattable.fmt_u64;
+/*[python end generated code: output=0194ba35c4d64ff3 input=ee9f6f5bb872d645]*/
+#undef _PACK
+ }
+ /* ctypes currently only supports platforms where the basic integer types
+ * (`char`, `short`, `int`, `long`, `long long`) have 1, 2, 4, or 8 bytes
+ * (i.e. 8 to 64 bits).
+ */
+ Py_UNREACHABLE();
+}
+
+
+/* Macro to call _ctypes_fixint_fielddesc for a given C type. */
+
+_Py_COMP_DIAG_PUSH
+#if defined(__GNUC__) && (__GNUC__ < 14)
+/* The signedness check expands to an expression that's always true or false.
+ * Older GCC gives a '-Wtype-limits' warning for this, which is a GCC bug
+ * (docs say it should "not warn for constant expressions"):
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86647
+ * Silence that warning.
+ */
+#pragma GCC diagnostic ignored "-Wtype-limits"
+#endif
+
+#define FIXINT_FIELDDESC_FOR(C_TYPE) \
+ _ctypes_fixint_fielddesc(sizeof(C_TYPE), (C_TYPE)-1 < 0)
+
/* Delayed initialization. Windows cannot statically reference dynamically
loaded addresses from DLLs. */
void
_ctypes_init_fielddesc(void)
{
- struct fielddesc *fd = formattable;
- for (; fd->code; ++fd) {
- switch (fd->code) {
- case 's': fd->pffi_type = &ffi_type_pointer; break;
- case 'b': fd->pffi_type = &ffi_type_schar; break;
- case 'B': fd->pffi_type = &ffi_type_uchar; break;
- case 'c': fd->pffi_type = &ffi_type_schar; break;
- case 'd': fd->pffi_type = &ffi_type_double; break;
+ /* Fixed-width integers */
+
+/*[python input]
+for nbytes in 8, 16, 32, 64:
+ for sgn in 'i', 'u':
+ is_signed = sgn == 'i'
+ u = 'u' if sgn == 'u' else 's'
+ parts = [
+ f"0",
+ f'&ffi_type_{u}int{nbytes}',
+ f'{sgn}{nbytes}_set',
+ f'{sgn}{nbytes}_get',
+ f'{sgn}{nbytes}_set_sw',
+ f'{sgn}{nbytes}_get_sw',
+ ]
+ print(f' formattable.fmt_{sgn}{nbytes} = (struct fielddesc){{')
+ print(f' {', '.join(parts)} }};')
+[python start generated code]*/
+ formattable.fmt_i8 = (struct fielddesc){
+ 0, &ffi_type_sint8, i8_set, i8_get, i8_set_sw, i8_get_sw };
+ formattable.fmt_u8 = (struct fielddesc){
+ 0, &ffi_type_uint8, u8_set, u8_get, u8_set_sw, u8_get_sw };
+ formattable.fmt_i16 = (struct fielddesc){
+ 0, &ffi_type_sint16, i16_set, i16_get, i16_set_sw, i16_get_sw };
+ formattable.fmt_u16 = (struct fielddesc){
+ 0, &ffi_type_uint16, u16_set, u16_get, u16_set_sw, u16_get_sw };
+ formattable.fmt_i32 = (struct fielddesc){
+ 0, &ffi_type_sint32, i32_set, i32_get, i32_set_sw, i32_get_sw };
+ formattable.fmt_u32 = (struct fielddesc){
+ 0, &ffi_type_uint32, u32_set, u32_get, u32_set_sw, u32_get_sw };
+ formattable.fmt_i64 = (struct fielddesc){
+ 0, &ffi_type_sint64, i64_set, i64_get, i64_set_sw, i64_get_sw };
+ formattable.fmt_u64 = (struct fielddesc){
+ 0, &ffi_type_uint64, u64_set, u64_get, u64_set_sw, u64_get_sw };
+/*[python end generated code: output=16806fe0ca3a9c4c input=850b8dd6388b1b10]*/
+
+
+ /* Native C integers.
+ * These use getters/setters for fixed-width ints but have their own
+ * `code` and `pffi_type`.
+ */
+
+/*[python input]
+for base_code, base_c_type in [
+ ('b', 'char'),
+ ('h', 'short'),
+ ('i', 'int'),
+ ('l', 'long'),
+ ('q', 'long long'),
+]:
+ for code, c_type, ffi_type in [
+ (base_code, 'signed ' + base_c_type, 's' + base_c_type),
+ (base_code.upper(), 'unsigned ' + base_c_type, 'u' + base_c_type),
+ ]:
+ print(f' formattable.fmt_{code} = *FIXINT_FIELDDESC_FOR({c_type});')
+ print(f" formattable.fmt_{code}.code = '{code}';")
+ if base_code == 'q':
+ # ffi doesn't have `long long`; keep use the fixint type
+ pass
+ else:
+ print(f' formattable.fmt_{code}.pffi_type = &ffi_type_{ffi_type};')
+[python start generated code]*/
+ formattable.fmt_b = *FIXINT_FIELDDESC_FOR(signed char);
+ formattable.fmt_b.code = 'b';
+ formattable.fmt_b.pffi_type = &ffi_type_schar;
+ formattable.fmt_B = *FIXINT_FIELDDESC_FOR(unsigned char);
+ formattable.fmt_B.code = 'B';
+ formattable.fmt_B.pffi_type = &ffi_type_uchar;
+ formattable.fmt_h = *FIXINT_FIELDDESC_FOR(signed short);
+ formattable.fmt_h.code = 'h';
+ formattable.fmt_h.pffi_type = &ffi_type_sshort;
+ formattable.fmt_H = *FIXINT_FIELDDESC_FOR(unsigned short);
+ formattable.fmt_H.code = 'H';
+ formattable.fmt_H.pffi_type = &ffi_type_ushort;
+ formattable.fmt_i = *FIXINT_FIELDDESC_FOR(signed int);
+ formattable.fmt_i.code = 'i';
+ formattable.fmt_i.pffi_type = &ffi_type_sint;
+ formattable.fmt_I = *FIXINT_FIELDDESC_FOR(unsigned int);
+ formattable.fmt_I.code = 'I';
+ formattable.fmt_I.pffi_type = &ffi_type_uint;
+ formattable.fmt_l = *FIXINT_FIELDDESC_FOR(signed long);
+ formattable.fmt_l.code = 'l';
+ formattable.fmt_l.pffi_type = &ffi_type_slong;
+ formattable.fmt_L = *FIXINT_FIELDDESC_FOR(unsigned long);
+ formattable.fmt_L.code = 'L';
+ formattable.fmt_L.pffi_type = &ffi_type_ulong;
+ formattable.fmt_q = *FIXINT_FIELDDESC_FOR(signed long long);
+ formattable.fmt_q.code = 'q';
+ formattable.fmt_Q = *FIXINT_FIELDDESC_FOR(unsigned long long);
+ formattable.fmt_Q.code = 'Q';
+/*[python end generated code: output=873c87a2e6b5075a input=ee814ca263aac18e]*/
+
+
+ /* Other types have bespoke setters and getters named `@_set` and `@_get`,
+ * where `@` is the type code.
+ * Some have swapped variants, `@_set_sw` and `@_get_sw`
+ */
+
+#define _TABLE_ENTRY(SYMBOL, FFI_TYPE, ...) \
+ formattable.fmt_ ## SYMBOL = \
+ (struct fielddesc){(#SYMBOL)[0], (FFI_TYPE), __VA_ARGS__}; \
+ ///////////////////////////////////////////////////////////////////////////
+
+#define TABLE_ENTRY(SYMBOL, FFI_TYPE) \
+ _TABLE_ENTRY(SYMBOL, FFI_TYPE, SYMBOL ## _set, SYMBOL ## _get) \
+ ///////////////////////////////////////////////////////////////////////////
+
+#define TABLE_ENTRY_SW(SYMBOL, FFI_TYPE) \
+ _TABLE_ENTRY(SYMBOL, FFI_TYPE, SYMBOL ## _set, \
+ SYMBOL ## _get, SYMBOL ## _set_sw, SYMBOL ## _get_sw) \
+ ///////////////////////////////////////////////////////////////////////////
+
+ TABLE_ENTRY_SW(d, &ffi_type_double);
#if defined(Py_HAVE_C_COMPLEX) && defined(Py_FFI_SUPPORT_C_COMPLEX)
- case 'C': fd->pffi_type = &ffi_type_complex_double; break;
- case 'E': fd->pffi_type = &ffi_type_complex_float; break;
- case 'F': fd->pffi_type = &ffi_type_complex_longdouble; break;
+ TABLE_ENTRY(C, &ffi_type_complex_double);
+ TABLE_ENTRY(E, &ffi_type_complex_float);
+ TABLE_ENTRY(F, &ffi_type_complex_longdouble);
#endif
- case 'g': fd->pffi_type = &ffi_type_longdouble; break;
- case 'f': fd->pffi_type = &ffi_type_float; break;
- case 'h': fd->pffi_type = &ffi_type_sshort; break;
- case 'H': fd->pffi_type = &ffi_type_ushort; break;
- case 'i': fd->pffi_type = &ffi_type_sint; break;
- case 'I': fd->pffi_type = &ffi_type_uint; break;
- /* XXX Hm, sizeof(int) == sizeof(long) doesn't hold on every platform */
- /* As soon as we can get rid of the type codes, this is no longer a problem */
- #if SIZEOF_LONG == 4
- case 'l': fd->pffi_type = &ffi_type_sint32; break;
- case 'L': fd->pffi_type = &ffi_type_uint32; break;
- #elif SIZEOF_LONG == 8
- case 'l': fd->pffi_type = &ffi_type_sint64; break;
- case 'L': fd->pffi_type = &ffi_type_uint64; break;
- #else
- #error
- #endif
- #if SIZEOF_LONG_LONG == 8
- case 'q': fd->pffi_type = &ffi_type_sint64; break;
- case 'Q': fd->pffi_type = &ffi_type_uint64; break;
- #else
- #error
- #endif
- case 'P': fd->pffi_type = &ffi_type_pointer; break;
- case 'z': fd->pffi_type = &ffi_type_pointer; break;
- case 'u':
- if (sizeof(wchar_t) == sizeof(short))
- fd->pffi_type = &ffi_type_sshort;
- else if (sizeof(wchar_t) == sizeof(int))
- fd->pffi_type = &ffi_type_sint;
- else if (sizeof(wchar_t) == sizeof(long))
- fd->pffi_type = &ffi_type_slong;
- else
- Py_UNREACHABLE();
- break;
- case 'U': fd->pffi_type = &ffi_type_pointer; break;
- case 'Z': fd->pffi_type = &ffi_type_pointer; break;
- #ifdef MS_WIN32
- case 'X': fd->pffi_type = &ffi_type_pointer; break;
- #endif
- case 'v': fd->pffi_type = &ffi_type_sshort; break;
- #if SIZEOF__BOOL == 1
- case '?': fd->pffi_type = &ffi_type_uchar; break; /* Also fallback for no native _Bool support */
- #elif SIZEOF__BOOL == SIZEOF_SHORT
- case '?': fd->pffi_type = &ffi_type_ushort; break;
- #elif SIZEOF__BOOL == SIZEOF_INT
- case '?': fd->pffi_type = &ffi_type_uint; break;
- #elif SIZEOF__BOOL == SIZEOF_LONG
- case '?': fd->pffi_type = &ffi_type_ulong; break;
- #elif SIZEOF__BOOL == SIZEOF_LONG_LONG
- case '?': fd->pffi_type = &ffi_type_ulong; break;
- #endif /* SIZEOF__BOOL */
- case 'O': fd->pffi_type = &ffi_type_pointer; break;
- default:
- Py_UNREACHABLE();
- }
- }
+ TABLE_ENTRY(g, &ffi_type_longdouble);
+ TABLE_ENTRY_SW(f, &ffi_type_float);
+ TABLE_ENTRY(v, &ffi_type_sshort); /* vBOOL */
+
+ // ctypes.c_char is signed for FFI, even where C wchar_t is unsigned.
+ TABLE_ENTRY(c, _ctypes_fixint_fielddesc(sizeof(char), true)->pffi_type);
+ // ctypes.c_wchar is signed for FFI, even where C wchar_t is unsigned.
+ TABLE_ENTRY(u, _ctypes_fixint_fielddesc(sizeof(wchar_t), true)->pffi_type);
+
+ TABLE_ENTRY(s, &ffi_type_pointer);
+ TABLE_ENTRY(P, &ffi_type_pointer);
+ TABLE_ENTRY(z, &ffi_type_pointer);
+ TABLE_ENTRY(U, &ffi_type_pointer);
+ TABLE_ENTRY(Z, &ffi_type_pointer);
+#ifdef MS_WIN32
+ TABLE_ENTRY(X, &ffi_type_pointer);
+#endif
+ TABLE_ENTRY(O, &ffi_type_pointer);
+
+#undef TABLE_ENTRY_SW
+#undef TABLE_ENTRY
+#undef _TABLE_ENTRY
+ /* bool has code '?', fill it in manually */
+
+ // ctypes.c_bool is unsigned for FFI, even where C bool is signed.
+ formattable.fmt_bool = *_ctypes_fixint_fielddesc(sizeof(bool), false);
+ formattable.fmt_bool.code = '?';
+ formattable.fmt_bool.setfunc = bool_set;
+ formattable.fmt_bool.getfunc = bool_get;
}
+#undef FIXINT_FIELDDESC_FOR
+_Py_COMP_DIAG_POP
struct fielddesc *
_ctypes_get_fielddesc(const char *fmt)
{
- static int initialized = 0;
- struct fielddesc *table = formattable;
-
+ static bool initialized = false;
+ static PyMutex mutex = {0};
+ PyMutex_Lock(&mutex);
if (!initialized) {
- initialized = 1;
_ctypes_init_fielddesc();
+ initialized = true;
}
-
- for (; table->code; ++table) {
- if (table->code == fmt[0])
- return table;
+ PyMutex_Unlock(&mutex);
+ struct fielddesc *result = NULL;
+ switch(fmt[0]) {
+/*[python input]
+for code in 'sbBcdCEFgfhHiIlLqQPzuUZXvO':
+ print(f" case '{code}': result = &formattable.fmt_{code}; break;")
+[python start generated code]*/
+ case 's': result = &formattable.fmt_s; break;
+ case 'b': result = &formattable.fmt_b; break;
+ case 'B': result = &formattable.fmt_B; break;
+ case 'c': result = &formattable.fmt_c; break;
+ case 'd': result = &formattable.fmt_d; break;
+ case 'C': result = &formattable.fmt_C; break;
+ case 'E': result = &formattable.fmt_E; break;
+ case 'F': result = &formattable.fmt_F; break;
+ case 'g': result = &formattable.fmt_g; break;
+ case 'f': result = &formattable.fmt_f; break;
+ case 'h': result = &formattable.fmt_h; break;
+ case 'H': result = &formattable.fmt_H; break;
+ case 'i': result = &formattable.fmt_i; break;
+ case 'I': result = &formattable.fmt_I; break;
+ case 'l': result = &formattable.fmt_l; break;
+ case 'L': result = &formattable.fmt_L; break;
+ case 'q': result = &formattable.fmt_q; break;
+ case 'Q': result = &formattable.fmt_Q; break;
+ case 'P': result = &formattable.fmt_P; break;
+ case 'z': result = &formattable.fmt_z; break;
+ case 'u': result = &formattable.fmt_u; break;
+ case 'U': result = &formattable.fmt_U; break;
+ case 'Z': result = &formattable.fmt_Z; break;
+ case 'X': result = &formattable.fmt_X; break;
+ case 'v': result = &formattable.fmt_v; break;
+ case 'O': result = &formattable.fmt_O; break;
+/*[python end generated code: output=81a8223dda9f81f7 input=2f59666d3c024edf]*/
+ case '?': result = &formattable.fmt_bool; break;
}
- return NULL;
+ if (!result || !result->code) {
+ return NULL;
+ }
+ assert(result->pffi_type);
+ assert(result->setfunc);
+ assert(result->getfunc);
+ return result;
}
+/*
+ Ideas: Implement VARIANT in this table, using 'V' code.
+*/
+
/*---------------- EOF ----------------*/
diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h
index 7e08040..45e00a5 100644
--- a/Modules/_ctypes/ctypes.h
+++ b/Modules/_ctypes/ctypes.h
@@ -113,8 +113,17 @@ extern PyType_Spec cthunk_spec;
typedef struct tagPyCArgObject PyCArgObject;
typedef struct tagCDataObject CDataObject;
-typedef PyObject *(* GETFUNC)(void *, Py_ssize_t size);
-typedef PyObject *(* SETFUNC)(void *, PyObject *value, Py_ssize_t size);
+
+// GETFUNC: convert the C value at *ptr* to Python object, return the object
+// SETFUNC: write content of the PyObject *value* to the location at *ptr*;
+// return a new reference to either *value*, or None for simple types
+// (see _CTYPES_DEBUG_KEEP).
+// Note that the *size* arg can have different meanings depending on context:
+// for string-like arrays it's the size in bytes
+// for int-style fields it's either the type size, or bitfiled info
+// that can be unpacked using the LOW_BIT & NUM_BITS macros.
+typedef PyObject *(* GETFUNC)(void *ptr, Py_ssize_t size);
+typedef PyObject *(* SETFUNC)(void *ptr, PyObject *value, Py_ssize_t size);
typedef PyCArgObject *(* PARAMFUNC)(ctypes_state *st, CDataObject *obj);
/* A default buffer in CDataObject, which can be used for small C types. If
@@ -239,9 +248,9 @@ extern CThunkObject *_ctypes_alloc_callback(ctypes_state *st,
/* a table entry describing a predefined ctypes type */
struct fielddesc {
char code;
+ ffi_type *pffi_type; /* always statically allocated */
SETFUNC setfunc;
GETFUNC getfunc;
- ffi_type *pffi_type; /* always statically allocated */
SETFUNC setfunc_swapped;
GETFUNC getfunc_swapped;
};