File indexing completed on 2026-08-04 09:20:03
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #pragma once
0012
0013 #include <pybind11/pytypes.h>
0014
0015 #include "common.h"
0016
0017 #include <algorithm>
0018 #include <array>
0019 #include <cstddef>
0020 #include <cstdint>
0021 #include <cstring>
0022 #include <iterator>
0023 #include <type_traits>
0024 #include <utility>
0025 #include <vector>
0026
0027 PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
0028
0029 PYBIND11_WARNING_DISABLE_MSVC(4127)
0030
0031 PYBIND11_NAMESPACE_BEGIN(detail)
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051 template <typename ArrayT, std::size_t InlineSize, typename VectorT = ArrayT>
0052 union inline_array_or_vector {
0053 struct inline_array {
0054 bool is_inline = true;
0055 std::uint32_t size = 0;
0056 std::array<ArrayT, InlineSize> arr;
0057 };
0058 struct heap_vector {
0059 bool is_inline = false;
0060 std::vector<VectorT> vec;
0061
0062 heap_vector() = default;
0063 heap_vector(std::size_t count, VectorT value) : vec(count, value) {}
0064 };
0065
0066 inline_array iarray;
0067 heap_vector hvector;
0068
0069 inline_array_or_vector() : iarray() {}
0070
0071 ~inline_array_or_vector() {
0072 if (is_inline()) {
0073 iarray.~inline_array();
0074 } else {
0075 hvector.~heap_vector();
0076 }
0077 }
0078
0079
0080 inline_array_or_vector(const inline_array_or_vector &) = delete;
0081 inline_array_or_vector &operator=(const inline_array_or_vector &) = delete;
0082
0083 inline_array_or_vector(inline_array_or_vector &&rhs) noexcept {
0084 if (rhs.is_inline()) {
0085 new (&iarray) inline_array(std::move(rhs.iarray));
0086 } else {
0087 new (&hvector) heap_vector(std::move(rhs.hvector));
0088 }
0089 assert(is_inline() == rhs.is_inline());
0090 }
0091
0092 inline_array_or_vector &operator=(inline_array_or_vector &&rhs) noexcept {
0093 if (this == &rhs) {
0094 return *this;
0095 }
0096
0097 if (is_inline()) {
0098 iarray.~inline_array();
0099 } else {
0100 hvector.~heap_vector();
0101 }
0102
0103 if (rhs.is_inline()) {
0104 new (&iarray) inline_array(std::move(rhs.iarray));
0105 } else {
0106 new (&hvector) heap_vector(std::move(rhs.hvector));
0107 }
0108 return *this;
0109 }
0110
0111 bool is_inline() const {
0112
0113
0114
0115
0116
0117 bool result = false;
0118 static_assert(offsetof(inline_array, is_inline) == 0,
0119 "untagged union implementation relies on this");
0120 static_assert(offsetof(heap_vector, is_inline) == 0,
0121 "untagged union implementation relies on this");
0122 std::memcpy(&result, reinterpret_cast<const char *>(this), sizeof(bool));
0123 return result;
0124 }
0125 };
0126
0127 template <typename T, std::size_t InlineSize>
0128 struct small_vector {
0129 public:
0130 small_vector() = default;
0131
0132
0133 small_vector(const small_vector &) = delete;
0134 small_vector &operator=(const small_vector &) = delete;
0135 small_vector(small_vector &&) noexcept = default;
0136 small_vector &operator=(small_vector &&) noexcept = default;
0137
0138 std::size_t size() const {
0139 if (is_inline()) {
0140 return m_repr.iarray.size;
0141 }
0142 return m_repr.hvector.vec.size();
0143 }
0144
0145 T const *data() const {
0146 if (is_inline()) {
0147 return m_repr.iarray.arr.data();
0148 }
0149 return m_repr.hvector.vec.data();
0150 }
0151
0152 T &operator[](std::size_t idx) {
0153 assert(idx < size());
0154 if (is_inline()) {
0155 return m_repr.iarray.arr[idx];
0156 }
0157 return m_repr.hvector.vec[idx];
0158 }
0159
0160 T const &operator[](std::size_t idx) const {
0161 assert(idx < size());
0162 if (is_inline()) {
0163 return m_repr.iarray.arr[idx];
0164 }
0165 return m_repr.hvector.vec[idx];
0166 }
0167
0168 void push_back(const T &x) { emplace_back(x); }
0169
0170 void push_back(T &&x) { emplace_back(std::move(x)); }
0171
0172 template <typename... Args>
0173 void emplace_back(Args &&...x) {
0174 if (is_inline()) {
0175 auto &ha = m_repr.iarray;
0176 if (ha.size == InlineSize) {
0177 move_to_heap_vector_with_reserved_size(InlineSize + 1);
0178 m_repr.hvector.vec.emplace_back(std::forward<Args>(x)...);
0179 } else {
0180 ha.arr[ha.size++] = T(std::forward<Args>(x)...);
0181 }
0182 } else {
0183 m_repr.hvector.vec.emplace_back(std::forward<Args>(x)...);
0184 }
0185 }
0186
0187 void reserve(std::size_t sz) {
0188 if (is_inline()) {
0189 if (sz > InlineSize) {
0190 move_to_heap_vector_with_reserved_size(sz);
0191 }
0192 } else {
0193 reserve_slow_path(sz);
0194 }
0195 }
0196
0197 private:
0198 using repr_type = inline_array_or_vector<T, InlineSize>;
0199 repr_type m_repr;
0200
0201 PYBIND11_NOINLINE void move_to_heap_vector_with_reserved_size(std::size_t reserved_size) {
0202 assert(is_inline());
0203 auto &ha = m_repr.iarray;
0204 using heap_vector = typename repr_type::heap_vector;
0205 heap_vector hv;
0206 hv.vec.reserve(reserved_size);
0207 static_assert(std::is_nothrow_move_constructible<T>::value,
0208 "this conversion is not exception safe");
0209 static_assert(std::is_nothrow_move_constructible<heap_vector>::value,
0210 "this conversion is not exception safe");
0211 std::move(ha.arr.begin(), ha.arr.begin() + ha.size, std::back_inserter(hv.vec));
0212 new (&m_repr.hvector) heap_vector(std::move(hv));
0213 }
0214
0215 PYBIND11_NOINLINE void reserve_slow_path(std::size_t sz) { m_repr.hvector.vec.reserve(sz); }
0216
0217 bool is_inline() const { return m_repr.is_inline(); }
0218 };
0219
0220
0221 template <std::size_t kRequestedInlineSize>
0222 struct small_vector<bool, kRequestedInlineSize> {
0223 private:
0224 public:
0225 small_vector() = default;
0226
0227
0228 small_vector(const small_vector &) = delete;
0229 small_vector &operator=(const small_vector &) = delete;
0230 small_vector(small_vector &&) noexcept = default;
0231 small_vector &operator=(small_vector &&) noexcept = default;
0232
0233 small_vector(std::size_t count, bool value) {
0234 if (count > kInlineSize) {
0235 new (&m_repr.hvector) typename repr_type::heap_vector(count, value);
0236 } else {
0237 auto &inline_arr = m_repr.iarray;
0238 inline_arr.arr.fill(value ? static_cast<std::size_t>(-1) : 0);
0239 inline_arr.size = static_cast<decltype(inline_arr.size)>(count);
0240 }
0241 }
0242
0243 std::size_t size() const {
0244 if (is_inline()) {
0245 return m_repr.iarray.size;
0246 }
0247 return m_repr.hvector.vec.size();
0248 }
0249
0250 void reserve(std::size_t sz) {
0251 if (is_inline()) {
0252 if (sz > kInlineSize) {
0253 move_to_heap_vector_with_reserved_size(sz);
0254 }
0255 } else {
0256 m_repr.hvector.vec.reserve(sz);
0257 }
0258 }
0259
0260 bool operator[](std::size_t idx) const {
0261 if (is_inline()) {
0262 return inline_index(idx);
0263 }
0264 assert(idx < m_repr.hvector.vec.size());
0265 return m_repr.hvector.vec[idx];
0266 }
0267
0268 void push_back(bool b) {
0269 if (is_inline()) {
0270 auto &ha = m_repr.iarray;
0271 if (ha.size == kInlineSize) {
0272 move_to_heap_vector_with_reserved_size(kInlineSize + 1);
0273 push_back_slow_path(b);
0274 } else {
0275 assert(ha.size < kInlineSize);
0276 const auto wbi = word_and_bit_index(ha.size++);
0277 assert(wbi.word < kWords);
0278 assert(wbi.bit < kBitsPerWord);
0279 if (b) {
0280 ha.arr[wbi.word] |= (static_cast<std::size_t>(1) << wbi.bit);
0281 } else {
0282 ha.arr[wbi.word] &= ~(static_cast<std::size_t>(1) << wbi.bit);
0283 }
0284 assert(operator[](ha.size - 1) == b);
0285 }
0286 } else {
0287 push_back_slow_path(b);
0288 }
0289 }
0290
0291 void set(std::size_t idx, bool value = true) {
0292 if (is_inline()) {
0293 auto &ha = m_repr.iarray;
0294 assert(ha.size < kInlineSize);
0295 const auto wbi = word_and_bit_index(idx);
0296 assert(wbi.word < kWords);
0297 assert(wbi.bit < kBitsPerWord);
0298 if (value) {
0299 ha.arr[wbi.word] |= (static_cast<std::size_t>(1) << wbi.bit);
0300 } else {
0301 ha.arr[wbi.word] &= ~(static_cast<std::size_t>(1) << wbi.bit);
0302 }
0303 } else {
0304 m_repr.hvector.vec[idx] = value;
0305 }
0306 }
0307
0308 void swap(small_vector &rhs) noexcept { std::swap(m_repr, rhs.m_repr); }
0309
0310 private:
0311 struct WordAndBitIndex {
0312 std::size_t word;
0313 std::size_t bit;
0314 };
0315
0316 static WordAndBitIndex word_and_bit_index(std::size_t idx) {
0317 return WordAndBitIndex{idx / kBitsPerWord, idx % kBitsPerWord};
0318 }
0319
0320 bool inline_index(std::size_t idx) const {
0321 const auto wbi = word_and_bit_index(idx);
0322 assert(wbi.word < kWords);
0323 assert(wbi.bit < kBitsPerWord);
0324 return m_repr.iarray.arr[wbi.word] & (static_cast<std::size_t>(1) << wbi.bit);
0325 }
0326
0327 PYBIND11_NOINLINE void move_to_heap_vector_with_reserved_size(std::size_t reserved_size) {
0328 auto &inline_arr = m_repr.iarray;
0329 using heap_vector = typename repr_type::heap_vector;
0330 heap_vector hv;
0331 hv.vec.reserve(reserved_size);
0332 for (std::size_t ii = 0; ii < inline_arr.size; ++ii) {
0333 hv.vec.push_back(inline_index(ii));
0334 }
0335 new (&m_repr.hvector) heap_vector(std::move(hv));
0336 }
0337
0338 PYBIND11_NOINLINE void push_back_slow_path(bool b) { m_repr.hvector.vec.push_back(b); }
0339
0340 static constexpr auto kBitsPerWord = 8 * sizeof(std::size_t);
0341 static constexpr auto kWords = (kRequestedInlineSize + kBitsPerWord - 1) / kBitsPerWord;
0342 static constexpr auto kInlineSize = kWords * kBitsPerWord;
0343
0344 using repr_type = inline_array_or_vector<std::size_t, kWords, bool>;
0345 repr_type m_repr;
0346
0347 bool is_inline() const { return m_repr.is_inline(); }
0348 };
0349
0350
0351 template <size_t N>
0352 using argument_vector = small_vector<handle, N>;
0353
0354
0355 template <size_t N>
0356 using args_convert_vector = small_vector<bool, N>;
0357
0358
0359
0360
0361 template <std::size_t InlineSize>
0362 class ref_small_vector {
0363 public:
0364 ref_small_vector() = default;
0365
0366 ~ref_small_vector() {
0367 for (std::size_t i = 0; i < m_ptrs.size(); ++i) {
0368 Py_XDECREF(m_ptrs[i]);
0369 }
0370 }
0371
0372
0373 ref_small_vector(const ref_small_vector &) = delete;
0374 ref_small_vector &operator=(const ref_small_vector &) = delete;
0375
0376
0377 ref_small_vector(ref_small_vector &&other) noexcept : m_ptrs(std::move(other.m_ptrs)) {
0378
0379 }
0380
0381 ref_small_vector &operator=(ref_small_vector &&other) noexcept {
0382 if (this != &other) {
0383
0384 for (std::size_t i = 0; i < m_ptrs.size(); ++i) {
0385 Py_XDECREF(m_ptrs[i]);
0386 }
0387 m_ptrs = std::move(other.m_ptrs);
0388 }
0389 return *this;
0390 }
0391
0392
0393 void push_back_steal(PyObject *p) { m_ptrs.push_back(p); }
0394
0395
0396 void push_back_borrow(PyObject *p) {
0397 Py_XINCREF(p);
0398 m_ptrs.push_back(p);
0399 }
0400
0401
0402 void push_back_null() { m_ptrs.push_back(nullptr); }
0403
0404 void reserve(std::size_t sz) { m_ptrs.reserve(sz); }
0405
0406 std::size_t size() const { return m_ptrs.size(); }
0407
0408 PyObject *operator[](std::size_t idx) const { return m_ptrs[idx]; }
0409
0410 PyObject *const *data() const { return m_ptrs.data(); }
0411
0412 private:
0413 small_vector<PyObject *, InlineSize> m_ptrs;
0414 };
0415
0416 PYBIND11_NAMESPACE_END(detail)
0417 PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)