Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-08 10:11:47

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file corecel/cont/Span.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include <cstddef>
0010 #include <type_traits>
0011 
0012 #include "corecel/Macros.hh"
0013 
0014 #include "detail/SpanImpl.hh"
0015 
0016 #if !CELER_DEVICE_COMPILE
0017 #    include "corecel/io/StreamableContainer.hh"
0018 #endif
0019 
0020 namespace celeritas
0021 {
0022 //---------------------------------------------------------------------------//
0023 //! Sentinel value for span of dynamic type
0024 constexpr std::size_t dynamic_extent = detail::dynamic_extent;
0025 
0026 //---------------------------------------------------------------------------//
0027 // FORWARD DECLARATIONS
0028 //---------------------------------------------------------------------------//
0029 template<class T, std::size_t N>
0030 class Array;
0031 
0032 //---------------------------------------------------------------------------//
0033 /*!
0034  * Non-owning device-compatible reference to a contiguous span of data.
0035  * \tparam T value type
0036  * \tparam Extent fixed size; defaults to dynamic.
0037  *
0038  * A \c Span, like \c std::string_view, provides access to externally managed
0039  * data. In Celeritas, this class is typically used as a return result
0040  * from accessing a range of elements in a \c Collection.
0041  *
0042  * This implementation is a \em nonconforming backport of the C++20 \c
0043  * std::span. Improvements for standards compatibility are welcome as long as
0044  * they retain the same behavior in device code. Important differences from
0045  * the standard \c std::span include:
0046  * - Supports a special marker/tag type \c LdgValue<T> which causes element
0047  *   accessors and iterators to use value-semantics loads (optimized device
0048  *   loads) instead of references.
0049  * - Uses a restricted constructor for iterators: instead of two separate
0050  *   iterator/end types, it uses only one.
0051  * - Provides additional free helpers tailored to Celeritas: \c make_span
0052  *   overloads for \c Array<T,N>, C arrays, and generic containers, plus
0053  *   \c to_array() convenience and a host-only \c operator<< using
0054  *   \c StreamableContainer.
0055  * - All public methods are decorated with \c CELER_CONSTEXPR_FUNCTION for
0056  *   host/device compatibility.
0057  * - Some subview helpers use \c CELER_EXPECT to check for bounds validation in
0058  *   debug builds.
0059  * - Dynamic-to-fixed conversion performs runtime checks when \c
0060  *   CELERITAS_DEBUG is on.
0061  *
0062  * \par Construction
0063  *
0064  * - Default: empty span
0065  * - Implicit: pointer and size
0066  * - Implicit: two \c contiguous_iterator \c (first,last)
0067  * - Implicit: C arrays and celeritas::Array when extents are compatible
0068  * - Implicit: fixed-to-dynamic Span
0069  *
0070  * \par Data access
0071  *
0072  * - Element access: \c operator[], \c front(), \c back()
0073  * - Observers: \c data(), \c size(), \c size_bytes(), \c empty()
0074  * - Iteration: \c begin(), \c end()
0075  *
0076  * \par Subviews and utilities
0077  *
0078  * - \c first<Count>(), \c first(count), \c last<Count>(), \c last(count)
0079  * - \c subspan<Offset,Count>() and \c subspan(offset,count) for compile-time
0080  *   and runtime subviews
0081  * - Deduction guides for pointer+size, iterator pairs, C arrays, and Array
0082  * - Free functions \c make_span(...) and \c to_array(...)
0083  */
0084 template<class T, std::size_t Extent = dynamic_extent>
0085 class Span
0086 {
0087     using SpanTraitsT = detail::SpanTraits<T>;
0088     static constexpr bool ndebug = !CELERITAS_DEBUG;
0089     static constexpr bool ndebug_or_dyn = (ndebug || Extent == dynamic_extent);
0090 
0091   public:
0092     //!@{
0093     //! \name Type aliases
0094     using element_type = typename SpanTraitsT::element_type;
0095     using value_type = std::remove_cv_t<element_type>;
0096     using size_type = std::size_t;
0097     using pointer = typename SpanTraitsT::pointer;
0098     using const_pointer = typename SpanTraitsT::const_pointer;
0099     using reference = typename SpanTraitsT::reference;
0100     using const_reference = typename SpanTraitsT::const_reference;
0101     using iterator = typename SpanTraitsT::iterator;
0102     using const_iterator = typename SpanTraitsT::const_iterator;
0103     //!@}
0104 
0105     //! Size (may be dynamic)
0106     static constexpr std::size_t extent = Extent;
0107 
0108   public:
0109     //// CONSTRUCTION ////
0110 
0111     //! Construct with default null pointer and size zero
0112     constexpr Span() = default;
0113 
0114     //! Construct from data and size
0115     CELER_CONSTEXPR_FUNCTION Span(pointer d,
0116                                   std::size_t s) noexcept(ndebug_or_dyn)
0117         : s_(d, s)
0118     {
0119     }
0120 
0121     /*!
0122      * Construct from two contiguous random-access iterators.
0123      *
0124      * \note If \c Extent is fixed-size, the \c SpanImpl will fire an assertion
0125      * in debug mode if the size does not match.
0126      * \todo should be explicit unless dynamic size (requires C++20)
0127      */
0128     template<class Iter>
0129     CELER_CONSTEXPR_FUNCTION Span(Iter first, Iter last) noexcept(ndebug_or_dyn)
0130         : s_(first == last ? nullptr : &(*first),
0131              static_cast<std::size_t>(last - first))
0132     {
0133         CELER_EXPECT(last >= first);
0134     }
0135 
0136     //! Construct from a C array
0137     template<std::size_t N,
0138              std::enable_if_t<N == Extent || Extent == dynamic_extent, bool>
0139              = true>
0140     CELER_CONSTEXPR_FUNCTION Span(element_type (&arr)[N]) noexcept : s_(arr, N)
0141     {
0142     }
0143 
0144     /*!
0145      * Construct implicitly from a mutable \c Array.
0146      *
0147      * Enabled only when \c N matches \c Extent exactly or \c Extent is
0148      * \c dynamic_extent, so the conversion is always statically safe and
0149      * can be implicit unconditionally.
0150      */
0151     template<class U,
0152              std::size_t N,
0153              std::enable_if_t<detail::is_array_convertible_v<U, T>
0154                                   && (N == Extent || Extent == dynamic_extent),
0155                               bool> = true>
0156     CELER_CONSTEXPR_FUNCTION Span(Array<U, N>& arr) noexcept
0157         : s_(arr.data(), N)
0158     {
0159     }
0160 
0161     /*!
0162      * Construct implicitly from a const \c Array.
0163      *
0164      * Enabled only when \c N matches \c Extent exactly or \c Extent is
0165      * \c dynamic_extent, so the conversion is always statically safe and
0166      * can be implicit unconditionally.
0167      */
0168     template<class U,
0169              std::size_t N,
0170              std::enable_if_t<detail::is_array_convertible_v<U const, T>
0171                                   && (N == Extent || Extent == dynamic_extent),
0172                               bool> = true>
0173     CELER_CONSTEXPR_FUNCTION Span(Array<U, N> const& arr) noexcept
0174         : s_(arr.data(), N)
0175     {
0176     }
0177 
0178     /*!
0179      * Construct \c implicitly from convertible span and extent.
0180      *
0181      * Note that the enable-if prevents LdgSpan->Span conversion.
0182      * Conversions that may require a runtime size check (dynamic-extent
0183      * source to fixed-extent destination) are made explicit to avoid
0184      * accidental UB, matching std::span's behavior.
0185      *
0186      * Implicit conversion for cases that are statically safe (e.g.,
0187      * fixed->dynamic, fixed->same-fixed, or just element-type
0188      * qualification changes).
0189      */
0190     template<class U,
0191              std::size_t E2,
0192              std::enable_if_t<detail::is_array_convertible_v<U, T>
0193                                   && (E2 == Extent || Extent == dynamic_extent),
0194                               bool> = true>
0195     CELER_CONSTEXPR_FUNCTION Span(Span<U, E2> other) noexcept(ndebug_or_dyn)
0196         : s_(other.data(), other.size())
0197     {
0198     }
0199 
0200     /*!
0201      * Require \em explicit conversion from dynamic to fixed extent.
0202      *
0203      * Runtime size compatibility is checked in \c detail::SpanImpl.
0204      */
0205     template<class U,
0206              std::size_t E2,
0207              std::enable_if_t<detail::is_array_convertible_v<U, T>
0208                                   && Extent != dynamic_extent && E2 == dynamic_extent,
0209                               bool> = true>
0210     CELER_CONSTEXPR_FUNCTION explicit Span(Span<U, E2> other) noexcept(
0211         ndebug_or_dyn)
0212         : s_(other.data(), other.size())
0213     {
0214     }
0215 
0216     CELER_DEFAULT_COPY_MOVE(Span);
0217     ~Span() = default;
0218 
0219     //// ACCESS ////
0220 
0221     //!@{
0222     //! \name Iterators
0223     CELER_CONSTEXPR_FUNCTION iterator begin() const { return s_.data; }
0224     CELER_CONSTEXPR_FUNCTION iterator end() const { return s_.data + s_.size; }
0225     //!@}
0226 
0227     //!@{
0228     //! \name Element access
0229     CELER_CONSTEXPR_FUNCTION reference operator[](std::size_t i) const
0230     {
0231         return s_.data[i];
0232     }
0233     CELER_CONSTEXPR_FUNCTION reference front() const { return s_.data[0]; }
0234     CELER_CONSTEXPR_FUNCTION reference back() const
0235     {
0236         return s_.data[s_.size - 1];
0237     }
0238     CELER_CONSTEXPR_FUNCTION pointer data() const
0239     {
0240         return static_cast<pointer>(s_.data);
0241     }
0242     //!@}
0243 
0244     //!@{
0245     //! \name Observers
0246     CELER_CONSTEXPR_FUNCTION bool empty() const { return s_.size == 0; }
0247     CELER_CONSTEXPR_FUNCTION std::size_t size() const { return s_.size; }
0248     CELER_CONSTEXPR_FUNCTION std::size_t size_bytes() const
0249     {
0250         return sizeof(element_type) * s_.size;
0251     }
0252     //!@}
0253 
0254     //!@{
0255     //! \name Subviews
0256     template<std::size_t Count>
0257     CELER_CONSTEXPR_FUNCTION Span<T, Count> first() const noexcept(ndebug)
0258     {
0259         CELER_EXPECT(Count == 0 || Count <= this->size());
0260         return {this->data(), Count};
0261     }
0262     CELER_CONSTEXPR_FUNCTION Span<T, dynamic_extent> first(
0263         std::size_t count) const noexcept(ndebug)
0264     {
0265         CELER_EXPECT(count <= this->size());
0266         return {this->data(), count};
0267     }
0268 
0269     template<std::size_t Offset, std::size_t Count = dynamic_extent>
0270     CELER_CONSTEXPR_FUNCTION
0271     Span<T, detail::subspan_extent(Extent, Offset, Count)>
0272     subspan() const noexcept(ndebug)
0273     {
0274         CELER_EXPECT((Count == dynamic_extent) || (Offset == 0 && Count == 0)
0275                      || (Offset + Count <= this->size()));
0276         return {this->data() + Offset,
0277                 detail::subspan_size(this->size(), Offset, Count)};
0278     }
0279     CELER_CONSTEXPR_FUNCTION Span<T, dynamic_extent> subspan(
0280         std::size_t offset, std::size_t count = dynamic_extent) const
0281         noexcept(ndebug)
0282     {
0283         CELER_EXPECT(offset + count <= this->size());
0284         return {this->data() + offset,
0285                 detail::subspan_size(this->size(), offset, count)};
0286     }
0287 
0288     template<std::size_t Count>
0289     CELER_CONSTEXPR_FUNCTION Span<T, Count> last() const noexcept(ndebug)
0290     {
0291         CELER_EXPECT(Count == 0 || Count <= this->size());
0292         return {this->data() + this->size() - Count, Count};
0293     }
0294     CELER_CONSTEXPR_FUNCTION Span<T, dynamic_extent> last(
0295         std::size_t count) const noexcept(ndebug)
0296     {
0297         CELER_EXPECT(count <= this->size());
0298         return {this->data() + this->size() - count, count};
0299     }
0300     //!@}
0301 
0302   private:
0303     //// DATA ////
0304     detail::SpanImpl<T, Extent> s_;
0305 };
0306 
0307 //---------------------------------------------------------------------------//
0308 // DEDUCTION GUIDES
0309 //---------------------------------------------------------------------------//
0310 
0311 // Deduction guide for pointer and size
0312 template<class T>
0313 CELER_CTAD_FUNCTION Span(T*, std::size_t) -> Span<T>;
0314 
0315 // Deduction guide for two iterators
0316 template<class Iter>
0317 CELER_CTAD_FUNCTION Span(Iter, Iter)
0318     -> Span<typename std::iterator_traits<Iter>::value_type>;
0319 
0320 // Deduction guide for C array
0321 template<class T, std::size_t N>
0322 CELER_CTAD_FUNCTION Span(T (&)[N]) -> Span<T, N>;
0323 
0324 // Deduction guide for mutable Array
0325 template<class T, std::size_t N>
0326 CELER_CTAD_FUNCTION Span(Array<T, N>&) -> Span<T, N>;
0327 
0328 // Deduction guide for const Array
0329 template<class T, std::size_t N>
0330 CELER_CTAD_FUNCTION Span(Array<T, N> const&) -> Span<T const, N>;
0331 
0332 //---------------------------------------------------------------------------//
0333 // FREE FUNCTIONS
0334 //---------------------------------------------------------------------------//
0335 //! Get a mutable fixed-size view to an array
0336 template<class T, std::size_t N>
0337 CELER_CONSTEXPR_FUNCTION Span<T, N> make_span(Array<T, N>& x)
0338 {
0339     return {x.data(), N};
0340 }
0341 
0342 //---------------------------------------------------------------------------//
0343 //! Get a constant fixed-size view to an array
0344 template<class T, std::size_t N>
0345 CELER_CONSTEXPR_FUNCTION Span<T const, N> make_span(Array<T, N> const& x)
0346 {
0347     return {x.data(), N};
0348 }
0349 
0350 //---------------------------------------------------------------------------//
0351 //! Get a mutable fixed-size view to a C array
0352 template<class T, std::size_t N>
0353 CELER_CONSTEXPR_FUNCTION Span<T, N> make_span(T (&arr)[N])
0354 {
0355     return {arr, N};
0356 }
0357 
0358 //---------------------------------------------------------------------------//
0359 //! Get a mutable view to a generic container
0360 template<class T>
0361 CELER_CONSTEXPR_FUNCTION Span<typename T::value_type> make_span(T& cont)
0362 {
0363     return {cont.data(), cont.size()};
0364 }
0365 
0366 //---------------------------------------------------------------------------//
0367 //! Get a const view to a generic container
0368 template<class T>
0369 CELER_CONSTEXPR_FUNCTION Span<typename T::value_type const> make_span(
0370     T const& cont)
0371 {
0372     return {cont.data(), cont.size()};
0373 }
0374 
0375 #if !CELER_DEVICE_COMPILE
0376 //---------------------------------------------------------------------------//
0377 /*!
0378  * Write the elements of array \a a to stream \a os.
0379  */
0380 template<class T, std::size_t N>
0381 CELER_FORCEINLINE std::ostream& operator<<(std::ostream& os,
0382                                            Span<T, N> const& s)
0383 {
0384     os << StreamableContainer{s.data(), s.size()};
0385     return os;
0386 }
0387 #endif
0388 
0389 //---------------------------------------------------------------------------//
0390 }  // namespace celeritas