diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index ef3a9a0f5898af..c28a378d181b59 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -2109,6 +2109,19 @@ Utility functions .. audit-event:: ctypes.wstring_at address,size ctypes.wstring_at +.. function:: buffer_at(address, size, allow_write=False) + + This function returns a memoryview object that references the + memory starting at *address* up to (but not including) *address + + size*. If *allow_write* is set to a truthy value then the + memoryview object is mutable. + + This function is similar to :funct:`string_at` with the key difference + of not making a copy of the specified memory. + + .. audit-event:: ctypes.buffer_at address,size,allow_write ctypes.buffer_at + + .. _ctypes-data-types: Data types diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 141142a57dcb3e..674286550730d7 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -538,6 +538,14 @@ def wstring_at(ptr, size=-1): Return the string at addr.""" return _wstring_at(ptr, size) +from _ctypes import _buffer_at_addr + +_buffer_at = PYFUNCTYPE(py_object, c_void_p, c_int, c_int)(_buffer_at_addr) +def buffer_at(ptr, size, allow_write=False): + """buffer_at(addr, size[, allow_write]) -> memoryview + + Return the buffer at addr.""" + return _buffer_at(ptr, size, bool(allow_write)) if _os.name == "nt": # COM stuff def DllGetClassObject(rclsid, riid, ppv): diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 6e0709cc1e4a4d..a77824b592d4a9 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5642,6 +5642,17 @@ wstring_at(const wchar_t *ptr, int size) return PyUnicode_FromWideChar(ptr, ssize); } +static PyObject * +buffer_at(char *ptr, int size, int allow_write) +{ + if (PySys_Audit("ctypes.buffer_at", "nii", (Py_ssize_t)ptr, size, + allow_write) < 0) { + return NULL; + } + + return PyMemoryView_FromMemory(ptr, size, + allow_write ? PyBUF_WRITE : PyBUF_READ); +} static struct PyModuleDef _ctypesmodule = { PyModuleDef_HEAD_INIT, @@ -5777,6 +5788,7 @@ _ctypes_add_objects(PyObject *mod) MOD_ADD("_string_at_addr", PyLong_FromVoidPtr(string_at)); MOD_ADD("_cast_addr", PyLong_FromVoidPtr(cast)); MOD_ADD("_wstring_at_addr", PyLong_FromVoidPtr(wstring_at)); + MOD_ADD("_buffer_at_addr", PyLong_FromVoidPtr(buffer_at)); /* If RTLD_LOCAL is not defined (Windows!), set it to zero. */ #if !HAVE_DECL_RTLD_LOCAL