summaryrefslogtreecommitdiffstats
path: root/Objects/typeobject.c
diff options
context:
space:
mode:
Diffstat (limited to 'Objects/typeobject.c')
-rw-r--r--Objects/typeobject.c197
1 files changed, 195 insertions, 2 deletions
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 171c76a..456b10e 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -6,6 +6,7 @@
#include "pycore_symtable.h" // _Py_Mangle()
#include "pycore_dict.h" // _PyDict_KeysSize()
#include "pycore_initconfig.h" // _PyStatus_OK()
+#include "pycore_memoryobject.h" // PyMemoryView_FromObjectAndFlags()
#include "pycore_moduleobject.h" // _PyModule_GetDef()
#include "pycore_object.h" // _PyType_HasFeature()
#include "pycore_long.h" // _PyLong_IsNegative()
@@ -8060,6 +8061,58 @@ wrap_descr_delete(PyObject *self, PyObject *args, void *wrapped)
}
static PyObject *
+wrap_buffer(PyObject *self, PyObject *args, void *wrapped)
+{
+ PyObject *arg = NULL;
+
+ if (!PyArg_UnpackTuple(args, "", 1, 1, &arg)) {
+ return NULL;
+ }
+ Py_ssize_t flags = PyNumber_AsSsize_t(arg, PyExc_OverflowError);
+ if (flags == -1 && PyErr_Occurred()) {
+ return NULL;
+ }
+ if (flags > INT_MAX) {
+ PyErr_SetString(PyExc_OverflowError,
+ "buffer flags too large");
+ return NULL;
+ }
+
+ return PyMemoryView_FromObjectAndFlags(self, Py_SAFE_DOWNCAST(flags, Py_ssize_t, int));
+}
+
+static PyObject *
+wrap_releasebuffer(PyObject *self, PyObject *args, void *wrapped)
+{
+ PyObject *arg = NULL;
+ if (!PyArg_UnpackTuple(args, "", 1, 1, &arg)) {
+ return NULL;
+ }
+ if (!PyMemoryView_Check(arg)) {
+ PyErr_SetString(PyExc_TypeError,
+ "expected a memoryview object");
+ return NULL;
+ }
+ PyMemoryViewObject *mview = (PyMemoryViewObject *)arg;
+ if (mview->view.obj != self) {
+ PyErr_SetString(PyExc_ValueError,
+ "memoryview's buffer is not this object");
+ return NULL;
+ }
+ if (mview->flags & _Py_MEMORYVIEW_RELEASED) {
+ PyErr_SetString(PyExc_ValueError,
+ "memoryview's buffer has already been released");
+ return NULL;
+ }
+ PyObject *res = PyObject_CallMethodNoArgs((PyObject *)mview, &_Py_ID(release));
+ if (res == NULL) {
+ return NULL;
+ }
+ Py_DECREF(res);
+ Py_RETURN_NONE;
+}
+
+static PyObject *
wrap_init(PyObject *self, PyObject *args, void *wrapped, PyObject *kwds)
{
initproc func = (initproc)wrapped;
@@ -8895,6 +8948,132 @@ slot_tp_finalize(PyObject *self)
PyErr_SetRaisedException(exc);
}
+typedef struct _PyBufferWrapper {
+ PyObject_HEAD
+ PyObject *mv;
+ PyObject *obj;
+} PyBufferWrapper;
+
+static int
+bufferwrapper_traverse(PyBufferWrapper *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->mv);
+ Py_VISIT(self->obj);
+ return 0;
+}
+
+static void
+bufferwrapper_dealloc(PyObject *self)
+{
+ PyBufferWrapper *bw = (PyBufferWrapper *)self;
+
+ _PyObject_GC_UNTRACK(self);
+ Py_XDECREF(bw->mv);
+ Py_XDECREF(bw->obj);
+ Py_TYPE(self)->tp_free(self);
+}
+
+static void
+bufferwrapper_releasebuf(PyObject *self, Py_buffer *view)
+{
+ PyBufferWrapper *bw = (PyBufferWrapper *)self;
+
+ assert(PyMemoryView_Check(bw->mv));
+ Py_TYPE(bw->mv)->tp_as_buffer->bf_releasebuffer(bw->mv, view);
+ if (Py_TYPE(bw->obj)->tp_as_buffer != NULL
+ && Py_TYPE(bw->obj)->tp_as_buffer->bf_releasebuffer != NULL) {
+ Py_TYPE(bw->obj)->tp_as_buffer->bf_releasebuffer(bw->obj, view);
+ }
+}
+
+static PyBufferProcs bufferwrapper_as_buffer = {
+ .bf_releasebuffer = bufferwrapper_releasebuf,
+};
+
+
+PyTypeObject _PyBufferWrapper_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ .tp_name = "_buffer_wrapper",
+ .tp_basicsize = sizeof(PyBufferWrapper),
+ .tp_alloc = PyType_GenericAlloc,
+ .tp_free = PyObject_GC_Del,
+ .tp_traverse = (traverseproc)bufferwrapper_traverse,
+ .tp_dealloc = bufferwrapper_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .tp_as_buffer = &bufferwrapper_as_buffer,
+};
+
+static int
+slot_bf_getbuffer(PyObject *self, Py_buffer *buffer, int flags)
+{
+ PyObject *flags_obj = PyLong_FromLong(flags);
+ if (flags_obj == NULL) {
+ return -1;
+ }
+ PyBufferWrapper *wrapper = NULL;
+ PyObject *stack[2] = {self, flags_obj};
+ PyObject *ret = vectorcall_method(&_Py_ID(__buffer__), stack, 2);
+ if (ret == NULL) {
+ goto fail;
+ }
+ if (!PyMemoryView_Check(ret)) {
+ PyErr_Format(PyExc_TypeError,
+ "__buffer__ returned non-memoryview object");
+ goto fail;
+ }
+
+ if (PyObject_GetBuffer(ret, buffer, flags) < 0) {
+ goto fail;
+ }
+ assert(buffer->obj == ret);
+
+ wrapper = PyObject_GC_New(PyBufferWrapper, &_PyBufferWrapper_Type);
+ if (wrapper == NULL) {
+ goto fail;
+ }
+ wrapper->mv = ret;
+ wrapper->obj = Py_NewRef(self);
+ _PyObject_GC_TRACK(wrapper);
+
+ buffer->obj = (PyObject *)wrapper;
+ Py_DECREF(ret);
+ Py_DECREF(flags_obj);
+ return 0;
+
+fail:
+ Py_XDECREF(wrapper);
+ Py_XDECREF(ret);
+ Py_DECREF(flags_obj);
+ return -1;
+}
+
+static void
+slot_bf_releasebuffer(PyObject *self, Py_buffer *buffer)
+{
+ PyObject *mv;
+ if (Py_TYPE(buffer->obj) == &_PyBufferWrapper_Type) {
+ // Make sure we pass the same memoryview to
+ // __release_buffer__() that __buffer__() returned.
+ mv = Py_NewRef(((PyBufferWrapper *)buffer->obj)->mv);
+ }
+ else {
+ mv = PyMemoryView_FromBuffer(buffer);
+ if (mv == NULL) {
+ PyErr_WriteUnraisable(self);
+ return;
+ }
+ }
+ PyObject *stack[2] = {self, mv};
+ PyObject *ret = vectorcall_method(&_Py_ID(__release_buffer__), stack, 2);
+ Py_DECREF(mv);
+ if (ret == NULL) {
+ PyErr_WriteUnraisable(self);
+ }
+ else {
+ Py_DECREF(ret);
+ }
+}
+
static PyObject *
slot_am_await(PyObject *self)
{
@@ -8962,6 +9141,7 @@ an all-zero entry.
#undef TPSLOT
#undef FLSLOT
+#undef BUFSLOT
#undef AMSLOT
#undef ETSLOT
#undef SQSLOT
@@ -8981,6 +9161,8 @@ an all-zero entry.
#define ETSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
{#NAME, offsetof(PyHeapTypeObject, SLOT), (void *)(FUNCTION), WRAPPER, \
PyDoc_STR(DOC), .name_strobj = &_Py_ID(NAME) }
+#define BUFSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
+ ETSLOT(NAME, as_buffer.SLOT, FUNCTION, WRAPPER, DOC)
#define AMSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
ETSLOT(NAME, as_async.SLOT, FUNCTION, WRAPPER, DOC)
#define SQSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
@@ -9062,6 +9244,13 @@ static pytype_slotdef slotdefs[] = {
"Create and return new object. See help(type) for accurate signature."),
TPSLOT(__del__, tp_finalize, slot_tp_finalize, (wrapperfunc)wrap_del, ""),
+ BUFSLOT(__buffer__, bf_getbuffer, slot_bf_getbuffer, wrap_buffer,
+ "__buffer__($self, flags, /)\n--\n\n"
+ "Return a buffer object that exposes the underlying memory of the object."),
+ BUFSLOT(__release_buffer__, bf_releasebuffer, slot_bf_releasebuffer, wrap_releasebuffer,
+ "__release_buffer__($self, /)\n--\n\n"
+ "Release the buffer object that exposes the underlying memory of the object."),
+
AMSLOT(__await__, am_await, slot_am_await, wrap_unaryfunc,
"__await__($self, /)\n--\n\nReturn an iterator to be used in await expression."),
AMSLOT(__aiter__, am_aiter, slot_am_aiter, wrap_unaryfunc,
@@ -9208,8 +9397,12 @@ slotptr(PyTypeObject *type, int ioffset)
/* Note: this depends on the order of the members of PyHeapTypeObject! */
assert(offset >= 0);
- assert((size_t)offset < offsetof(PyHeapTypeObject, as_buffer));
- if ((size_t)offset >= offsetof(PyHeapTypeObject, as_sequence)) {
+ assert((size_t)offset < offsetof(PyHeapTypeObject, ht_name));
+ if ((size_t)offset >= offsetof(PyHeapTypeObject, as_buffer)) {
+ ptr = (char *)type->tp_as_buffer;
+ offset -= offsetof(PyHeapTypeObject, as_buffer);
+ }
+ else if ((size_t)offset >= offsetof(PyHeapTypeObject, as_sequence)) {
ptr = (char *)type->tp_as_sequence;
offset -= offsetof(PyHeapTypeObject, as_sequence);
}