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.
@@ -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 SWISS_USE_PORTABLE 1
145
- #define GROUP_SIZE 8
146
- #define GROUP_MASK 0xFFu
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
- static inline __m128i ctrl_load(const uint8_t *ctrl) {
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
- static inline uint32_t ctrl_match_h2_vec(__m128i cv, uint8_t h2) {
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
- static inline uint32_t ctrl_match_empty_vec(__m128i cv) {
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
- static inline uint32_t ctrl_match_empty_or_deleted_vec(__m128i cv) {
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
- static inline uint32_t ctrl_match_empty(const uint8_t *ctrl) {
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
- static inline uint32_t neon_movemask(uint8x8_t v) {
176
- static const uint8_t power_of_two[8] = {1, 2, 4, 8, 16, 32, 64, 128};
177
- uint8x8_t bits = vand_u8(v, vld1_u8(power_of_two));
178
- bits = vpadd_u8(bits, bits);
179
- bits = vpadd_u8(bits, bits);
180
- bits = vpadd_u8(bits, bits);
181
- return (uint32_t)vget_lane_u8(bits, 0);
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
- static inline uint8x8_t ctrl_load(const uint8_t *ctrl) {
185
- return vld1_u8(ctrl);
203
+ SH_ALWAYS_INLINE ctrl_vec_t ctrl_load(const uint8_t *ctrl) {
204
+ return vld1q_u8(ctrl);
186
205
  }
187
206
 
188
- static inline uint32_t ctrl_match_h2_vec(uint8x8_t cv, uint8_t h2) {
189
- return neon_movemask(vceq_u8(cv, vdup_n_u8(h2)));
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
- static inline uint32_t ctrl_match_empty_vec(uint8x8_t cv) {
193
- return neon_movemask(vceq_u8(cv, vdup_n_u8(CTRL_EMPTY)));
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
- static inline uint32_t ctrl_match_empty_or_deleted_vec(uint8x8_t cv) {
197
- uint8x8_t msb = vshr_n_u8(cv, 7);
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
- static inline uint32_t ctrl_match_empty(const uint8_t *ctrl) {
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 /* portable */
223
+ #else /* SWAR */
207
224
 
208
- static inline uint32_t ctrl_match_h2_raw(const uint8_t *ctrl, uint8_t h2) {
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 = 0x0101010101010101ULL * h2;
236
+ uint64_t broadcast = SWAR_LSB * h2;
212
237
  uint64_t xored = c ^ broadcast;
213
- uint64_t result = (xored - 0x0101010101010101ULL) & ~xored & 0x8080808080808080ULL;
214
- uint32_t mask = 0;
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
- static inline uint32_t ctrl_match_empty_raw(const uint8_t *ctrl) {
223
- uint32_t mask = 0;
224
- for (int i = 0; i < 8; i++) {
225
- if (ctrl[i] == CTRL_EMPTY)
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
- static inline uint32_t ctrl_match_empty_or_deleted_raw(const uint8_t *ctrl) {
232
- uint32_t mask = 0;
233
- for (int i = 0; i < 8; i++) {
234
- if (ctrl[i] & 0x80)
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
- static inline uint32_t ctrl_match_empty(const uint8_t *ctrl) {
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 /* SIMD selection */
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
- static inline int ctz32(uint32_t v) {
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
- static inline uint64_t compute_hash(VALUE key) {
297
- uint64_t v;
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
- v = (uint64_t)FIX2LONG(key) ^ swiss_hash_seed0;
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
- if (SYMBOL_P(key)) {
305
- v = (uint64_t)SYM2ID(key) ^ swiss_hash_seed0;
306
- return v * FIBONACCI_HASH_C;
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
- const char *ptr = RSTRING_PTR(key);
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
- static inline int keys_equal(VALUE a, VALUE b) {
331
- if (a == b) return 1;
332
- if (FIXNUM_P(a) || SYMBOL_P(a) || SPECIAL_CONST_P(a)) return 0;
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
- if (RB_TYPE_P(a, T_STRING) && RB_TYPE_P(b, T_STRING)) {
335
- long la = RSTRING_LEN(a);
336
- if (la != RSTRING_LEN(b)) return 0;
337
- const char *pa = RSTRING_PTR(a);
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
- int ea = ENCODING_GET(a);
342
- int eb = ENCODING_GET(b);
343
- if (ea == eb) {
344
- return memcmp(pa, pb, (size_t)la) == 0;
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
- if (ENC_CODERANGE(a) == ENC_CODERANGE_7BIT &&
348
- ENC_CODERANGE(b) == ENC_CODERANGE_7BIT) {
349
- return memcmp(pa, pb, (size_t)la) == 0;
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
- if (rb_enc_compatible(a, b)) {
353
- return memcmp(pa, pb, (size_t)la) == 0;
354
- }
355
- return rb_eql(a, b);
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
- static inline VALUE prepare_key(VALUE key) {
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
- static inline ProbeSeq probe_start(uint64_t h1, size_t group_mask) {
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
- static inline void probe_next(ProbeSeq *ps) {
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
- static VALUE *swiss_lookup(SwissHash *sh, VALUE key) {
433
- uint64_t hash = compute_hash(key);
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 = ctz32(match);
579
+ int slot = CTRL_MASK_SLOT(match);
457
580
  Slot *s = &sh->slots[off + slot];
458
- if (keys_equal(s->key, key)) {
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
- static inline void swiss_insert_rehash(SwissHash *sh, uint64_t hash, VALUE key, VALUE value) {
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), sh->group_mask);
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
- uint32_t empty_mask = ctrl_match_empty(sh->ctrl + off);
654
+ ctrl_mask_t empty_mask = ctrl_match_empty(ctrl + off);
480
655
  if (empty_mask) {
481
- size_t idx = off + ctz32(empty_mask);
482
- sh->ctrl[idx] = h2;
483
- sh->slots[idx].key = key;
484
- sh->slots[idx].value = value;
485
- sh->size++;
486
- sh->growth_left--;
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
- static void swiss_compact(SwissHash *sh) {
498
- size_t cap = sh->capacity;
499
- size_t old_size = sh->size;
500
-
501
- uint8_t *old_ctrl = sh->ctrl;
502
- Slot *old_slots = sh->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 = (uint8_t *)malloc(cap);
505
- sh->slots = (Slot *)malloc(cap * sizeof(Slot));
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
- if (!sh->ctrl || !sh->slots) {
508
- free(sh->ctrl);
509
- free(sh->slots);
510
- sh->ctrl = old_ctrl;
511
- sh->slots = old_slots;
512
- sh->size = old_size;
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
- memset(sh->ctrl, CTRL_EMPTY, cap);
518
- sh->size = 0;
519
- sh->growth_left = cap * MAX_LOAD_NUM / MAX_LOAD_DEN;
520
- sh->tombstone_count = 0;
521
-
522
- for (size_t i = 0; i < cap; i++) {
523
- uint8_t c = old_ctrl[i];
524
- if (c != CTRL_EMPTY && c != CTRL_DELETED) {
525
- uint64_t hash = compute_hash(old_slots[i].key);
526
- swiss_insert_rehash(sh, hash, old_slots[i].key, old_slots[i].value);
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
- free(old_ctrl);
531
- free(old_slots);
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 swiss_insert(SwissHash *sh, VALUE key, VALUE 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
- #if defined(SWISS_USE_SSE2)
553
- __m128i cv = ctrl_load(sh->ctrl + off);
554
- SH_PREFETCH(&sh->slots[off]);
555
- uint32_t match = ctrl_match_h2_vec(cv, h2);
556
- uint32_t empty = ctrl_match_empty_vec(cv);
557
- uint32_t avail = ctrl_match_empty_or_deleted_vec(cv);
558
- #elif defined(SWISS_USE_NEON)
559
- uint8x8_t cv = ctrl_load(sh->ctrl + off);
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
- uint32_t match = ctrl_match_h2_raw(sh->ctrl + off, h2);
566
- uint32_t empty = ctrl_match_empty_raw(sh->ctrl + off);
567
- uint32_t avail = ctrl_match_empty_or_deleted_raw(sh->ctrl + off);
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 = ctz32(match);
784
+ int slot = CTRL_MASK_SLOT(match);
571
785
  size_t idx = off + slot;
572
- if (keys_equal(sh->slots[idx].key, key)) {
573
- sh->slots[idx].value = value;
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 + ctz32(avail);
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
- MUTATE_GUARD_BEGIN(sh);
803
+ swiss_place_new(sh, insert_idx, h2, key, value);
804
+ return value;
805
+ }
589
806
 
590
- if (sh->ctrl[insert_idx] == CTRL_EMPTY) {
591
- sh->growth_left--;
592
- } else {
593
- sh->tombstone_count--;
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
- MUTATE_GUARD_END(sh);
853
+ swiss_place_new(sh, insert_idx, h2, key, value);
601
854
  return value;
602
855
  }
603
856
 
604
- static VALUE swiss_delete(SwissHash *sh, VALUE key) {
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 = ctz32(match);
902
+ int slot = CTRL_MASK_SLOT(match);
629
903
  size_t idx = off + slot;
630
- if (keys_equal(sh->slots[idx].key, key)) {
631
- VALUE old_value = sh->slots[idx].value;
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
- MUTATE_GUARD_END(sh);
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 void swiss_grow(SwissHash *sh) {
653
- size_t old_cap = sh->capacity;
654
- size_t old_size = sh->size;
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
- memset(sh->ctrl, CTRL_EMPTY, new_cap);
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->num_groups = new_num_groups;
678
- sh->group_mask = new_num_groups - 1;
679
- sh->capacity = new_cap;
680
- sh->size = 0;
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
- for (size_t i = 0; i < old_cap; i++) {
685
- uint8_t c = old_ctrl[i];
686
- if (c != CTRL_EMPTY && c != CTRL_DELETED) {
687
- uint64_t hash = compute_hash(old_slots[i].key);
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
- free(old_ctrl);
693
- free(old_slots);
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 (RB_UNLIKELY(!(FIXNUM_P(key) || SYMBOL_P(key)))) {
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 = swiss_lookup(sh, key);
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("portable/SWAR"));
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, "compact!", swiss_hash_compact_bang, 0);
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
  }