mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-25 14:45:12 +00:00
parent
bd24155b8b
commit
dc65d66171
@ -22,13 +22,14 @@ struct buffer_info {
|
|||||||
ssize_t ndim = 0; // Number of dimensions
|
ssize_t ndim = 0; // Number of dimensions
|
||||||
std::vector<ssize_t> shape; // Shape of the tensor (1 entry per dimension)
|
std::vector<ssize_t> shape; // Shape of the tensor (1 entry per dimension)
|
||||||
std::vector<ssize_t> strides; // Number of bytes between adjacent entries (for each per dimension)
|
std::vector<ssize_t> strides; // Number of bytes between adjacent entries (for each per dimension)
|
||||||
|
bool readonly = false; // flag to indicate if the underlying storage may be written to
|
||||||
|
|
||||||
buffer_info() { }
|
buffer_info() { }
|
||||||
|
|
||||||
buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim,
|
buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim,
|
||||||
detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in)
|
detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in, bool readonly=false)
|
||||||
: ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim),
|
: ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim),
|
||||||
shape(std::move(shape_in)), strides(std::move(strides_in)) {
|
shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) {
|
||||||
if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size())
|
if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size())
|
||||||
pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length");
|
pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length");
|
||||||
for (size_t i = 0; i < (size_t) ndim; ++i)
|
for (size_t i = 0; i < (size_t) ndim; ++i)
|
||||||
@ -36,19 +37,23 @@ struct buffer_info {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
buffer_info(T *ptr, detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in)
|
buffer_info(T *ptr, detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in, bool readonly=false)
|
||||||
: buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor<T>::format(), static_cast<ssize_t>(shape_in->size()), std::move(shape_in), std::move(strides_in)) { }
|
: buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor<T>::format(), static_cast<ssize_t>(shape_in->size()), std::move(shape_in), std::move(strides_in), readonly) { }
|
||||||
|
|
||||||
buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size)
|
buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size, bool readonly=false)
|
||||||
: buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}) { }
|
: buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) { }
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
buffer_info(T *ptr, ssize_t size)
|
buffer_info(T *ptr, ssize_t size, bool readonly=false)
|
||||||
: buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size) { }
|
: buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly) { }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
buffer_info(const T *ptr, ssize_t size, bool readonly=true)
|
||||||
|
: buffer_info(const_cast<T*>(ptr), sizeof(T), format_descriptor<T>::format(), size, readonly) { }
|
||||||
|
|
||||||
explicit buffer_info(Py_buffer *view, bool ownview = true)
|
explicit buffer_info(Py_buffer *view, bool ownview = true)
|
||||||
: buffer_info(view->buf, view->itemsize, view->format, view->ndim,
|
: buffer_info(view->buf, view->itemsize, view->format, view->ndim,
|
||||||
{view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}) {
|
{view->shape, view->shape + view->ndim}, {view->strides, view->strides + view->ndim}, view->readonly) {
|
||||||
this->view = view;
|
this->view = view;
|
||||||
this->ownview = ownview;
|
this->ownview = ownview;
|
||||||
}
|
}
|
||||||
@ -70,6 +75,7 @@ struct buffer_info {
|
|||||||
strides = std::move(rhs.strides);
|
strides = std::move(rhs.strides);
|
||||||
std::swap(view, rhs.view);
|
std::swap(view, rhs.view);
|
||||||
std::swap(ownview, rhs.ownview);
|
std::swap(ownview, rhs.ownview);
|
||||||
|
readonly = rhs.readonly;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,8 +87,8 @@ private:
|
|||||||
struct private_ctr_tag { };
|
struct private_ctr_tag { };
|
||||||
|
|
||||||
buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim,
|
buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim,
|
||||||
detail::any_container<ssize_t> &&shape_in, detail::any_container<ssize_t> &&strides_in)
|
detail::any_container<ssize_t> &&shape_in, detail::any_container<ssize_t> &&strides_in, bool readonly)
|
||||||
: buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in)) { }
|
: buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) { }
|
||||||
|
|
||||||
Py_buffer *view = nullptr;
|
Py_buffer *view = nullptr;
|
||||||
bool ownview = false;
|
bool ownview = false;
|
||||||
|
@ -491,6 +491,13 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla
|
|||||||
view->len = view->itemsize;
|
view->len = view->itemsize;
|
||||||
for (auto s : info->shape)
|
for (auto s : info->shape)
|
||||||
view->len *= s;
|
view->len *= s;
|
||||||
|
view->readonly = info->readonly;
|
||||||
|
if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) {
|
||||||
|
if (view)
|
||||||
|
view->obj = nullptr;
|
||||||
|
PyErr_SetString(PyExc_BufferError, "Writable buffer requested for readonly storage");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
|
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
|
||||||
view->format = const_cast<char *>(info->format.c_str());
|
view->format = const_cast<char *>(info->format.c_str());
|
||||||
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
|
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
|
||||||
|
@ -1345,7 +1345,7 @@ public:
|
|||||||
buf.strides = py_strides.data();
|
buf.strides = py_strides.data();
|
||||||
buf.shape = py_shape.data();
|
buf.shape = py_shape.data();
|
||||||
buf.suboffsets = nullptr;
|
buf.suboffsets = nullptr;
|
||||||
buf.readonly = false;
|
buf.readonly = info.readonly;
|
||||||
buf.internal = nullptr;
|
buf.internal = nullptr;
|
||||||
|
|
||||||
m_ptr = PyMemoryView_FromBuffer(&buf);
|
m_ptr = PyMemoryView_FromBuffer(&buf);
|
||||||
|
@ -180,7 +180,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const std::out_of_range &) {}
|
catch (const std::out_of_range&) {}
|
||||||
if (!t1) throw std::runtime_error("Unknown class passed to ConstructorStats::get()");
|
if (!t1) throw std::runtime_error("Unknown class passed to ConstructorStats::get()");
|
||||||
auto &cs1 = get(*t1);
|
auto &cs1 = get(*t1);
|
||||||
// If we have both a t1 and t2 match, one is probably the trampoline class; return whichever
|
// If we have both a t1 and t2 match, one is probably the trampoline class; return whichever
|
||||||
|
@ -166,4 +166,30 @@ TEST_SUBMODULE(buffers, m) {
|
|||||||
.def_readwrite("value", (int32_t DerivedBuffer::*) &DerivedBuffer::value)
|
.def_readwrite("value", (int32_t DerivedBuffer::*) &DerivedBuffer::value)
|
||||||
.def_buffer(&DerivedBuffer::get_buffer_info);
|
.def_buffer(&DerivedBuffer::get_buffer_info);
|
||||||
|
|
||||||
|
struct BufferReadOnly {
|
||||||
|
const uint8_t value = 0;
|
||||||
|
BufferReadOnly(uint8_t value): value(value) {}
|
||||||
|
|
||||||
|
py::buffer_info get_buffer_info() {
|
||||||
|
return py::buffer_info(&value, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
py::class_<BufferReadOnly>(m, "BufferReadOnly", py::buffer_protocol())
|
||||||
|
.def(py::init<uint8_t>())
|
||||||
|
.def_buffer(&BufferReadOnly::get_buffer_info);
|
||||||
|
|
||||||
|
struct BufferReadOnlySelect {
|
||||||
|
uint8_t value = 0;
|
||||||
|
bool readonly = false;
|
||||||
|
|
||||||
|
py::buffer_info get_buffer_info() {
|
||||||
|
return py::buffer_info(&value, 1, readonly);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
py::class_<BufferReadOnlySelect>(m, "BufferReadOnlySelect", py::buffer_protocol())
|
||||||
|
.def(py::init<>())
|
||||||
|
.def_readwrite("value", &BufferReadOnlySelect::value)
|
||||||
|
.def_readwrite("readonly", &BufferReadOnlySelect::readonly)
|
||||||
|
.def_buffer(&BufferReadOnlySelect::get_buffer_info);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
|
import io
|
||||||
import struct
|
import struct
|
||||||
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from pybind11_tests import buffers as m
|
from pybind11_tests import buffers as m
|
||||||
from pybind11_tests import ConstructorStats
|
from pybind11_tests import ConstructorStats
|
||||||
|
|
||||||
|
PY3 = sys.version_info[0] >= 3
|
||||||
|
|
||||||
pytestmark = pytest.requires_numpy
|
pytestmark = pytest.requires_numpy
|
||||||
|
|
||||||
with pytest.suppress(ImportError):
|
with pytest.suppress(ImportError):
|
||||||
@ -85,3 +91,28 @@ def test_pointer_to_member_fn():
|
|||||||
buf.value = 0x12345678
|
buf.value = 0x12345678
|
||||||
value = struct.unpack('i', bytearray(buf))[0]
|
value = struct.unpack('i', bytearray(buf))[0]
|
||||||
assert value == 0x12345678
|
assert value == 0x12345678
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.unsupported_on_pypy
|
||||||
|
def test_readonly_buffer():
|
||||||
|
buf = m.BufferReadOnly(0x64)
|
||||||
|
view = memoryview(buf)
|
||||||
|
assert view[0] == 0x64 if PY3 else b'd'
|
||||||
|
assert view.readonly
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.unsupported_on_pypy
|
||||||
|
def test_selective_readonly_buffer():
|
||||||
|
buf = m.BufferReadOnlySelect()
|
||||||
|
|
||||||
|
memoryview(buf)[0] = 0x64 if PY3 else b'd'
|
||||||
|
assert buf.value == 0x64
|
||||||
|
|
||||||
|
io.BytesIO(b'A').readinto(buf)
|
||||||
|
assert buf.value == ord(b'A')
|
||||||
|
|
||||||
|
buf.readonly = True
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
memoryview(buf)[0] = 0 if PY3 else b'\0'
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
io.BytesIO(b'1').readinto(buf)
|
||||||
|
Loading…
Reference in New Issue
Block a user