From eeb0d4e1fd96c58a1de76c1d974684c1e5e91f10 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 19 Nov 2024 16:43:54 +0100 Subject: [PATCH 01/17] Consistently pass the size to getfunc/setfunc --- Modules/_ctypes/_ctypes.c | 4 +-- Modules/_ctypes/callbacks.c | 2 +- Modules/_ctypes/cfield.c | 64 +++++++++++++++++++++++++++++++++++++ Modules/_ctypes/ctypes.h | 13 ++++++-- 4 files changed, 78 insertions(+), 5 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 34529bce496d88..0c40fe9e464ee1 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1970,7 +1970,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; @@ -2430,7 +2430,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 7b9f6437c7d55f..89c0749a093765 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 3220852c8398e0..0ad29725897051 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -454,6 +454,7 @@ Py_ssize_t NUM_BITS(Py_ssize_t bitsize) { static PyObject * b_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(signed char))); long val; if (get_long(value, &val) < 0) return NULL; @@ -465,6 +466,7 @@ b_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * b_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(signed char))); signed char val = *(signed char *)ptr; GET_BITFIELD(val, size); return PyLong_FromLong(val); @@ -473,6 +475,7 @@ b_get(void *ptr, Py_ssize_t size) static PyObject * B_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned char))); unsigned long val; if (get_ulong(value, &val) < 0) return NULL; @@ -484,6 +487,7 @@ B_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * B_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned char))); unsigned char val = *(unsigned char *)ptr; GET_BITFIELD(val, size); return PyLong_FromLong(val); @@ -492,6 +496,7 @@ B_get(void *ptr, Py_ssize_t size) static PyObject * h_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(short))); long val; short x; if (get_long(value, &val) < 0) @@ -506,6 +511,7 @@ h_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * h_set_sw(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(short))); long val; short field; if (get_long(value, &val) < 0) { @@ -522,6 +528,7 @@ h_set_sw(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * h_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(short))); short val; memcpy(&val, ptr, sizeof(val)); GET_BITFIELD(val, size); @@ -531,6 +538,7 @@ h_get(void *ptr, Py_ssize_t size) static PyObject * h_get_sw(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(short))); short val; memcpy(&val, ptr, sizeof(val)); val = SWAP_SHORT(val); @@ -541,6 +549,7 @@ h_get_sw(void *ptr, Py_ssize_t size) static PyObject * H_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned short))); unsigned long val; unsigned short x; if (get_ulong(value, &val) < 0) @@ -554,6 +563,7 @@ H_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * H_set_sw(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned short))); unsigned long val; unsigned short field; if (get_ulong(value, &val) < 0) { @@ -571,6 +581,7 @@ H_set_sw(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * H_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned short))); unsigned short val; memcpy(&val, ptr, sizeof(val)); GET_BITFIELD(val, size); @@ -580,6 +591,7 @@ H_get(void *ptr, Py_ssize_t size) static PyObject * H_get_sw(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned short))); unsigned short val; memcpy(&val, ptr, sizeof(val)); val = SWAP_SHORT(val); @@ -590,6 +602,7 @@ H_get_sw(void *ptr, Py_ssize_t size) static PyObject * i_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(int))); long val; int x; if (get_long(value, &val) < 0) @@ -603,6 +616,7 @@ i_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * i_set_sw(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(int))); long val; int field; if (get_long(value, &val) < 0) { @@ -620,6 +634,7 @@ i_set_sw(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * i_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(int))); int val; memcpy(&val, ptr, sizeof(val)); GET_BITFIELD(val, size); @@ -629,6 +644,7 @@ i_get(void *ptr, Py_ssize_t size) static PyObject * i_get_sw(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(int))); int val; memcpy(&val, ptr, sizeof(val)); val = SWAP_INT(val); @@ -645,6 +661,7 @@ i_get_sw(void *ptr, Py_ssize_t size) static PyObject * vBOOL_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; @@ -660,12 +677,14 @@ vBOOL_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * vBOOL_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(short int))); return PyBool_FromLong((long)*(short int *)ptr); } 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; @@ -681,12 +700,14 @@ bool_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * bool_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(bool))); return PyBool_FromLong((long)*(_Bool *)ptr); } static PyObject * I_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned int))); unsigned long val; unsigned int x; if (get_ulong(value, &val) < 0) @@ -700,6 +721,7 @@ I_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * I_set_sw(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned int))); unsigned long val; unsigned int field; if (get_ulong(value, &val) < 0) { @@ -717,6 +739,7 @@ I_set_sw(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * I_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned int))); unsigned int val; memcpy(&val, ptr, sizeof(val)); GET_BITFIELD(val, size); @@ -726,6 +749,7 @@ I_get(void *ptr, Py_ssize_t size) static PyObject * I_get_sw(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned int))); unsigned int val; memcpy(&val, ptr, sizeof(val)); val = SWAP_INT(val); @@ -736,6 +760,7 @@ I_get_sw(void *ptr, Py_ssize_t size) static PyObject * l_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(long))); long val; long x; if (get_long(value, &val) < 0) @@ -749,6 +774,7 @@ l_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * l_set_sw(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(long))); long val; long field; if (get_long(value, &val) < 0) { @@ -766,6 +792,7 @@ l_set_sw(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * l_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(long))); long val; memcpy(&val, ptr, sizeof(val)); GET_BITFIELD(val, size); @@ -775,6 +802,7 @@ l_get(void *ptr, Py_ssize_t size) static PyObject * l_get_sw(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(long))); long val; memcpy(&val, ptr, sizeof(val)); val = SWAP_LONG(val); @@ -785,6 +813,7 @@ l_get_sw(void *ptr, Py_ssize_t size) static PyObject * L_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned long))); unsigned long val; unsigned long x; if (get_ulong(value, &val) < 0) @@ -798,6 +827,7 @@ L_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * L_set_sw(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned long))); unsigned long val; unsigned long field; if (get_ulong(value, &val) < 0) { @@ -815,6 +845,7 @@ L_set_sw(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * L_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned long))); unsigned long val; memcpy(&val, ptr, sizeof(val)); GET_BITFIELD(val, size); @@ -824,6 +855,7 @@ L_get(void *ptr, Py_ssize_t size) static PyObject * L_get_sw(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned long))); unsigned long val; memcpy(&val, ptr, sizeof(val)); val = SWAP_LONG(val); @@ -834,6 +866,7 @@ L_get_sw(void *ptr, Py_ssize_t size) static PyObject * q_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(long long))); long long val; long long x; if (get_longlong(value, &val) < 0) @@ -847,6 +880,7 @@ q_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * q_set_sw(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(long long))); long long val; long long field; if (get_longlong(value, &val) < 0) { @@ -863,6 +897,7 @@ q_set_sw(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * q_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(long long))); long long val; memcpy(&val, ptr, sizeof(val)); GET_BITFIELD(val, size); @@ -872,6 +907,7 @@ q_get(void *ptr, Py_ssize_t size) static PyObject * q_get_sw(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(long long))); long long val; memcpy(&val, ptr, sizeof(val)); val = SWAP_LONG_LONG(val); @@ -882,6 +918,7 @@ q_get_sw(void *ptr, Py_ssize_t size) static PyObject * Q_set(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned long long))); unsigned long long val; unsigned long long x; if (get_ulonglong(value, &val) < 0) @@ -895,6 +932,7 @@ Q_set(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * Q_set_sw(void *ptr, PyObject *value, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned long long))); unsigned long long val; unsigned long long field; if (get_ulonglong(value, &val) < 0) { @@ -911,6 +949,7 @@ Q_set_sw(void *ptr, PyObject *value, Py_ssize_t size) static PyObject * Q_get(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned long long))); unsigned long long val; memcpy(&val, ptr, sizeof(val)); GET_BITFIELD(val, size); @@ -920,6 +959,7 @@ Q_get(void *ptr, Py_ssize_t size) static PyObject * Q_get_sw(void *ptr, Py_ssize_t size) { + assert(NUM_BITS(size) || (size == sizeof(unsigned long long))); unsigned long long val; memcpy(&val, ptr, sizeof(val)); val = SWAP_LONG_LONG(val); @@ -935,6 +975,7 @@ Q_get_sw(void *ptr, Py_ssize_t size) 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); @@ -947,6 +988,7 @@ 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); @@ -955,6 +997,7 @@ g_get(void *ptr, Py_ssize_t size) 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); @@ -967,6 +1010,7 @@ 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); @@ -976,6 +1020,7 @@ d_get(void *ptr, Py_ssize_t size) 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()) { @@ -989,6 +1034,7 @@ 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)); @@ -998,6 +1044,7 @@ C_get(void *ptr, Py_ssize_t size) 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()) { @@ -1011,6 +1058,7 @@ 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)); @@ -1020,6 +1068,7 @@ E_get(void *ptr, Py_ssize_t size) 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()) { @@ -1033,6 +1082,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)); @@ -1043,6 +1093,7 @@ F_get(void *ptr, Py_ssize_t size) 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); @@ -1061,6 +1112,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 @@ -1071,6 +1123,7 @@ d_get_sw(void *ptr, Py_ssize_t size) 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); @@ -1083,6 +1136,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); @@ -1091,6 +1145,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); @@ -1109,6 +1164,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 @@ -1129,6 +1185,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()) @@ -1143,6 +1200,7 @@ 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); @@ -1152,6 +1210,7 @@ O_set(void *ptr, PyObject *value, Py_ssize_t size) 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, @@ -1198,6 +1257,7 @@ 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); } @@ -1205,6 +1265,7 @@ c_get(void *ptr, Py_ssize_t size) 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)) { @@ -1238,6 +1299,7 @@ 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); } @@ -1502,6 +1564,7 @@ BSTR_get(void *ptr, Py_ssize_t size) 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; @@ -1533,6 +1596,7 @@ 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; } diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 7e0804054cded4..aa4fb39c942081 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 From e33c92953b363454351b4073424e813e17b7415d Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 22 Nov 2024 19:04:13 +0100 Subject: [PATCH 02/17] Switch to fixed-width integers Replace formattable by a switch. Generate some repetitive parts of handling individual C types. --- Modules/_ctypes/cfield.c | 1104 +++++++++++++++----------------------- Modules/_ctypes/ctypes.h | 2 +- 2 files changed, 429 insertions(+), 677 deletions(-) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 0ad29725897051..0b4ff89da19670 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -314,61 +314,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 */ @@ -398,35 +343,9 @@ 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)(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 - /***************************************************************** * The setter methods return an object which must be kept alive, to keep the * data valid which has been stored in the memory block. The ctypes object @@ -448,218 +367,138 @@ 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) -{ - assert(NUM_BITS(size) || (size == sizeof(signed char))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(signed char))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned char))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned char))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(short))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(short))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(short))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(short))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned short))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned short))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned short))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned short))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(int))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(int))); - 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 { \ + int res = PyLong_AsNativeBytes( \ + value, &val, (NBITS) / 8, \ + Py_ASNATIVEBYTES_NATIVE_ENDIAN \ + | Py_ASNATIVEBYTES_ALLOW_INDEX); \ + if (res < 0) { \ + return NULL; \ + } \ + } \ + *(CTYPE*)ptr = SET(CTYPE, *(CTYPE*)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, size_arg); \ + if (res == NULL) { \ + return NULL; \ + } \ + 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) -{ - assert(NUM_BITS(size) || (size == sizeof(int))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(int))); - 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)) { @@ -675,12 +514,13 @@ 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 */ static PyObject * bool_set(void *ptr, PyObject *value, Py_ssize_t size) { @@ -704,274 +544,7 @@ 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned int))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned int))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned int))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned int))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(long))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(long))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(long))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(long))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned long))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned long))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned long))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned long))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(long long))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(long long))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(long long))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(long long))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned long long))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned long long))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned long long))); - 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) -{ - assert(NUM_BITS(size) || (size == sizeof(unsigned long long))); - unsigned long long val; - memcpy(&val, ptr, sizeof(val)); - val = SWAP_LONG_LONG(val); - GET_BITFIELD(val, size); - return PyLong_FromUnsignedLongLong(val); -} - -/***************************************************************** - * non-integer accessor methods, not supporting bit fields - */ - - +/* g: long double */ static PyObject * g_set(void *ptr, PyObject *value, Py_ssize_t size) { @@ -994,6 +567,7 @@ g_get(void *ptr, Py_ssize_t size) return PyFloat_FromDouble(val); } +/* d: double */ static PyObject * d_set(void *ptr, PyObject *value, Py_ssize_t size) { @@ -1017,6 +591,7 @@ d_get(void *ptr, Py_ssize_t size) } #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) { @@ -1041,6 +616,7 @@ C_get(void *ptr, Py_ssize_t size) return PyComplex_FromDoubles(creal(x), cimag(x)); } +/* E: float complex */ static PyObject * E_set(void *ptr, PyObject *value, Py_ssize_t size) { @@ -1065,6 +641,7 @@ E_get(void *ptr, Py_ssize_t size) return PyComplex_FromDoubles(crealf(x), cimagf(x)); } +/* F: long double complex */ static PyObject * F_set(void *ptr, PyObject *value, Py_ssize_t size) { @@ -1090,6 +667,7 @@ F_get(void *ptr, Py_ssize_t size) } #endif +/* d: double */ static PyObject * d_set_sw(void *ptr, PyObject *value, Py_ssize_t size) { @@ -1120,6 +698,7 @@ d_get_sw(void *ptr, Py_ssize_t size) #endif } +/* f: float */ static PyObject * f_set(void *ptr, PyObject *value, Py_ssize_t size) { @@ -1172,6 +751,7 @@ f_get_sw(void *ptr, Py_ssize_t size) #endif } +/* O: Python object */ /* py_object refcounts: @@ -1207,6 +787,7 @@ O_set(void *ptr, PyObject *value, Py_ssize_t size) } +/* c: a single byte-character */ static PyObject * c_set(void *ptr, PyObject *value, Py_ssize_t size) { @@ -1303,7 +884,7 @@ u_get(void *ptr, Py_ssize_t size) 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) { @@ -1362,6 +943,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) { @@ -1411,6 +993,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) { @@ -1447,6 +1030,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) { @@ -1501,8 +1085,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; @@ -1546,7 +1131,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; @@ -1561,6 +1146,7 @@ BSTR_get(void *ptr, Py_ssize_t size) } #endif +/* P: generic pointer */ static PyObject * P_set(void *ptr, PyObject *value, Py_ssize_t size) { @@ -1603,148 +1189,314 @@ P_get(void *ptr, Py_ssize_t size) 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. + */ + +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=8f85b8f329d7b5f6 input=22ef3654bd8451ae]*/ +#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 */ +#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=3aab96e70e704ccc input=6f6d00bc889a7107]*/ + + + /* 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 */ + + TABLE_ENTRY(c, FIXINT_FIELDDESC_FOR(char)->pffi_type); + TABLE_ENTRY(u, FIXINT_FIELDDESC_FOR(wchar_t)->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 */ + formattable.fmt_bool = *FIXINT_FIELDDESC_FOR(bool); + formattable.fmt_bool.code = '?'; + formattable.fmt_bool.setfunc = bool_set; + formattable.fmt_bool.getfunc = bool_get; } +#undef FIXINT_FIELDDESC_FOR struct fielddesc * _ctypes_get_fielddesc(const char *fmt) { static int initialized = 0; - struct fielddesc *table = formattable; - if (!initialized) { - initialized = 1; _ctypes_init_fielddesc(); - } - for (; table->code; ++table) { - if (table->code == fmt[0]) - return table; + initialized = 1; } - return NULL; + 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; + } + 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. + Use '?' as code for BOOL. +*/ + /*---------------- EOF ----------------*/ diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index aa4fb39c942081..45e00a538fb5a5 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -248,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; }; From a11c0d9b596b5c9e292b6420b455ffde13f22b51 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 26 Nov 2024 14:27:10 +0100 Subject: [PATCH 03/17] Remove comment for obsolete idea --- Modules/_ctypes/cfield.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 0b4ff89da19670..d14d52ba13f0da 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -1496,7 +1496,6 @@ for code in 'sbBcdCEFgfhHiIlLqQPzuUZXvO': /* Ideas: Implement VARIANT in this table, using 'V' code. - Use '?' as code for BOOL. */ /*---------------- EOF ----------------*/ From c69b65fe72f02a17d4e5ededa589f3bbe6c9d55a Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 26 Nov 2024 16:10:22 +0100 Subject: [PATCH 04/17] Fix the size argument --- Modules/_ctypes/cfield.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index d14d52ba13f0da..24793a9293896b 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -393,7 +393,7 @@ Py_ssize_t NUM_BITS(Py_ssize_t bitsize) { return NULL; \ } \ } \ - *(CTYPE*)ptr = SET(CTYPE, *(CTYPE*)ptr, val, (NBITS) / 8); \ + *(CTYPE*)ptr = SET(CTYPE, *(CTYPE*)ptr, val, size_arg); \ _RET(value); \ } \ \ @@ -415,7 +415,7 @@ Py_ssize_t NUM_BITS(Py_ssize_t bitsize) { TAG ## _set_sw(void *ptr, PyObject *value, Py_ssize_t size_arg) \ { \ CTYPE val; \ - PyObject *res = TAG ## _set(&val, value, size_arg); \ + PyObject *res = TAG ## _set(&val, value, (NBITS) / 8); \ if (res == NULL) { \ return NULL; \ } \ From eb8cf98e1e9646c8c71d80d650acc364b3621fcb Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 26 Nov 2024 16:19:43 +0100 Subject: [PATCH 05/17] Re-run Clinic --- Modules/_ctypes/cfield.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 24793a9293896b..36ca0e3f3884f6 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -1265,14 +1265,14 @@ for nbytes in 8, 16, 32, 64: 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; + 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=8f85b8f329d7b5f6 input=22ef3654bd8451ae]*/ #undef _PACK } @@ -1355,33 +1355,33 @@ for base_code, base_c_type in [ 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = FIXINT_FIELDDESC_FOR(signed long long); formattable.fmt_q.code = 'q'; - formattable.fmt_Q = *FIXINT_FIELDDESC_FOR(unsigned long long); + formattable.fmt_Q = FIXINT_FIELDDESC_FOR(unsigned long long); formattable.fmt_Q.code = 'Q'; /*[python end generated code: output=3aab96e70e704ccc input=6f6d00bc889a7107]*/ From b9ed7278328b6bf75d82408048fa2e4049e5649e Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 26 Nov 2024 16:20:56 +0100 Subject: [PATCH 06/17] Regen Clinic --- Modules/_ctypes/cfield.c | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 36ca0e3f3884f6..b024c31f78d5d7 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -1263,17 +1263,17 @@ 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};') + + 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=8f85b8f329d7b5f6 input=22ef3654bd8451ae]*/ + 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 @@ -1347,7 +1347,7 @@ for base_code, base_c_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} = *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 @@ -1355,35 +1355,35 @@ for base_code, base_c_type in [ 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 = *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 = *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 = *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 = *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 = *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 = *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 = *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 = *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 = *FIXINT_FIELDDESC_FOR(signed long long); formattable.fmt_q.code = 'q'; - formattable.fmt_Q = FIXINT_FIELDDESC_FOR(unsigned long long); + formattable.fmt_Q = *FIXINT_FIELDDESC_FOR(unsigned long long); formattable.fmt_Q.code = 'Q'; -/*[python end generated code: output=3aab96e70e704ccc input=6f6d00bc889a7107]*/ +/*[python end generated code: output=873c87a2e6b5075a input=ee814ca263aac18e]*/ /* Other types have bespoke setters and getters named `@_set` and `@_get`, From 7cbe57ceca172ca729ebdc97facdb25459ea8472 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 26 Nov 2024 16:58:04 +0100 Subject: [PATCH 07/17] Specify signedness of the FFI types, don't match the C types --- Modules/_ctypes/cfield.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index b024c31f78d5d7..41768c390409a4 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -1415,8 +1415,10 @@ for base_code, base_c_type in [ TABLE_ENTRY_SW(f, &ffi_type_float); TABLE_ENTRY(v, &ffi_type_sshort); /* vBOOL */ - TABLE_ENTRY(c, FIXINT_FIELDDESC_FOR(char)->pffi_type); - TABLE_ENTRY(u, FIXINT_FIELDDESC_FOR(wchar_t)->pffi_type); + // 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); @@ -1434,7 +1436,8 @@ for base_code, base_c_type in [ /* bool has code '?', fill it in manually */ - formattable.fmt_bool = *FIXINT_FIELDDESC_FOR(bool); + // 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; From 144a3b0544d04bf5290e7b546e59876334d4982a Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 26 Nov 2024 16:59:31 +0100 Subject: [PATCH 08/17] Avoid compiler warnings --- Include/pyport.h | 5 +++++ Modules/_ctypes/cfield.c | 10 ++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Include/pyport.h b/Include/pyport.h index 2b6bd4c21110e5..da52f680198a11 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -288,20 +288,25 @@ extern "C" { #define _Py_COMP_DIAG_PUSH _Pragma("clang diagnostic push") #define _Py_COMP_DIAG_IGNORE_DEPR_DECLS \ _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#define _Py_COMP_DIAG_IGNORE_TYPE_LIMITS #define _Py_COMP_DIAG_POP _Pragma("clang diagnostic pop") #elif defined(__GNUC__) \ && ((__GNUC__ >= 5) || (__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) #define _Py_COMP_DIAG_PUSH _Pragma("GCC diagnostic push") #define _Py_COMP_DIAG_IGNORE_DEPR_DECLS \ _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#define _Py_COMP_DIAG_IGNORE_TYPE_LIMITS \ + _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") #define _Py_COMP_DIAG_POP _Pragma("GCC diagnostic pop") #elif defined(_MSC_VER) #define _Py_COMP_DIAG_PUSH __pragma(warning(push)) #define _Py_COMP_DIAG_IGNORE_DEPR_DECLS __pragma(warning(disable: 4996)) +#define _Py_COMP_DIAG_IGNORE_TYPE_LIMITS #define _Py_COMP_DIAG_POP __pragma(warning(pop)) #else #define _Py_COMP_DIAG_PUSH #define _Py_COMP_DIAG_IGNORE_DEPR_DECLS +#define _Py_COMP_DIAG_IGNORE_TYPE_LIMITS #define _Py_COMP_DIAG_POP #endif diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 41768c390409a4..9af231ee531cee 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -385,7 +385,7 @@ Py_ssize_t NUM_BITS(Py_ssize_t bitsize) { (PyLongObject *)value); \ } \ else { \ - int res = PyLong_AsNativeBytes( \ + Py_ssize_t res = PyLong_AsNativeBytes( \ value, &val, (NBITS) / 8, \ Py_ASNATIVEBYTES_NATIVE_ENDIAN \ | Py_ASNATIVEBYTES_ALLOW_INDEX); \ @@ -1283,7 +1283,12 @@ for nbytes in 8, 16, 32, 64: Py_UNREACHABLE(); } -/* Macro to call _ctypes_fixint_fielddesc for a given C type */ +/* Macro to call _ctypes_fixint_fielddesc for a given C type. + * The signedness check expands an expression that's always true or false; + * we silence the GCC warning '-Wtype-limits' for this. + */ +_Py_COMP_DIAG_PUSH +_Py_COMP_DIAG_IGNORE_TYPE_LIMITS #define FIXINT_FIELDDESC_FOR(C_TYPE) \ _ctypes_fixint_fielddesc(sizeof(C_TYPE), (C_TYPE)-1 < 0) @@ -1443,6 +1448,7 @@ for base_code, base_c_type in [ formattable.fmt_bool.getfunc = bool_get; } #undef FIXINT_FIELDDESC_FOR +_Py_COMP_DIAG_POP struct fielddesc * _ctypes_get_fielddesc(const char *fmt) From fcae66bf1f432d7ba8fa85f60e018986bc8ec461 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 26 Nov 2024 18:54:35 +0100 Subject: [PATCH 09/17] Silence the GCC warning in a more local way. It's a GCC bug. --- Include/pyport.h | 5 ----- Modules/_ctypes/cfield.c | 18 +++++++++++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Include/pyport.h b/Include/pyport.h index da52f680198a11..2b6bd4c21110e5 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -288,25 +288,20 @@ extern "C" { #define _Py_COMP_DIAG_PUSH _Pragma("clang diagnostic push") #define _Py_COMP_DIAG_IGNORE_DEPR_DECLS \ _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") -#define _Py_COMP_DIAG_IGNORE_TYPE_LIMITS #define _Py_COMP_DIAG_POP _Pragma("clang diagnostic pop") #elif defined(__GNUC__) \ && ((__GNUC__ >= 5) || (__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) #define _Py_COMP_DIAG_PUSH _Pragma("GCC diagnostic push") #define _Py_COMP_DIAG_IGNORE_DEPR_DECLS \ _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") -#define _Py_COMP_DIAG_IGNORE_TYPE_LIMITS \ - _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") #define _Py_COMP_DIAG_POP _Pragma("GCC diagnostic pop") #elif defined(_MSC_VER) #define _Py_COMP_DIAG_PUSH __pragma(warning(push)) #define _Py_COMP_DIAG_IGNORE_DEPR_DECLS __pragma(warning(disable: 4996)) -#define _Py_COMP_DIAG_IGNORE_TYPE_LIMITS #define _Py_COMP_DIAG_POP __pragma(warning(pop)) #else #define _Py_COMP_DIAG_PUSH #define _Py_COMP_DIAG_IGNORE_DEPR_DECLS -#define _Py_COMP_DIAG_IGNORE_TYPE_LIMITS #define _Py_COMP_DIAG_POP #endif diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 9af231ee531cee..d4fcfb1f213a21 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -1283,12 +1283,20 @@ for nbytes in 8, 16, 32, 64: Py_UNREACHABLE(); } -/* Macro to call _ctypes_fixint_fielddesc for a given C type. - * The signedness check expands an expression that's always true or false; - * we silence the GCC warning '-Wtype-limits' for this. - */ + +/* Macro to call _ctypes_fixint_fielddesc for a given C type. */ + _Py_COMP_DIAG_PUSH -_Py_COMP_DIAG_IGNORE_TYPE_LIMITS +#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) From 05ba9899bcd62b991d51bc3ffad385dd1d79915b Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 6 Dec 2024 15:44:55 +0100 Subject: [PATCH 10/17] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Modules/_ctypes/cfield.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index d4fcfb1f213a21..3e5d8a93e9b342 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -380,9 +380,10 @@ Py_ssize_t NUM_BITS(Py_ssize_t bitsize) { assert(NUM_BITS(size_arg) || (size_arg == (NBITS) / 8)); \ CTYPE val; \ if (PyLong_Check(value) \ - && PyUnstable_Long_IsCompact((PyLongObject *)value)) { \ + && PyUnstable_Long_IsCompact((PyLongObject *)value)) \ + { \ val = (CTYPE)PyUnstable_Long_CompactValue( \ - (PyLongObject *)value); \ + (PyLongObject *)value); \ } \ else { \ Py_ssize_t res = PyLong_AsNativeBytes( \ @@ -884,7 +885,7 @@ u_get(void *ptr, Py_ssize_t size) return PyUnicode_FromWideChar((wchar_t *)ptr, 1); } -/* U - a wchar_t* unicode string */ +/* U: a wchar_t* unicode string */ static PyObject * U_get(void *ptr, Py_ssize_t size) { From 7c08d60da964ae868549eed0296182a2816a0b17 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 6 Dec 2024 15:24:22 +0100 Subject: [PATCH 11/17] Add parens around macro argument --- Modules/_ctypes/cfield.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 3e5d8a93e9b342..2249a7850860d0 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -344,7 +344,7 @@ Py_ssize_t NUM_BITS(Py_ssize_t bitsize) { #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) + : (type)(v)) /***************************************************************** * The setter methods return an object which must be kept alive, to keep the From 50c82bed90acfb44043338a567eda51b47cec65f Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 6 Dec 2024 15:36:40 +0100 Subject: [PATCH 12/17] Fix refcounting for _CTYPES_DEBUG_KEEP builds --- Modules/_ctypes/cfield.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 2249a7850860d0..833c1d5fc6e186 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -420,6 +420,7 @@ Py_ssize_t NUM_BITS(Py_ssize_t bitsize) { if (res == NULL) { \ return NULL; \ } \ + Py_DECREF(res); \ CTYPE field; \ memcpy(&field, ptr, sizeof(field)); \ field = PY_SWAPFUNC(field); \ From 7375c09dc4810daa3e5ad1957ea2f985e890b3b0 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 6 Dec 2024 15:39:23 +0100 Subject: [PATCH 13/17] Consistency in comments. Why not. --- Modules/_ctypes/cfield.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 833c1d5fc6e186..36e874c4c7504b 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -844,7 +844,7 @@ c_get(void *ptr, Py_ssize_t size) 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) { From 870a03849990e1cc45c7a6fe9c2bc654d7cc7258 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 9 Dec 2024 12:20:38 +0100 Subject: [PATCH 14/17] Include & use `bool` rather than `_Bool` --- Modules/_ctypes/cfield.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 36e874c4c7504b..ede65c24f7f8ce 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 // bool #include #include "ctypes.h" @@ -522,7 +523,7 @@ v_get(void *ptr, Py_ssize_t size) return PyBool_FromLong((long)*(short int *)ptr); } -/* bool ('?'): _Bool */ +/* bool ('?'): bool (i.e. _Bool) */ static PyObject * bool_set(void *ptr, PyObject *value, Py_ssize_t size) { @@ -531,10 +532,10 @@ bool_set(void *ptr, PyObject *value, Py_ssize_t size) case -1: return NULL; case 0: - *(_Bool *)ptr = 0; + *(bool *)ptr = 0; _RET(value); default: - *(_Bool *)ptr = 1; + *(bool *)ptr = 1; _RET(value); } } @@ -543,7 +544,7 @@ static PyObject * bool_get(void *ptr, Py_ssize_t size) { assert(NUM_BITS(size) || (size == sizeof(bool))); - return PyBool_FromLong((long)*(_Bool *)ptr); + return PyBool_FromLong((long)*(bool *)ptr); } /* g: long double */ From 1b41ace5734297732c89cc53d222fea718ce9533 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 9 Dec 2024 13:14:20 +0100 Subject: [PATCH 15/17] Use a mutex around _ctypes_init_fielddesc --- Modules/_ctypes/cfield.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index ede65c24f7f8ce..8f92db544d9cb4 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -1194,7 +1194,7 @@ P_get(void *ptr, Py_ssize_t size) /* Table with info about all formats. * Must be accessed via _ctypes_get_fielddesc, which initializes it on - * first use. + * first use. After initialization it's treated as constant & read-only. */ struct formattable { @@ -1464,12 +1464,14 @@ _Py_COMP_DIAG_POP struct fielddesc * _ctypes_get_fielddesc(const char *fmt) { - static int initialized = 0; + static bool initialized = 0; + static PyMutex mutex = {0}; + PyMutex_Lock(&mutex); if (!initialized) { _ctypes_init_fielddesc(); - initialized = 1; } + PyMutex_Unlock(&mutex); struct fielddesc *result = NULL; switch(fmt[0]) { /*[python input] From 34098c351dd1ec217ce7360e5d3cf9ea4c3711a1 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 13 Dec 2024 11:56:28 +0100 Subject: [PATCH 16/17] Nitpick: Use true/false for bool --- Modules/_ctypes/cfield.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 398d0809da79ac..9c1b9ce5f30bf0 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -1470,12 +1470,12 @@ _Py_COMP_DIAG_POP struct fielddesc * _ctypes_get_fielddesc(const char *fmt) { - static bool initialized = 0; + static bool initialized = false; static PyMutex mutex = {0}; PyMutex_Lock(&mutex); if (!initialized) { _ctypes_init_fielddesc(); - initialized = 1; + initialized = true; } PyMutex_Unlock(&mutex); struct fielddesc *result = NULL; From 5155293361de5db6abe76b50a6510452441e4fb7 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 18 Dec 2024 11:57:59 +0100 Subject: [PATCH 17/17] Avoid unaligned load --- Modules/_ctypes/cfield.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 9c1b9ce5f30bf0..dcac9da75360a4 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -401,7 +401,10 @@ Py_ssize_t NUM_BITS(Py_ssize_t bitsize) { return NULL; \ } \ } \ - *(CTYPE*)ptr = SET(CTYPE, *(CTYPE*)ptr, val, size_arg); \ + CTYPE prev; \ + memcpy(&prev, ptr, (NBITS) / 8); \ + val = SET(CTYPE, prev, val, size_arg); \ + memcpy(ptr, &val, (NBITS) / 8); \ _RET(value); \ } \ \