Objects/memoryobject.c
cpython 3.14 @ ab2d84fe1023/Objects/memoryobject.c
memoryobject.c implements CPython's memoryview type. A PyMemoryViewObject wraps a Py_buffer descriptor (pointer to the underlying memory, format string, shape, strides, suboffsets, and an export count). The buffer protocol lets numpy arrays, bytes, bytearray, and ctypes objects share memory without copying. memoryview is the user-visible handle into that shared region.
Map
| Lines | Symbol | Role | gopy |
|---|---|---|---|
| 1-60 | includes, MV_C_CONTIGUOUS macros | Header and contiguity flag helpers | not ported |
| 61-180 | PyBUF_* flag validation, init_strides_from_shape | Buffer layout initialisation | not ported |
| 181-320 | memory_alloc, mbuf_alloc, mbuf_add_view | Allocation of the managed-buffer layer | not ported |
| 321-480 | PyMemoryView_FromObject, PyMemoryView_FromBuffer | Public constructors; call bf_getbuffer on the target | not ported |
| 481-620 | memory_getbuffer, memory_releasebuffer | Implement bf_getbuffer/bf_releasebuffer for nested views | not ported |
| 621-760 | copy_single, copy_rec | Element-wise copy respecting strides and suboffsets | not ported |
| 761-900 | pack_single, unpack_single | Read/write one typed element using the format code | not ported |
| 901-1040 | memoryview_getitem, memoryview_setitem | sq_item/sq_ass_item slots; index into a flat or multi-dimensional view | not ported |
| 1041-1200 | memoryview_subscript, memoryview_ass_subscript | Slice and multi-index support via mp_subscript | not ported |
| 1201-1380 | memory_richcompare | Element-by-element equality comparison between two views | not ported |
| 1381-1520 | memory_hash | Compute hash over the contiguous byte representation | not ported |
| 1521-1660 | memory_tobytes, memory_tolist, memory_toreadonly | Conversion methods | not ported |
| 1661-1800 | memory_cast | Reinterpret the buffer under a new format/shape | not ported |
| 1801-1940 | memory_repr, memory_getattro | Repr and attribute access for shape/strides/format properties | not ported |
| 1941-2060 | memory_enter, memory_exit | Context manager protocol; __exit__ calls memory_release | not ported |
| 2061-2200 | PyMemoryView_Type definition | Full PyTypeObject with buffer, sequence, and mapping slots | not ported |
Reading
Buffer acquisition and the managed-buffer layer (lines 181 to 480)
cpython 3.14 @ ab2d84fe1023/Objects/memoryobject.c#L181-480
CPython introduces a two-level structure to handle nested views safely. A "managed buffer" (_PyManagedBufferObject) is an internal reference-counted wrapper that holds the root Py_buffer and an export count. Every memoryview that points into the same root object shares one managed buffer:
static _PyManagedBufferObject *
mbuf_alloc(void)
{
_PyManagedBufferObject *mbuf;
mbuf = (_PyManagedBufferObject *)
PyObject_GC_New(_PyManagedBufferObject, &_PyManagedBuffer_Type);
if (mbuf == NULL)
return NULL;
mbuf->flags = 0;
mbuf->exports = 0;
memset(&mbuf->master, 0, sizeof(Py_buffer));
mbuf->master.obj = NULL;
return mbuf;
}
PyMemoryView_FromObject calls bf_getbuffer on the source once and stores the result in a new managed buffer. Slicing that view creates a second PyMemoryViewObject sharing the same managed buffer, so the underlying bf_releasebuffer is called exactly once when all views are released.
Element access via format codes (lines 761 to 1040)
cpython 3.14 @ ab2d84fe1023/Objects/memoryobject.c#L761-1040
pack_single and unpack_single implement a mini interpreter over the buffer's format string. Each letter maps to a C type; for example 'd' reads or writes a double, 'i' a signed 32-bit integer. memoryview_getitem uses the stride array to compute the byte offset for a linear index and then delegates to unpack_single:
static PyObject *
memoryview_getitem(PyMemoryViewObject *self, Py_ssize_t index)
{
Py_buffer *view = &self->view;
char *ptr;
CHECK_RELEASED(self);
if (view->ndim != 1) {
PyErr_SetString(PyExc_NotImplementedError,
"multi-dimensional sub-views are not implemented");
return NULL;
}
ptr = (char *)view->buf + view->strides[0] * index;
return unpack_single(self, ptr, view->format);
}
Strides allow the view to describe non-contiguous memory such as a column slice of a 2-D array, without moving any data.
memory_cast and format reinterpretation (lines 1661 to 1800)
cpython 3.14 @ ab2d84fe1023/Objects/memoryobject.c#L1661-1800
memoryview.cast(format) is one of the more complex methods. It validates that the total byte length is divisible by the new element size, builds a fresh shape tuple, and returns a new PyMemoryViewObject pointing into the same managed buffer with a different format and strides:
static PyObject *
memory_cast(PyMemoryViewObject *self, PyObject *args, PyObject *kwds)
{
...
/* new view shares the same managed buffer */
mv = (PyMemoryViewObject *)
mbuf_add_incomplete_view(self->mbuf, NULL, ndim == 0 ? 1 : ndim);
if (mv == NULL)
goto error;
mv->view.format = PyBytes_AS_STRING(format_as_bytes);
mv->view.itemsize = itemsize;
...
}
Because the managed buffer's export count is incremented, the original source object stays alive as long as any view, regardless of format, references it.
gopy mirror
memoryobject.c has no counterpart in gopy yet. A future port would live in objects/memoryview.go. The buffer protocol itself (the bf_getbuffer / bf_releasebuffer pair) would need to be expressed as a Go interface, likely BufferProtocol, that bytes, bytearray, and array objects implement. The managed-buffer two-level structure maps naturally to a Go struct with a reference count protected by a mutex.
CPython 3.14 changes
CPython 3.14 added memoryview.toreadonly() as a stable API (the method existed in 3.12 but was not in the stable ABI). The memory_cast path gained stricter validation rejecting casts that would create views with suboffsets, closing a potential out-of-bounds access. The Py_buffer.obj ownership rules were clarified in the documentation but the C code itself is unchanged in structure.