1 | /* |
2 | pybind11/buffer_info.h: Python buffer object interface |
3 | |
4 | Copyright (c) 2016 Wenzel Jakob <[email protected]> |
5 | |
6 | All rights reserved. Use of this source code is governed by a |
7 | BSD-style license that can be found in the LICENSE file. |
8 | */ |
9 | |
10 | #pragma once |
11 | |
12 | #include "detail/common.h" |
13 | |
14 | PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) |
15 | |
16 | PYBIND11_NAMESPACE_BEGIN(detail) |
17 | |
18 | // Default, C-style strides |
19 | inline std::vector<ssize_t> c_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) { |
20 | auto ndim = shape.size(); |
21 | std::vector<ssize_t> strides(ndim, itemsize); |
22 | if (ndim > 0) { |
23 | for (size_t i = ndim - 1; i > 0; --i) { |
24 | strides[i - 1] = strides[i] * shape[i]; |
25 | } |
26 | } |
27 | return strides; |
28 | } |
29 | |
30 | // F-style strides; default when constructing an array_t with `ExtraFlags & f_style` |
31 | inline std::vector<ssize_t> f_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) { |
32 | auto ndim = shape.size(); |
33 | std::vector<ssize_t> strides(ndim, itemsize); |
34 | for (size_t i = 1; i < ndim; ++i) { |
35 | strides[i] = strides[i - 1] * shape[i - 1]; |
36 | } |
37 | return strides; |
38 | } |
39 | |
40 | PYBIND11_NAMESPACE_END(detail) |
41 | |
42 | /// Information record describing a Python buffer object |
43 | struct buffer_info { |
44 | void *ptr = nullptr; // Pointer to the underlying storage |
45 | ssize_t itemsize = 0; // Size of individual items in bytes |
46 | ssize_t size = 0; // Total number of entries |
47 | std::string format; // For homogeneous buffers, this should be set to |
48 | // format_descriptor<T>::format() |
49 | ssize_t ndim = 0; // Number of dimensions |
50 | std::vector<ssize_t> shape; // Shape of the tensor (1 entry per dimension) |
51 | std::vector<ssize_t> strides; // Number of bytes between adjacent entries |
52 | // (for each per dimension) |
53 | bool readonly = false; // flag to indicate if the underlying storage may be written to |
54 | |
55 | buffer_info() = default; |
56 | |
57 | buffer_info(void *ptr, |
58 | ssize_t itemsize, |
59 | const std::string &format, |
60 | ssize_t ndim, |
61 | detail::any_container<ssize_t> shape_in, |
62 | detail::any_container<ssize_t> strides_in, |
63 | bool readonly = false) |
64 | : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), |
65 | shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) { |
66 | if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) { |
67 | pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length" ); |
68 | } |
69 | for (size_t i = 0; i < (size_t) ndim; ++i) { |
70 | size *= shape[i]; |
71 | } |
72 | } |
73 | |
74 | template <typename T> |
75 | buffer_info(T *ptr, |
76 | detail::any_container<ssize_t> shape_in, |
77 | detail::any_container<ssize_t> strides_in, |
78 | bool readonly = false) |
79 | : buffer_info(private_ctr_tag(), |
80 | ptr, |
81 | sizeof(T), |
82 | format_descriptor<T>::format(), |
83 | static_cast<ssize_t>(shape_in->size()), |
84 | std::move(shape_in), |
85 | std::move(strides_in), |
86 | readonly) {} |
87 | |
88 | buffer_info(void *ptr, |
89 | ssize_t itemsize, |
90 | const std::string &format, |
91 | ssize_t size, |
92 | bool readonly = false) |
93 | : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) {} |
94 | |
95 | template <typename T> |
96 | buffer_info(T *ptr, ssize_t size, bool readonly = false) |
97 | : buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly) {} |
98 | |
99 | template <typename T> |
100 | buffer_info(const T *ptr, ssize_t size, bool readonly = true) |
101 | : buffer_info( |
102 | const_cast<T *>(ptr), sizeof(T), format_descriptor<T>::format(), size, readonly) {} |
103 | |
104 | explicit buffer_info(Py_buffer *view, bool ownview = true) |
105 | : buffer_info( |
106 | view->buf, |
107 | view->itemsize, |
108 | view->format, |
109 | view->ndim, |
110 | {view->shape, view->shape + view->ndim}, |
111 | /* Though buffer::request() requests PyBUF_STRIDES, ctypes objects |
112 | * ignore this flag and return a view with NULL strides. |
113 | * When strides are NULL, build them manually. */ |
114 | view->strides |
115 | ? std::vector<ssize_t>(view->strides, view->strides + view->ndim) |
116 | : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize), |
117 | (view->readonly != 0)) { |
118 | // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) |
119 | this->m_view = view; |
120 | // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) |
121 | this->ownview = ownview; |
122 | } |
123 | |
124 | buffer_info(const buffer_info &) = delete; |
125 | buffer_info &operator=(const buffer_info &) = delete; |
126 | |
127 | buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); } |
128 | |
129 | buffer_info &operator=(buffer_info &&rhs) noexcept { |
130 | ptr = rhs.ptr; |
131 | itemsize = rhs.itemsize; |
132 | size = rhs.size; |
133 | format = std::move(rhs.format); |
134 | ndim = rhs.ndim; |
135 | shape = std::move(rhs.shape); |
136 | strides = std::move(rhs.strides); |
137 | std::swap(m_view, rhs.m_view); |
138 | std::swap(ownview, rhs.ownview); |
139 | readonly = rhs.readonly; |
140 | return *this; |
141 | } |
142 | |
143 | ~buffer_info() { |
144 | if (m_view && ownview) { |
145 | PyBuffer_Release(m_view); |
146 | delete m_view; |
147 | } |
148 | } |
149 | |
150 | Py_buffer *view() const { return m_view; } |
151 | Py_buffer *&view() { return m_view; } |
152 | |
153 | private: |
154 | struct private_ctr_tag {}; |
155 | |
156 | buffer_info(private_ctr_tag, |
157 | void *ptr, |
158 | ssize_t itemsize, |
159 | const std::string &format, |
160 | ssize_t ndim, |
161 | detail::any_container<ssize_t> &&shape_in, |
162 | detail::any_container<ssize_t> &&strides_in, |
163 | bool readonly) |
164 | : buffer_info( |
165 | ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) {} |
166 | |
167 | Py_buffer *m_view = nullptr; |
168 | bool ownview = false; |
169 | }; |
170 | |
171 | PYBIND11_NAMESPACE_BEGIN(detail) |
172 | |
173 | template <typename T, typename SFINAE = void> |
174 | struct compare_buffer_info { |
175 | static bool compare(const buffer_info &b) { |
176 | return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T); |
177 | } |
178 | }; |
179 | |
180 | template <typename T> |
181 | struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> { |
182 | static bool compare(const buffer_info &b) { |
183 | return (size_t) b.itemsize == sizeof(T) |
184 | && (b.format == format_descriptor<T>::value |
185 | || ((sizeof(T) == sizeof(long)) |
186 | && b.format == (std::is_unsigned<T>::value ? "L" : "l" )) |
187 | || ((sizeof(T) == sizeof(size_t)) |
188 | && b.format == (std::is_unsigned<T>::value ? "N" : "n" ))); |
189 | } |
190 | }; |
191 | |
192 | PYBIND11_NAMESPACE_END(detail) |
193 | PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) |
194 | |