aboutsummaryrefslogtreecommitdiff
path: root/include/pybind11/buffer_info.h
blob: b99ee8bef422a6dc65d27e364eded545615e9ccc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/*
    pybind11/buffer_info.h: Python buffer object interface

    Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>

    All rights reserved. Use of this source code is governed by a
    BSD-style license that can be found in the LICENSE file.
*/

#pragma once

#include "detail/common.h"

PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)

PYBIND11_NAMESPACE_BEGIN(detail)

// Default, C-style strides
inline std::vector<ssize_t> c_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) {
    auto ndim = shape.size();
    std::vector<ssize_t> strides(ndim, itemsize);
    if (ndim > 0) {
        for (size_t i = ndim - 1; i > 0; --i) {
            strides[i - 1] = strides[i] * shape[i];
        }
    }
    return strides;
}

// F-style strides; default when constructing an array_t with `ExtraFlags & f_style`
inline std::vector<ssize_t> f_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) {
    auto ndim = shape.size();
    std::vector<ssize_t> strides(ndim, itemsize);
    for (size_t i = 1; i < ndim; ++i) {
        strides[i] = strides[i - 1] * shape[i - 1];
    }
    return strides;
}

template <typename T, typename SFINAE = void>
struct compare_buffer_info;

PYBIND11_NAMESPACE_END(detail)

/// Information record describing a Python buffer object
struct buffer_info {
    void *ptr = nullptr;          // Pointer to the underlying storage
    ssize_t itemsize = 0;         // Size of individual items in bytes
    ssize_t size = 0;             // Total number of entries
    std::string format;           // For homogeneous buffers, this should be set to
                                  // format_descriptor<T>::format()
    ssize_t ndim = 0;             // Number of dimensions
    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)
    bool readonly = false;        // flag to indicate if the underlying storage may be written to

    buffer_info() = default;

    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,
                bool readonly = false)
        : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim),
          shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) {
        if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) {
            pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length");
        }
        for (size_t i = 0; i < (size_t) ndim; ++i) {
            size *= shape[i];
        }
    }

    template <typename T>
    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),
                      readonly) {}

    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}, readonly) {}

    template <typename T>
    buffer_info(T *ptr, ssize_t size, bool readonly = false)
        : 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)
        : buffer_info(
            view->buf,
            view->itemsize,
            view->format,
            view->ndim,
            {view->shape, view->shape + view->ndim},
            /* Though buffer::request() requests PyBUF_STRIDES, ctypes objects
             * ignore this flag and return a view with NULL strides.
             * When strides are NULL, build them manually.  */
            view->strides
                ? std::vector<ssize_t>(view->strides, view->strides + view->ndim)
                : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize),
            (view->readonly != 0)) {
        // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
        this->m_view = view;
        // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
        this->ownview = ownview;
    }

    buffer_info(const buffer_info &) = delete;
    buffer_info &operator=(const buffer_info &) = delete;

    buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); }

    buffer_info &operator=(buffer_info &&rhs) noexcept {
        ptr = rhs.ptr;
        itemsize = rhs.itemsize;
        size = rhs.size;
        format = std::move(rhs.format);
        ndim = rhs.ndim;
        shape = std::move(rhs.shape);
        strides = std::move(rhs.strides);
        std::swap(m_view, rhs.m_view);
        std::swap(ownview, rhs.ownview);
        readonly = rhs.readonly;
        return *this;
    }

    ~buffer_info() {
        if (m_view && ownview) {
            PyBuffer_Release(m_view);
            delete m_view;
        }
    }

    Py_buffer *view() const { return m_view; }
    Py_buffer *&view() { return m_view; }

    /* True if the buffer item type is equivalent to `T`. */
    // To define "equivalent" by example:
    // `buffer_info::item_type_is_equivalent_to<int>(b)` and
    // `buffer_info::item_type_is_equivalent_to<long>(b)` may both be true
    // on some platforms, but `int` and `unsigned` will never be equivalent.
    // For the ground truth, please inspect `detail::compare_buffer_info<>`.
    template <typename T>
    bool item_type_is_equivalent_to() const {
        return detail::compare_buffer_info<T>::compare(*this);
    }

private:
    struct private_ctr_tag {};

    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,
                bool readonly)
        : buffer_info(
            ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) {}

    Py_buffer *m_view = nullptr;
    bool ownview = false;
};

PYBIND11_NAMESPACE_BEGIN(detail)

template <typename T, typename SFINAE>
struct compare_buffer_info {
    static bool compare(const buffer_info &b) {
        // NOLINTNEXTLINE(bugprone-sizeof-expression) Needed for `PyObject *`
        return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T);
    }
};

template <typename T>
struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> {
    static bool compare(const buffer_info &b) {
        return (size_t) b.itemsize == sizeof(T)
               && (b.format == format_descriptor<T>::value
                   || ((sizeof(T) == sizeof(long))
                       && b.format == (std::is_unsigned<T>::value ? "L" : "l"))
                   || ((sizeof(T) == sizeof(size_t))
                       && b.format == (std::is_unsigned<T>::value ? "N" : "n")));
    }
};

PYBIND11_NAMESPACE_END(detail)
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)