Line data Source code
1 : /* PickleBuffer object implementation */
2 :
3 : #define PY_SSIZE_T_CLEAN
4 : #include "Python.h"
5 : #include <stddef.h>
6 :
7 : typedef struct {
8 : PyObject_HEAD
9 : /* The view exported by the original object */
10 : Py_buffer view;
11 : PyObject *weakreflist;
12 : } PyPickleBufferObject;
13 :
14 : /* C API */
15 :
16 : PyObject *
17 0 : PyPickleBuffer_FromObject(PyObject *base)
18 : {
19 0 : PyTypeObject *type = &PyPickleBuffer_Type;
20 : PyPickleBufferObject *self;
21 :
22 0 : self = (PyPickleBufferObject *) type->tp_alloc(type, 0);
23 0 : if (self == NULL) {
24 0 : return NULL;
25 : }
26 0 : self->view.obj = NULL;
27 0 : self->weakreflist = NULL;
28 0 : if (PyObject_GetBuffer(base, &self->view, PyBUF_FULL_RO) < 0) {
29 0 : Py_DECREF(self);
30 0 : return NULL;
31 : }
32 0 : return (PyObject *) self;
33 : }
34 :
35 : const Py_buffer *
36 110 : PyPickleBuffer_GetBuffer(PyObject *obj)
37 : {
38 110 : PyPickleBufferObject *self = (PyPickleBufferObject *) obj;
39 :
40 110 : if (!PyPickleBuffer_Check(obj)) {
41 0 : PyErr_Format(PyExc_TypeError,
42 : "expected PickleBuffer, %.200s found",
43 0 : Py_TYPE(obj)->tp_name);
44 0 : return NULL;
45 : }
46 110 : if (self->view.obj == NULL) {
47 0 : PyErr_SetString(PyExc_ValueError,
48 : "operation forbidden on released PickleBuffer object");
49 0 : return NULL;
50 : }
51 110 : return &self->view;
52 : }
53 :
54 : int
55 0 : PyPickleBuffer_Release(PyObject *obj)
56 : {
57 0 : PyPickleBufferObject *self = (PyPickleBufferObject *) obj;
58 :
59 0 : if (!PyPickleBuffer_Check(obj)) {
60 0 : PyErr_Format(PyExc_TypeError,
61 : "expected PickleBuffer, %.200s found",
62 0 : Py_TYPE(obj)->tp_name);
63 0 : return -1;
64 : }
65 0 : PyBuffer_Release(&self->view);
66 0 : return 0;
67 : }
68 :
69 : static PyObject *
70 371 : picklebuf_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
71 : {
72 : PyPickleBufferObject *self;
73 : PyObject *base;
74 371 : char *keywords[] = {"", NULL};
75 :
76 371 : if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:PickleBuffer",
77 : keywords, &base)) {
78 1 : return NULL;
79 : }
80 :
81 370 : self = (PyPickleBufferObject *) type->tp_alloc(type, 0);
82 370 : if (self == NULL) {
83 0 : return NULL;
84 : }
85 370 : self->view.obj = NULL;
86 370 : self->weakreflist = NULL;
87 370 : if (PyObject_GetBuffer(base, &self->view, PyBUF_FULL_RO) < 0) {
88 2 : Py_DECREF(self);
89 2 : return NULL;
90 : }
91 368 : return (PyObject *) self;
92 : }
93 :
94 : static int
95 2 : picklebuf_traverse(PyPickleBufferObject *self, visitproc visit, void *arg)
96 : {
97 2 : Py_VISIT(self->view.obj);
98 2 : return 0;
99 : }
100 :
101 : static int
102 0 : picklebuf_clear(PyPickleBufferObject *self)
103 : {
104 0 : PyBuffer_Release(&self->view);
105 0 : return 0;
106 : }
107 :
108 : static void
109 370 : picklebuf_dealloc(PyPickleBufferObject *self)
110 : {
111 370 : PyObject_GC_UnTrack(self);
112 370 : if (self->weakreflist != NULL)
113 0 : PyObject_ClearWeakRefs((PyObject *) self);
114 370 : PyBuffer_Release(&self->view);
115 370 : Py_TYPE(self)->tp_free((PyObject *) self);
116 370 : }
117 :
118 : /* Buffer API */
119 :
120 : static int
121 317 : picklebuf_getbuf(PyPickleBufferObject *self, Py_buffer *view, int flags)
122 : {
123 317 : if (self->view.obj == NULL) {
124 1 : PyErr_SetString(PyExc_ValueError,
125 : "operation forbidden on released PickleBuffer object");
126 1 : return -1;
127 : }
128 316 : return PyObject_GetBuffer(self->view.obj, view, flags);
129 : }
130 :
131 : static void
132 0 : picklebuf_releasebuf(PyPickleBufferObject *self, Py_buffer *view)
133 : {
134 : /* Since our bf_getbuffer redirects to the original object, this
135 : * implementation is never called. It only exists to signal that
136 : * buffers exported by PickleBuffer have non-trivial releasing
137 : * behaviour (see check in Python/getargs.c).
138 : */
139 0 : }
140 :
141 : static PyBufferProcs picklebuf_as_buffer = {
142 : .bf_getbuffer = (getbufferproc) picklebuf_getbuf,
143 : .bf_releasebuffer = (releasebufferproc) picklebuf_releasebuf,
144 : };
145 :
146 : /* Methods */
147 :
148 : static PyObject *
149 293 : picklebuf_raw(PyPickleBufferObject *self, PyObject *Py_UNUSED(ignored))
150 : {
151 293 : if (self->view.obj == NULL) {
152 1 : PyErr_SetString(PyExc_ValueError,
153 : "operation forbidden on released PickleBuffer object");
154 1 : return NULL;
155 : }
156 292 : if (self->view.suboffsets != NULL
157 292 : || !PyBuffer_IsContiguous(&self->view, 'A')) {
158 2 : PyErr_SetString(PyExc_BufferError,
159 : "cannot extract raw buffer from non-contiguous buffer");
160 2 : return NULL;
161 : }
162 290 : PyObject *m = PyMemoryView_FromObject((PyObject *) self);
163 290 : if (m == NULL) {
164 0 : return NULL;
165 : }
166 290 : PyMemoryViewObject *mv = (PyMemoryViewObject *) m;
167 290 : assert(mv->view.suboffsets == NULL);
168 : /* Mutate memoryview instance to make it a "raw" memoryview */
169 290 : mv->view.format = "B";
170 290 : mv->view.ndim = 1;
171 290 : mv->view.itemsize = 1;
172 : /* shape = (length,) */
173 290 : mv->view.shape = &mv->view.len;
174 : /* strides = (1,) */
175 290 : mv->view.strides = &mv->view.itemsize;
176 : /* Fix memoryview state flags */
177 : /* XXX Expose memoryobject.c's init_flags() instead? */
178 290 : mv->flags = _Py_MEMORYVIEW_C | _Py_MEMORYVIEW_FORTRAN;
179 290 : return m;
180 : }
181 :
182 : PyDoc_STRVAR(picklebuf_raw_doc,
183 : "raw($self, /)\n--\n\
184 : \n\
185 : Return a memoryview of the raw memory underlying this buffer.\n\
186 : Will raise BufferError is the buffer isn't contiguous.");
187 :
188 : static PyObject *
189 3 : picklebuf_release(PyPickleBufferObject *self, PyObject *Py_UNUSED(ignored))
190 : {
191 3 : PyBuffer_Release(&self->view);
192 3 : Py_RETURN_NONE;
193 : }
194 :
195 : PyDoc_STRVAR(picklebuf_release_doc,
196 : "release($self, /)\n--\n\
197 : \n\
198 : Release the underlying buffer exposed by the PickleBuffer object.");
199 :
200 : static PyMethodDef picklebuf_methods[] = {
201 : {"raw", (PyCFunction) picklebuf_raw, METH_NOARGS, picklebuf_raw_doc},
202 : {"release", (PyCFunction) picklebuf_release, METH_NOARGS, picklebuf_release_doc},
203 : {NULL, NULL}
204 : };
205 :
206 : PyTypeObject PyPickleBuffer_Type = {
207 : PyVarObject_HEAD_INIT(NULL, 0)
208 : .tp_name = "pickle.PickleBuffer",
209 : .tp_doc = PyDoc_STR("Wrapper for potentially out-of-band buffers"),
210 : .tp_basicsize = sizeof(PyPickleBufferObject),
211 : .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
212 : .tp_new = picklebuf_new,
213 : .tp_dealloc = (destructor) picklebuf_dealloc,
214 : .tp_traverse = (traverseproc) picklebuf_traverse,
215 : .tp_clear = (inquiry) picklebuf_clear,
216 : .tp_weaklistoffset = offsetof(PyPickleBufferObject, weakreflist),
217 : .tp_as_buffer = &picklebuf_as_buffer,
218 : .tp_methods = picklebuf_methods,
219 : };
|