swiss_hash 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +82 -0
- data/README.md +166 -83
- data/ext/swiss_hash/swiss_hash.c +1164 -281
- data/lib/swiss_hash/version.rb +1 -1
- data/lib/swiss_hash.rb +37 -9
- metadata +4 -7
data/ext/swiss_hash/swiss_hash.c
CHANGED
|
@@ -12,6 +12,20 @@
|
|
|
12
12
|
#endif
|
|
13
13
|
#endif
|
|
14
14
|
|
|
15
|
+
#ifndef RB_LIKELY
|
|
16
|
+
#if defined(__GNUC__) || defined(__clang__)
|
|
17
|
+
#define RB_LIKELY(x) __builtin_expect(!!(x), 1)
|
|
18
|
+
#else
|
|
19
|
+
#define RB_LIKELY(x) (x)
|
|
20
|
+
#endif
|
|
21
|
+
#endif
|
|
22
|
+
|
|
23
|
+
#if defined(__GNUC__) || defined(__clang__)
|
|
24
|
+
#define SH_ALWAYS_INLINE static inline __attribute__((always_inline))
|
|
25
|
+
#else
|
|
26
|
+
#define SH_ALWAYS_INLINE static inline
|
|
27
|
+
#endif
|
|
28
|
+
|
|
15
29
|
#if defined(__GNUC__) || defined(__clang__)
|
|
16
30
|
#define SH_PREFETCH(p) __builtin_prefetch((const void *)(p), 0, 1)
|
|
17
31
|
#else
|
|
@@ -20,6 +34,7 @@
|
|
|
20
34
|
|
|
21
35
|
static uint64_t swiss_hash_seed0;
|
|
22
36
|
static uint64_t swiss_hash_seed1;
|
|
37
|
+
static VALUE cSwissHashHash;
|
|
23
38
|
|
|
24
39
|
static void init_hash_seed(void) {
|
|
25
40
|
VALUE seed_val = rb_hash(INT2FIX(0));
|
|
@@ -131,119 +146,126 @@ static inline uint64_t wyhash(const void *data, size_t len, uint64_t seed) {
|
|
|
131
146
|
#if defined(__x86_64__) || defined(_M_X64)
|
|
132
147
|
#define SWISS_USE_SSE2 1
|
|
133
148
|
#include <emmintrin.h>
|
|
134
|
-
|
|
135
|
-
#define GROUP_SIZE 16
|
|
136
|
-
#define GROUP_MASK 0xFFFFu
|
|
149
|
+
#define GROUP_SIZE 16
|
|
137
150
|
#elif defined(__aarch64__) || defined(_M_ARM64)
|
|
138
151
|
#define SWISS_USE_NEON 1
|
|
139
152
|
#include <arm_neon.h>
|
|
140
|
-
|
|
141
|
-
#define GROUP_SIZE 8
|
|
142
|
-
#define GROUP_MASK 0xFFu
|
|
153
|
+
#define GROUP_SIZE 16
|
|
143
154
|
#else
|
|
144
|
-
#define
|
|
145
|
-
#define GROUP_SIZE
|
|
146
|
-
#
|
|
155
|
+
#define SWISS_USE_SWAR 1
|
|
156
|
+
#define GROUP_SIZE 8
|
|
157
|
+
#endif
|
|
158
|
+
|
|
159
|
+
#if defined(SWISS_USE_SSE2) || defined(SWISS_USE_NEON)
|
|
160
|
+
#define SWISS_USE_VEC 1
|
|
147
161
|
#endif
|
|
148
162
|
|
|
149
163
|
#ifdef SWISS_USE_SSE2
|
|
150
164
|
|
|
151
|
-
|
|
165
|
+
typedef __m128i ctrl_vec_t;
|
|
166
|
+
|
|
167
|
+
SH_ALWAYS_INLINE ctrl_vec_t ctrl_load(const uint8_t *ctrl) {
|
|
152
168
|
return _mm_loadu_si128((const __m128i *)ctrl);
|
|
153
169
|
}
|
|
154
170
|
|
|
155
|
-
|
|
171
|
+
SH_ALWAYS_INLINE uint32_t ctrl_match_h2_vec(ctrl_vec_t cv, uint8_t h2) {
|
|
156
172
|
__m128i cmp = _mm_cmpeq_epi8(cv, _mm_set1_epi8((char)h2));
|
|
157
173
|
return (uint32_t)_mm_movemask_epi8(cmp);
|
|
158
174
|
}
|
|
159
175
|
|
|
160
|
-
|
|
176
|
+
SH_ALWAYS_INLINE uint32_t ctrl_match_empty_vec(ctrl_vec_t cv) {
|
|
161
177
|
__m128i cmp = _mm_cmpeq_epi8(cv, _mm_set1_epi8((char)CTRL_EMPTY));
|
|
162
178
|
return (uint32_t)_mm_movemask_epi8(cmp);
|
|
163
179
|
}
|
|
164
180
|
|
|
165
|
-
|
|
181
|
+
SH_ALWAYS_INLINE uint32_t ctrl_match_empty_or_deleted_vec(ctrl_vec_t cv) {
|
|
166
182
|
return (uint32_t)_mm_movemask_epi8(cv);
|
|
167
183
|
}
|
|
168
184
|
|
|
169
|
-
|
|
185
|
+
SH_ALWAYS_INLINE uint32_t ctrl_match_empty(const uint8_t *ctrl) {
|
|
170
186
|
return ctrl_match_empty_vec(ctrl_load(ctrl));
|
|
171
187
|
}
|
|
172
188
|
|
|
173
189
|
#elif defined(SWISS_USE_NEON)
|
|
174
190
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
191
|
+
typedef uint8x16_t ctrl_vec_t;
|
|
192
|
+
typedef uint64_t ctrl_mask_t;
|
|
193
|
+
|
|
194
|
+
#define CTRL_MASK_SLOT(m) ((int)(ctz64(m) >> 2))
|
|
195
|
+
#define NEON_NIBBLE_MASK 0x1111111111111111ULL
|
|
196
|
+
|
|
197
|
+
SH_ALWAYS_INLINE ctrl_mask_t neon_mask64(uint8x16_t cmp) {
|
|
198
|
+
/* 4 bits per slot (0x0 / 0xF); keep one bit per nibble so mask &= mask-1 works. */
|
|
199
|
+
uint64_t packed = vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(cmp), 4)), 0);
|
|
200
|
+
return packed & NEON_NIBBLE_MASK;
|
|
182
201
|
}
|
|
183
202
|
|
|
184
|
-
|
|
185
|
-
return
|
|
203
|
+
SH_ALWAYS_INLINE ctrl_vec_t ctrl_load(const uint8_t *ctrl) {
|
|
204
|
+
return vld1q_u8(ctrl);
|
|
186
205
|
}
|
|
187
206
|
|
|
188
|
-
|
|
189
|
-
return
|
|
207
|
+
SH_ALWAYS_INLINE ctrl_mask_t ctrl_match_h2_vec(ctrl_vec_t cv, uint8_t h2) {
|
|
208
|
+
return neon_mask64(vceqq_u8(cv, vdupq_n_u8(h2)));
|
|
190
209
|
}
|
|
191
210
|
|
|
192
|
-
|
|
193
|
-
return
|
|
211
|
+
SH_ALWAYS_INLINE ctrl_mask_t ctrl_match_empty_vec(ctrl_vec_t cv) {
|
|
212
|
+
return neon_mask64(vceqq_u8(cv, vdupq_n_u8((uint8_t)CTRL_EMPTY)));
|
|
194
213
|
}
|
|
195
214
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
uint8x8_t match = vceq_u8(msb, vdup_n_u8(1));
|
|
199
|
-
return neon_movemask(match);
|
|
215
|
+
SH_ALWAYS_INLINE ctrl_mask_t ctrl_match_empty_or_deleted_vec(ctrl_vec_t cv) {
|
|
216
|
+
return neon_mask64(vcltzq_s8(vreinterpretq_s8_u8(cv)));
|
|
200
217
|
}
|
|
201
218
|
|
|
202
|
-
|
|
219
|
+
SH_ALWAYS_INLINE ctrl_mask_t ctrl_match_empty(const uint8_t *ctrl) {
|
|
203
220
|
return ctrl_match_empty_vec(ctrl_load(ctrl));
|
|
204
221
|
}
|
|
205
222
|
|
|
206
|
-
#else /*
|
|
223
|
+
#else /* SWAR */
|
|
207
224
|
|
|
208
|
-
|
|
225
|
+
#define SWAR_LSB 0x0101010101010101ULL
|
|
226
|
+
#define SWAR_MSB 0x8080808080808080ULL
|
|
227
|
+
|
|
228
|
+
SH_ALWAYS_INLINE uint32_t ctrl_bitmask_from_msb(uint64_t msb_bits) {
|
|
229
|
+
uint64_t bits = (msb_bits & SWAR_MSB) >> 7;
|
|
230
|
+
return (uint32_t)((bits * 0x0102040810204080ULL) >> 56);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
SH_ALWAYS_INLINE uint32_t ctrl_match_h2_raw(const uint8_t *ctrl, uint8_t h2) {
|
|
209
234
|
uint64_t c;
|
|
210
235
|
memcpy(&c, ctrl, 8);
|
|
211
|
-
uint64_t broadcast =
|
|
236
|
+
uint64_t broadcast = SWAR_LSB * h2;
|
|
212
237
|
uint64_t xored = c ^ broadcast;
|
|
213
|
-
uint64_t result = (xored -
|
|
214
|
-
|
|
215
|
-
for (int i = 0; i < 8; i++) {
|
|
216
|
-
if (result & (0x80ULL << (i * 8)))
|
|
217
|
-
mask |= (1u << i);
|
|
218
|
-
}
|
|
219
|
-
return mask;
|
|
238
|
+
uint64_t result = (xored - SWAR_LSB) & ~xored & SWAR_MSB;
|
|
239
|
+
return ctrl_bitmask_from_msb(result);
|
|
220
240
|
}
|
|
221
241
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
mask |= (1u << i);
|
|
227
|
-
}
|
|
228
|
-
return mask;
|
|
242
|
+
SH_ALWAYS_INLINE uint32_t ctrl_match_empty_raw(const uint8_t *ctrl) {
|
|
243
|
+
uint64_t c;
|
|
244
|
+
memcpy(&c, ctrl, 8);
|
|
245
|
+
return ctrl_bitmask_from_msb((c & ~(c << 6)) & SWAR_MSB);
|
|
229
246
|
}
|
|
230
247
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
mask |= (1u << i);
|
|
236
|
-
}
|
|
237
|
-
return mask;
|
|
248
|
+
SH_ALWAYS_INLINE uint32_t ctrl_match_empty_or_deleted_raw(const uint8_t *ctrl) {
|
|
249
|
+
uint64_t c;
|
|
250
|
+
memcpy(&c, ctrl, 8);
|
|
251
|
+
return ctrl_bitmask_from_msb(c);
|
|
238
252
|
}
|
|
239
253
|
|
|
240
|
-
|
|
254
|
+
SH_ALWAYS_INLINE uint32_t ctrl_match_empty(const uint8_t *ctrl) {
|
|
241
255
|
return ctrl_match_empty_raw(ctrl);
|
|
242
256
|
}
|
|
243
257
|
|
|
244
|
-
#endif /*
|
|
258
|
+
#endif /* control-byte matching selection */
|
|
259
|
+
|
|
260
|
+
#ifndef CTRL_MASK_SLOT
|
|
261
|
+
typedef uint32_t ctrl_mask_t;
|
|
262
|
+
#define CTRL_MASK_SLOT(m) ctz32(m)
|
|
263
|
+
#endif
|
|
245
264
|
|
|
246
|
-
|
|
265
|
+
#if defined(__GNUC__) || defined(__clang__)
|
|
266
|
+
__attribute__((unused))
|
|
267
|
+
#endif
|
|
268
|
+
SH_ALWAYS_INLINE int ctz32(uint32_t v) {
|
|
247
269
|
#if defined(__GNUC__) || defined(__clang__)
|
|
248
270
|
return __builtin_ctz(v);
|
|
249
271
|
#elif defined(_MSC_VER)
|
|
@@ -260,6 +282,21 @@ static inline int ctz32(uint32_t v) {
|
|
|
260
282
|
#endif
|
|
261
283
|
}
|
|
262
284
|
|
|
285
|
+
SH_ALWAYS_INLINE int ctz64(uint64_t v) {
|
|
286
|
+
#if defined(__GNUC__) || defined(__clang__)
|
|
287
|
+
return (int)__builtin_ctzll(v);
|
|
288
|
+
#elif defined(_MSC_VER)
|
|
289
|
+
unsigned long idx;
|
|
290
|
+
_BitScanForward64(&idx, v);
|
|
291
|
+
return (int)idx;
|
|
292
|
+
#else
|
|
293
|
+
if ((uint32_t)v) {
|
|
294
|
+
return ctz32((uint32_t)v);
|
|
295
|
+
}
|
|
296
|
+
return 32 + ctz32((uint32_t)(v >> 32));
|
|
297
|
+
#endif
|
|
298
|
+
}
|
|
299
|
+
|
|
263
300
|
typedef struct {
|
|
264
301
|
VALUE key;
|
|
265
302
|
VALUE value;
|
|
@@ -277,44 +314,49 @@ typedef struct {
|
|
|
277
314
|
uint8_t mutating;
|
|
278
315
|
} SwissHash;
|
|
279
316
|
|
|
280
|
-
#define MUTATE_GUARD_BEGIN(sh) \
|
|
281
|
-
do { \
|
|
282
|
-
if ((sh)->mutating) { \
|
|
283
|
-
rb_raise(rb_eRuntimeError, "SwissHash: reentrant modification detected " \
|
|
284
|
-
"(#hash or #eql? callback modified the same table)"); \
|
|
285
|
-
} \
|
|
286
|
-
(sh)->mutating = 1; \
|
|
287
|
-
} while (0)
|
|
288
|
-
|
|
289
|
-
#define MUTATE_GUARD_END(sh) \
|
|
290
|
-
do { \
|
|
291
|
-
(sh)->mutating = 0; \
|
|
292
|
-
} while (0)
|
|
293
|
-
|
|
294
317
|
#define FIBONACCI_HASH_C 0x9E3779B97F4A7C15ULL
|
|
295
318
|
|
|
296
|
-
|
|
297
|
-
uint64_t
|
|
319
|
+
SH_ALWAYS_INLINE uint64_t hash_fixnum(VALUE key) {
|
|
320
|
+
return ((uint64_t)FIX2LONG(key) ^ swiss_hash_seed0) * FIBONACCI_HASH_C;
|
|
321
|
+
}
|
|
298
322
|
|
|
323
|
+
SH_ALWAYS_INLINE uint64_t hash_immediate(VALUE key) {
|
|
299
324
|
if (FIXNUM_P(key)) {
|
|
300
|
-
|
|
301
|
-
return v * FIBONACCI_HASH_C;
|
|
325
|
+
return hash_fixnum(key);
|
|
302
326
|
}
|
|
327
|
+
return ((uint64_t)SYM2ID(key) ^ swiss_hash_seed0) * FIBONACCI_HASH_C;
|
|
328
|
+
}
|
|
303
329
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
330
|
+
SH_ALWAYS_INLINE uint64_t hash_string(VALUE key) {
|
|
331
|
+
const char *ptr = RSTRING_PTR(key);
|
|
332
|
+
long len = RSTRING_LEN(key);
|
|
333
|
+
uint64_t seed = swiss_hash_seed0;
|
|
334
|
+
|
|
335
|
+
if (len > 0) {
|
|
336
|
+
int enc = ENCODING_GET(key);
|
|
337
|
+
if (enc) {
|
|
338
|
+
int cr = ENC_CODERANGE(key);
|
|
339
|
+
if (cr == ENC_CODERANGE_UNKNOWN) {
|
|
340
|
+
cr = rb_enc_str_coderange(key);
|
|
341
|
+
}
|
|
342
|
+
if (cr != ENC_CODERANGE_7BIT) {
|
|
343
|
+
seed ^= (uint64_t)enc;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return wyhash(ptr ? ptr : (const char *)"", (size_t)len, seed);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
SH_ALWAYS_INLINE uint64_t compute_hash(VALUE key) {
|
|
351
|
+
if (FIXNUM_P(key) || SYMBOL_P(key)) {
|
|
352
|
+
return hash_immediate(key);
|
|
307
353
|
}
|
|
308
354
|
|
|
309
355
|
if (RB_TYPE_P(key, T_STRING)) {
|
|
310
|
-
|
|
311
|
-
long len = RSTRING_LEN(key);
|
|
312
|
-
int enc_idx = ENCODING_GET(key);
|
|
313
|
-
uint64_t str_seed = swiss_hash_seed0 ^ (uint64_t)enc_idx;
|
|
314
|
-
return wyhash(ptr ? ptr : (const char *)"", (size_t)len, str_seed);
|
|
356
|
+
return hash_string(key);
|
|
315
357
|
}
|
|
316
358
|
|
|
317
|
-
v = (uint64_t)NUM2LONG(rb_hash(key));
|
|
359
|
+
uint64_t v = (uint64_t)NUM2LONG(rb_hash(key));
|
|
318
360
|
v ^= swiss_hash_seed1;
|
|
319
361
|
v ^= v >> 33;
|
|
320
362
|
v *= 0xff51afd7ed558ccdULL;
|
|
@@ -327,56 +369,92 @@ static inline uint64_t compute_hash(VALUE key) {
|
|
|
327
369
|
#define H1(hash) ((hash) >> 7)
|
|
328
370
|
#define H2(hash) ((uint8_t)((hash) & H2_MASK))
|
|
329
371
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
if (
|
|
372
|
+
SH_ALWAYS_INLINE int strings_equal(VALUE a, VALUE b) {
|
|
373
|
+
long la = RSTRING_LEN(a);
|
|
374
|
+
if (la != RSTRING_LEN(b))
|
|
375
|
+
return 0;
|
|
333
376
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
const char *pb = RSTRING_PTR(b);
|
|
339
|
-
if (pa == pb) return 1;
|
|
377
|
+
const char *pa = RSTRING_PTR(a);
|
|
378
|
+
const char *pb = RSTRING_PTR(b);
|
|
379
|
+
if (pa == pb)
|
|
380
|
+
return 1;
|
|
340
381
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
382
|
+
int ea = ENCODING_GET(a);
|
|
383
|
+
int eb = ENCODING_GET(b);
|
|
384
|
+
if (ea == eb) {
|
|
385
|
+
return memcmp(pa, pb, (size_t)la) == 0;
|
|
386
|
+
}
|
|
346
387
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
}
|
|
388
|
+
if (ENC_CODERANGE(a) == ENC_CODERANGE_7BIT && ENC_CODERANGE(b) == ENC_CODERANGE_7BIT) {
|
|
389
|
+
return memcmp(pa, pb, (size_t)la) == 0;
|
|
390
|
+
}
|
|
351
391
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
392
|
+
if (rb_enc_compatible(a, b)) {
|
|
393
|
+
return memcmp(pa, pb, (size_t)la) == 0;
|
|
394
|
+
}
|
|
395
|
+
return rb_eql(a, b);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
SH_ALWAYS_INLINE int keys_equal(VALUE a, VALUE b) {
|
|
399
|
+
if (a == b)
|
|
400
|
+
return 1;
|
|
401
|
+
if (FIXNUM_P(a) || SYMBOL_P(a) || SPECIAL_CONST_P(a))
|
|
402
|
+
return 0;
|
|
403
|
+
|
|
404
|
+
if (RB_TYPE_P(a, T_STRING) && RB_TYPE_P(b, T_STRING)) {
|
|
405
|
+
return strings_equal(a, b);
|
|
356
406
|
}
|
|
357
407
|
|
|
358
408
|
return rb_eql(a, b);
|
|
359
409
|
}
|
|
360
410
|
|
|
361
|
-
|
|
411
|
+
#ifndef SWISS_HASH_COPY_STRING_KEYS
|
|
412
|
+
#define SWISS_HASH_COPY_STRING_KEYS 1
|
|
413
|
+
#endif
|
|
414
|
+
|
|
415
|
+
SH_ALWAYS_INLINE VALUE prepare_key(VALUE key) {
|
|
362
416
|
if (RB_TYPE_P(key, T_STRING)) {
|
|
417
|
+
#if SWISS_HASH_COPY_STRING_KEYS
|
|
363
418
|
if (!OBJ_FROZEN(key)) {
|
|
364
419
|
key = rb_str_new_frozen(key);
|
|
365
420
|
}
|
|
366
|
-
|
|
367
|
-
rb_enc_str_coderange(key);
|
|
421
|
+
#endif
|
|
368
422
|
}
|
|
369
423
|
return key;
|
|
370
424
|
}
|
|
371
425
|
|
|
426
|
+
SH_ALWAYS_INLINE size_t swiss_ctrl_bytes(size_t capacity) {
|
|
427
|
+
return (capacity + 15u) & ~(size_t)15u;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
static int swiss_alloc_block(size_t capacity, uint8_t **ctrl, Slot **slots) {
|
|
431
|
+
if (capacity == 0 || capacity > (SIZE_MAX / (sizeof(Slot) + 16))) {
|
|
432
|
+
return 0;
|
|
433
|
+
}
|
|
434
|
+
size_t ctrl_bytes = swiss_ctrl_bytes(capacity);
|
|
435
|
+
if (capacity > (SIZE_MAX - ctrl_bytes) / sizeof(Slot)) {
|
|
436
|
+
return 0;
|
|
437
|
+
}
|
|
438
|
+
uint8_t *block = (uint8_t *)malloc(ctrl_bytes + capacity * sizeof(Slot));
|
|
439
|
+
if (!block) {
|
|
440
|
+
return 0;
|
|
441
|
+
}
|
|
442
|
+
*ctrl = block;
|
|
443
|
+
*slots = (Slot *)(block + ctrl_bytes);
|
|
444
|
+
return 1;
|
|
445
|
+
}
|
|
446
|
+
|
|
372
447
|
static void swiss_free_arrays(SwissHash *sh) {
|
|
373
448
|
free(sh->ctrl);
|
|
374
449
|
sh->ctrl = NULL;
|
|
375
|
-
free(sh->slots);
|
|
376
450
|
sh->slots = NULL;
|
|
377
451
|
}
|
|
378
452
|
|
|
379
453
|
static void swiss_init(SwissHash *sh, size_t min_capacity) {
|
|
454
|
+
if (sh->ctrl) {
|
|
455
|
+
swiss_free_arrays(sh);
|
|
456
|
+
}
|
|
457
|
+
|
|
380
458
|
size_t min_groups = (min_capacity + GROUP_SIZE - 1) / GROUP_SIZE;
|
|
381
459
|
size_t num_groups = 1;
|
|
382
460
|
while (num_groups < min_groups)
|
|
@@ -385,7 +463,22 @@ static void swiss_init(SwissHash *sh, size_t min_capacity) {
|
|
|
385
463
|
num_groups = 2;
|
|
386
464
|
|
|
387
465
|
size_t capacity = num_groups * GROUP_SIZE;
|
|
466
|
+
uint8_t *ctrl = NULL;
|
|
467
|
+
Slot *slots = NULL;
|
|
468
|
+
|
|
469
|
+
if (!swiss_alloc_block(capacity, &ctrl, &slots)) {
|
|
470
|
+
sh->capacity = 0;
|
|
471
|
+
sh->num_groups = 0;
|
|
472
|
+
sh->group_mask = 0;
|
|
473
|
+
sh->size = 0;
|
|
474
|
+
sh->growth_left = 0;
|
|
475
|
+
sh->tombstone_count = 0;
|
|
476
|
+
sh->mutating = 0;
|
|
477
|
+
rb_raise(rb_eNoMemError, "failed to allocate SwissHash");
|
|
478
|
+
}
|
|
388
479
|
|
|
480
|
+
sh->ctrl = ctrl;
|
|
481
|
+
sh->slots = slots;
|
|
389
482
|
sh->num_groups = num_groups;
|
|
390
483
|
sh->group_mask = num_groups - 1;
|
|
391
484
|
sh->capacity = capacity;
|
|
@@ -393,18 +486,6 @@ static void swiss_init(SwissHash *sh, size_t min_capacity) {
|
|
|
393
486
|
sh->tombstone_count = 0;
|
|
394
487
|
sh->mutating = 0;
|
|
395
488
|
sh->growth_left = capacity * MAX_LOAD_NUM / MAX_LOAD_DEN;
|
|
396
|
-
|
|
397
|
-
sh->ctrl = (uint8_t *)malloc(capacity);
|
|
398
|
-
sh->slots = (Slot *)malloc(capacity * sizeof(Slot));
|
|
399
|
-
|
|
400
|
-
if (!sh->ctrl || !sh->slots) {
|
|
401
|
-
free(sh->ctrl);
|
|
402
|
-
free(sh->slots);
|
|
403
|
-
sh->ctrl = NULL;
|
|
404
|
-
sh->slots = NULL;
|
|
405
|
-
rb_raise(rb_eNoMemError, "failed to allocate SwissHash");
|
|
406
|
-
}
|
|
407
|
-
|
|
408
489
|
memset(sh->ctrl, CTRL_EMPTY, capacity);
|
|
409
490
|
}
|
|
410
491
|
|
|
@@ -414,7 +495,7 @@ typedef struct {
|
|
|
414
495
|
size_t group_mask;
|
|
415
496
|
} ProbeSeq;
|
|
416
497
|
|
|
417
|
-
|
|
498
|
+
SH_ALWAYS_INLINE ProbeSeq probe_start(uint64_t h1, size_t group_mask) {
|
|
418
499
|
ProbeSeq ps;
|
|
419
500
|
ps.group_idx = (size_t)(h1)&group_mask;
|
|
420
501
|
ps.stride = 0;
|
|
@@ -422,40 +503,82 @@ static inline ProbeSeq probe_start(uint64_t h1, size_t group_mask) {
|
|
|
422
503
|
return ps;
|
|
423
504
|
}
|
|
424
505
|
|
|
425
|
-
|
|
506
|
+
SH_ALWAYS_INLINE void probe_next(ProbeSeq *ps) {
|
|
426
507
|
ps->stride++;
|
|
427
508
|
ps->group_idx = (ps->group_idx + ps->stride) & ps->group_mask;
|
|
428
509
|
}
|
|
429
510
|
|
|
430
511
|
#define GROUP_OFF(gi) ((gi) * GROUP_SIZE)
|
|
431
512
|
|
|
432
|
-
|
|
433
|
-
|
|
513
|
+
#ifdef SWISS_USE_VEC
|
|
514
|
+
#define SH_SCAN_GROUP(sh, off, h2, match, empty) \
|
|
515
|
+
do { \
|
|
516
|
+
ctrl_vec_t cv = ctrl_load((sh)->ctrl + (off)); \
|
|
517
|
+
SH_PREFETCH(&(sh)->slots[off]); \
|
|
518
|
+
(match) = ctrl_match_h2_vec(cv, (h2)); \
|
|
519
|
+
(empty) = ctrl_match_empty_vec(cv); \
|
|
520
|
+
} while (0)
|
|
521
|
+
#define SH_SCAN_GROUP_INSERT(sh, off, h2, match, empty, avail) \
|
|
522
|
+
do { \
|
|
523
|
+
ctrl_vec_t cv = ctrl_load((sh)->ctrl + (off)); \
|
|
524
|
+
SH_PREFETCH(&(sh)->slots[off]); \
|
|
525
|
+
(match) = ctrl_match_h2_vec(cv, (h2)); \
|
|
526
|
+
(empty) = ctrl_match_empty_vec(cv); \
|
|
527
|
+
(avail) = ctrl_match_empty_or_deleted_vec(cv); \
|
|
528
|
+
} while (0)
|
|
529
|
+
#else
|
|
530
|
+
#define SH_SCAN_GROUP(sh, off, h2, match, empty) \
|
|
531
|
+
do { \
|
|
532
|
+
SH_PREFETCH(&(sh)->slots[off]); \
|
|
533
|
+
(match) = ctrl_match_h2_raw((sh)->ctrl + (off), (h2)); \
|
|
534
|
+
(empty) = ctrl_match_empty_raw((sh)->ctrl + (off)); \
|
|
535
|
+
} while (0)
|
|
536
|
+
#define SH_SCAN_GROUP_INSERT(sh, off, h2, match, empty, avail) \
|
|
537
|
+
do { \
|
|
538
|
+
SH_PREFETCH(&(sh)->slots[off]); \
|
|
539
|
+
(match) = ctrl_match_h2_raw((sh)->ctrl + (off), (h2)); \
|
|
540
|
+
(empty) = ctrl_match_empty_raw((sh)->ctrl + (off)); \
|
|
541
|
+
(avail) = ctrl_match_empty_or_deleted_raw((sh)->ctrl + (off)); \
|
|
542
|
+
} while (0)
|
|
543
|
+
#endif
|
|
544
|
+
|
|
545
|
+
static inline VALUE *swiss_lookup_hashed(SwissHash *sh, VALUE key, uint64_t hash, int identity_only) {
|
|
434
546
|
uint8_t h2 = H2(hash);
|
|
435
547
|
ProbeSeq ps = probe_start(H1(hash), sh->group_mask);
|
|
436
548
|
|
|
437
549
|
for (;;) {
|
|
438
550
|
size_t off = GROUP_OFF(ps.group_idx);
|
|
551
|
+
ctrl_mask_t match, empty;
|
|
552
|
+
SH_SCAN_GROUP(sh, off, h2, match, empty);
|
|
553
|
+
|
|
554
|
+
while (match) {
|
|
555
|
+
int slot = CTRL_MASK_SLOT(match);
|
|
556
|
+
Slot *s = &sh->slots[off + slot];
|
|
557
|
+
if (RB_LIKELY(s->key == key) || (!identity_only && keys_equal(s->key, key))) {
|
|
558
|
+
return &s->value;
|
|
559
|
+
}
|
|
560
|
+
match &= match - 1;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (empty)
|
|
564
|
+
return NULL;
|
|
565
|
+
probe_next(&ps);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
static inline VALUE *swiss_lookup_string(SwissHash *sh, VALUE key, uint64_t hash) {
|
|
570
|
+
uint8_t h2 = H2(hash);
|
|
571
|
+
ProbeSeq ps = probe_start(H1(hash), sh->group_mask);
|
|
572
|
+
|
|
573
|
+
for (;;) {
|
|
574
|
+
size_t off = GROUP_OFF(ps.group_idx);
|
|
575
|
+
ctrl_mask_t match, empty;
|
|
576
|
+
SH_SCAN_GROUP(sh, off, h2, match, empty);
|
|
439
577
|
|
|
440
|
-
#if defined(SWISS_USE_SSE2)
|
|
441
|
-
__m128i cv = ctrl_load(sh->ctrl + off);
|
|
442
|
-
SH_PREFETCH(&sh->slots[off]);
|
|
443
|
-
uint32_t match = ctrl_match_h2_vec(cv, h2);
|
|
444
|
-
uint32_t empty = ctrl_match_empty_vec(cv);
|
|
445
|
-
#elif defined(SWISS_USE_NEON)
|
|
446
|
-
uint8x8_t cv = ctrl_load(sh->ctrl + off);
|
|
447
|
-
SH_PREFETCH(&sh->slots[off]);
|
|
448
|
-
uint32_t match = ctrl_match_h2_vec(cv, h2);
|
|
449
|
-
uint32_t empty = ctrl_match_empty_vec(cv);
|
|
450
|
-
#else
|
|
451
|
-
SH_PREFETCH(&sh->slots[off]);
|
|
452
|
-
uint32_t match = ctrl_match_h2_raw(sh->ctrl + off, h2);
|
|
453
|
-
uint32_t empty = ctrl_match_empty_raw(sh->ctrl + off);
|
|
454
|
-
#endif
|
|
455
578
|
while (match) {
|
|
456
|
-
int slot =
|
|
579
|
+
int slot = CTRL_MASK_SLOT(match);
|
|
457
580
|
Slot *s = &sh->slots[off + slot];
|
|
458
|
-
if (
|
|
581
|
+
if (RB_LIKELY(s->key == key) || (RB_TYPE_P(s->key, T_STRING) && strings_equal(s->key, key))) {
|
|
459
582
|
return &s->value;
|
|
460
583
|
}
|
|
461
584
|
match &= match - 1;
|
|
@@ -467,23 +590,75 @@ static VALUE *swiss_lookup(SwissHash *sh, VALUE key) {
|
|
|
467
590
|
}
|
|
468
591
|
}
|
|
469
592
|
|
|
593
|
+
typedef struct {
|
|
594
|
+
SwissHash *sh;
|
|
595
|
+
VALUE key;
|
|
596
|
+
VALUE value;
|
|
597
|
+
VALUE result;
|
|
598
|
+
VALUE *found;
|
|
599
|
+
} CallbackCtx;
|
|
600
|
+
|
|
601
|
+
SH_ALWAYS_INLINE int key_may_callback(VALUE key) {
|
|
602
|
+
return !(FIXNUM_P(key) || SYMBOL_P(key) || RB_TYPE_P(key, T_STRING));
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
static void swiss_lock(SwissHash *sh) {
|
|
606
|
+
if (sh->mutating) {
|
|
607
|
+
rb_raise(rb_eRuntimeError, "SwissHash: reentrant modification detected "
|
|
608
|
+
"(#hash or #eql? callback modified the same table)");
|
|
609
|
+
}
|
|
610
|
+
sh->mutating = 1;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
static VALUE swiss_unlock_ensure(VALUE shv) {
|
|
614
|
+
((SwissHash *)shv)->mutating = 0;
|
|
615
|
+
return Qnil;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
static VALUE swiss_lookup_custom(VALUE arg) {
|
|
619
|
+
CallbackCtx *c = (CallbackCtx *)arg;
|
|
620
|
+
c->found = swiss_lookup_hashed(c->sh, c->key, compute_hash(c->key), 0);
|
|
621
|
+
return Qnil;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
static inline VALUE *swiss_lookup(SwissHash *sh, VALUE key) {
|
|
625
|
+
if (FIXNUM_P(key)) {
|
|
626
|
+
return swiss_lookup_hashed(sh, key, hash_fixnum(key), 1);
|
|
627
|
+
}
|
|
628
|
+
if (SYMBOL_P(key)) {
|
|
629
|
+
return swiss_lookup_hashed(sh, key, hash_immediate(key), 1);
|
|
630
|
+
}
|
|
631
|
+
if (RB_TYPE_P(key, T_STRING)) {
|
|
632
|
+
return swiss_lookup_string(sh, key, hash_string(key));
|
|
633
|
+
}
|
|
634
|
+
if (sh->mutating) {
|
|
635
|
+
return swiss_lookup_hashed(sh, key, compute_hash(key), 0);
|
|
636
|
+
}
|
|
637
|
+
CallbackCtx ctx = {sh, key, Qnil, Qnil, NULL};
|
|
638
|
+
swiss_lock(sh);
|
|
639
|
+
rb_ensure(swiss_lookup_custom, (VALUE)&ctx, swiss_unlock_ensure, (VALUE)sh);
|
|
640
|
+
return ctx.found;
|
|
641
|
+
}
|
|
642
|
+
|
|
470
643
|
static void swiss_grow(SwissHash *sh);
|
|
471
644
|
static void swiss_compact(SwissHash *sh);
|
|
472
645
|
|
|
473
|
-
|
|
646
|
+
SH_ALWAYS_INLINE void swiss_insert_rehash_into(uint8_t *ctrl, Slot *slots, size_t group_mask,
|
|
647
|
+
size_t *size, size_t *growth_left,
|
|
648
|
+
uint64_t hash, VALUE key, VALUE value) {
|
|
474
649
|
uint8_t h2 = H2(hash);
|
|
475
|
-
ProbeSeq ps = probe_start(H1(hash),
|
|
650
|
+
ProbeSeq ps = probe_start(H1(hash), group_mask);
|
|
476
651
|
|
|
477
652
|
for (;;) {
|
|
478
653
|
size_t off = GROUP_OFF(ps.group_idx);
|
|
479
|
-
|
|
654
|
+
ctrl_mask_t empty_mask = ctrl_match_empty(ctrl + off);
|
|
480
655
|
if (empty_mask) {
|
|
481
|
-
size_t idx = off +
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
656
|
+
size_t idx = off + (size_t)CTRL_MASK_SLOT(empty_mask);
|
|
657
|
+
ctrl[idx] = h2;
|
|
658
|
+
slots[idx].key = key;
|
|
659
|
+
slots[idx].value = value;
|
|
660
|
+
(*size)++;
|
|
661
|
+
(*growth_left)--;
|
|
487
662
|
return;
|
|
488
663
|
}
|
|
489
664
|
probe_next(&ps);
|
|
@@ -494,44 +669,87 @@ static inline int should_compact(SwissHash *sh) {
|
|
|
494
669
|
return sh->tombstone_count >= sh->capacity / TOMBSTONE_COMPACT_DIVISOR;
|
|
495
670
|
}
|
|
496
671
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
uint8_t *old_ctrl
|
|
502
|
-
Slot *old_slots
|
|
672
|
+
typedef struct {
|
|
673
|
+
SwissHash *sh;
|
|
674
|
+
uint8_t *new_ctrl;
|
|
675
|
+
Slot *new_slots;
|
|
676
|
+
const uint8_t *old_ctrl;
|
|
677
|
+
const Slot *old_slots;
|
|
678
|
+
size_t old_cap;
|
|
679
|
+
size_t new_cap;
|
|
680
|
+
size_t new_num_groups;
|
|
681
|
+
} RebuildCtx;
|
|
682
|
+
|
|
683
|
+
static VALUE swiss_rebuild_body(VALUE arg) {
|
|
684
|
+
RebuildCtx *ctx = (RebuildCtx *)arg;
|
|
685
|
+
size_t group_mask = ctx->new_num_groups - 1;
|
|
686
|
+
size_t size = 0;
|
|
687
|
+
size_t growth_left = ctx->new_cap * MAX_LOAD_NUM / MAX_LOAD_DEN;
|
|
688
|
+
|
|
689
|
+
for (size_t i = 0; i < ctx->old_cap; i++) {
|
|
690
|
+
uint8_t c = ctx->old_ctrl[i];
|
|
691
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
692
|
+
VALUE key = ctx->old_slots[i].key;
|
|
693
|
+
uint64_t hash = FIXNUM_P(key) ? hash_fixnum(key) : compute_hash(key);
|
|
694
|
+
swiss_insert_rehash_into(ctx->new_ctrl, ctx->new_slots, group_mask,
|
|
695
|
+
&size, &growth_left, hash, key, ctx->old_slots[i].value);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
503
698
|
|
|
504
|
-
sh->ctrl
|
|
505
|
-
sh->
|
|
699
|
+
free(ctx->sh->ctrl);
|
|
700
|
+
ctx->sh->ctrl = ctx->new_ctrl;
|
|
701
|
+
ctx->sh->slots = ctx->new_slots;
|
|
702
|
+
ctx->sh->num_groups = ctx->new_num_groups;
|
|
703
|
+
ctx->sh->group_mask = group_mask;
|
|
704
|
+
ctx->sh->capacity = ctx->new_cap;
|
|
705
|
+
ctx->sh->size = size;
|
|
706
|
+
ctx->sh->growth_left = growth_left;
|
|
707
|
+
ctx->sh->tombstone_count = 0;
|
|
708
|
+
ctx->new_ctrl = NULL;
|
|
709
|
+
ctx->new_slots = NULL;
|
|
710
|
+
return Qnil;
|
|
711
|
+
}
|
|
506
712
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
sh->growth_left = 0;
|
|
514
|
-
rb_raise(rb_eNoMemError, "failed to compact SwissHash");
|
|
713
|
+
static VALUE swiss_rebuild_ensure(VALUE arg) {
|
|
714
|
+
RebuildCtx *ctx = (RebuildCtx *)arg;
|
|
715
|
+
if (ctx->new_ctrl) {
|
|
716
|
+
free(ctx->new_ctrl);
|
|
717
|
+
ctx->new_ctrl = NULL;
|
|
718
|
+
ctx->new_slots = NULL;
|
|
515
719
|
}
|
|
720
|
+
return Qnil;
|
|
721
|
+
}
|
|
516
722
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
sh
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
723
|
+
static void swiss_rebuild(SwissHash *sh, size_t new_num_groups) {
|
|
724
|
+
RebuildCtx ctx;
|
|
725
|
+
memset(&ctx, 0, sizeof(ctx));
|
|
726
|
+
ctx.sh = sh;
|
|
727
|
+
ctx.old_ctrl = sh->ctrl;
|
|
728
|
+
ctx.old_slots = sh->slots;
|
|
729
|
+
ctx.old_cap = sh->capacity;
|
|
730
|
+
ctx.new_num_groups = new_num_groups;
|
|
731
|
+
ctx.new_cap = new_num_groups * GROUP_SIZE;
|
|
732
|
+
|
|
733
|
+
if (!swiss_alloc_block(ctx.new_cap, &ctx.new_ctrl, &ctx.new_slots)) {
|
|
734
|
+
rb_raise(rb_eNoMemError, "failed to rebuild SwissHash");
|
|
528
735
|
}
|
|
736
|
+
memset(ctx.new_ctrl, CTRL_EMPTY, ctx.new_cap);
|
|
737
|
+
rb_ensure(swiss_rebuild_body, (VALUE)&ctx, swiss_rebuild_ensure, (VALUE)&ctx);
|
|
738
|
+
}
|
|
529
739
|
|
|
530
|
-
|
|
531
|
-
|
|
740
|
+
SH_ALWAYS_INLINE void swiss_place_new(SwissHash *sh, size_t insert_idx, uint8_t h2, VALUE key, VALUE value) {
|
|
741
|
+
if (sh->ctrl[insert_idx] == CTRL_EMPTY) {
|
|
742
|
+
sh->growth_left--;
|
|
743
|
+
} else {
|
|
744
|
+
sh->tombstone_count--;
|
|
745
|
+
}
|
|
746
|
+
sh->ctrl[insert_idx] = h2;
|
|
747
|
+
sh->slots[insert_idx].key = key;
|
|
748
|
+
sh->slots[insert_idx].value = value;
|
|
749
|
+
sh->size++;
|
|
532
750
|
}
|
|
533
751
|
|
|
534
|
-
static VALUE
|
|
752
|
+
static VALUE swiss_insert_hashed(SwissHash *sh, VALUE key, VALUE value, uint64_t hash, int identity_only, int may_callback) {
|
|
535
753
|
if (sh->growth_left == 0) {
|
|
536
754
|
if (should_compact(sh)) {
|
|
537
755
|
swiss_compact(sh);
|
|
@@ -540,44 +758,41 @@ static VALUE swiss_insert(SwissHash *sh, VALUE key, VALUE value) {
|
|
|
540
758
|
}
|
|
541
759
|
}
|
|
542
760
|
|
|
543
|
-
uint64_t hash = compute_hash(key);
|
|
544
761
|
uint8_t h2 = H2(hash);
|
|
545
762
|
ProbeSeq ps = probe_start(H1(hash), sh->group_mask);
|
|
546
|
-
|
|
547
763
|
size_t insert_idx = (size_t)-1;
|
|
548
764
|
|
|
549
765
|
for (;;) {
|
|
550
766
|
size_t off = GROUP_OFF(ps.group_idx);
|
|
551
|
-
|
|
552
|
-
#
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
SH_PREFETCH(&sh->slots[off]);
|
|
561
|
-
uint32_t match = ctrl_match_h2_vec(cv, h2);
|
|
562
|
-
uint32_t empty = ctrl_match_empty_vec(cv);
|
|
563
|
-
uint32_t avail = ctrl_match_empty_or_deleted_vec(cv);
|
|
767
|
+
ctrl_mask_t match, empty, avail;
|
|
768
|
+
#ifdef SWISS_USE_VEC
|
|
769
|
+
{
|
|
770
|
+
ctrl_vec_t cv = ctrl_load(sh->ctrl + off);
|
|
771
|
+
SH_PREFETCH(&sh->slots[off]);
|
|
772
|
+
match = ctrl_match_h2_vec(cv, h2);
|
|
773
|
+
empty = ctrl_match_empty_vec(cv);
|
|
774
|
+
avail = (sh->tombstone_count == 0) ? empty : ctrl_match_empty_or_deleted_vec(cv);
|
|
775
|
+
}
|
|
564
776
|
#else
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
777
|
+
SH_SCAN_GROUP_INSERT(sh, off, h2, match, empty, avail);
|
|
778
|
+
if (sh->tombstone_count == 0) {
|
|
779
|
+
avail = empty;
|
|
780
|
+
}
|
|
568
781
|
#endif
|
|
782
|
+
|
|
569
783
|
while (match) {
|
|
570
|
-
int slot =
|
|
784
|
+
int slot = CTRL_MASK_SLOT(match);
|
|
571
785
|
size_t idx = off + slot;
|
|
572
|
-
|
|
573
|
-
|
|
786
|
+
Slot *s = &sh->slots[idx];
|
|
787
|
+
if (RB_LIKELY(s->key == key) || (!identity_only && keys_equal(s->key, key))) {
|
|
788
|
+
s->value = value;
|
|
574
789
|
return value;
|
|
575
790
|
}
|
|
576
791
|
match &= match - 1;
|
|
577
792
|
}
|
|
578
793
|
|
|
579
794
|
if (insert_idx == (size_t)-1 && avail) {
|
|
580
|
-
insert_idx = off +
|
|
795
|
+
insert_idx = off + (size_t)CTRL_MASK_SLOT(avail);
|
|
581
796
|
}
|
|
582
797
|
|
|
583
798
|
if (empty)
|
|
@@ -585,112 +800,172 @@ static VALUE swiss_insert(SwissHash *sh, VALUE key, VALUE value) {
|
|
|
585
800
|
probe_next(&ps);
|
|
586
801
|
}
|
|
587
802
|
|
|
588
|
-
|
|
803
|
+
swiss_place_new(sh, insert_idx, h2, key, value);
|
|
804
|
+
return value;
|
|
805
|
+
}
|
|
589
806
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
807
|
+
SH_ALWAYS_INLINE VALUE swiss_insert_fixnum(SwissHash *sh, VALUE key, VALUE value) {
|
|
808
|
+
if (RB_UNLIKELY(sh->growth_left == 0)) {
|
|
809
|
+
if (should_compact(sh)) {
|
|
810
|
+
swiss_compact(sh);
|
|
811
|
+
} else {
|
|
812
|
+
swiss_grow(sh);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
uint64_t hash = hash_fixnum(key);
|
|
817
|
+
uint8_t h2 = H2(hash);
|
|
818
|
+
ProbeSeq ps = probe_start(H1(hash), sh->group_mask);
|
|
819
|
+
size_t insert_idx = (size_t)-1;
|
|
820
|
+
|
|
821
|
+
for (;;) {
|
|
822
|
+
size_t off = GROUP_OFF(ps.group_idx);
|
|
823
|
+
ctrl_mask_t match, empty, avail;
|
|
824
|
+
#ifdef SWISS_USE_VEC
|
|
825
|
+
ctrl_vec_t cv = ctrl_load(sh->ctrl + off);
|
|
826
|
+
SH_PREFETCH(&sh->slots[off]);
|
|
827
|
+
match = ctrl_match_h2_vec(cv, h2);
|
|
828
|
+
empty = ctrl_match_empty_vec(cv);
|
|
829
|
+
avail = (sh->tombstone_count == 0) ? empty : ctrl_match_empty_or_deleted_vec(cv);
|
|
830
|
+
#else
|
|
831
|
+
SH_SCAN_GROUP_INSERT(sh, off, h2, match, empty, avail);
|
|
832
|
+
if (sh->tombstone_count == 0) {
|
|
833
|
+
avail = empty;
|
|
834
|
+
}
|
|
835
|
+
#endif
|
|
836
|
+
while (match) {
|
|
837
|
+
int slot = CTRL_MASK_SLOT(match);
|
|
838
|
+
if (RB_LIKELY(sh->slots[off + slot].key == key)) {
|
|
839
|
+
sh->slots[off + slot].value = value;
|
|
840
|
+
return value;
|
|
841
|
+
}
|
|
842
|
+
match &= match - 1;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
if (insert_idx == (size_t)-1 && avail) {
|
|
846
|
+
insert_idx = off + (size_t)CTRL_MASK_SLOT(avail);
|
|
847
|
+
}
|
|
848
|
+
if (RB_LIKELY(empty))
|
|
849
|
+
break;
|
|
850
|
+
probe_next(&ps);
|
|
594
851
|
}
|
|
595
|
-
sh->ctrl[insert_idx] = h2;
|
|
596
|
-
sh->slots[insert_idx].key = key;
|
|
597
|
-
sh->slots[insert_idx].value = value;
|
|
598
|
-
sh->size++;
|
|
599
852
|
|
|
600
|
-
|
|
853
|
+
swiss_place_new(sh, insert_idx, h2, key, value);
|
|
601
854
|
return value;
|
|
602
855
|
}
|
|
603
856
|
|
|
604
|
-
static VALUE
|
|
857
|
+
static VALUE swiss_insert_unlocked(VALUE arg) {
|
|
858
|
+
CallbackCtx *c = (CallbackCtx *)arg;
|
|
859
|
+
if (FIXNUM_P(c->key)) {
|
|
860
|
+
c->result = swiss_insert_fixnum(c->sh, c->key, c->value);
|
|
861
|
+
} else if (SYMBOL_P(c->key)) {
|
|
862
|
+
c->result = swiss_insert_hashed(c->sh, c->key, c->value, hash_immediate(c->key), 1, 0);
|
|
863
|
+
} else if (RB_TYPE_P(c->key, T_STRING)) {
|
|
864
|
+
c->result = swiss_insert_hashed(c->sh, c->key, c->value, hash_string(c->key), 0, 0);
|
|
865
|
+
} else {
|
|
866
|
+
c->result = swiss_insert_hashed(c->sh, c->key, c->value, compute_hash(c->key), 0, 0);
|
|
867
|
+
}
|
|
868
|
+
return c->result;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
static VALUE swiss_insert(SwissHash *sh, VALUE key, VALUE value) {
|
|
872
|
+
if (sh->mutating) {
|
|
873
|
+
rb_raise(rb_eRuntimeError, "SwissHash: reentrant modification detected "
|
|
874
|
+
"(#hash or #eql? callback modified the same table)");
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
CallbackCtx ctx = {sh, key, value, Qnil, NULL};
|
|
878
|
+
if (!key_may_callback(key) && sh->growth_left != 0) {
|
|
879
|
+
return swiss_insert_unlocked((VALUE)&ctx);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
swiss_lock(sh);
|
|
883
|
+
rb_ensure(swiss_insert_unlocked, (VALUE)&ctx, swiss_unlock_ensure, (VALUE)sh);
|
|
884
|
+
return ctx.result;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
static VALUE swiss_delete_unlocked(VALUE arg) {
|
|
888
|
+
CallbackCtx *c = (CallbackCtx *)arg;
|
|
889
|
+
SwissHash *sh = c->sh;
|
|
890
|
+
VALUE key = c->key;
|
|
605
891
|
uint64_t hash = compute_hash(key);
|
|
892
|
+
int identity_only = FIXNUM_P(key) || SYMBOL_P(key);
|
|
606
893
|
uint8_t h2 = H2(hash);
|
|
607
894
|
ProbeSeq ps = probe_start(H1(hash), sh->group_mask);
|
|
608
895
|
|
|
609
896
|
for (;;) {
|
|
610
897
|
size_t off = GROUP_OFF(ps.group_idx);
|
|
898
|
+
ctrl_mask_t match, empty;
|
|
899
|
+
SH_SCAN_GROUP(sh, off, h2, match, empty);
|
|
611
900
|
|
|
612
|
-
#if defined(SWISS_USE_SSE2)
|
|
613
|
-
__m128i cv = ctrl_load(sh->ctrl + off);
|
|
614
|
-
SH_PREFETCH(&sh->slots[off]);
|
|
615
|
-
uint32_t match = ctrl_match_h2_vec(cv, h2);
|
|
616
|
-
uint32_t empty = ctrl_match_empty_vec(cv);
|
|
617
|
-
#elif defined(SWISS_USE_NEON)
|
|
618
|
-
uint8x8_t cv = ctrl_load(sh->ctrl + off);
|
|
619
|
-
SH_PREFETCH(&sh->slots[off]);
|
|
620
|
-
uint32_t match = ctrl_match_h2_vec(cv, h2);
|
|
621
|
-
uint32_t empty = ctrl_match_empty_vec(cv);
|
|
622
|
-
#else
|
|
623
|
-
SH_PREFETCH(&sh->slots[off]);
|
|
624
|
-
uint32_t match = ctrl_match_h2_raw(sh->ctrl + off, h2);
|
|
625
|
-
uint32_t empty = ctrl_match_empty_raw(sh->ctrl + off);
|
|
626
|
-
#endif
|
|
627
901
|
while (match) {
|
|
628
|
-
int slot =
|
|
902
|
+
int slot = CTRL_MASK_SLOT(match);
|
|
629
903
|
size_t idx = off + slot;
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
MUTATE_GUARD_BEGIN(sh);
|
|
904
|
+
Slot *s = &sh->slots[idx];
|
|
905
|
+
if (RB_LIKELY(s->key == key) || (!identity_only && keys_equal(s->key, key))) {
|
|
906
|
+
VALUE old_value = s->value;
|
|
634
907
|
sh->ctrl[idx] = CTRL_DELETED;
|
|
635
908
|
sh->slots[idx].key = Qnil;
|
|
636
909
|
sh->slots[idx].value = Qnil;
|
|
637
910
|
sh->size--;
|
|
638
911
|
sh->tombstone_count++;
|
|
639
|
-
|
|
640
|
-
|
|
912
|
+
c->result = old_value;
|
|
641
913
|
return old_value;
|
|
642
914
|
}
|
|
643
915
|
match &= match - 1;
|
|
644
916
|
}
|
|
645
917
|
|
|
646
|
-
if (empty)
|
|
918
|
+
if (empty) {
|
|
919
|
+
c->result = Qnil;
|
|
647
920
|
return Qnil;
|
|
921
|
+
}
|
|
648
922
|
probe_next(&ps);
|
|
649
923
|
}
|
|
650
924
|
}
|
|
651
925
|
|
|
652
|
-
static
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
uint8_t *old_ctrl = sh->ctrl;
|
|
657
|
-
Slot *old_slots = sh->slots;
|
|
658
|
-
|
|
659
|
-
size_t new_num_groups = sh->num_groups * 2;
|
|
660
|
-
size_t new_cap = new_num_groups * GROUP_SIZE;
|
|
661
|
-
|
|
662
|
-
sh->ctrl = (uint8_t *)malloc(new_cap);
|
|
663
|
-
sh->slots = (Slot *)malloc(new_cap * sizeof(Slot));
|
|
664
|
-
|
|
665
|
-
if (!sh->ctrl || !sh->slots) {
|
|
666
|
-
free(sh->ctrl);
|
|
667
|
-
free(sh->slots);
|
|
668
|
-
sh->ctrl = old_ctrl;
|
|
669
|
-
sh->slots = old_slots;
|
|
670
|
-
sh->size = old_size;
|
|
671
|
-
sh->growth_left = 0;
|
|
672
|
-
rb_raise(rb_eNoMemError, "failed to grow SwissHash");
|
|
926
|
+
static VALUE swiss_delete(SwissHash *sh, VALUE key) {
|
|
927
|
+
if (sh->mutating) {
|
|
928
|
+
rb_raise(rb_eRuntimeError, "SwissHash: reentrant modification detected "
|
|
929
|
+
"(#hash or #eql? callback modified the same table)");
|
|
673
930
|
}
|
|
674
931
|
|
|
675
|
-
|
|
932
|
+
CallbackCtx ctx = {sh, key, Qnil, Qnil, NULL};
|
|
933
|
+
if (!key_may_callback(key)) {
|
|
934
|
+
return swiss_delete_unlocked((VALUE)&ctx);
|
|
935
|
+
}
|
|
676
936
|
|
|
677
|
-
sh
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
sh->growth_left = new_cap * MAX_LOAD_NUM / MAX_LOAD_DEN;
|
|
682
|
-
sh->tombstone_count = 0;
|
|
937
|
+
swiss_lock(sh);
|
|
938
|
+
rb_ensure(swiss_delete_unlocked, (VALUE)&ctx, swiss_unlock_ensure, (VALUE)sh);
|
|
939
|
+
return ctx.result;
|
|
940
|
+
}
|
|
683
941
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
swiss_insert_rehash(sh, hash, old_slots[i].key, old_slots[i].value);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
942
|
+
typedef struct {
|
|
943
|
+
SwissHash *sh;
|
|
944
|
+
size_t new_num_groups;
|
|
945
|
+
} RebuildCall;
|
|
691
946
|
|
|
692
|
-
|
|
693
|
-
|
|
947
|
+
static VALUE swiss_rebuild_call(VALUE arg) {
|
|
948
|
+
RebuildCall *c = (RebuildCall *)arg;
|
|
949
|
+
swiss_rebuild(c->sh, c->new_num_groups);
|
|
950
|
+
return Qnil;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
static void swiss_rebuild_locked(SwissHash *sh, size_t new_num_groups) {
|
|
954
|
+
RebuildCall call = {sh, new_num_groups};
|
|
955
|
+
if (sh->mutating) {
|
|
956
|
+
swiss_rebuild(sh, new_num_groups);
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
swiss_lock(sh);
|
|
960
|
+
rb_ensure(swiss_rebuild_call, (VALUE)&call, swiss_unlock_ensure, (VALUE)sh);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
static void swiss_grow(SwissHash *sh) {
|
|
964
|
+
swiss_rebuild_locked(sh, sh->num_groups * 2);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
static void swiss_compact(SwissHash *sh) {
|
|
968
|
+
swiss_rebuild_locked(sh, sh->num_groups);
|
|
694
969
|
}
|
|
695
970
|
|
|
696
971
|
static void swiss_hash_mark(void *ptr) {
|
|
@@ -750,9 +1025,42 @@ static VALUE swiss_hash_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
750
1025
|
return self;
|
|
751
1026
|
}
|
|
752
1027
|
|
|
1028
|
+
static VALUE swiss_hash_initialize_copy(VALUE self, VALUE original) {
|
|
1029
|
+
if (self == original) {
|
|
1030
|
+
return self;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
SwissHash *src;
|
|
1034
|
+
SwissHash *dst;
|
|
1035
|
+
TypedData_Get_Struct(original, SwissHash, &swiss_hash_type, src);
|
|
1036
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, dst);
|
|
1037
|
+
|
|
1038
|
+
if (dst->ctrl || dst->slots) {
|
|
1039
|
+
swiss_free_arrays(dst);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
if (!src->ctrl || !src->slots) {
|
|
1043
|
+
swiss_init(dst, 16);
|
|
1044
|
+
return self;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
swiss_init(dst, src->capacity);
|
|
1048
|
+
memcpy(dst->ctrl, src->ctrl, src->capacity * sizeof(uint8_t));
|
|
1049
|
+
memcpy(dst->slots, src->slots, src->capacity * sizeof(Slot));
|
|
1050
|
+
dst->size = src->size;
|
|
1051
|
+
dst->growth_left = src->growth_left;
|
|
1052
|
+
dst->tombstone_count = src->tombstone_count;
|
|
1053
|
+
dst->mutating = 0;
|
|
1054
|
+
|
|
1055
|
+
return self;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
753
1058
|
static VALUE swiss_hash_aset(VALUE self, VALUE key, VALUE value) {
|
|
754
1059
|
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
755
|
-
if (
|
|
1060
|
+
if (FIXNUM_P(key)) {
|
|
1061
|
+
return swiss_insert_fixnum(sh, key, value);
|
|
1062
|
+
}
|
|
1063
|
+
if (RB_UNLIKELY(!SYMBOL_P(key))) {
|
|
756
1064
|
key = prepare_key(key);
|
|
757
1065
|
}
|
|
758
1066
|
return swiss_insert(sh, key, value);
|
|
@@ -760,7 +1068,12 @@ static VALUE swiss_hash_aset(VALUE self, VALUE key, VALUE value) {
|
|
|
760
1068
|
|
|
761
1069
|
static VALUE swiss_hash_aref(VALUE self, VALUE key) {
|
|
762
1070
|
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
763
|
-
VALUE *val
|
|
1071
|
+
VALUE *val;
|
|
1072
|
+
if (FIXNUM_P(key)) {
|
|
1073
|
+
val = swiss_lookup_hashed(sh, key, hash_fixnum(key), 1);
|
|
1074
|
+
} else {
|
|
1075
|
+
val = swiss_lookup(sh, key);
|
|
1076
|
+
}
|
|
764
1077
|
return val ? *val : Qnil;
|
|
765
1078
|
}
|
|
766
1079
|
|
|
@@ -838,15 +1151,546 @@ static VALUE swiss_hash_values(VALUE self) {
|
|
|
838
1151
|
return ary;
|
|
839
1152
|
}
|
|
840
1153
|
|
|
1154
|
+
static VALUE swiss_hash_each_key(VALUE self) {
|
|
1155
|
+
SwissHash *sh;
|
|
1156
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1157
|
+
|
|
1158
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1159
|
+
|
|
1160
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1161
|
+
uint8_t c = sh->ctrl[i];
|
|
1162
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1163
|
+
rb_yield(sh->slots[i].key);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
return self;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
static VALUE swiss_hash_each_value(VALUE self) {
|
|
1171
|
+
SwissHash *sh;
|
|
1172
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1173
|
+
|
|
1174
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1175
|
+
|
|
1176
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1177
|
+
uint8_t c = sh->ctrl[i];
|
|
1178
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1179
|
+
rb_yield(sh->slots[i].value);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
return self;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
static VALUE swiss_hash_to_h(VALUE self) {
|
|
1187
|
+
SwissHash *sh;
|
|
1188
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1189
|
+
|
|
1190
|
+
VALUE hash = rb_hash_new();
|
|
1191
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1192
|
+
uint8_t c = sh->ctrl[i];
|
|
1193
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1194
|
+
rb_hash_aset(hash, sh->slots[i].key, sh->slots[i].value);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
return hash;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
static VALUE swiss_hash_to_a(VALUE self) {
|
|
1202
|
+
SwissHash *sh;
|
|
1203
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1204
|
+
|
|
1205
|
+
VALUE ary = rb_ary_new_capa(sh->size);
|
|
1206
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1207
|
+
uint8_t c = sh->ctrl[i];
|
|
1208
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1209
|
+
VALUE pair = rb_ary_new_capa(2);
|
|
1210
|
+
rb_ary_push(pair, sh->slots[i].key);
|
|
1211
|
+
rb_ary_push(pair, sh->slots[i].value);
|
|
1212
|
+
rb_ary_push(ary, pair);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
return ary;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
static VALUE swiss_hash_fetch(int argc, VALUE *argv, VALUE self) {
|
|
1220
|
+
VALUE key;
|
|
1221
|
+
VALUE default_value;
|
|
1222
|
+
rb_scan_args(argc, argv, "11", &key, &default_value);
|
|
1223
|
+
|
|
1224
|
+
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
1225
|
+
VALUE *val = swiss_lookup(sh, key);
|
|
1226
|
+
if (val) {
|
|
1227
|
+
return *val;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
if (rb_block_given_p()) {
|
|
1231
|
+
if (argc == 2) {
|
|
1232
|
+
rb_warn("block supersedes default value argument");
|
|
1233
|
+
}
|
|
1234
|
+
return rb_yield(key);
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
if (argc == 2) {
|
|
1238
|
+
return default_value;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
VALUE inspected = rb_inspect(key);
|
|
1242
|
+
VALUE message = rb_str_plus(rb_str_new_cstr("key not found: "), inspected);
|
|
1243
|
+
rb_exc_raise(rb_exc_new_str(rb_eKeyError, message));
|
|
1244
|
+
|
|
1245
|
+
return Qnil;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
static VALUE swiss_hash_values_at(int argc, VALUE *argv, VALUE self) {
|
|
1249
|
+
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
1250
|
+
VALUE ary = rb_ary_new_capa((long)argc);
|
|
1251
|
+
|
|
1252
|
+
for (int i = 0; i < argc; i++) {
|
|
1253
|
+
VALUE *val = swiss_lookup(sh, argv[i]);
|
|
1254
|
+
rb_ary_push(ary, val ? *val : Qnil);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
return ary;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
static VALUE swiss_hash_value_p(VALUE self, VALUE value) {
|
|
1261
|
+
SwissHash *sh;
|
|
1262
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1263
|
+
|
|
1264
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1265
|
+
uint8_t c = sh->ctrl[i];
|
|
1266
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1267
|
+
if (RTEST(rb_equal(sh->slots[i].value, value))) {
|
|
1268
|
+
return Qtrue;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
return Qfalse;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
static VALUE swiss_hash_key_for_value(VALUE self, VALUE value) {
|
|
1277
|
+
SwissHash *sh;
|
|
1278
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1279
|
+
|
|
1280
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1281
|
+
uint8_t c = sh->ctrl[i];
|
|
1282
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1283
|
+
if (RTEST(rb_equal(sh->slots[i].value, value))) {
|
|
1284
|
+
return sh->slots[i].key;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
return Qnil;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
841
1292
|
static VALUE swiss_hash_key_p(VALUE self, VALUE key) {
|
|
842
1293
|
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
843
1294
|
VALUE *val = swiss_lookup(sh, key);
|
|
844
1295
|
return val ? Qtrue : Qfalse;
|
|
845
1296
|
}
|
|
846
1297
|
|
|
1298
|
+
static VALUE swiss_hash_new_like(VALUE self, size_t capacity) {
|
|
1299
|
+
VALUE argv[1] = {SIZET2NUM(capacity)};
|
|
1300
|
+
return rb_class_new_instance(1, argv, rb_obj_class(self));
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
static VALUE swiss_hash_store_prepared(SwissHash *sh, VALUE key, VALUE value) {
|
|
1304
|
+
if (RB_UNLIKELY(!(FIXNUM_P(key) || SYMBOL_P(key)))) {
|
|
1305
|
+
key = prepare_key(key);
|
|
1306
|
+
}
|
|
1307
|
+
return swiss_insert(sh, key, value);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
typedef struct {
|
|
1311
|
+
VALUE self;
|
|
1312
|
+
SwissHash *sh;
|
|
1313
|
+
int has_block;
|
|
1314
|
+
} MergeCtx;
|
|
1315
|
+
|
|
1316
|
+
static void swiss_hash_merge_one_pair(MergeCtx *ctx, VALUE key, VALUE value) {
|
|
1317
|
+
if (ctx->has_block) {
|
|
1318
|
+
VALUE *old = swiss_lookup(ctx->sh, key);
|
|
1319
|
+
if (old) {
|
|
1320
|
+
value = rb_yield_values(3, key, *old, value);
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
swiss_hash_store_prepared(ctx->sh, key, value);
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
static int swiss_hash_merge_ruby_hash_i(VALUE key, VALUE value, VALUE arg) {
|
|
1327
|
+
swiss_hash_merge_one_pair((MergeCtx *)arg, key, value);
|
|
1328
|
+
return ST_CONTINUE;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
static void swiss_hash_merge_swiss_hash(MergeCtx *ctx, VALUE other) {
|
|
1332
|
+
SwissHash *src;
|
|
1333
|
+
TypedData_Get_Struct(other, SwissHash, &swiss_hash_type, src);
|
|
1334
|
+
|
|
1335
|
+
for (size_t i = 0; i < src->capacity; i++) {
|
|
1336
|
+
uint8_t c = src->ctrl[i];
|
|
1337
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1338
|
+
swiss_hash_merge_one_pair(ctx, src->slots[i].key, src->slots[i].value);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
static void swiss_hash_merge_one(MergeCtx *ctx, VALUE other) {
|
|
1344
|
+
if (RTEST(rb_obj_is_kind_of(other, cSwissHashHash))) {
|
|
1345
|
+
swiss_hash_merge_swiss_hash(ctx, other);
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
VALUE hash = rb_check_hash_type(other);
|
|
1350
|
+
if (NIL_P(hash)) {
|
|
1351
|
+
rb_raise(rb_eTypeError, "no implicit conversion of %s into Hash", rb_obj_classname(other));
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
rb_hash_foreach(hash, swiss_hash_merge_ruby_hash_i, (VALUE)ctx);
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
static VALUE swiss_hash_merge_bang(int argc, VALUE *argv, VALUE self) {
|
|
1358
|
+
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
1359
|
+
MergeCtx ctx = {self, sh, rb_block_given_p()};
|
|
1360
|
+
|
|
1361
|
+
for (int i = 0; i < argc; i++) {
|
|
1362
|
+
swiss_hash_merge_one(&ctx, argv[i]);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
return self;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
static VALUE swiss_hash_merge(int argc, VALUE *argv, VALUE self) {
|
|
1369
|
+
VALUE copy = rb_obj_dup(self);
|
|
1370
|
+
swiss_hash_merge_bang(argc, argv, copy);
|
|
1371
|
+
return copy;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
static VALUE swiss_hash_replace(VALUE self, VALUE other) {
|
|
1375
|
+
swiss_hash_clear(self);
|
|
1376
|
+
VALUE argv[1] = {other};
|
|
1377
|
+
swiss_hash_merge_bang(1, argv, self);
|
|
1378
|
+
return self;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
static VALUE swiss_hash_to_sh(VALUE self) {
|
|
1382
|
+
return rb_obj_dup(self);
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
static VALUE swiss_hash_fetch_values(int argc, VALUE *argv, VALUE self) {
|
|
1386
|
+
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
1387
|
+
VALUE ary = rb_ary_new_capa((long)argc);
|
|
1388
|
+
int has_block = rb_block_given_p();
|
|
1389
|
+
|
|
1390
|
+
for (int i = 0; i < argc; i++) {
|
|
1391
|
+
VALUE *val = swiss_lookup(sh, argv[i]);
|
|
1392
|
+
if (val) {
|
|
1393
|
+
rb_ary_push(ary, *val);
|
|
1394
|
+
} else if (has_block) {
|
|
1395
|
+
rb_ary_push(ary, rb_yield(argv[i]));
|
|
1396
|
+
} else {
|
|
1397
|
+
VALUE key_argv[1] = {argv[i]};
|
|
1398
|
+
rb_ary_push(ary, swiss_hash_fetch(1, key_argv, self));
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
return ary;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
static VALUE swiss_hash_slice(int argc, VALUE *argv, VALUE self) {
|
|
1406
|
+
SwissHash *src = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
1407
|
+
VALUE result = swiss_hash_new_like(self, (size_t)argc);
|
|
1408
|
+
SwissHash *dst = (SwissHash *)RTYPEDDATA_DATA(result);
|
|
1409
|
+
|
|
1410
|
+
for (int i = 0; i < argc; i++) {
|
|
1411
|
+
VALUE *val = swiss_lookup(src, argv[i]);
|
|
1412
|
+
if (val) {
|
|
1413
|
+
swiss_hash_store_prepared(dst, argv[i], *val);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
return result;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
static VALUE swiss_hash_except(int argc, VALUE *argv, VALUE self) {
|
|
1421
|
+
VALUE result = rb_obj_dup(self);
|
|
1422
|
+
for (int i = 0; i < argc; i++) {
|
|
1423
|
+
swiss_hash_delete(result, argv[i]);
|
|
1424
|
+
}
|
|
1425
|
+
return result;
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
static VALUE swiss_hash_invert(VALUE self) {
|
|
1429
|
+
SwissHash *src;
|
|
1430
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, src);
|
|
1431
|
+
VALUE result = swiss_hash_new_like(self, src->size);
|
|
1432
|
+
SwissHash *dst = (SwissHash *)RTYPEDDATA_DATA(result);
|
|
1433
|
+
|
|
1434
|
+
for (size_t i = 0; i < src->capacity; i++) {
|
|
1435
|
+
uint8_t c = src->ctrl[i];
|
|
1436
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1437
|
+
swiss_hash_store_prepared(dst, src->slots[i].value, src->slots[i].key);
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
return result;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
static VALUE swiss_hash_assoc(VALUE self, VALUE object) {
|
|
1445
|
+
SwissHash *sh;
|
|
1446
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1447
|
+
|
|
1448
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1449
|
+
uint8_t c = sh->ctrl[i];
|
|
1450
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1451
|
+
if (RTEST(rb_equal(sh->slots[i].key, object))) {
|
|
1452
|
+
VALUE ary = rb_ary_new_capa(2);
|
|
1453
|
+
rb_ary_push(ary, sh->slots[i].key);
|
|
1454
|
+
rb_ary_push(ary, sh->slots[i].value);
|
|
1455
|
+
return ary;
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
return Qnil;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
static VALUE swiss_hash_rassoc(VALUE self, VALUE object) {
|
|
1464
|
+
SwissHash *sh;
|
|
1465
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1466
|
+
|
|
1467
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1468
|
+
uint8_t c = sh->ctrl[i];
|
|
1469
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1470
|
+
if (RTEST(rb_equal(sh->slots[i].value, object))) {
|
|
1471
|
+
VALUE ary = rb_ary_new_capa(2);
|
|
1472
|
+
rb_ary_push(ary, sh->slots[i].key);
|
|
1473
|
+
rb_ary_push(ary, sh->slots[i].value);
|
|
1474
|
+
return ary;
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
return Qnil;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
static VALUE swiss_hash_shift(VALUE self) {
|
|
1483
|
+
SwissHash *sh;
|
|
1484
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1485
|
+
|
|
1486
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1487
|
+
uint8_t c = sh->ctrl[i];
|
|
1488
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1489
|
+
VALUE key = sh->slots[i].key;
|
|
1490
|
+
VALUE value = sh->slots[i].value;
|
|
1491
|
+
sh->ctrl[i] = CTRL_DELETED;
|
|
1492
|
+
sh->slots[i].key = Qnil;
|
|
1493
|
+
sh->slots[i].value = Qnil;
|
|
1494
|
+
sh->size--;
|
|
1495
|
+
sh->tombstone_count++;
|
|
1496
|
+
|
|
1497
|
+
VALUE ary = rb_ary_new_capa(2);
|
|
1498
|
+
rb_ary_push(ary, key);
|
|
1499
|
+
rb_ary_push(ary, value);
|
|
1500
|
+
return ary;
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
return Qnil;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
static VALUE swiss_hash_delete_if(VALUE self) {
|
|
1508
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1509
|
+
|
|
1510
|
+
SwissHash *sh;
|
|
1511
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1512
|
+
|
|
1513
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1514
|
+
uint8_t c = sh->ctrl[i];
|
|
1515
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1516
|
+
VALUE key = sh->slots[i].key;
|
|
1517
|
+
VALUE value = sh->slots[i].value;
|
|
1518
|
+
if (RTEST(rb_yield_values(2, key, value))) {
|
|
1519
|
+
swiss_delete(sh, key);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
return self;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
static VALUE swiss_hash_keep_if(VALUE self) {
|
|
1528
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1529
|
+
|
|
1530
|
+
SwissHash *sh;
|
|
1531
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1532
|
+
|
|
1533
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1534
|
+
uint8_t c = sh->ctrl[i];
|
|
1535
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1536
|
+
VALUE key = sh->slots[i].key;
|
|
1537
|
+
VALUE value = sh->slots[i].value;
|
|
1538
|
+
if (!RTEST(rb_yield_values(2, key, value))) {
|
|
1539
|
+
swiss_delete(sh, key);
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
return self;
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
static VALUE swiss_hash_select(VALUE self) {
|
|
1548
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1549
|
+
|
|
1550
|
+
SwissHash *src;
|
|
1551
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, src);
|
|
1552
|
+
VALUE result = swiss_hash_new_like(self, src->size);
|
|
1553
|
+
SwissHash *dst = (SwissHash *)RTYPEDDATA_DATA(result);
|
|
1554
|
+
|
|
1555
|
+
for (size_t i = 0; i < src->capacity; i++) {
|
|
1556
|
+
uint8_t c = src->ctrl[i];
|
|
1557
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1558
|
+
VALUE key = src->slots[i].key;
|
|
1559
|
+
VALUE value = src->slots[i].value;
|
|
1560
|
+
if (RTEST(rb_yield_values(2, key, value))) {
|
|
1561
|
+
swiss_hash_store_prepared(dst, key, value);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
return result;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
static VALUE swiss_hash_reject(VALUE self) {
|
|
1570
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1571
|
+
|
|
1572
|
+
SwissHash *src;
|
|
1573
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, src);
|
|
1574
|
+
VALUE result = swiss_hash_new_like(self, src->size);
|
|
1575
|
+
SwissHash *dst = (SwissHash *)RTYPEDDATA_DATA(result);
|
|
1576
|
+
|
|
1577
|
+
for (size_t i = 0; i < src->capacity; i++) {
|
|
1578
|
+
uint8_t c = src->ctrl[i];
|
|
1579
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1580
|
+
VALUE key = src->slots[i].key;
|
|
1581
|
+
VALUE value = src->slots[i].value;
|
|
1582
|
+
if (!RTEST(rb_yield_values(2, key, value))) {
|
|
1583
|
+
swiss_hash_store_prepared(dst, key, value);
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
return result;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
static VALUE swiss_hash_select_bang(VALUE self) {
|
|
1592
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1593
|
+
|
|
1594
|
+
size_t old_size = ((SwissHash *)RTYPEDDATA_DATA(self))->size;
|
|
1595
|
+
swiss_hash_keep_if(self);
|
|
1596
|
+
return ((SwissHash *)RTYPEDDATA_DATA(self))->size == old_size ? Qnil : self;
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
static VALUE swiss_hash_reject_bang(VALUE self) {
|
|
1600
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1601
|
+
|
|
1602
|
+
size_t old_size = ((SwissHash *)RTYPEDDATA_DATA(self))->size;
|
|
1603
|
+
swiss_hash_delete_if(self);
|
|
1604
|
+
return ((SwissHash *)RTYPEDDATA_DATA(self))->size == old_size ? Qnil : self;
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
static VALUE swiss_hash_compact(VALUE self) {
|
|
1608
|
+
SwissHash *src;
|
|
1609
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, src);
|
|
1610
|
+
VALUE result = swiss_hash_new_like(self, src->size);
|
|
1611
|
+
SwissHash *dst = (SwissHash *)RTYPEDDATA_DATA(result);
|
|
1612
|
+
|
|
1613
|
+
for (size_t i = 0; i < src->capacity; i++) {
|
|
1614
|
+
uint8_t c = src->ctrl[i];
|
|
1615
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED && !NIL_P(src->slots[i].value)) {
|
|
1616
|
+
swiss_hash_store_prepared(dst, src->slots[i].key, src->slots[i].value);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
return result;
|
|
1621
|
+
}
|
|
1622
|
+
|
|
847
1623
|
static VALUE swiss_hash_compact_bang(VALUE self) {
|
|
848
1624
|
SwissHash *sh;
|
|
849
1625
|
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1626
|
+
size_t old_size = sh->size;
|
|
1627
|
+
|
|
1628
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1629
|
+
uint8_t c = sh->ctrl[i];
|
|
1630
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED && NIL_P(sh->slots[i].value)) {
|
|
1631
|
+
VALUE key = sh->slots[i].key;
|
|
1632
|
+
swiss_delete(sh, key);
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
return sh->size == old_size ? Qnil : self;
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
static VALUE swiss_hash_transform_values(VALUE self) {
|
|
1640
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1641
|
+
|
|
1642
|
+
SwissHash *src;
|
|
1643
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, src);
|
|
1644
|
+
VALUE result = swiss_hash_new_like(self, src->size);
|
|
1645
|
+
SwissHash *dst = (SwissHash *)RTYPEDDATA_DATA(result);
|
|
1646
|
+
|
|
1647
|
+
for (size_t i = 0; i < src->capacity; i++) {
|
|
1648
|
+
uint8_t c = src->ctrl[i];
|
|
1649
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1650
|
+
swiss_hash_store_prepared(dst, src->slots[i].key, rb_yield(src->slots[i].value));
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
return result;
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
static VALUE swiss_hash_transform_values_bang(VALUE self) {
|
|
1658
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1659
|
+
|
|
1660
|
+
SwissHash *sh;
|
|
1661
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1662
|
+
|
|
1663
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1664
|
+
uint8_t c = sh->ctrl[i];
|
|
1665
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1666
|
+
sh->slots[i].value = rb_yield(sh->slots[i].value);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
return self;
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
static VALUE swiss_hash_transform_keys(VALUE self) {
|
|
1674
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1675
|
+
|
|
1676
|
+
SwissHash *src;
|
|
1677
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, src);
|
|
1678
|
+
VALUE result = swiss_hash_new_like(self, src->size);
|
|
1679
|
+
SwissHash *dst = (SwissHash *)RTYPEDDATA_DATA(result);
|
|
1680
|
+
|
|
1681
|
+
for (size_t i = 0; i < src->capacity; i++) {
|
|
1682
|
+
uint8_t c = src->ctrl[i];
|
|
1683
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1684
|
+
swiss_hash_store_prepared(dst, rb_yield(src->slots[i].key), src->slots[i].value);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
return result;
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
static VALUE swiss_hash_compact_storage_bang(VALUE self) {
|
|
1692
|
+
SwissHash *sh;
|
|
1693
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
850
1694
|
|
|
851
1695
|
if (sh->tombstone_count > 0) {
|
|
852
1696
|
swiss_compact(sh);
|
|
@@ -875,7 +1719,7 @@ static VALUE swiss_hash_stats(VALUE self) {
|
|
|
875
1719
|
#elif defined(SWISS_USE_NEON)
|
|
876
1720
|
rb_hash_aset(hash, ID2SYM(rb_intern("simd")), rb_str_new_cstr("NEON"));
|
|
877
1721
|
#else
|
|
878
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("simd")), rb_str_new_cstr("
|
|
1722
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("simd")), rb_str_new_cstr("SWAR"));
|
|
879
1723
|
#endif
|
|
880
1724
|
rb_hash_aset(hash, ID2SYM(rb_intern("layout")), rb_str_new_cstr("hybrid"));
|
|
881
1725
|
|
|
@@ -887,9 +1731,11 @@ void Init_swiss_hash(void) {
|
|
|
887
1731
|
|
|
888
1732
|
VALUE mSwissHash = rb_define_module("SwissHash");
|
|
889
1733
|
VALUE cHash = rb_define_class_under(mSwissHash, "Hash", rb_cObject);
|
|
1734
|
+
cSwissHashHash = cHash;
|
|
890
1735
|
|
|
891
1736
|
rb_define_alloc_func(cHash, swiss_hash_alloc);
|
|
892
1737
|
rb_define_method(cHash, "initialize", swiss_hash_initialize, -1);
|
|
1738
|
+
rb_define_method(cHash, "initialize_copy", swiss_hash_initialize_copy, 1);
|
|
893
1739
|
rb_define_method(cHash, "[]=", swiss_hash_aset, 2);
|
|
894
1740
|
rb_define_method(cHash, "store", swiss_hash_aset, 2);
|
|
895
1741
|
rb_define_method(cHash, "[]", swiss_hash_aref, 1);
|
|
@@ -899,11 +1745,48 @@ void Init_swiss_hash(void) {
|
|
|
899
1745
|
rb_define_method(cHash, "empty?", swiss_hash_empty_p, 0);
|
|
900
1746
|
rb_define_method(cHash, "clear", swiss_hash_clear, 0);
|
|
901
1747
|
rb_define_method(cHash, "each", swiss_hash_each, 0);
|
|
1748
|
+
rb_define_method(cHash, "each_pair", swiss_hash_each, 0);
|
|
1749
|
+
rb_define_method(cHash, "each_key", swiss_hash_each_key, 0);
|
|
1750
|
+
rb_define_method(cHash, "each_value", swiss_hash_each_value, 0);
|
|
902
1751
|
rb_define_method(cHash, "keys", swiss_hash_keys, 0);
|
|
903
1752
|
rb_define_method(cHash, "values", swiss_hash_values, 0);
|
|
1753
|
+
rb_define_method(cHash, "to_h", swiss_hash_to_h, 0);
|
|
1754
|
+
rb_define_method(cHash, "to_a", swiss_hash_to_a, 0);
|
|
1755
|
+
rb_define_method(cHash, "fetch", swiss_hash_fetch, -1);
|
|
1756
|
+
rb_define_method(cHash, "values_at", swiss_hash_values_at, -1);
|
|
1757
|
+
rb_define_method(cHash, "fetch_values", swiss_hash_fetch_values, -1);
|
|
1758
|
+
rb_define_method(cHash, "merge!", swiss_hash_merge_bang, -1);
|
|
1759
|
+
rb_define_method(cHash, "update", swiss_hash_merge_bang, -1);
|
|
1760
|
+
rb_define_method(cHash, "merge", swiss_hash_merge, -1);
|
|
1761
|
+
rb_define_method(cHash, "replace", swiss_hash_replace, 1);
|
|
1762
|
+
rb_define_method(cHash, "to_sh", swiss_hash_to_sh, 0);
|
|
1763
|
+
rb_define_method(cHash, "slice", swiss_hash_slice, -1);
|
|
1764
|
+
rb_define_method(cHash, "except", swiss_hash_except, -1);
|
|
1765
|
+
rb_define_method(cHash, "invert", swiss_hash_invert, 0);
|
|
1766
|
+
rb_define_method(cHash, "assoc", swiss_hash_assoc, 1);
|
|
1767
|
+
rb_define_method(cHash, "rassoc", swiss_hash_rassoc, 1);
|
|
1768
|
+
rb_define_method(cHash, "shift", swiss_hash_shift, 0);
|
|
1769
|
+
rb_define_method(cHash, "delete_if", swiss_hash_delete_if, 0);
|
|
1770
|
+
rb_define_method(cHash, "keep_if", swiss_hash_keep_if, 0);
|
|
1771
|
+
rb_define_method(cHash, "select", swiss_hash_select, 0);
|
|
1772
|
+
rb_define_method(cHash, "filter", swiss_hash_select, 0);
|
|
1773
|
+
rb_define_method(cHash, "reject", swiss_hash_reject, 0);
|
|
1774
|
+
rb_define_method(cHash, "select!", swiss_hash_select_bang, 0);
|
|
1775
|
+
rb_define_method(cHash, "filter!", swiss_hash_select_bang, 0);
|
|
1776
|
+
rb_define_method(cHash, "reject!", swiss_hash_reject_bang, 0);
|
|
1777
|
+
rb_define_method(cHash, "compact", swiss_hash_compact, 0);
|
|
1778
|
+
rb_define_method(cHash, "compact!", swiss_hash_compact_bang, 0);
|
|
1779
|
+
rb_define_method(cHash, "transform_values", swiss_hash_transform_values, 0);
|
|
1780
|
+
rb_define_method(cHash, "transform_values!", swiss_hash_transform_values_bang, 0);
|
|
1781
|
+
rb_define_method(cHash, "transform_keys", swiss_hash_transform_keys, 0);
|
|
904
1782
|
rb_define_method(cHash, "key?", swiss_hash_key_p, 1);
|
|
905
1783
|
rb_define_method(cHash, "has_key?", swiss_hash_key_p, 1);
|
|
906
1784
|
rb_define_method(cHash, "include?", swiss_hash_key_p, 1);
|
|
907
|
-
rb_define_method(cHash, "
|
|
1785
|
+
rb_define_method(cHash, "member?", swiss_hash_key_p, 1);
|
|
1786
|
+
rb_define_method(cHash, "value?", swiss_hash_value_p, 1);
|
|
1787
|
+
rb_define_method(cHash, "has_value?", swiss_hash_value_p, 1);
|
|
1788
|
+
rb_define_method(cHash, "key", swiss_hash_key_for_value, 1);
|
|
1789
|
+
rb_define_method(cHash, "compact_storage!", swiss_hash_compact_storage_bang, 0);
|
|
1790
|
+
rb_define_method(cHash, "__compact_storage!", swiss_hash_compact_storage_bang, 0);
|
|
908
1791
|
rb_define_method(cHash, "stats", swiss_hash_stats, 0);
|
|
909
1792
|
}
|