Back to home page

EIC code displayed by LXR

 
 

    


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

0001 //----------------------------------*-C++-*----------------------------------//
0002 // Copyright 2022-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/math/HashUtils.hh
0007 // TODO for v1.0: rename to Hasher.hh
0008 //---------------------------------------------------------------------------//
0009 #pragma once
0010 
0011 #include <cstddef>
0012 #include <functional>
0013 #include <initializer_list>
0014 #include <type_traits>
0015 
0016 #include "corecel/Macros.hh"
0017 #include "corecel/cont/Span.hh"
0018 
0019 #include "detail/FnvHasher.hh"
0020 
0021 namespace celeritas
0022 {
0023 //---------------------------------------------------------------------------//
0024 // TODO: add CMake configuration argument so this can be swapped out with e.g.
0025 // xxHash
0026 using Hasher = detail::FnvHasher<std::size_t>;
0027 
0028 //---------------------------------------------------------------------------//
0029 /*!
0030  * Hash a span of contiguous data without padding.
0031  *
0032  * This should generally only be used if \c has_unique_object_representations_v
0033  * is \c true, because e.g. structs have padding so this may result in reading
0034  * uninitialized data or giving two equal structs different hashes.
0035  */
0036 template<class T, std::size_t N>
0037 std::size_t hash_as_bytes(Span<T const, N> s)
0038 {
0039     std::size_t result{};
0040     Hasher hash{&result};
0041     hash(Span<std::byte const>{reinterpret_cast<std::byte const*>(s.data()),
0042                                s.size() * sizeof(T)});
0043     return result;
0044 }
0045 
0046 //---------------------------------------------------------------------------//
0047 /*!
0048  * Combine hashes of the given arguments using a fast hash algorithm.
0049  *
0050  * See https://florianjw.de/en/variadic_templates.html for why we constructed
0051  * this as such. By making the variadic template use function argument
0052  * expansion rather than recursion, we can unpack the args in a left-to-right
0053  * order. The `(HASH,0)` construction is to give the unpacked expression a
0054  * return type; and putting these in an `initializer_list` constructor
0055  * guarantees the hashes are evaluated from left to right (unlike a typical
0056  * argument expansion where the orders may be arbitrary).
0057  */
0058 template<class... Args>
0059 std::size_t hash_combine(Args const&... args)
0060 {
0061     // Construct a hasher and initialize
0062     std::size_t result{};
0063     [[maybe_unused]] Hasher hash{&result};
0064 
0065     // Hash each one of the arguments sequentially by expanding into an unused
0066     // initializer list.
0067     (void)std::initializer_list<int>{(hash(std::hash<Args>()(args)), 0)...};
0068 
0069     return result;
0070 }
0071 //---------------------------------------------------------------------------//
0072 }  // namespace celeritas
0073 
0074 //---------------------------------------------------------------------------//
0075 //! \cond
0076 namespace std
0077 {
0078 //---------------------------------------------------------------------------//
0079 /*!
0080  * Hash specialization for celeritas span.
0081  *
0082  * This has three distinct cases:
0083  * 1. Unique object representation: use for structs without padding or floats.
0084  * 2. Floating point values: these will hash uniquely if they're not NaN.
0085  * 3. All other types: combine the hash of their individual values.
0086  */
0087 template<class T, std::size_t Extent>
0088 struct hash<celeritas::Span<T, Extent>>
0089 {
0090     std::size_t operator()(celeritas::Span<T, Extent> const& s) const
0091     {
0092         if constexpr (std::has_unique_object_representations_v<T>)
0093         {
0094             return celeritas::hash_as_bytes(s);
0095         }
0096         else if constexpr (std::is_floating_point_v<T>)
0097         {
0098             CELER_EXPECT(([&s] {
0099                 for (auto const& v : s)
0100                 {
0101                     if (v != v)
0102                         return false;
0103                 }
0104                 return true;
0105             }()));
0106             return celeritas::hash_as_bytes(s);
0107         }
0108         else
0109         {
0110             std::size_t result{};
0111             celeritas::Hasher hash{&result};
0112             for (auto const& v : s)
0113             {
0114                 hash(std::hash<std::remove_cv_t<T>>{}(v));
0115             }
0116             return result;
0117         }
0118     }
0119 };
0120 
0121 //---------------------------------------------------------------------------//
0122 }  // namespace std
0123 //! \endcond