#pragma once #include #include #if __SSE4_2__ #include #endif namespace DB { /// For aggregation by SipHash, UUID type or concatenation of several fields. struct UInt128 { /// Suppress gcc7 warnings: 'prev_key.DB::UInt128::low' may be used uninitialized in this function #if !__clang__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif UInt64 low; UInt64 high; UInt128() = default; explicit UInt128(const UInt64 rhs) : low(0), high(rhs) { } explicit UInt128(const UInt64 low, const UInt64 high) : low(low), high(high) { } bool inline operator== (const UInt128 rhs) const { return std::tie(low, high) == std::tie(rhs.low, rhs.high); } bool inline operator!= (const UInt128 rhs) const { return !(*this == rhs); } bool inline operator< (const UInt128 rhs) const { return std::tie(low, high) < std::tie(rhs.low, rhs.high); } bool inline operator<= (const UInt128 rhs) const { return !(rhs < *this); } bool inline operator> (const UInt128 rhs) const { return rhs < *this; } bool inline operator>= (const UInt128 rhs) const { return !(*this < rhs); } /** Type stored in the database will contain no more than 64 bits at the moment, don't need * to check the `high` element */ template bool inline operator== (const T rhs) const { return *this == UInt128(0, rhs); } template bool inline operator!= (const T rhs) const { return *this != UInt128(0, rhs); } template bool inline operator>= (const T rhs) const { return *this >= UInt128(0, rhs); } template bool inline operator> (const T rhs) const { return *this > UInt128(0, rhs); } template bool inline operator<= (const T rhs) const { return *this <= UInt128(0, rhs); } template bool inline operator< (const T rhs) const { return *this < UInt128(0, rhs); } template explicit operator T() const { return static_cast(high); } #if !__clang__ #pragma GCC diagnostic pop #endif UInt128 & operator= (const UInt64 rhs) { low = 0; high = rhs; return *this; } }; template bool inline operator== (T a, const UInt128 b) { return b == a; } template bool inline operator!= (T a, const UInt128 b) { return b != a; } template bool inline operator>= (T a, const UInt128 b) { return b.low == 0 && a >= static_cast(b.high); } template bool inline operator> (T a, const UInt128 b) { return b.low == 0 && a > static_cast(b.high); } template bool inline operator<= (T a, const UInt128 b) { return b.low != 0 || a <= static_cast(b.high); } template bool inline operator< (T a, const UInt128 b) { return b.low != 0 || a < static_cast(b.high); } struct UInt128Hash { size_t operator()(UInt128 x) const { return CityHash_v1_0_2::Hash128to64({x.low, x.high}); } }; #if __SSE4_2__ struct UInt128HashCRC32 { size_t operator()(UInt128 x) const { UInt64 crc = -1ULL; crc = _mm_crc32_u64(crc, x.low); crc = _mm_crc32_u64(crc, x.high); return crc; } }; #else /// On other platforms we do not use CRC32. NOTE This can be confusing. struct UInt128HashCRC32 : public UInt128Hash {}; #endif struct UInt128TrivialHash { size_t operator()(UInt128 x) const { return x.low; } }; /** Used for aggregation, for putting a large number of constant-length keys in a hash table. */ struct UInt256 { /// Suppress gcc7 warnings: 'prev_key.DB::UInt256::a' may be used uninitialized in this function #if !__clang__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif UInt64 a; UInt64 b; UInt64 c; UInt64 d; bool operator== (const UInt256 rhs) const { return a == rhs.a && b == rhs.b && c == rhs.c && d == rhs.d; /* So it's no better. return 0xFFFF == _mm_movemask_epi8(_mm_and_si128( _mm_cmpeq_epi8( _mm_loadu_si128(reinterpret_cast(&a)), _mm_loadu_si128(reinterpret_cast(&rhs.a))), _mm_cmpeq_epi8( _mm_loadu_si128(reinterpret_cast(&c)), _mm_loadu_si128(reinterpret_cast(&rhs.c)))));*/ } bool operator!= (const UInt256 rhs) const { return !operator==(rhs); } bool operator== (const UInt64 rhs) const { return a == rhs && b == 0 && c == 0 && d == 0; } bool operator!= (const UInt64 rhs) const { return !operator==(rhs); } #if !__clang__ #pragma GCC diagnostic pop #endif UInt256 & operator= (const UInt64 rhs) { a = rhs; b = 0; c = 0; d = 0; return *this; } }; struct UInt256Hash { size_t operator()(UInt256 x) const { /// NOTE suboptimal return CityHash_v1_0_2::Hash128to64({CityHash_v1_0_2::Hash128to64({x.a, x.b}), CityHash_v1_0_2::Hash128to64({x.c, x.d})}); } }; #if __SSE4_2__ struct UInt256HashCRC32 { size_t operator()(UInt256 x) const { UInt64 crc = -1ULL; crc = _mm_crc32_u64(crc, x.a); crc = _mm_crc32_u64(crc, x.b); crc = _mm_crc32_u64(crc, x.c); crc = _mm_crc32_u64(crc, x.d); return crc; } }; #else /// We do not need to use CRC32 on other platforms. NOTE This can be confusing. struct UInt256HashCRC32 { DefaultHash hash64; size_t operator()(UInt256 x) const { /// TODO This is not optimal. return hash64(hash64(hash64(hash64(x.a) ^ x.b) ^ x.c) ^ x.d); } }; #endif } /// Overload hash for type casting namespace std { template <> struct hash { size_t operator()(const DB::UInt128 & u) const { return CityHash_v1_0_2::Hash128to64({u.low, u.high}); } }; template <> struct is_signed { static constexpr bool value = false; }; template <> struct is_unsigned { static constexpr bool value = true; }; }