Skip to content

Commit 78ffba4

Browse files
encukoupicnixz
andauthored
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 <[email protected]>
1 parent ba45e5c commit 78ffba4

File tree

4 files changed

+501
-652
lines changed

4 files changed

+501
-652
lines changed

Modules/_ctypes/_ctypes.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -1979,7 +1979,7 @@ c_void_p_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value)
19791979
return NULL;
19801980
parg->pffi_type = &ffi_type_pointer;
19811981
parg->tag = 'P';
1982-
parg->obj = fd->setfunc(&parg->value, value, 0);
1982+
parg->obj = fd->setfunc(&parg->value, value, sizeof(void*));
19831983
if (parg->obj == NULL) {
19841984
Py_DECREF(parg);
19851985
return NULL;
@@ -2444,7 +2444,7 @@ PyCSimpleType_from_param_impl(PyObject *type, PyTypeObject *cls,
24442444

24452445
parg->tag = fmt[0];
24462446
parg->pffi_type = fd->pffi_type;
2447-
parg->obj = fd->setfunc(&parg->value, value, 0);
2447+
parg->obj = fd->setfunc(&parg->value, value, info->size);
24482448
if (parg->obj)
24492449
return (PyObject *)parg;
24502450
PyObject *exc = PyErr_GetRaisedException();

Modules/_ctypes/callbacks.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ static void _CallPythonObject(ctypes_state *st,
264264
be the result. EXCEPT when restype is py_object - Python
265265
itself knows how to manage the refcount of these objects.
266266
*/
267-
PyObject *keep = setfunc(mem, result, 0);
267+
PyObject *keep = setfunc(mem, result, restype->size);
268268

269269
if (keep == NULL) {
270270
/* Could not convert callback result. */

0 commit comments

Comments
 (0)