| 1 | #pragma once |
| 2 | |
| 3 | #include <tuple> |
| 4 | #include <sstream> |
| 5 | #include <iomanip> |
| 6 | #include <city.h> |
| 7 | |
| 8 | #include <Core/Types.h> |
| 9 | |
| 10 | #ifdef __SSE4_2__ |
| 11 | #include <nmmintrin.h> |
| 12 | #endif |
| 13 | |
| 14 | |
| 15 | namespace DB |
| 16 | { |
| 17 | |
| 18 | /// For aggregation by SipHash, UUID type or concatenation of several fields. |
| 19 | struct UInt128 |
| 20 | { |
| 21 | /// Suppress gcc7 warnings: 'prev_key.DB::UInt128::low' may be used uninitialized in this function |
| 22 | #if !__clang__ |
| 23 | #pragma GCC diagnostic push |
| 24 | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" |
| 25 | #endif |
| 26 | |
| 27 | /// This naming assumes little endian. |
| 28 | UInt64 low; |
| 29 | UInt64 high; |
| 30 | |
| 31 | UInt128() = default; |
| 32 | explicit UInt128(const UInt64 low_, const UInt64 high_) : low(low_), high(high_) {} |
| 33 | explicit UInt128(const UInt64 rhs) : low(rhs), high() {} |
| 34 | |
| 35 | auto tuple() const { return std::tie(high, low); } |
| 36 | |
| 37 | String toHexString() const |
| 38 | { |
| 39 | std::ostringstream os; |
| 40 | os << std::setw(16) << std::setfill('0') << std::hex << high << low; |
| 41 | return String(os.str()); |
| 42 | } |
| 43 | |
| 44 | bool inline operator== (const UInt128 rhs) const { return tuple() == rhs.tuple(); } |
| 45 | bool inline operator!= (const UInt128 rhs) const { return tuple() != rhs.tuple(); } |
| 46 | bool inline operator< (const UInt128 rhs) const { return tuple() < rhs.tuple(); } |
| 47 | bool inline operator<= (const UInt128 rhs) const { return tuple() <= rhs.tuple(); } |
| 48 | bool inline operator> (const UInt128 rhs) const { return tuple() > rhs.tuple(); } |
| 49 | bool inline operator>= (const UInt128 rhs) const { return tuple() >= rhs.tuple(); } |
| 50 | |
| 51 | template <typename T> bool inline operator== (const T rhs) const { return *this == UInt128(rhs); } |
| 52 | template <typename T> bool inline operator!= (const T rhs) const { return *this != UInt128(rhs); } |
| 53 | template <typename T> bool inline operator>= (const T rhs) const { return *this >= UInt128(rhs); } |
| 54 | template <typename T> bool inline operator> (const T rhs) const { return *this > UInt128(rhs); } |
| 55 | template <typename T> bool inline operator<= (const T rhs) const { return *this <= UInt128(rhs); } |
| 56 | template <typename T> bool inline operator< (const T rhs) const { return *this < UInt128(rhs); } |
| 57 | |
| 58 | template <typename T> explicit operator T() const { return static_cast<T>(low); } |
| 59 | |
| 60 | #if !__clang__ |
| 61 | #pragma GCC diagnostic pop |
| 62 | #endif |
| 63 | |
| 64 | UInt128 & operator= (const UInt64 rhs) { low = rhs; high = 0; return *this; } |
| 65 | }; |
| 66 | |
| 67 | template <typename T> bool inline operator== (T a, const UInt128 b) { return UInt128(a) == b; } |
| 68 | template <typename T> bool inline operator!= (T a, const UInt128 b) { return UInt128(a) != b; } |
| 69 | template <typename T> bool inline operator>= (T a, const UInt128 b) { return UInt128(a) >= b; } |
| 70 | template <typename T> bool inline operator> (T a, const UInt128 b) { return UInt128(a) > b; } |
| 71 | template <typename T> bool inline operator<= (T a, const UInt128 b) { return UInt128(a) <= b; } |
| 72 | template <typename T> bool inline operator< (T a, const UInt128 b) { return UInt128(a) < b; } |
| 73 | |
| 74 | template <> inline constexpr bool IsNumber<UInt128> = true; |
| 75 | template <> struct TypeName<UInt128> { static const char * get() { return "UInt128" ; } }; |
| 76 | template <> struct TypeId<UInt128> { static constexpr const TypeIndex value = TypeIndex::UInt128; }; |
| 77 | |
| 78 | struct UInt128Hash |
| 79 | { |
| 80 | size_t operator()(UInt128 x) const |
| 81 | { |
| 82 | return CityHash_v1_0_2::Hash128to64({x.low, x.high}); |
| 83 | } |
| 84 | }; |
| 85 | |
| 86 | #ifdef __SSE4_2__ |
| 87 | |
| 88 | struct UInt128HashCRC32 |
| 89 | { |
| 90 | size_t operator()(UInt128 x) const |
| 91 | { |
| 92 | UInt64 crc = -1ULL; |
| 93 | crc = _mm_crc32_u64(crc, x.low); |
| 94 | crc = _mm_crc32_u64(crc, x.high); |
| 95 | return crc; |
| 96 | } |
| 97 | }; |
| 98 | |
| 99 | #else |
| 100 | |
| 101 | /// On other platforms we do not use CRC32. NOTE This can be confusing. |
| 102 | struct UInt128HashCRC32 : public UInt128Hash {}; |
| 103 | |
| 104 | #endif |
| 105 | |
| 106 | struct UInt128TrivialHash |
| 107 | { |
| 108 | size_t operator()(UInt128 x) const { return x.low; } |
| 109 | }; |
| 110 | |
| 111 | |
| 112 | |
| 113 | /** Used for aggregation, for putting a large number of constant-length keys in a hash table. |
| 114 | */ |
| 115 | struct UInt256 |
| 116 | { |
| 117 | |
| 118 | /// Suppress gcc7 warnings: 'prev_key.DB::UInt256::a' may be used uninitialized in this function |
| 119 | #if !__clang__ |
| 120 | #pragma GCC diagnostic push |
| 121 | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" |
| 122 | #endif |
| 123 | |
| 124 | UInt64 a; |
| 125 | UInt64 b; |
| 126 | UInt64 c; |
| 127 | UInt64 d; |
| 128 | |
| 129 | bool operator== (const UInt256 rhs) const |
| 130 | { |
| 131 | return a == rhs.a && b == rhs.b && c == rhs.c && d == rhs.d; |
| 132 | |
| 133 | /* So it's no better. |
| 134 | return 0xFFFF == _mm_movemask_epi8(_mm_and_si128( |
| 135 | _mm_cmpeq_epi8( |
| 136 | _mm_loadu_si128(reinterpret_cast<const __m128i *>(&a)), |
| 137 | _mm_loadu_si128(reinterpret_cast<const __m128i *>(&rhs.a))), |
| 138 | _mm_cmpeq_epi8( |
| 139 | _mm_loadu_si128(reinterpret_cast<const __m128i *>(&c)), |
| 140 | _mm_loadu_si128(reinterpret_cast<const __m128i *>(&rhs.c)))));*/ |
| 141 | } |
| 142 | |
| 143 | bool operator!= (const UInt256 rhs) const { return !operator==(rhs); } |
| 144 | |
| 145 | bool operator== (const UInt64 rhs) const { return a == rhs && b == 0 && c == 0 && d == 0; } |
| 146 | bool operator!= (const UInt64 rhs) const { return !operator==(rhs); } |
| 147 | |
| 148 | #if !__clang__ |
| 149 | #pragma GCC diagnostic pop |
| 150 | #endif |
| 151 | |
| 152 | UInt256 & operator= (const UInt64 rhs) { a = rhs; b = 0; c = 0; d = 0; return *this; } |
| 153 | }; |
| 154 | |
| 155 | struct UInt256Hash |
| 156 | { |
| 157 | size_t operator()(UInt256 x) const |
| 158 | { |
| 159 | /// NOTE suboptimal |
| 160 | return CityHash_v1_0_2::Hash128to64({CityHash_v1_0_2::Hash128to64({x.a, x.b}), CityHash_v1_0_2::Hash128to64({x.c, x.d})}); |
| 161 | } |
| 162 | }; |
| 163 | |
| 164 | #ifdef __SSE4_2__ |
| 165 | |
| 166 | struct UInt256HashCRC32 |
| 167 | { |
| 168 | size_t operator()(UInt256 x) const |
| 169 | { |
| 170 | UInt64 crc = -1ULL; |
| 171 | crc = _mm_crc32_u64(crc, x.a); |
| 172 | crc = _mm_crc32_u64(crc, x.b); |
| 173 | crc = _mm_crc32_u64(crc, x.c); |
| 174 | crc = _mm_crc32_u64(crc, x.d); |
| 175 | return crc; |
| 176 | } |
| 177 | }; |
| 178 | |
| 179 | #else |
| 180 | |
| 181 | /// We do not need to use CRC32 on other platforms. NOTE This can be confusing. |
| 182 | struct UInt256HashCRC32 : public UInt256Hash {}; |
| 183 | |
| 184 | #endif |
| 185 | |
| 186 | } |
| 187 | |
| 188 | template <> struct is_signed<DB::UInt128> |
| 189 | { |
| 190 | static constexpr bool value = false; |
| 191 | }; |
| 192 | |
| 193 | template <> struct is_unsigned<DB::UInt128> |
| 194 | { |
| 195 | static constexpr bool value = true; |
| 196 | }; |
| 197 | |
| 198 | template <> struct is_integral<DB::UInt128> |
| 199 | { |
| 200 | static constexpr bool value = true; |
| 201 | }; |
| 202 | |
| 203 | // Operator +, -, /, *, % aren't implemented so it's not an arithmetic type |
| 204 | template <> struct is_arithmetic<DB::UInt128> |
| 205 | { |
| 206 | static constexpr bool value = false; |
| 207 | }; |
| 208 | |
| 209 | /// Overload hash for type casting |
| 210 | namespace std |
| 211 | { |
| 212 | template <> struct hash<DB::UInt128> |
| 213 | { |
| 214 | size_t operator()(const DB::UInt128 & u) const |
| 215 | { |
| 216 | return CityHash_v1_0_2::Hash128to64({u.low, u.high}); |
| 217 | } |
| 218 | }; |
| 219 | |
| 220 | } |
| 221 | |