Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:54:47

0001 //----------------------------------*-C++-*----------------------------------//
0002 // Copyright 2023-2024 UT-Battelle, LLC, and other Celeritas developers.
0003 // See the top-level COPYRIGHT file for details.
0004 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0005 //---------------------------------------------------------------------------//
0006 //! \file corecel/data/DedupeCollectionBuilder.hh
0007 //---------------------------------------------------------------------------//
0008 #pragma once
0009 
0010 #include <algorithm>
0011 #include <cstddef>
0012 #include <unordered_set>
0013 
0014 #include "corecel/math/HashUtils.hh"
0015 
0016 #include "Collection.hh"
0017 
0018 namespace celeritas
0019 {
0020 //---------------------------------------------------------------------------//
0021 /*!
0022  * Build collections, returning the same ID for reused data spans.
0023  */
0024 template<class T, class I = ItemId<T>>
0025 class DedupeCollectionBuilder
0026 {
0027   public:
0028     //!@{
0029     //! \name Type aliases
0030     using CollectionT = Collection<T, Ownership::value, MemSpace::host, I>;
0031     using value_type = T;
0032     using size_type = typename CollectionT::size_type;
0033     using ItemIdT = typename CollectionT::ItemIdT;
0034     using ItemRangeT = typename CollectionT::ItemRangeT;
0035     //!@}
0036 
0037   public:
0038     // Construct from a collection
0039     explicit DedupeCollectionBuilder(CollectionT* collection);
0040 
0041     //// ACCESSORS ////
0042 
0043     //! Number of elements in the collection
0044     size_type size() const { return this->storage().size(); }
0045 
0046     //! Get the size as an ID type
0047     ItemIdT size_id() const { return ItemIdT{this->size()}; }
0048 
0049     //// MUTATORS ////
0050 
0051     // Reserve space and hash table space
0052     inline void reserve(std::size_t count);
0053 
0054     // Extend with a series of elements, returning the range inserted
0055     template<class InputIterator>
0056     inline ItemRangeT insert_back(InputIterator first, InputIterator last);
0057 
0058     // Extend with a series of elements from an initializer list
0059     inline ItemRangeT insert_back(std::initializer_list<value_type> init);
0060 
0061     // Append a single element
0062     inline ItemIdT push_back(value_type const& element);
0063 
0064   private:
0065     //// CLASSES ////
0066 
0067     using StorageT = typename CollectionT::StorageT;
0068 
0069     struct HashRange
0070     {
0071         StorageT* storage{nullptr};
0072         std::size_t operator()(ItemRangeT) const;
0073     };
0074     struct EqualRange
0075     {
0076         StorageT* storage{nullptr};
0077         bool operator()(ItemRangeT const&, ItemRangeT const&) const;
0078     };
0079 
0080     //// DATA ////
0081 
0082     CollectionT* col_;
0083     std::unordered_set<ItemRangeT, HashRange, EqualRange> ranges_;
0084 
0085     //// FUNCTIONS ////
0086 
0087     //!@{
0088     //! Access storage
0089     CELER_FORCEINLINE StorageT& storage() { return col_->storage(); }
0090     CELER_FORCEINLINE StorageT const& storage() const
0091     {
0092         return col_->storage();
0093     }
0094     //!@}
0095 };
0096 
0097 //---------------------------------------------------------------------------//
0098 // INLINE DEFINITIONS
0099 //---------------------------------------------------------------------------//
0100 /*!
0101  * Construct from a collection.
0102  */
0103 template<class T, class I>
0104 DedupeCollectionBuilder<T, I>::DedupeCollectionBuilder(CollectionT* collection)
0105     : col_{collection}
0106     , ranges_{0, HashRange{&this->storage()}, EqualRange{&this->storage()}}
0107 {
0108     CELER_EXPECT(col_);
0109 }
0110 
0111 //---------------------------------------------------------------------------//
0112 /*!
0113  * Reserve space for elements.
0114  */
0115 template<class T, class I>
0116 void DedupeCollectionBuilder<T, I>::reserve(std::size_t count)
0117 {
0118     this->storage().reserve(count);
0119     ranges_.reserve(count);
0120 }
0121 
0122 //---------------------------------------------------------------------------//
0123 /*!
0124  * Insert the given list of elements at the end of the allocation.
0125  */
0126 template<class T, class I>
0127 template<class InputIterator>
0128 auto DedupeCollectionBuilder<T, I>::insert_back(InputIterator first,
0129                                                 InputIterator last) -> 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