17db96d56Sopenharmony_ci/* PickleBuffer object implementation */
27db96d56Sopenharmony_ci
37db96d56Sopenharmony_ci#define PY_SSIZE_T_CLEAN
47db96d56Sopenharmony_ci#include "Python.h"
57db96d56Sopenharmony_ci#include <stddef.h>
67db96d56Sopenharmony_ci
77db96d56Sopenharmony_citypedef struct {
87db96d56Sopenharmony_ci    PyObject_HEAD
97db96d56Sopenharmony_ci    /* The view exported by the original object */
107db96d56Sopenharmony_ci    Py_buffer view;
117db96d56Sopenharmony_ci    PyObject *weakreflist;
127db96d56Sopenharmony_ci} PyPickleBufferObject;
137db96d56Sopenharmony_ci
147db96d56Sopenharmony_ci/* C API */
157db96d56Sopenharmony_ci
167db96d56Sopenharmony_ciPyObject *
177db96d56Sopenharmony_ciPyPickleBuffer_FromObject(PyObject *base)
187db96d56Sopenharmony_ci{
197db96d56Sopenharmony_ci    PyTypeObject *type = &PyPickleBuffer_Type;
207db96d56Sopenharmony_ci    PyPickleBufferObject *self;
217db96d56Sopenharmony_ci
227db96d56Sopenharmony_ci    self = (PyPickleBufferObject *) type->tp_alloc(type, 0);
237db96d56Sopenharmony_ci    if (self == NULL) {
247db96d56Sopenharmony_ci        return NULL;
257db96d56Sopenharmony_ci    }
267db96d56Sopenharmony_ci    self->view.obj = NULL;
277db96d56Sopenharmony_ci    self->weakreflist = NULL;
287db96d56Sopenharmony_ci    if (PyObject_GetBuffer(base, &self->view, PyBUF_FULL_RO) < 0) {
297db96d56Sopenharmony_ci        Py_DECREF(self);
307db96d56Sopenharmony_ci        return NULL;
317db96d56Sopenharmony_ci    }
327db96d56Sopenharmony_ci    return (PyObject *) self;
337db96d56Sopenharmony_ci}
347db96d56Sopenharmony_ci
357db96d56Sopenharmony_ciconst Py_buffer *
367db96d56Sopenharmony_ciPyPickleBuffer_GetBuffer(PyObject *obj)
377db96d56Sopenharmony_ci{
387db96d56Sopenharmony_ci    PyPickleBufferObject *self = (PyPickleBufferObject *) obj;
397db96d56Sopenharmony_ci
407db96d56Sopenharmony_ci    if (!PyPickleBuffer_Check(obj)) {
417db96d56Sopenharmony_ci        PyErr_Format(PyExc_TypeError,
427db96d56Sopenharmony_ci                     "expected PickleBuffer, %.200s found",
437db96d56Sopenharmony_ci                     Py_TYPE(obj)->tp_name);
447db96d56Sopenharmony_ci        return NULL;
457db96d56Sopenharmony_ci    }
467db96d56Sopenharmony_ci    if (self->view.obj == NULL) {
477db96d56Sopenharmony_ci        PyErr_SetString(PyExc_ValueError,
487db96d56Sopenharmony_ci                        "operation forbidden on released PickleBuffer object");
497db96d56Sopenharmony_ci        return NULL;
507db96d56Sopenharmony_ci    }
517db96d56Sopenharmony_ci    return &self->view;
527db96d56Sopenharmony_ci}
537db96d56Sopenharmony_ci
547db96d56Sopenharmony_ciint
557db96d56Sopenharmony_ciPyPickleBuffer_Release(PyObject *obj)
567db96d56Sopenharmony_ci{
577db96d56Sopenharmony_ci    PyPickleBufferObject *self = (PyPickleBufferObject *) obj;
587db96d56Sopenharmony_ci
597db96d56Sopenharmony_ci    if (!PyPickleBuffer_Check(obj)) {
607db96d56Sopenharmony_ci        PyErr_Format(PyExc_TypeError,
617db96d56Sopenharmony_ci                     "expected PickleBuffer, %.200s found",
627db96d56Sopenharmony_ci                     Py_TYPE(obj)->tp_name);
637db96d56Sopenharmony_ci        return -1;
647db96d56Sopenharmony_ci    }
657db96d56Sopenharmony_ci    PyBuffer_Release(&self->view);
667db96d56Sopenharmony_ci    return 0;
677db96d56Sopenharmony_ci}
687db96d56Sopenharmony_ci
697db96d56Sopenharmony_cistatic PyObject *
707db96d56Sopenharmony_cipicklebuf_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
717db96d56Sopenharmony_ci{
727db96d56Sopenharmony_ci    PyPickleBufferObject *self;
737db96d56Sopenharmony_ci    PyObject *base;
747db96d56Sopenharmony_ci    char *keywords[] = {"", NULL};
757db96d56Sopenharmony_ci
767db96d56Sopenharmony_ci    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:PickleBuffer",
777db96d56Sopenharmony_ci                                     keywords, &base)) {
787db96d56Sopenharmony_ci        return NULL;
797db96d56Sopenharmony_ci    }
807db96d56Sopenharmony_ci
817db96d56Sopenharmony_ci    self = (PyPickleBufferObject *) type->tp_alloc(type, 0);
827db96d56Sopenharmony_ci    if (self == NULL) {
837db96d56Sopenharmony_ci        return NULL;
847db96d56Sopenharmony_ci    }
857db96d56Sopenharmony_ci    self->view.obj = NULL;
867db96d56Sopenharmony_ci    self->weakreflist = NULL;
877db96d56Sopenharmony_ci    if (PyObject_GetBuffer(base, &self->view, PyBUF_FULL_RO) < 0) {
887db96d56Sopenharmony_ci        Py_DECREF(self);
897db96d56Sopenharmony_ci        return NULL;
907db96d56Sopenharmony_ci    }
917db96d56Sopenharmony_ci    return (PyObject *) self;
927db96d56Sopenharmony_ci}
937db96d56Sopenharmony_ci
947db96d56Sopenharmony_cistatic int
957db96d56Sopenharmony_cipicklebuf_traverse(PyPickleBufferObject *self, visitproc visit, void *arg)
967db96d56Sopenharmony_ci{
977db96d56Sopenharmony_ci    Py_VISIT(self->view.obj);
987db96d56Sopenharmony_ci    return 0;
997db96d56Sopenharmony_ci}
1007db96d56Sopenharmony_ci
1017db96d56Sopenharmony_cistatic int
1027db96d56Sopenharmony_cipicklebuf_clear(PyPickleBufferObject *self)
1037db96d56Sopenharmony_ci{
1047db96d56Sopenharmony_ci    PyBuffer_Release(&self->view);
1057db96d56Sopenharmony_ci    return 0;
1067db96d56Sopenharmony_ci}
1077db96d56Sopenharmony_ci
1087db96d56Sopenharmony_cistatic void
1097db96d56Sopenharmony_cipicklebuf_dealloc(PyPickleBufferObject *self)
1107db96d56Sopenharmony_ci{
1117db96d56Sopenharmony_ci    PyObject_GC_UnTrack(self);
1127db96d56Sopenharmony_ci    if (self->weakreflist != NULL)
1137db96d56Sopenharmony_ci        PyObject_ClearWeakRefs((PyObject *) self);
1147db96d56Sopenharmony_ci    PyBuffer_Release(&self->view);
1157db96d56Sopenharmony_ci    Py_TYPE(self)->tp_free((PyObject *) self);
1167db96d56Sopenharmony_ci}
1177db96d56Sopenharmony_ci
1187db96d56Sopenharmony_ci/* Buffer API */
1197db96d56Sopenharmony_ci
1207db96d56Sopenharmony_cistatic int
1217db96d56Sopenharmony_cipicklebuf_getbuf(PyPickleBufferObject *self, Py_buffer *view, int flags)
1227db96d56Sopenharmony_ci{
1237db96d56Sopenharmony_ci    if (self->view.obj == NULL) {
1247db96d56Sopenharmony_ci        PyErr_SetString(PyExc_ValueError,
1257db96d56Sopenharmony_ci                        "operation forbidden on released PickleBuffer object");
1267db96d56Sopenharmony_ci        return -1;
1277db96d56Sopenharmony_ci    }
1287db96d56Sopenharmony_ci    return PyObject_GetBuffer(self->view.obj, view, flags);
1297db96d56Sopenharmony_ci}
1307db96d56Sopenharmony_ci
1317db96d56Sopenharmony_cistatic void
1327db96d56Sopenharmony_cipicklebuf_releasebuf(PyPickleBufferObject *self, Py_buffer *view)
1337db96d56Sopenharmony_ci{
1347db96d56Sopenharmony_ci    /* Since our bf_getbuffer redirects to the original object, this
1357db96d56Sopenharmony_ci     * implementation is never called.  It only exists to signal that
1367db96d56Sopenharmony_ci     * buffers exported by PickleBuffer have non-trivial releasing
1377db96d56Sopenharmony_ci     * behaviour (see check in Python/getargs.c).
1387db96d56Sopenharmony_ci     */
1397db96d56Sopenharmony_ci}
1407db96d56Sopenharmony_ci
1417db96d56Sopenharmony_cistatic PyBufferProcs picklebuf_as_buffer = {
1427db96d56Sopenharmony_ci    .bf_getbuffer = (getbufferproc) picklebuf_getbuf,
1437db96d56Sopenharmony_ci    .bf_releasebuffer = (releasebufferproc) picklebuf_releasebuf,
1447db96d56Sopenharmony_ci};
1457db96d56Sopenharmony_ci
1467db96d56Sopenharmony_ci/* Methods */
1477db96d56Sopenharmony_ci
1487db96d56Sopenharmony_cistatic PyObject *
1497db96d56Sopenharmony_cipicklebuf_raw(PyPickleBufferObject *self, PyObject *Py_UNUSED(ignored))
1507db96d56Sopenharmony_ci{
1517db96d56Sopenharmony_ci    if (self->view.obj == NULL) {
1527db96d56Sopenharmony_ci        PyErr_SetString(PyExc_ValueError,
1537db96d56Sopenharmony_ci                        "operation forbidden on released PickleBuffer object");
1547db96d56Sopenharmony_ci        return NULL;
1557db96d56Sopenharmony_ci    }
1567db96d56Sopenharmony_ci    if (self->view.suboffsets != NULL
1577db96d56Sopenharmony_ci        || !PyBuffer_IsContiguous(&self->view, 'A')) {
1587db96d56Sopenharmony_ci        PyErr_SetString(PyExc_BufferError,
1597db96d56Sopenharmony_ci                        "cannot extract raw buffer from non-contiguous buffer");
1607db96d56Sopenharmony_ci        return NULL;
1617db96d56Sopenharmony_ci    }
1627db96d56Sopenharmony_ci    PyObject *m = PyMemoryView_FromObject((PyObject *) self);
1637db96d56Sopenharmony_ci    if (m == NULL) {
1647db96d56Sopenharmony_ci        return NULL;
1657db96d56Sopenharmony_ci    }
1667db96d56Sopenharmony_ci    PyMemoryViewObject *mv = (PyMemoryViewObject *) m;
1677db96d56Sopenharmony_ci    assert(mv->view.suboffsets == NULL);
1687db96d56Sopenharmony_ci    /* Mutate memoryview instance to make it a "raw" memoryview */
1697db96d56Sopenharmony_ci    mv->view.format = "B";
1707db96d56Sopenharmony_ci    mv->view.ndim = 1;
1717db96d56Sopenharmony_ci    mv->view.itemsize = 1;
1727db96d56Sopenharmony_ci    /* shape = (length,) */
1737db96d56Sopenharmony_ci    mv->view.shape = &mv->view.len;
1747db96d56Sopenharmony_ci    /* strides = (1,) */
1757db96d56Sopenharmony_ci    mv->view.strides = &mv->view.itemsize;
1767db96d56Sopenharmony_ci    /* Fix memoryview state flags */
1777db96d56Sopenharmony_ci    /* XXX Expose memoryobject.c's init_flags() instead? */
1787db96d56Sopenharmony_ci    mv->flags = _Py_MEMORYVIEW_C | _Py_MEMORYVIEW_FORTRAN;
1797db96d56Sopenharmony_ci    return m;
1807db96d56Sopenharmony_ci}
1817db96d56Sopenharmony_ci
1827db96d56Sopenharmony_ciPyDoc_STRVAR(picklebuf_raw_doc,
1837db96d56Sopenharmony_ci"raw($self, /)\n--\n\
1847db96d56Sopenharmony_ci\n\
1857db96d56Sopenharmony_ciReturn a memoryview of the raw memory underlying this buffer.\n\
1867db96d56Sopenharmony_ciWill raise BufferError is the buffer isn't contiguous.");
1877db96d56Sopenharmony_ci
1887db96d56Sopenharmony_cistatic PyObject *
1897db96d56Sopenharmony_cipicklebuf_release(PyPickleBufferObject *self, PyObject *Py_UNUSED(ignored))
1907db96d56Sopenharmony_ci{
1917db96d56Sopenharmony_ci    PyBuffer_Release(&self->view);
1927db96d56Sopenharmony_ci    Py_RETURN_NONE;
1937db96d56Sopenharmony_ci}
1947db96d56Sopenharmony_ci
1957db96d56Sopenharmony_ciPyDoc_STRVAR(picklebuf_release_doc,
1967db96d56Sopenharmony_ci"release($self, /)\n--\n\
1977db96d56Sopenharmony_ci\n\
1987db96d56Sopenharmony_ciRelease the underlying buffer exposed by the PickleBuffer object.");
1997db96d56Sopenharmony_ci
2007db96d56Sopenharmony_cistatic PyMethodDef picklebuf_methods[] = {
2017db96d56Sopenharmony_ci    {"raw",     (PyCFunction) picklebuf_raw,     METH_NOARGS, picklebuf_raw_doc},
2027db96d56Sopenharmony_ci    {"release", (PyCFunction) picklebuf_release, METH_NOARGS, picklebuf_release_doc},
2037db96d56Sopenharmony_ci    {NULL,      NULL}
2047db96d56Sopenharmony_ci};
2057db96d56Sopenharmony_ci
2067db96d56Sopenharmony_ciPyTypeObject PyPickleBuffer_Type = {
2077db96d56Sopenharmony_ci    PyVarObject_HEAD_INIT(NULL, 0)
2087db96d56Sopenharmony_ci    .tp_name = "pickle.PickleBuffer",
2097db96d56Sopenharmony_ci    .tp_doc = PyDoc_STR("Wrapper for potentially out-of-band buffers"),
2107db96d56Sopenharmony_ci    .tp_basicsize = sizeof(PyPickleBufferObject),
2117db96d56Sopenharmony_ci    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
2127db96d56Sopenharmony_ci    .tp_new = picklebuf_new,
2137db96d56Sopenharmony_ci    .tp_dealloc = (destructor) picklebuf_dealloc,
2147db96d56Sopenharmony_ci    .tp_traverse = (traverseproc) picklebuf_traverse,
2157db96d56Sopenharmony_ci    .tp_clear = (inquiry) picklebuf_clear,
2167db96d56Sopenharmony_ci    .tp_weaklistoffset = offsetof(PyPickleBufferObject, weakreflist),
2177db96d56Sopenharmony_ci    .tp_as_buffer = &picklebuf_as_buffer,
2187db96d56Sopenharmony_ci    .tp_methods = picklebuf_methods,
2197db96d56Sopenharmony_ci};
220