Line data Source code
1 : // SPDX-License-Identifier: BSD-3-Clause 2 : // Copyright (c) 2023 Scipp contributors (https://github.com/scipp) 3 : /// @file 4 : /// @author Simon Heybrock 5 : #pragma once 6 : 7 : #include <algorithm> 8 : #include <memory> 9 : 10 : #include "scipp/common/index.h" 11 : #include "scipp/core/parallel.h" 12 : 13 : namespace scipp::core { 14 : 15 : /// Replacement for C++20 std::make_unique_for_overwrite 16 : template <class T> 17 2431637 : auto make_unique_for_overwrite_array(const scipp::index size) { 18 : // This is specifically written in this way to avoid an internal cppcheck 19 : // error which happens when we try to handle both arrays and 'normal' pointers 20 : // using std::remove_extent_t<T> as the type we pass to the unique_ptr. 21 : using Ptr = std::unique_ptr<T[]>; 22 : // We add a size and sign check to avoid warnings about exceeding maximum 23 : // object size. See e.g. 24 : // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=85783#c3 25 2431637 : if ((size <= PTRDIFF_MAX) && (size >= 0)) 26 4632461 : return Ptr(new T[size]); 27 0 : throw std::runtime_error( 28 : "Allocation size is either negative or exceeds PTRDIFF_MAX"); 29 : } 30 : 31 : /// Tag for requesting default-initialization in methods of class element_array. 32 : struct init_for_overwrite_t {}; 33 : static constexpr auto init_for_overwrite = init_for_overwrite_t{}; 34 : 35 : /// Internal data container for Variable. 36 : /// 37 : /// This provides a vector-like storage for arrays of elements in a variable. 38 : /// The reasons for not using std::vector are: 39 : /// - Avoiding the std::vector<bool> specialization which would cause issues 40 : /// with thread-safety. 41 : /// - Support default-initialized arrays as an internal optimization in 42 : /// implementing transform. This avoids costly initialization in cases where 43 : /// data would be immediately overwritten afterwards. 44 : /// - As a minor benefit, since the implementation has to store a pointer and a 45 : /// size, we can at the same time support an "optional" behavior, as used for 46 : /// the array of variances in a variable. 47 : template <class T> class element_array { 48 : public: 49 : using value_type = T; 50 : 51 682860 : element_array() noexcept = default; 52 : 53 96483 : explicit element_array(const scipp::index new_size, const T &value = T()) { 54 96483 : resize(new_size, init_for_overwrite); 55 96483 : parallel::parallel_for( 56 301455 : parallel::blocked_range(0, size()), [&](const auto &range) { 57 96483 : std::fill(data() + range.begin(), data() + range.end(), value); 58 : }); 59 96483 : } 60 : 61 : /// Construct with default-initialized elements. 62 : /// Use with care, fundamental types are not initialized. 63 1691898 : element_array(const scipp::index new_size, const init_for_overwrite_t &) { 64 1691898 : resize(new_size, init_for_overwrite); 65 1691898 : } 66 : 67 : template < 68 : class Iter, 69 : std::enable_if_t< 70 : std::is_assignable<T &, decltype(*std::declval<Iter>())>{}, int> = 0> 71 659768 : element_array(Iter first, Iter last) { 72 659768 : const scipp::index size = std::distance(first, last); 73 659768 : resize(size, init_for_overwrite); 74 659768 : parallel::parallel_for( 75 1134656 : parallel::blocked_range(0, size), [&](const auto &range) { 76 659768 : std::copy(first + range.begin(), first + range.end(), 77 659768 : data() + range.begin()); 78 : }); 79 659768 : } 80 : 81 : template < 82 : class Container, 83 : std::enable_if_t< 84 : std::is_assignable_v<T &, typename Container::value_type>, int> = 0> 85 29872 : explicit element_array(const Container &c) 86 29872 : : element_array(c.begin(), c.end()) {} 87 : 88 605340 : element_array(std::initializer_list<T> init) 89 605340 : : element_array(init.begin(), init.end()) {} 90 : 91 6945801 : element_array(element_array &&other) noexcept 92 6945801 : : m_size(other.m_size), m_data(std::move(other.m_data)) { 93 6945801 : other.m_size = -1; 94 6945801 : } 95 : 96 3512 : element_array(const element_array &other) 97 3512 : : element_array(from_other(other)) {} 98 : 99 702526 : element_array &operator=(element_array &&other) noexcept { 100 702526 : m_data = std::move(other.m_data); 101 702526 : m_size = other.m_size; 102 702526 : other.m_size = -1; 103 702526 : return *this; 104 : } 105 : 106 0 : element_array &operator=(const element_array &other) { 107 0 : return *this = element_array(other.begin(), other.end()); 108 : } 109 : 110 2546711 : explicit operator bool() const noexcept { return m_size != -1; } 111 6583156 : scipp::index size() const noexcept { return m_size; } 112 : [[nodiscard]] bool empty() const noexcept { return size() == 0; } 113 5468877 : const T *data() const noexcept { return m_data.get(); } 114 4408773 : T *data() noexcept { return m_data.get(); } 115 25253 : const T *begin() const noexcept { return data(); } 116 42955 : T *begin() noexcept { return data(); } 117 25253 : const T *end() const noexcept { 118 25253 : return m_size < 0 ? begin() : data() + size(); 119 : } 120 : T *end() noexcept { return m_size < 0 ? begin() : data() + size(); } 121 : 122 : void reset() noexcept { 123 : m_data.reset(); 124 : m_size = -1; 125 : } 126 : 127 : /// Resize the array. 128 : /// 129 : /// Unlike std::vector::resize, this does *not* preserve existing element 130 : /// values. 131 : void resize(const scipp::index new_size) { *this = element_array(new_size); } 132 : 133 : /// Resize with default-initialized elements. Use with care. 134 2448149 : void resize(const scipp::index new_size, const init_for_overwrite_t &) { 135 2448149 : if (new_size == 0) { 136 16512 : m_data.reset(); 137 16512 : m_size = 0; 138 2431637 : } else if (new_size != size()) { 139 2431637 : m_data = make_unique_for_overwrite_array<T>(new_size); 140 2431637 : m_size = new_size; 141 : } 142 2448149 : } 143 : 144 : private: 145 3512 : element_array from_other(const element_array &other) { 146 3512 : if (other.size() == -1) { 147 0 : return element_array(); 148 3512 : } else if (other.size() == 0) { 149 40 : return element_array(0); 150 : } else { 151 3472 : return element_array(other.begin(), other.end()); 152 : } 153 : } 154 : scipp::index m_size{-1}; 155 : std::unique_ptr<T[]> m_data; 156 : }; 157 : 158 : } // namespace scipp::core 159 : 160 : namespace scipp { 161 : using core::element_array; 162 : }