picklebufobject.c 6.0 KB

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