Ruby 4.1.0dev (2026-10-20 revision 957a081892437c842a94cfd9443b0bdc569d1ef2)
xxh3.c
1
18#include "prism/internal/xxh3.h"
19
23
24#include <string.h>
25
26#if defined(PRISM_HAS_NEON) && defined(PRISM_LITTLE_ENDIAN_COMPTIME)
27 #include <arm_neon.h>
28 #define XXH3_NEON
29#elif defined(PRISM_HAS_SSE2)
30 #include <emmintrin.h>
31 #define XXH3_SSE2
32#endif
33
34#if defined(__SIZEOF_INT128__)
35static uint64_t
36XXH3_mul128_fold64(uint64_t lhs, uint64_t rhs) {
37 __uint128_t x = (__uint128_t) lhs * (__uint128_t) rhs;
38 return ((uint64_t) (x & 0xFFFFFFFFFFFFFFFFULL)) ^ ((uint64_t) (x >> 64));
39}
40#else
41#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
42#include <intrin.h>
43#endif
44
45static uint64_t
46XXH3_mul128_fold64(uint64_t lhs, uint64_t rhs) {
47 uint64_t lo, hi;
48#if defined(_MSC_VER) && defined(_M_X64)
49 lo = _umul128(lhs, rhs, &hi);
50#elif defined(_MSC_VER) && defined(_M_ARM64)
51 hi = __umulh(lhs, rhs);
52 lo = lhs * rhs;
53#else
54 uint64_t lo_lo = (lhs & 0xFFFFFFFF) * (rhs & 0xFFFFFFFF);
55 uint64_t hi_lo = (lhs >> 32) * (rhs & 0xFFFFFFFF);
56 uint64_t lo_hi = (lhs & 0xFFFFFFFF) * (rhs >> 32);
57 uint64_t hi_hi = (lhs >> 32) * (rhs >> 32);
58
59 uint64_t cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi;
60 hi = hi_hi + (hi_lo >> 32) + (cross >> 32);
61 lo = (cross << 32) | (lo_lo & 0xFFFFFFFF);
62#endif
63 return lo ^ hi;
64}
65#endif
66
67static uint32_t
68read32(const void* ptr) {
69 uint32_t val;
70 memcpy(&val, ptr, sizeof(val));
71 if (!PRISM_LITTLE_ENDIAN_RUNTIME) {
72 val = pm_bswap32(val);
73 }
74 return val;
75}
76
77static uint64_t
78read64(const void* ptr) {
79 uint64_t val;
80 memcpy(&val, ptr, sizeof(val));
81 if (!PRISM_LITTLE_ENDIAN_RUNTIME) {
82 val = pm_bswap64(val);
83 }
84 return val;
85}
86
87static const uint64_t PRIME32_1 = 0x9E3779B1U;
88static const uint64_t PRIME32_2 = 0x85EBCA77U;
89static const uint64_t PRIME32_3 = 0xC2B2AE3DU;
90static const uint64_t PRIME64_1 = 0x9E3779B185EBCA87ULL;
91static const uint64_t PRIME64_2 = 0xC2B2AE3D27D4EB4FULL;
92static const uint64_t PRIME64_3 = 0x165667B19E3779F9ULL;
93static const uint64_t PRIME64_4 = 0x85EBCA77C2B2AE63ULL;
94static const uint64_t PRIME64_5 = 0x27D4EB2F165667C5ULL;
95static const uint64_t PRIME_MX1 = 0x165667919E3779F9ULL;
96static const uint64_t PRIME_MX2 = 0x9FB21C651E98DF25ULL;
97
98static const uint8_t secret[192] = {
99 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c,
100 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f,
101 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21,
102 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c,
103 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3,
104 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8,
105 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d,
106 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64,
107 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb,
108 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e,
109 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce,
110 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e,
111};
112
113static uint64_t
114avalanche(uint64_t x) {
115 x ^= (x >> 37);
116 x *= PRIME_MX1;
117 x ^= (x >> 32);
118 return x;
119}
120
121static uint64_t
122avalanche_XXH64(uint64_t x) {
123 x ^= (x >> 33);
124 x *= PRIME64_2;
125 x ^= (x >> 29);
126 x *= PRIME64_3;
127 x ^= (x >> 32);
128 return x;
129}
130
131static uint64_t
132XXH3_64_empty(void) {
133 return avalanche_XXH64(read64(secret + 56) ^ read64(secret + 64));
134}
135
136static uint64_t
137XXH3_64_1to3(const uint8_t *input, size_t length) {
138 uint32_t combined = (uint32_t) input[length - 1] | ((uint32_t) length << 8) | ((uint32_t) input[0] << 16) | ((uint32_t) input[length >> 1] << 24);
139 return avalanche_XXH64((uint64_t) (read32(secret) ^ read32(secret + 4)) ^ (uint64_t) combined);
140}
141
142static uint64_t
143XXH3_64_4to8(const uint8_t *input, size_t length) {
144#define rot(x_, r_) (((x_) << (r_)) | ((x_) >> (64 - (r_))))
145
146 uint64_t combined = (uint64_t) read32(input + length - 4) | ((uint64_t) read32(input) << 32);
147 uint64_t value = (read64(secret + 8) ^ read64(secret + 16)) ^ combined;
148 value ^= rot(value, 49) ^ rot(value, 24);
149 value *= PRIME_MX2;
150 value ^= ((value >> 35) + length);
151 value *= PRIME_MX2;
152 value ^= (value >> 28);
153 return value;
154
155#undef rot
156}
157
158static uint64_t
159XXH3_64_9to16(const uint8_t *input, size_t length) {
160 uint64_t lo = (read64(secret + 24) ^ read64(secret + 32)) ^ read64(input);
161 uint64_t hi = (read64(secret + 40) ^ read64(secret + 48)) ^ read64(input + length - 8);
162 return avalanche(length + pm_bswap64(lo) + hi + XXH3_mul128_fold64(lo, hi));
163}
164
165static uint64_t
166XXH3_64_17to240(const uint8_t *input, size_t length) {
167#define mix(data_, offset_) XXH3_mul128_fold64(read64((data_)) ^ read64(secret + (offset_)), read64((data_) + 8) ^ read64(secret + (offset_) + 8))
168
169 uint64_t acc = length * PRIME64_1;
170
171 if (length <= 128) {
172 size_t idx = (length - 1) / 32;
173 do {
174 acc += mix(input + idx * 16, idx * 32) + mix(input + length - idx * 16 - 16, idx * 32 + 16);
175 } while (idx--);
176 } else {
177 for (size_t idx = 0; idx < 8; idx++) {
178 acc += mix(input + idx * 16, idx * 16);
179 }
180 acc = avalanche(acc);
181 for (size_t idx = 8; idx < length >> 4; idx++) {
182 acc += mix(input + idx * 16, (idx - 8) * 16 + 3);
183 }
184 acc += mix(input + length - 16, 119);
185 }
186
187 return avalanche(acc);
188
189#undef mix
190}
191
192static void
193accumulate(uint64_t acc[8], const uint8_t *stripe, size_t secret_offset) {
194#if defined(XXH3_NEON)
195 for (int idx = 0; idx < 8; idx += 2) {
196 uint64x2_t data = vreinterpretq_u64_u8(vld1q_u8(stripe + 8 * idx));
197 uint64x2_t value = veorq_u64(data, vreinterpretq_u64_u8(vld1q_u8(secret + secret_offset + 8 * idx)));
198 uint64x2_t product = vmull_u32(vmovn_u64(value), vshrn_n_u64(value, 32));
199 vst1q_u64(acc + idx, vaddq_u64(vld1q_u64(acc + idx), vaddq_u64(vextq_u64(data, data, 1), product)));
200 }
201#elif defined(XXH3_SSE2)
202 for (int idx = 0; idx < 8; idx += 2) {
203 __m128i data = _mm_loadu_si128((const __m128i *) (stripe + 8 * idx));
204 __m128i value = _mm_xor_si128(data, _mm_loadu_si128((const __m128i *) (secret + secret_offset + 8 * idx)));
205 __m128i product = _mm_mul_epu32(value, _mm_shuffle_epi32(value, _MM_SHUFFLE(0, 3, 0, 1)));
206 _mm_storeu_si128((__m128i *) (acc + idx), _mm_add_epi64(_mm_loadu_si128((const __m128i *) (acc + idx)), _mm_add_epi64(_mm_shuffle_epi32(data, _MM_SHUFFLE(1, 0, 3, 2)), product)));
207 }
208#else
209 for (int idx = 0; idx < 8; idx++) {
210 uint64_t data = read64(stripe + 8 * idx);
211 uint64_t value = data ^ read64(secret + secret_offset + 8 * idx);
212 acc[idx ^ 1] = acc[idx ^ 1] + data;
213 acc[idx] += (value & 0xFFFFFFFF) * (value >> 32);
214 }
215#endif
216}
217
218static uint64_t
219XXH3_64_large(const uint8_t *input, size_t length) {
220 const size_t secret_length = sizeof(secret);
221 const size_t stripes_per_block = (secret_length - 64) / 8;
222 const size_t block_size = 64 * stripes_per_block;
223 const size_t block_count = (length - 1) / block_size;
224 const size_t last_offset = block_count * block_size;
225
226 uint64_t acc[8] = {
227 PRIME32_3, PRIME64_1, PRIME64_2, PRIME64_3,
228 PRIME64_4, PRIME32_2, PRIME64_5, PRIME32_1
229 };
230
231 for (size_t block_idx = 0; block_idx < block_count; block_idx++) {
232 const uint8_t *block = input + block_idx * block_size;
233
234 for (size_t stripe_idx = 0; stripe_idx < stripes_per_block; stripe_idx++) {
235 accumulate(acc, block + stripe_idx * 64, stripe_idx * 8);
236 }
237
238#if defined(XXH3_NEON)
239 const uint32x2_t prime = vdup_n_u32((uint32_t) PRIME32_1);
240 for (size_t idx = 0; idx < 8; idx += 2) {
241 uint64x2_t value = vld1q_u64(acc + idx);
242 value = veorq_u64(value, vshrq_n_u64(value, 47));
243 value = veorq_u64(value, vreinterpretq_u64_u8(vld1q_u8(secret + secret_length - 64 + idx * 8)));
244 vst1q_u64(acc + idx, vaddq_u64(vmull_u32(vmovn_u64(value), prime), vshlq_n_u64(vmull_u32(vshrn_n_u64(value, 32), prime), 32)));
245 }
246#elif defined(XXH3_SSE2)
247 const __m128i prime = _mm_set1_epi32((int) (uint32_t) PRIME32_1);
248 for (size_t idx = 0; idx < 8; idx += 2) {
249 __m128i value = _mm_loadu_si128((const __m128i *) (acc + idx));
250 value = _mm_xor_si128(value, _mm_srli_epi64(value, 47));
251 value = _mm_xor_si128(value, _mm_loadu_si128((const __m128i *) (secret + secret_length - 64 + idx * 8)));
252 _mm_storeu_si128((__m128i *) (acc + idx), _mm_add_epi64(_mm_mul_epu32(value, prime), _mm_slli_epi64(_mm_mul_epu32(_mm_shuffle_epi32(value, _MM_SHUFFLE(0, 3, 0, 1)), prime), 32)));
253 }
254#else
255 for (size_t idx = 0; idx < 8; idx++) {
256 acc[idx] ^= (acc[idx] >> 47);
257 acc[idx] ^= read64(secret + secret_length - 64 + idx * 8);
258 acc[idx] *= PRIME32_1;
259 }
260#endif
261 }
262
263 for (size_t stripe_idx = 0; stripe_idx < (length - last_offset - 1) / 64; stripe_idx++) {
264 accumulate(acc, input + last_offset + stripe_idx * 64, stripe_idx * 8);
265 }
266
267 accumulate(acc, input + length - 64, secret_length - 71);
268
269 uint64_t result = (uint64_t) length * PRIME64_1;
270 for (size_t idx = 0; idx < 4; idx++) {
271 result += XXH3_mul128_fold64(acc[idx * 2] ^ read64(secret + 11 + 16 * idx), acc[idx * 2 + 1] ^ read64(secret + 11 + 16 * idx + 8));
272 }
273
274 return avalanche(result);
275}
276
277uint64_t
278pm_xxh3(const uint8_t *input, size_t length) {
279 if (length == 0) {
280 return XXH3_64_empty();
281 } else if (length <= 3) {
282 return XXH3_64_1to3(input, length);
283 } else if (length <= 8) {
284 return XXH3_64_4to8(input, length);
285 } else if (length <= 16) {
286 return XXH3_64_9to16(input, length);
287 } else if (length <= 240) {
288 return XXH3_64_17to240(input, length);
289 } else {
290 return XXH3_64_large(input, length);
291 }
292}
#define pm_bswap32(x_)
Byte swap utilities for 32-bit and 64-bit integers.
Definition bswap.h:11