|
| 1 | +/** |
| 2 | + * This file contains code extracted from boost 1.78, by wcjohns 20240809. |
| 3 | + * |
| 4 | + * It is the minimal amount of the boost hash implementation that we need to |
| 5 | + * hash a SpecFile to generate a UUID based on spectrum file contents. |
| 6 | + * We use `boost::hash` because `std::hash` is not guaranteed to be stable |
| 7 | + * across executions of the same executable. |
| 8 | + * |
| 9 | + * Installing boost, particularly on Windows, for just this functionality |
| 10 | + * has proven to be a burden for developers, so this code was extracted |
| 11 | + * to be stand-alone. |
| 12 | + * |
| 13 | + * Some function signatures have been changed to support this extraction |
| 14 | + * of a small amount of the code - this is not the complete implementation |
| 15 | + * of `boost::hash`, just what we use in SpecUtils. The actual computation |
| 16 | + * code is left unchanged, and it was checked for a number of spectrum |
| 17 | + * files that this code produced the same answer as boost proper. |
| 18 | +*/ |
| 19 | + |
| 20 | +// wcjohns: original notice from the top of `boost/container_hash/hash.hpp`: |
| 21 | +// |
| 22 | +// Copyright 2005-2014 Daniel James. |
| 23 | +// Distributed under the Boost Software License, Version 1.0. (See accompanying |
| 24 | +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| 25 | + |
| 26 | +// Based on Peter Dimov's proposal |
| 27 | +// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf |
| 28 | +// issue 6.18. |
| 29 | +// |
| 30 | +// This also contains public domain code from MurmurHash. From the |
| 31 | +// MurmurHash header: |
| 32 | + |
| 33 | +// MurmurHash3 was written by Austin Appleby, and is placed in the public |
| 34 | +// domain. The author hereby disclaims copyright to this source code. |
| 35 | + |
| 36 | + |
| 37 | +#if !defined(SpecUtils_boost_hash_hpp) |
| 38 | +#define SpecUtils_boost_hash_hpp |
| 39 | + |
| 40 | +#include <string> |
| 41 | +#include <vector> |
| 42 | +#include <cstdint> |
| 43 | +#include <climits> |
| 44 | +#include <type_traits> |
| 45 | + |
| 46 | +namespace boost_hash |
| 47 | +{ |
| 48 | + // Forward declaration |
| 49 | + template <class T> |
| 50 | + void hash_combine( std::size_t& seed, T const& v ); |
| 51 | + |
| 52 | + namespace hash_detail |
| 53 | + { |
| 54 | + template <typename T> |
| 55 | + typename std::enable_if<sizeof(T) == 8, std::size_t>::type |
| 56 | + hash_mix( T x ) |
| 57 | + { |
| 58 | + std::uint64_t const m = (std::uint64_t(0xe9846af) << 32) + 0x9b1a615d; |
| 59 | + |
| 60 | + x ^= x >> 32; |
| 61 | + x *= m; |
| 62 | + x ^= x >> 32; |
| 63 | + x *= m; |
| 64 | + x ^= x >> 28; |
| 65 | + |
| 66 | + return x; |
| 67 | + } |
| 68 | + |
| 69 | + template <typename T> |
| 70 | + typename std::enable_if<sizeof(T) == 4, std::size_t>::type |
| 71 | + hash_mix( T x ) |
| 72 | + { |
| 73 | + std::uint32_t const m1 = 0x21f0aaad; |
| 74 | + std::uint32_t const m2 = 0x735a2d97; |
| 75 | + |
| 76 | + x ^= x >> 16; |
| 77 | + x *= m1; |
| 78 | + x ^= x >> 15; |
| 79 | + x *= m2; |
| 80 | + x ^= x >> 15; |
| 81 | + |
| 82 | + return x; |
| 83 | + } |
| 84 | + |
| 85 | + template<class T, |
| 86 | + bool bigger_than_size_t = (sizeof(T) > sizeof(std::size_t)), |
| 87 | + bool is_unsigned = std::is_unsigned<T>::value, |
| 88 | + std::size_t size_t_bits = sizeof(std::size_t) * CHAR_BIT, |
| 89 | + std::size_t type_bits = sizeof(T) * CHAR_BIT> |
| 90 | + struct hash_integral_impl; |
| 91 | + |
| 92 | + template<class T, bool is_unsigned, std::size_t size_t_bits, std::size_t type_bits> struct hash_integral_impl<T, false, is_unsigned, size_t_bits, type_bits> |
| 93 | + { |
| 94 | + static std::size_t fn( T v ) |
| 95 | + { |
| 96 | + return static_cast<std::size_t>( v ); |
| 97 | + } |
| 98 | + }; |
| 99 | + |
| 100 | + template<class T, std::size_t size_t_bits, std::size_t type_bits> struct hash_integral_impl<T, true, false, size_t_bits, type_bits> |
| 101 | + { |
| 102 | + static std::size_t fn( T v ) |
| 103 | + { |
| 104 | + typedef typename std::make_unsigned<T>::type U; |
| 105 | + |
| 106 | + if( v >= 0 ) |
| 107 | + { |
| 108 | + return hash_integral_impl<U>::fn( static_cast<U>( v ) ); |
| 109 | + } |
| 110 | + else |
| 111 | + { |
| 112 | + return ~hash_integral_impl<U>::fn( static_cast<U>( ~static_cast<U>( v ) ) ); |
| 113 | + } |
| 114 | + } |
| 115 | + }; |
| 116 | + |
| 117 | + template<class T> struct hash_integral_impl<T, true, true, 32, 64> |
| 118 | + { |
| 119 | + static std::size_t fn( T v ) |
| 120 | + { |
| 121 | + std::size_t seed = 0; |
| 122 | + |
| 123 | + seed = static_cast<std::size_t>( v >> 32 ) + hash_detail::hash_mix( seed ); |
| 124 | + seed = static_cast<std::size_t>( v ) + hash_detail::hash_mix( seed ); |
| 125 | + |
| 126 | + return seed; |
| 127 | + } |
| 128 | + }; |
| 129 | + |
| 130 | + |
| 131 | + // float |
| 132 | + template <typename T> |
| 133 | + typename std::enable_if<sizeof(T) == 4, std::size_t>::type |
| 134 | + hash_float_impl( T v ) |
| 135 | + { |
| 136 | + std::uint32_t w; |
| 137 | + std::memcpy( &w, &v, sizeof( v ) ); |
| 138 | + |
| 139 | + return w; |
| 140 | + } |
| 141 | + |
| 142 | + // double |
| 143 | + template <typename T> |
| 144 | + typename std::enable_if<sizeof(T) == 8, std::size_t>::type |
| 145 | + hash_float_impl( T v ) |
| 146 | + { |
| 147 | + std::uint64_t w; |
| 148 | + std::memcpy( &w, &v, sizeof( v ) ); |
| 149 | + |
| 150 | + //return boost_hash::hash_value( w ); |
| 151 | + return hash_detail::hash_integral_impl<std::uint64_t>::fn( w ); |
| 152 | + } |
| 153 | + |
| 154 | + |
| 155 | + std::size_t hash_range( std::size_t seed, const char *first, const char *last ) |
| 156 | + { |
| 157 | + std::size_t n = static_cast<std::size_t>( last - first ); |
| 158 | + |
| 159 | + for( ; n >= 4; first += 4, n -= 4 ) |
| 160 | + { |
| 161 | + std::uint32_t w = |
| 162 | + static_cast<std::uint32_t>( static_cast<unsigned char>( first[0] ) ) | |
| 163 | + static_cast<std::uint32_t>( static_cast<unsigned char>( first[1] ) ) << 8 | |
| 164 | + static_cast<std::uint32_t>( static_cast<unsigned char>( first[2] ) ) << 16 | |
| 165 | + static_cast<std::uint32_t>( static_cast<unsigned char>( first[3] ) ) << 24; |
| 166 | + |
| 167 | + hash_combine( seed, w ); |
| 168 | + } |
| 169 | + |
| 170 | + { |
| 171 | + std::uint32_t w = 0x01u; |
| 172 | + |
| 173 | + switch( n ) |
| 174 | + { |
| 175 | + case 1: |
| 176 | + |
| 177 | + w = |
| 178 | + static_cast<std::uint32_t>( static_cast<unsigned char>( first[0] ) ) | |
| 179 | + 0x0100u; |
| 180 | + |
| 181 | + break; |
| 182 | + |
| 183 | + case 2: |
| 184 | + |
| 185 | + w = |
| 186 | + static_cast<std::uint32_t>( static_cast<unsigned char>( first[0] ) ) | |
| 187 | + static_cast<std::uint32_t>( static_cast<unsigned char>( first[1] ) ) << 8 | |
| 188 | + 0x010000u; |
| 189 | + |
| 190 | + break; |
| 191 | + |
| 192 | + case 3: |
| 193 | + |
| 194 | + w = |
| 195 | + static_cast<std::uint32_t>( static_cast<unsigned char>( first[0] ) ) | |
| 196 | + static_cast<std::uint32_t>( static_cast<unsigned char>( first[1] ) ) << 8 | |
| 197 | + static_cast<std::uint32_t>( static_cast<unsigned char>( first[2] ) ) << 16 | |
| 198 | + 0x01000000u; |
| 199 | + |
| 200 | + break; |
| 201 | + } |
| 202 | + |
| 203 | + hash_combine( seed, w ); |
| 204 | + } |
| 205 | + |
| 206 | + return seed; |
| 207 | + } |
| 208 | + }//namespace hash_detail |
| 209 | + |
| 210 | + // Hash floating point types (e.g., `float` and `double`) |
| 211 | + template <typename T> |
| 212 | + typename std::enable_if<std::is_floating_point<T>::value, std::size_t>::type |
| 213 | + hash_value( T v ) |
| 214 | + { |
| 215 | + return boost_hash::hash_detail::hash_float_impl( v ); |
| 216 | + } |
| 217 | + |
| 218 | + // Hash integral types (e.g., `int` and `uint64_t`, etc) |
| 219 | + template <typename T> |
| 220 | + typename std::enable_if<std::is_integral<T>::value, std::size_t>::type |
| 221 | + hash_value( T v ) |
| 222 | + { |
| 223 | + return hash_detail::hash_integral_impl<T>::fn( v ); |
| 224 | + } |
| 225 | + |
| 226 | + std::size_t hash_value( const std::string &v ) |
| 227 | + { |
| 228 | + return boost_hash::hash_detail::hash_range( 0, v.data(), v.data() + v.size() ); |
| 229 | + } |
| 230 | + |
| 231 | + template <class T> |
| 232 | + std::size_t hash_value( const std::vector<T> &v ) |
| 233 | + { |
| 234 | + std::size_t seed = 0; |
| 235 | + for( const T &val : v ) |
| 236 | + hash_combine<T>(seed, val ); |
| 237 | + |
| 238 | + return seed; |
| 239 | + } |
| 240 | + |
| 241 | + |
| 242 | + template <class T> |
| 243 | + void hash_combine( std::size_t& seed, T const& v ) |
| 244 | + { |
| 245 | + seed = boost_hash::hash_detail::hash_mix( seed + 0x9e3779b9 + boost_hash::hash_value( v ) ); |
| 246 | + } |
| 247 | +}//namespace boost_hash |
| 248 | + |
| 249 | +#endif //SpecUtils_boost_hash_hpp |
0 commit comments