Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-13 08:34:43

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/data/DedupeCollectionBuilder.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include <algorithm>
0010 #include <cstddef>
0011 #include <unordered_set>
0012 
0013 #include "corecel/math/HashUtils.hh"
0014 
0015 #include "Collection.hh"
0016 
0017 namespace celeritas
0018 {
0019 //---------------------------------------------------------------------------//
0020 /*!
0021  * Build collections, returning the same ID for reused data spans.
0022  */
0023 template<class T, class I = ItemId<T>>
0024 class DedupeCollectionBuilder
0025 {
0026   public:
0027     //!@{
0028     //! \name Type aliases
0029     using CollectionT = Collection<T, Ownership::value, MemSpace::host, I>;
0030     using value_type = T;
0031     using size_type = typename CollectionT::size_type;
0032     using ItemIdT = typename CollectionT::ItemIdT;
0033     using ItemRangeT = typename CollectionT::ItemRangeT;
0034     //!@}
0035 
0036   public:
0037     // Construct from a collection
0038     explicit DedupeCollectionBuilder(CollectionT* collection);
0039 
0040     //// ACCESSORS ////
0041 
0042     //! Number of elements in the collection
0043     size_type size() const { return this->storage().size(); }
0044 
0045     //! Get the size as an ID type
0046     ItemIdT size_id() const { return ItemIdT{this->size()}; }
0047 
0048     //// MUTATORS ////
0049 
0050     // Reserve space and hash table space
0051     inline void reserve(std::size_t count);
0052 
0053     // Extend with a series of elements, returning the range inserted
0054     template<class InputIterator>
0055     inline ItemRangeT insert_back(InputIterator first, InputIterator last);
0056 
0057     // Extend with a series of elements from an initializer list
0058     inline ItemRangeT insert_back(std::initializer_list<value_type> init);
0059 
0060     // Append a single element
0061     inline ItemIdT push_back(value_type const& element);
0062 
0063   private:
0064     //// CLASSES ////
0065 
0066     using StorageT = typename CollectionT::StorageT;
0067 
0068     struct HashRange
0069     {
0070         StorageT* storage{nullptr};
0071         std::size_t operator()(ItemRangeT) const;
0072     };
0073     struct EqualRange
0074     {
0075         StorageT* storage{nullptr};
0076         bool operator()(ItemRangeT const&, ItemRangeT const&) const;
0077     };
0078 
0079     //// DATA ////
0080 
0081     CollectionT* col_;
0082     std::unordered_set<ItemRangeT, HashRange, EqualRange> ranges_;
0083 
0084     //// FUNCTIONS ////
0085 
0086     //!@{
0087     //! Access storage
0088     CELER_FORCEINLINE StorageT& storage() { return col_->storage(); }
0089     CELER_FORCEINLINE StorageT const& storage() const
0090     {
0091         return col_->storage();
0092     }
0093     //!@}
0094 };
0095 
0096 //---------------------------------------------------------------------------//
0097 // INLINE DEFINITIONS
0098 //---------------------------------------------------------------------------//
0099 /*!
0100  * Construct from a collection.
0101  */
0102 template<class T, class I>
0103 DedupeCollectionBuilder<T, I>::DedupeCollectionBuilder(CollectionT* collection)
0104     : col_{collection}
0105     , ranges_{0, HashRange{&this->storage()}, EqualRange{&this->storage()}}
0106 {
0107     CELER_EXPECT(col_);
0108 }
0109 
0110 //---------------------------------------------------------------------------//
0111 /*!
0112  * Reserve space for elements.
0113  */
0114 template<class T, class I>
0115 void DedupeCollectionBuilder<T, I>::reserve(std::size_t count)
0116 {
0117     this->storage().reserve(count);
0118     ranges_.reserve(count);
0119 }
0120 
0121 //---------------------------------------------------------------------------//
0122 /*!
0123  * Insert the given list of elements at the end of the allocation.
0124  */
0125 template<class T, class I>
0126 template<class InputIterator>
0127 auto DedupeCollectionBuilder<T, I>::insert_back(InputIterator first,
0128                                                 InputIterator last)
0129     -> ItemRangeT
0130 {
0131     auto& s = this->storage();
0132 
0133     auto start = this->size_id();
0134     s.insert(s.end(), first, last);
0135     ItemRangeT result{start, this->size_id()};
0136     auto [iter, inserted] = ranges_.insert(result);
0137     if (!inserted)
0138     {
0139         // Roll back the change by erasing the last elements
0140         s.erase(s.begin() + start.unchecked_get(), s.end());
0141         // Return existing range
0142         return *iter;
0143     }
0144     return result;
0145 }
0146 
0147 //---------------------------------------------------------------------------//
0148 /*!
0149  * Insert the given list of elements at the end of the allocation.
0150  */
0151 template<class T, class I>
0152 auto DedupeCollectionBuilder<T, I>::insert_back(std::initializer_list<T> init)
0153     -> ItemRangeT
0154 {
0155     return this->insert_back(init.begin(), init.end());
0156 }
0157 
0158 //---------------------------------------------------------------------------//
0159 /*!
0160  * Add a new element to the end of the allocation.
0161  */
0162 template<class T, class I>
0163 auto DedupeCollectionBuilder<T, I>::push_back(T const& el) -> ItemIdT
0164 {
0165     auto result = this->size_id();
0166     this->storage().push_back(el);
0167     return result;
0168 }
0169 
0170 //---------------------------------------------------------------------------//
0171 // NESTED CLASSES
0172 //---------------------------------------------------------------------------//
0173 /*!
0174  * Compute a hash of the data underlying a range.
0175  *
0176  * By default, this uses the std::hash specialization in \c HashUtils.
0177  */
0178 template<class T, class I>
0179 std::size_t
0180 DedupeCollectionBuilder<T, I>::HashRange::operator()(ItemRangeT r) const
0181 {
0182     CELER_EXPECT(*r.end() <= storage->size());
0183 
0184     // Hash data pointed to by range
0185     Span<T const> data{storage->data() + r.begin()->unchecked_get(), r.size()};
0186     return std::hash<decltype(data)>{}(data);
0187 }
0188 
0189 //---------------------------------------------------------------------------//
0190 /*!
0191  * Compare as equal the data referenced by two ranges.
0192  */
0193 template<class T, class I>
0194 bool DedupeCollectionBuilder<T, I>::EqualRange::operator()(
0195     ItemRangeT const& a, ItemRangeT const& b) const
0196 {
0197     CELER_EXPECT(*a.end() <= storage->size());
0198     CELER_EXPECT(*b.end() <= storage->size());
0199 
0200     auto values_equal = [&s = *storage](ItemIdT left, ItemIdT right) {
0201         return s[left.unchecked_get()] == s[right.unchecked_get()];
0202     };
0203     return std::equal(a.begin(), a.end(), b.begin(), b.end(), values_equal);
0204 }
0205 
0206 //---------------------------------------------------------------------------//
0207 }  // namespace celeritas