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 | |