LCOV - code coverage report
Current view: top level - core/include/scipp/core - element_array.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 57 61 93.4 %
Date: 2024-04-28 01:25:40 Functions: 411 898 45.8 %

          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             : }

Generated by: LCOV version 1.14