Skip to content

Commit 308eddb

Browse files
committed
Add support for native PickleBuffer as_buffer
1 parent 5914356 commit 308eddb

File tree

5 files changed

+146
-2
lines changed

5 files changed

+146
-2
lines changed

graalpython/com.oracle.graal.python.cext/src/capi.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@ typedef struct {
9696
PyObject *mapping;
9797
} mappingproxyobject;
9898

99+
typedef struct {
100+
PyObject_HEAD
101+
/* The view exported by the original object */
102+
Py_buffer view;
103+
PyObject *weakreflist;
104+
} PyPickleBufferObject;
105+
106+
99107
static void object_dealloc(PyObject *self) {
100108
Py_TYPE(self)->tp_free(self);
101109
}
@@ -278,6 +286,16 @@ void bytearray_releasebuffer(PyByteArrayObject *obj, Py_buffer *view);
278286
int memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags);
279287
void memory_releasebuf(PyMemoryViewObject *self, Py_buffer *view);
280288

289+
/* PICKLEBUFFER */
290+
static int
291+
picklebuf_getbuf(PyPickleBufferObject *self, Py_buffer *view, int flags)
292+
{
293+
PyObject *self_view_obj = GraalPyTruffle_PickleBuffer_viewobj(self);
294+
return PyObject_GetBuffer(self_view_obj, view, flags);
295+
}
296+
297+
static void empty_releasebuf(PyObject *self, Py_buffer *view) {}
298+
281299
static void initialize_bufferprocs() {
282300
static PyBufferProcs bytes_as_buffer = {
283301
(getbufferproc)bytes_buffer_getbuffer, /* bf_getbuffer */
@@ -305,6 +323,12 @@ static void initialize_bufferprocs() {
305323
array_as_buffer.bf_releasebuffer = GraalPyTruffle_Array_releasebuffer,
306324
Arraytype.tp_as_buffer = &array_as_buffer;
307325
GraalPy_set_PyTypeObject_tp_as_buffer(&Arraytype, &array_as_buffer);
326+
327+
static PyBufferProcs picklebuf_as_buffer;
328+
picklebuf_as_buffer.bf_getbuffer = (getbufferproc)picklebuf_getbuf,
329+
picklebuf_as_buffer.bf_releasebuffer = empty_releasebuf,
330+
PyPickleBuffer_Type.tp_as_buffer = &picklebuf_as_buffer;
331+
GraalPy_set_PyTypeObject_tp_as_buffer(&PyPickleBuffer_Type, &picklebuf_as_buffer);
308332
}
309333

310334
int is_builtin_type(PyTypeObject *tp) {

graalpython/com.oracle.graal.python.cext/src/capi.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,8 @@ PY_TRUFFLE_TYPE(UnionType_Type, "_ctypes.UnionType", &PyType_Type, sizeof(PyO
342342
PY_TRUFFLE_TYPE(PyCPointerType_Type, "PyCPointerType", &PyType_Type, sizeof(PyObject)) \
343343
PY_TRUFFLE_TYPE(PyCArrayType_Type, "PyCArrayType", &PyType_Type, sizeof(PyObject)) \
344344
PY_TRUFFLE_TYPE(PyCoro_Type, "coroutine", &PyType_Type, sizeof(PyCoroObject)) \
345+
/* PyPickleBufferObject (PyObject_HEAD + Py_buffer + PyObject*) is defined within Objects/picklebufobject.c, so its not exposed. */ \
346+
PY_TRUFFLE_TYPE(PyPickleBuffer_Type, "_pickle.PickleBuffer", &PyType_Type, sizeof(PyPickleBufferObject)) \
345347
PY_TRUFFLE_TYPE_UNIMPLEMENTED(_PyAIterWrapper_Type) \
346348
PY_TRUFFLE_TYPE_UNIMPLEMENTED(_PyAsyncGenASend_Type) \
347349
PY_TRUFFLE_TYPE_UNIMPLEMENTED(_PyAsyncGenAThrow_Type) \
@@ -385,7 +387,6 @@ PY_TRUFFLE_TYPE_UNIMPLEMENTED(PyODictItems_Type) \
385387
PY_TRUFFLE_TYPE_UNIMPLEMENTED(PyODictIter_Type) \
386388
PY_TRUFFLE_TYPE_UNIMPLEMENTED(PyODictKeys_Type) \
387389
PY_TRUFFLE_TYPE_UNIMPLEMENTED(PyODictValues_Type) \
388-
PY_TRUFFLE_TYPE_UNIMPLEMENTED(PyPickleBuffer_Type) \
389390
PY_TRUFFLE_TYPE_UNIMPLEMENTED(PyRangeIter_Type) \
390391
PY_TRUFFLE_TYPE_UNIMPLEMENTED(PySetIter_Type) \
391392
PY_TRUFFLE_TYPE_UNIMPLEMENTED(PySortWrapper_Type) \

graalpython/com.oracle.graal.python.cext/src/obmalloc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ int PyTruffle_AllocMemory(size_t size) {
110110

111111
void PyTruffle_FreeMemory(size_t size) {
112112
if (PyTruffle_AllocatedMemory < size) {
113-
PyTruffle_Log(PY_TRUFFLE_LOG_INFO, "PyTruffle_FreeMemory: freed memory size (%lu) is larger than allocated memory size (%lu)\n", size, PyTruffle_AllocMemory);
113+
PyTruffle_Log(PY_TRUFFLE_LOG_INFO, "PyTruffle_FreeMemory: freed memory size (%lu) is larger than allocated memory size (%lu)\n", size, PyTruffle_AllocatedMemory);
114114
PyTruffle_AllocatedMemory = size;
115115
}
116116
PyTruffle_AllocatedMemory -= size;

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_bytes.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,45 @@ def test_create_from_buffer_exception(self):
582582
self.assertRaises(ValueError, bytes, TestType())
583583
self.assertRaises(ValueError, bytearray, TestType())
584584

585+
def test_tp_as_buffer_pickle(self):
586+
TestAsBufferPickle = CPyExtType(
587+
"TestAsBufferPickle",
588+
"""
589+
static PyObject* get_bytes(PyObject* self, PyObject *obj) {
590+
Py_buffer view;
591+
if (PyObject_GetBuffer(obj, &view, PyBUF_FULL_RO) != 0)
592+
return NULL;
593+
PyObject* bytes = PyBytes_FromStringAndSize(view.buf, view.len);
594+
PyBuffer_Release(&view);
595+
return bytes;
596+
}
597+
""",
598+
tp_methods='{"get_bytes", (PyCFunction)get_bytes, METH_O | METH_CLASS, ""}',
599+
)
600+
TestType = CPyExtType(
601+
"TestMemoryViewBufferPickle",
602+
"""
603+
char buf[] = {1,2,3,4};
604+
int getbuffer(TestMemoryViewBufferPickleObject *self, Py_buffer *view, int flags) {
605+
return PyBuffer_FillInfo(view, (PyObject*)self, buf, 4, 1, flags);
606+
}
607+
void releasebuffer(TestMemoryViewBufferPickleObject *self, Py_buffer *view) {}
608+
609+
static PyBufferProcs as_buffer = {
610+
(getbufferproc)getbuffer,
611+
(releasebufferproc)releasebuffer,
612+
};
613+
""",
614+
tp_as_buffer='&as_buffer',
615+
)
616+
obj = TestType()
617+
b = bytes([1,2,3,4]) # same as `buf[] = {1,2,3,4};`
618+
import pickle
619+
b2 = TestAsBufferPickle.get_bytes(pickle.PickleBuffer(memoryview(obj)))
620+
b1 = TestAsBufferPickle.get_bytes(pickle.PickleBuffer(bytearray([1,2,3,4])))
621+
assert b == b1
622+
assert b == b2
623+
585624

586625
class TestNativeSubclass(unittest.TestCase):
587626
def test_builtins(self):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.graal.python.builtins.modules.cext;
42+
43+
import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Ignored;
44+
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObject;
45+
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectBorrowed;
46+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.ValueError;
47+
48+
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin;
49+
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiUnaryBuiltinNode;
50+
import com.oracle.graal.python.builtins.modules.pickle.PPickleBuffer;
51+
import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAccessLibrary;
52+
import com.oracle.graal.python.nodes.ErrorMessages;
53+
import com.oracle.graal.python.nodes.PRaiseNode;
54+
import com.oracle.truffle.api.dsl.Bind;
55+
import com.oracle.truffle.api.dsl.Cached;
56+
import com.oracle.truffle.api.dsl.Specialization;
57+
import com.oracle.truffle.api.library.CachedLibrary;
58+
import com.oracle.truffle.api.nodes.Node;
59+
60+
public final class PythonCextPickleBufferBuiltins {
61+
62+
@CApiBuiltin(ret = PyObjectBorrowed, args = {PyObject}, call = Ignored)
63+
abstract static class PyTruffle_PickleBuffer_viewobj extends CApiUnaryBuiltinNode {
64+
@Specialization
65+
static Object getviewobj(PPickleBuffer object,
66+
@Bind("this") Node inliningTarget,
67+
@CachedLibrary(limit = "3") PythonBufferAccessLibrary bufferLib,
68+
@Cached PRaiseNode.Lazy raiseNode) {
69+
Object owner = null;
70+
if (object.getView() != null) {
71+
owner = bufferLib.getOwner(object.getView());
72+
}
73+
if (owner == null) {
74+
throw raiseNode.get(inliningTarget).raise(ValueError, ErrorMessages.OP_FORBIDDEN_ON_OBJECT, "PickleBuffer");
75+
}
76+
return owner;
77+
}
78+
79+
}
80+
}

0 commit comments

Comments
 (0)