swiss_hash 0.1.0 → 0.1.2
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 +73 -0
- data/LICENSE.txt +1 -1
- data/README.md +199 -73
- data/ext/swiss_hash/swiss_hash.c +709 -114
- 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,8 +12,29 @@
|
|
|
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
|
+
|
|
29
|
+
#if defined(__GNUC__) || defined(__clang__)
|
|
30
|
+
#define SH_PREFETCH(p) __builtin_prefetch((const void *)(p), 0, 1)
|
|
31
|
+
#else
|
|
32
|
+
#define SH_PREFETCH(p) ((void)0)
|
|
33
|
+
#endif
|
|
34
|
+
|
|
15
35
|
static uint64_t swiss_hash_seed0;
|
|
16
36
|
static uint64_t swiss_hash_seed1;
|
|
37
|
+
static VALUE cSwissHashHash;
|
|
17
38
|
|
|
18
39
|
static void init_hash_seed(void) {
|
|
19
40
|
VALUE seed_val = rb_hash(INT2FIX(0));
|
|
@@ -115,7 +136,6 @@ static inline uint64_t wyhash(const void *data, size_t len, uint64_t seed) {
|
|
|
115
136
|
return _wymix(s1 ^ len, _wymix(a ^ s1, b ^ seed));
|
|
116
137
|
}
|
|
117
138
|
|
|
118
|
-
#define GROUP_SIZE 8
|
|
119
139
|
#define CTRL_EMPTY 0x80
|
|
120
140
|
#define CTRL_DELETED 0xFE
|
|
121
141
|
#define H2_MASK 0x7F
|
|
@@ -126,111 +146,77 @@ static inline uint64_t wyhash(const void *data, size_t len, uint64_t seed) {
|
|
|
126
146
|
#if defined(__x86_64__) || defined(_M_X64)
|
|
127
147
|
#define SWISS_USE_SSE2 1
|
|
128
148
|
#include <emmintrin.h>
|
|
129
|
-
|
|
130
|
-
#define
|
|
131
|
-
#
|
|
149
|
+
|
|
150
|
+
#define GROUP_SIZE 16
|
|
151
|
+
#define GROUP_MASK 0xFFFFu
|
|
132
152
|
#else
|
|
133
|
-
#define
|
|
153
|
+
#define SWISS_USE_SWAR 1
|
|
154
|
+
#define GROUP_SIZE 8
|
|
155
|
+
#define GROUP_MASK 0xFFu
|
|
134
156
|
#endif
|
|
135
157
|
|
|
136
158
|
#ifdef SWISS_USE_SSE2
|
|
137
159
|
|
|
138
160
|
static inline __m128i ctrl_load(const uint8_t *ctrl) {
|
|
139
|
-
return
|
|
161
|
+
return _mm_loadu_si128((const __m128i *)ctrl);
|
|
140
162
|
}
|
|
141
163
|
|
|
142
164
|
static inline uint32_t ctrl_match_h2_vec(__m128i cv, uint8_t h2) {
|
|
143
165
|
__m128i cmp = _mm_cmpeq_epi8(cv, _mm_set1_epi8((char)h2));
|
|
144
|
-
return (uint32_t)_mm_movemask_epi8(cmp)
|
|
166
|
+
return (uint32_t)_mm_movemask_epi8(cmp);
|
|
145
167
|
}
|
|
146
168
|
|
|
147
169
|
static inline uint32_t ctrl_match_empty_vec(__m128i cv) {
|
|
148
170
|
__m128i cmp = _mm_cmpeq_epi8(cv, _mm_set1_epi8((char)CTRL_EMPTY));
|
|
149
|
-
return (uint32_t)_mm_movemask_epi8(cmp)
|
|
171
|
+
return (uint32_t)_mm_movemask_epi8(cmp);
|
|
150
172
|
}
|
|
151
173
|
|
|
152
174
|
static inline uint32_t ctrl_match_empty_or_deleted_vec(__m128i cv) {
|
|
153
|
-
return (uint32_t)_mm_movemask_epi8(cv)
|
|
175
|
+
return (uint32_t)_mm_movemask_epi8(cv);
|
|
154
176
|
}
|
|
155
177
|
|
|
156
178
|
static inline uint32_t ctrl_match_empty(const uint8_t *ctrl) {
|
|
157
179
|
return ctrl_match_empty_vec(ctrl_load(ctrl));
|
|
158
180
|
}
|
|
159
181
|
|
|
160
|
-
#
|
|
161
|
-
|
|
162
|
-
static inline uint32_t neon_movemask(uint8x8_t v) {
|
|
163
|
-
static const uint8_t power_of_two[8] = {1, 2, 4, 8, 16, 32, 64, 128};
|
|
164
|
-
uint8x8_t bits = vand_u8(v, vld1_u8(power_of_two));
|
|
165
|
-
bits = vpadd_u8(bits, bits);
|
|
166
|
-
bits = vpadd_u8(bits, bits);
|
|
167
|
-
bits = vpadd_u8(bits, bits);
|
|
168
|
-
return (uint32_t)vget_lane_u8(bits, 0);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
static inline uint8x8_t ctrl_load(const uint8_t *ctrl) {
|
|
172
|
-
return vld1_u8(ctrl);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
static inline uint32_t ctrl_match_h2_vec(uint8x8_t cv, uint8_t h2) {
|
|
176
|
-
return neon_movemask(vceq_u8(cv, vdup_n_u8(h2)));
|
|
177
|
-
}
|
|
182
|
+
#else /* SWAR */
|
|
178
183
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
184
|
+
#define SWAR_LSB 0x0101010101010101ULL
|
|
185
|
+
#define SWAR_MSB 0x8080808080808080ULL
|
|
182
186
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
return neon_movemask(match);
|
|
187
|
+
SH_ALWAYS_INLINE uint32_t ctrl_bitmask_from_msb(uint64_t msb_bits) {
|
|
188
|
+
uint64_t bits = (msb_bits & SWAR_MSB) >> 7;
|
|
189
|
+
return (uint32_t)((bits * 0x0102040810204080ULL) >> 56);
|
|
187
190
|
}
|
|
188
191
|
|
|
189
|
-
|
|
190
|
-
return ctrl_match_empty_vec(ctrl_load(ctrl));
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
#else /* portable */
|
|
194
|
-
|
|
195
|
-
static inline uint32_t ctrl_match_h2_raw(const uint8_t *ctrl, uint8_t h2) {
|
|
192
|
+
SH_ALWAYS_INLINE uint32_t ctrl_match_h2_raw(const uint8_t *ctrl, uint8_t h2) {
|
|
196
193
|
uint64_t c;
|
|
197
194
|
memcpy(&c, ctrl, 8);
|
|
198
|
-
uint64_t broadcast =
|
|
195
|
+
uint64_t broadcast = SWAR_LSB * h2;
|
|
199
196
|
uint64_t xored = c ^ broadcast;
|
|
200
|
-
uint64_t result = (xored -
|
|
201
|
-
|
|
202
|
-
for (int i = 0; i < 8; i++) {
|
|
203
|
-
if (result & (0x80ULL << (i * 8)))
|
|
204
|
-
mask |= (1u << i);
|
|
205
|
-
}
|
|
206
|
-
return mask;
|
|
197
|
+
uint64_t result = (xored - SWAR_LSB) & ~xored & SWAR_MSB;
|
|
198
|
+
return ctrl_bitmask_from_msb(result);
|
|
207
199
|
}
|
|
208
200
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
mask |= (1u << i);
|
|
214
|
-
}
|
|
215
|
-
return mask;
|
|
201
|
+
SH_ALWAYS_INLINE uint32_t ctrl_match_empty_raw(const uint8_t *ctrl) {
|
|
202
|
+
uint64_t c;
|
|
203
|
+
memcpy(&c, ctrl, 8);
|
|
204
|
+
return ctrl_bitmask_from_msb((c & ~(c << 6)) & SWAR_MSB);
|
|
216
205
|
}
|
|
217
206
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
mask |= (1u << i);
|
|
223
|
-
}
|
|
224
|
-
return mask;
|
|
207
|
+
SH_ALWAYS_INLINE uint32_t ctrl_match_empty_or_deleted_raw(const uint8_t *ctrl) {
|
|
208
|
+
uint64_t c;
|
|
209
|
+
memcpy(&c, ctrl, 8);
|
|
210
|
+
return ctrl_bitmask_from_msb(c);
|
|
225
211
|
}
|
|
226
212
|
|
|
227
|
-
|
|
213
|
+
SH_ALWAYS_INLINE uint32_t ctrl_match_empty(const uint8_t *ctrl) {
|
|
228
214
|
return ctrl_match_empty_raw(ctrl);
|
|
229
215
|
}
|
|
230
216
|
|
|
231
|
-
#endif /*
|
|
217
|
+
#endif /* control-byte matching selection */
|
|
232
218
|
|
|
233
|
-
|
|
219
|
+
SH_ALWAYS_INLINE int ctz32(uint32_t v) {
|
|
234
220
|
#if defined(__GNUC__) || defined(__clang__)
|
|
235
221
|
return __builtin_ctz(v);
|
|
236
222
|
#elif defined(_MSC_VER)
|
|
@@ -280,7 +266,7 @@ typedef struct {
|
|
|
280
266
|
|
|
281
267
|
#define FIBONACCI_HASH_C 0x9E3779B97F4A7C15ULL
|
|
282
268
|
|
|
283
|
-
|
|
269
|
+
SH_ALWAYS_INLINE uint64_t compute_hash(VALUE key) {
|
|
284
270
|
uint64_t v;
|
|
285
271
|
|
|
286
272
|
if (FIXNUM_P(key)) {
|
|
@@ -296,7 +282,7 @@ static inline uint64_t compute_hash(VALUE key) {
|
|
|
296
282
|
if (RB_TYPE_P(key, T_STRING)) {
|
|
297
283
|
const char *ptr = RSTRING_PTR(key);
|
|
298
284
|
long len = RSTRING_LEN(key);
|
|
299
|
-
int enc_idx =
|
|
285
|
+
int enc_idx = ENCODING_GET(key);
|
|
300
286
|
uint64_t str_seed = swiss_hash_seed0 ^ (uint64_t)enc_idx;
|
|
301
287
|
return wyhash(ptr ? ptr : (const char *)"", (size_t)len, str_seed);
|
|
302
288
|
}
|
|
@@ -314,22 +300,33 @@ static inline uint64_t compute_hash(VALUE key) {
|
|
|
314
300
|
#define H1(hash) ((hash) >> 7)
|
|
315
301
|
#define H2(hash) ((uint8_t)((hash) & H2_MASK))
|
|
316
302
|
|
|
317
|
-
|
|
303
|
+
SH_ALWAYS_INLINE int keys_equal(VALUE a, VALUE b) {
|
|
318
304
|
if (a == b)
|
|
319
305
|
return 1;
|
|
320
|
-
|
|
321
|
-
int ta = TYPE(a);
|
|
322
|
-
if (ta == T_FIXNUM || ta == T_SYMBOL)
|
|
306
|
+
if (FIXNUM_P(a) || SYMBOL_P(a) || SPECIAL_CONST_P(a))
|
|
323
307
|
return 0;
|
|
324
308
|
|
|
325
|
-
if (
|
|
309
|
+
if (RB_TYPE_P(a, T_STRING) && RB_TYPE_P(b, T_STRING)) {
|
|
326
310
|
long la = RSTRING_LEN(a);
|
|
327
|
-
|
|
328
|
-
if (la != lb)
|
|
311
|
+
if (la != RSTRING_LEN(b))
|
|
329
312
|
return 0;
|
|
313
|
+
const char *pa = RSTRING_PTR(a);
|
|
314
|
+
const char *pb = RSTRING_PTR(b);
|
|
315
|
+
if (pa == pb)
|
|
316
|
+
return 1;
|
|
317
|
+
|
|
318
|
+
int ea = ENCODING_GET(a);
|
|
319
|
+
int eb = ENCODING_GET(b);
|
|
320
|
+
if (ea == eb) {
|
|
321
|
+
return memcmp(pa, pb, (size_t)la) == 0;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (ENC_CODERANGE(a) == ENC_CODERANGE_7BIT && ENC_CODERANGE(b) == ENC_CODERANGE_7BIT) {
|
|
325
|
+
return memcmp(pa, pb, (size_t)la) == 0;
|
|
326
|
+
}
|
|
330
327
|
|
|
331
328
|
if (rb_enc_compatible(a, b)) {
|
|
332
|
-
return memcmp(
|
|
329
|
+
return memcmp(pa, pb, (size_t)la) == 0;
|
|
333
330
|
}
|
|
334
331
|
return rb_eql(a, b);
|
|
335
332
|
}
|
|
@@ -337,9 +334,18 @@ static inline int keys_equal(VALUE a, VALUE b) {
|
|
|
337
334
|
return rb_eql(a, b);
|
|
338
335
|
}
|
|
339
336
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
337
|
+
#ifndef SWISS_HASH_COPY_STRING_KEYS
|
|
338
|
+
#define SWISS_HASH_COPY_STRING_KEYS 1
|
|
339
|
+
#endif
|
|
340
|
+
|
|
341
|
+
SH_ALWAYS_INLINE VALUE prepare_key(VALUE key) {
|
|
342
|
+
if (RB_TYPE_P(key, T_STRING)) {
|
|
343
|
+
#if SWISS_HASH_COPY_STRING_KEYS
|
|
344
|
+
if (!OBJ_FROZEN(key)) {
|
|
345
|
+
key = rb_str_new_frozen(key);
|
|
346
|
+
}
|
|
347
|
+
#endif
|
|
348
|
+
rb_enc_str_coderange(key);
|
|
343
349
|
}
|
|
344
350
|
return key;
|
|
345
351
|
}
|
|
@@ -370,7 +376,7 @@ static void swiss_init(SwissHash *sh, size_t min_capacity) {
|
|
|
370
376
|
sh->growth_left = capacity * MAX_LOAD_NUM / MAX_LOAD_DEN;
|
|
371
377
|
|
|
372
378
|
sh->ctrl = (uint8_t *)malloc(capacity);
|
|
373
|
-
sh->slots = (Slot *)
|
|
379
|
+
sh->slots = (Slot *)malloc(capacity * sizeof(Slot));
|
|
374
380
|
|
|
375
381
|
if (!sh->ctrl || !sh->slots) {
|
|
376
382
|
free(sh->ctrl);
|
|
@@ -389,7 +395,7 @@ typedef struct {
|
|
|
389
395
|
size_t group_mask;
|
|
390
396
|
} ProbeSeq;
|
|
391
397
|
|
|
392
|
-
|
|
398
|
+
SH_ALWAYS_INLINE ProbeSeq probe_start(uint64_t h1, size_t group_mask) {
|
|
393
399
|
ProbeSeq ps;
|
|
394
400
|
ps.group_idx = (size_t)(h1)&group_mask;
|
|
395
401
|
ps.stride = 0;
|
|
@@ -397,7 +403,7 @@ static inline ProbeSeq probe_start(uint64_t h1, size_t group_mask) {
|
|
|
397
403
|
return ps;
|
|
398
404
|
}
|
|
399
405
|
|
|
400
|
-
|
|
406
|
+
SH_ALWAYS_INLINE void probe_next(ProbeSeq *ps) {
|
|
401
407
|
ps->stride++;
|
|
402
408
|
ps->group_idx = (ps->group_idx + ps->stride) & ps->group_mask;
|
|
403
409
|
}
|
|
@@ -414,20 +420,18 @@ static VALUE *swiss_lookup(SwissHash *sh, VALUE key) {
|
|
|
414
420
|
|
|
415
421
|
#if defined(SWISS_USE_SSE2)
|
|
416
422
|
__m128i cv = ctrl_load(sh->ctrl + off);
|
|
417
|
-
|
|
418
|
-
uint32_t empty = ctrl_match_empty_vec(cv);
|
|
419
|
-
#elif defined(SWISS_USE_NEON)
|
|
420
|
-
uint8x8_t cv = ctrl_load(sh->ctrl + off);
|
|
423
|
+
SH_PREFETCH(&sh->slots[off]);
|
|
421
424
|
uint32_t match = ctrl_match_h2_vec(cv, h2);
|
|
422
425
|
uint32_t empty = ctrl_match_empty_vec(cv);
|
|
423
426
|
#else
|
|
427
|
+
SH_PREFETCH(&sh->slots[off]);
|
|
424
428
|
uint32_t match = ctrl_match_h2_raw(sh->ctrl + off, h2);
|
|
425
429
|
uint32_t empty = ctrl_match_empty_raw(sh->ctrl + off);
|
|
426
430
|
#endif
|
|
427
431
|
while (match) {
|
|
428
432
|
int slot = ctz32(match);
|
|
429
433
|
Slot *s = &sh->slots[off + slot];
|
|
430
|
-
if (keys_equal(s->key, key)) {
|
|
434
|
+
if (RB_LIKELY(s->key == key) || keys_equal(s->key, key)) {
|
|
431
435
|
return &s->value;
|
|
432
436
|
}
|
|
433
437
|
match &= match - 1;
|
|
@@ -442,7 +446,7 @@ static VALUE *swiss_lookup(SwissHash *sh, VALUE key) {
|
|
|
442
446
|
static void swiss_grow(SwissHash *sh);
|
|
443
447
|
static void swiss_compact(SwissHash *sh);
|
|
444
448
|
|
|
445
|
-
|
|
449
|
+
SH_ALWAYS_INLINE void swiss_insert_rehash(SwissHash *sh, uint64_t hash, VALUE key, VALUE value) {
|
|
446
450
|
uint8_t h2 = H2(hash);
|
|
447
451
|
ProbeSeq ps = probe_start(H1(hash), sh->group_mask);
|
|
448
452
|
|
|
@@ -474,7 +478,7 @@ static void swiss_compact(SwissHash *sh) {
|
|
|
474
478
|
Slot *old_slots = sh->slots;
|
|
475
479
|
|
|
476
480
|
sh->ctrl = (uint8_t *)malloc(cap);
|
|
477
|
-
sh->slots = (Slot *)
|
|
481
|
+
sh->slots = (Slot *)malloc(cap * sizeof(Slot));
|
|
478
482
|
|
|
479
483
|
if (!sh->ctrl || !sh->slots) {
|
|
480
484
|
free(sh->ctrl);
|
|
@@ -523,15 +527,12 @@ static VALUE swiss_insert(SwissHash *sh, VALUE key, VALUE value) {
|
|
|
523
527
|
|
|
524
528
|
#if defined(SWISS_USE_SSE2)
|
|
525
529
|
__m128i cv = ctrl_load(sh->ctrl + off);
|
|
526
|
-
|
|
527
|
-
uint32_t empty = ctrl_match_empty_vec(cv);
|
|
528
|
-
uint32_t avail = ctrl_match_empty_or_deleted_vec(cv);
|
|
529
|
-
#elif defined(SWISS_USE_NEON)
|
|
530
|
-
uint8x8_t cv = ctrl_load(sh->ctrl + off);
|
|
530
|
+
SH_PREFETCH(&sh->slots[off]);
|
|
531
531
|
uint32_t match = ctrl_match_h2_vec(cv, h2);
|
|
532
532
|
uint32_t empty = ctrl_match_empty_vec(cv);
|
|
533
533
|
uint32_t avail = ctrl_match_empty_or_deleted_vec(cv);
|
|
534
534
|
#else
|
|
535
|
+
SH_PREFETCH(&sh->slots[off]);
|
|
535
536
|
uint32_t match = ctrl_match_h2_raw(sh->ctrl + off, h2);
|
|
536
537
|
uint32_t empty = ctrl_match_empty_raw(sh->ctrl + off);
|
|
537
538
|
uint32_t avail = ctrl_match_empty_or_deleted_raw(sh->ctrl + off);
|
|
@@ -539,8 +540,9 @@ static VALUE swiss_insert(SwissHash *sh, VALUE key, VALUE value) {
|
|
|
539
540
|
while (match) {
|
|
540
541
|
int slot = ctz32(match);
|
|
541
542
|
size_t idx = off + slot;
|
|
542
|
-
|
|
543
|
-
|
|
543
|
+
Slot *s = &sh->slots[idx];
|
|
544
|
+
if (RB_LIKELY(s->key == key) || keys_equal(s->key, key)) {
|
|
545
|
+
s->value = value;
|
|
544
546
|
return value;
|
|
545
547
|
}
|
|
546
548
|
match &= match - 1;
|
|
@@ -581,21 +583,20 @@ static VALUE swiss_delete(SwissHash *sh, VALUE key) {
|
|
|
581
583
|
|
|
582
584
|
#if defined(SWISS_USE_SSE2)
|
|
583
585
|
__m128i cv = ctrl_load(sh->ctrl + off);
|
|
584
|
-
|
|
585
|
-
uint32_t empty = ctrl_match_empty_vec(cv);
|
|
586
|
-
#elif defined(SWISS_USE_NEON)
|
|
587
|
-
uint8x8_t cv = ctrl_load(sh->ctrl + off);
|
|
586
|
+
SH_PREFETCH(&sh->slots[off]);
|
|
588
587
|
uint32_t match = ctrl_match_h2_vec(cv, h2);
|
|
589
588
|
uint32_t empty = ctrl_match_empty_vec(cv);
|
|
590
589
|
#else
|
|
590
|
+
SH_PREFETCH(&sh->slots[off]);
|
|
591
591
|
uint32_t match = ctrl_match_h2_raw(sh->ctrl + off, h2);
|
|
592
592
|
uint32_t empty = ctrl_match_empty_raw(sh->ctrl + off);
|
|
593
593
|
#endif
|
|
594
594
|
while (match) {
|
|
595
595
|
int slot = ctz32(match);
|
|
596
596
|
size_t idx = off + slot;
|
|
597
|
-
|
|
598
|
-
|
|
597
|
+
Slot *s = &sh->slots[idx];
|
|
598
|
+
if (RB_LIKELY(s->key == key) || keys_equal(s->key, key)) {
|
|
599
|
+
VALUE old_value = s->value;
|
|
599
600
|
|
|
600
601
|
MUTATE_GUARD_BEGIN(sh);
|
|
601
602
|
sh->ctrl[idx] = CTRL_DELETED;
|
|
@@ -627,7 +628,7 @@ static void swiss_grow(SwissHash *sh) {
|
|
|
627
628
|
size_t new_cap = new_num_groups * GROUP_SIZE;
|
|
628
629
|
|
|
629
630
|
sh->ctrl = (uint8_t *)malloc(new_cap);
|
|
630
|
-
sh->slots = (Slot *)
|
|
631
|
+
sh->slots = (Slot *)malloc(new_cap * sizeof(Slot));
|
|
631
632
|
|
|
632
633
|
if (!sh->ctrl || !sh->slots) {
|
|
633
634
|
free(sh->ctrl);
|
|
@@ -717,9 +718,38 @@ static VALUE swiss_hash_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
717
718
|
return self;
|
|
718
719
|
}
|
|
719
720
|
|
|
721
|
+
static VALUE swiss_hash_initialize_copy(VALUE self, VALUE original) {
|
|
722
|
+
if (self == original) {
|
|
723
|
+
return self;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
SwissHash *src;
|
|
727
|
+
SwissHash *dst;
|
|
728
|
+
TypedData_Get_Struct(original, SwissHash, &swiss_hash_type, src);
|
|
729
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, dst);
|
|
730
|
+
|
|
731
|
+
if (dst->ctrl || dst->slots) {
|
|
732
|
+
swiss_free_arrays(dst);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
if (!src->ctrl || !src->slots) {
|
|
736
|
+
swiss_init(dst, 16);
|
|
737
|
+
return self;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
swiss_init(dst, src->capacity);
|
|
741
|
+
memcpy(dst->ctrl, src->ctrl, src->capacity * sizeof(uint8_t));
|
|
742
|
+
memcpy(dst->slots, src->slots, src->capacity * sizeof(Slot));
|
|
743
|
+
dst->size = src->size;
|
|
744
|
+
dst->growth_left = src->growth_left;
|
|
745
|
+
dst->tombstone_count = src->tombstone_count;
|
|
746
|
+
dst->mutating = 0;
|
|
747
|
+
|
|
748
|
+
return self;
|
|
749
|
+
}
|
|
750
|
+
|
|
720
751
|
static VALUE swiss_hash_aset(VALUE self, VALUE key, VALUE value) {
|
|
721
|
-
SwissHash *sh;
|
|
722
|
-
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
752
|
+
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
723
753
|
if (RB_UNLIKELY(!(FIXNUM_P(key) || SYMBOL_P(key)))) {
|
|
724
754
|
key = prepare_key(key);
|
|
725
755
|
}
|
|
@@ -727,15 +757,13 @@ static VALUE swiss_hash_aset(VALUE self, VALUE key, VALUE value) {
|
|
|
727
757
|
}
|
|
728
758
|
|
|
729
759
|
static VALUE swiss_hash_aref(VALUE self, VALUE key) {
|
|
730
|
-
SwissHash *sh;
|
|
731
|
-
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
760
|
+
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
732
761
|
VALUE *val = swiss_lookup(sh, key);
|
|
733
762
|
return val ? *val : Qnil;
|
|
734
763
|
}
|
|
735
764
|
|
|
736
765
|
static VALUE swiss_hash_delete(VALUE self, VALUE key) {
|
|
737
|
-
SwissHash *sh;
|
|
738
|
-
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
766
|
+
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
739
767
|
return swiss_delete(sh, key);
|
|
740
768
|
}
|
|
741
769
|
|
|
@@ -808,16 +836,546 @@ static VALUE swiss_hash_values(VALUE self) {
|
|
|
808
836
|
return ary;
|
|
809
837
|
}
|
|
810
838
|
|
|
811
|
-
static VALUE
|
|
839
|
+
static VALUE swiss_hash_each_key(VALUE self) {
|
|
812
840
|
SwissHash *sh;
|
|
813
841
|
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
842
|
+
|
|
843
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
844
|
+
|
|
845
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
846
|
+
uint8_t c = sh->ctrl[i];
|
|
847
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
848
|
+
rb_yield(sh->slots[i].key);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
return self;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
static VALUE swiss_hash_each_value(VALUE self) {
|
|
856
|
+
SwissHash *sh;
|
|
857
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
858
|
+
|
|
859
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
860
|
+
|
|
861
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
862
|
+
uint8_t c = sh->ctrl[i];
|
|
863
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
864
|
+
rb_yield(sh->slots[i].value);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
return self;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
static VALUE swiss_hash_to_h(VALUE self) {
|
|
872
|
+
SwissHash *sh;
|
|
873
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
874
|
+
|
|
875
|
+
VALUE hash = rb_hash_new();
|
|
876
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
877
|
+
uint8_t c = sh->ctrl[i];
|
|
878
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
879
|
+
rb_hash_aset(hash, sh->slots[i].key, sh->slots[i].value);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
return hash;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
static VALUE swiss_hash_to_a(VALUE self) {
|
|
887
|
+
SwissHash *sh;
|
|
888
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
889
|
+
|
|
890
|
+
VALUE ary = rb_ary_new_capa(sh->size);
|
|
891
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
892
|
+
uint8_t c = sh->ctrl[i];
|
|
893
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
894
|
+
VALUE pair = rb_ary_new_capa(2);
|
|
895
|
+
rb_ary_push(pair, sh->slots[i].key);
|
|
896
|
+
rb_ary_push(pair, sh->slots[i].value);
|
|
897
|
+
rb_ary_push(ary, pair);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
return ary;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
static VALUE swiss_hash_fetch(int argc, VALUE *argv, VALUE self) {
|
|
905
|
+
VALUE key;
|
|
906
|
+
VALUE default_value;
|
|
907
|
+
rb_scan_args(argc, argv, "11", &key, &default_value);
|
|
908
|
+
|
|
909
|
+
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
910
|
+
VALUE *val = swiss_lookup(sh, key);
|
|
911
|
+
if (val) {
|
|
912
|
+
return *val;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
if (rb_block_given_p()) {
|
|
916
|
+
if (argc == 2) {
|
|
917
|
+
rb_warn("block supersedes default value argument");
|
|
918
|
+
}
|
|
919
|
+
return rb_yield(key);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
if (argc == 2) {
|
|
923
|
+
return default_value;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
VALUE inspected = rb_inspect(key);
|
|
927
|
+
VALUE message = rb_str_plus(rb_str_new_cstr("key not found: "), inspected);
|
|
928
|
+
rb_exc_raise(rb_exc_new_str(rb_eKeyError, message));
|
|
929
|
+
|
|
930
|
+
return Qnil;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
static VALUE swiss_hash_values_at(int argc, VALUE *argv, VALUE self) {
|
|
934
|
+
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
935
|
+
VALUE ary = rb_ary_new_capa((long)argc);
|
|
936
|
+
|
|
937
|
+
for (int i = 0; i < argc; i++) {
|
|
938
|
+
VALUE *val = swiss_lookup(sh, argv[i]);
|
|
939
|
+
rb_ary_push(ary, val ? *val : Qnil);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
return ary;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
static VALUE swiss_hash_value_p(VALUE self, VALUE value) {
|
|
946
|
+
SwissHash *sh;
|
|
947
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
948
|
+
|
|
949
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
950
|
+
uint8_t c = sh->ctrl[i];
|
|
951
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
952
|
+
if (RTEST(rb_equal(sh->slots[i].value, value))) {
|
|
953
|
+
return Qtrue;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
return Qfalse;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
static VALUE swiss_hash_key_for_value(VALUE self, VALUE value) {
|
|
962
|
+
SwissHash *sh;
|
|
963
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
964
|
+
|
|
965
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
966
|
+
uint8_t c = sh->ctrl[i];
|
|
967
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
968
|
+
if (RTEST(rb_equal(sh->slots[i].value, value))) {
|
|
969
|
+
return sh->slots[i].key;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
return Qnil;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
static VALUE swiss_hash_key_p(VALUE self, VALUE key) {
|
|
978
|
+
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
814
979
|
VALUE *val = swiss_lookup(sh, key);
|
|
815
980
|
return val ? Qtrue : Qfalse;
|
|
816
981
|
}
|
|
817
982
|
|
|
983
|
+
static VALUE swiss_hash_new_like(VALUE self, size_t capacity) {
|
|
984
|
+
VALUE argv[1] = {SIZET2NUM(capacity)};
|
|
985
|
+
return rb_class_new_instance(1, argv, rb_obj_class(self));
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
static VALUE swiss_hash_store_prepared(SwissHash *sh, VALUE key, VALUE value) {
|
|
989
|
+
if (RB_UNLIKELY(!(FIXNUM_P(key) || SYMBOL_P(key)))) {
|
|
990
|
+
key = prepare_key(key);
|
|
991
|
+
}
|
|
992
|
+
return swiss_insert(sh, key, value);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
typedef struct {
|
|
996
|
+
VALUE self;
|
|
997
|
+
SwissHash *sh;
|
|
998
|
+
int has_block;
|
|
999
|
+
} MergeCtx;
|
|
1000
|
+
|
|
1001
|
+
static void swiss_hash_merge_one_pair(MergeCtx *ctx, VALUE key, VALUE value) {
|
|
1002
|
+
if (ctx->has_block) {
|
|
1003
|
+
VALUE *old = swiss_lookup(ctx->sh, key);
|
|
1004
|
+
if (old) {
|
|
1005
|
+
value = rb_yield_values(3, key, *old, value);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
swiss_hash_store_prepared(ctx->sh, key, value);
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
static int swiss_hash_merge_ruby_hash_i(VALUE key, VALUE value, VALUE arg) {
|
|
1012
|
+
swiss_hash_merge_one_pair((MergeCtx *)arg, key, value);
|
|
1013
|
+
return ST_CONTINUE;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
static void swiss_hash_merge_swiss_hash(MergeCtx *ctx, VALUE other) {
|
|
1017
|
+
SwissHash *src;
|
|
1018
|
+
TypedData_Get_Struct(other, SwissHash, &swiss_hash_type, src);
|
|
1019
|
+
|
|
1020
|
+
for (size_t i = 0; i < src->capacity; i++) {
|
|
1021
|
+
uint8_t c = src->ctrl[i];
|
|
1022
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1023
|
+
swiss_hash_merge_one_pair(ctx, src->slots[i].key, src->slots[i].value);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
static void swiss_hash_merge_one(MergeCtx *ctx, VALUE other) {
|
|
1029
|
+
if (RTEST(rb_obj_is_kind_of(other, cSwissHashHash))) {
|
|
1030
|
+
swiss_hash_merge_swiss_hash(ctx, other);
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
VALUE hash = rb_check_hash_type(other);
|
|
1035
|
+
if (NIL_P(hash)) {
|
|
1036
|
+
rb_raise(rb_eTypeError, "no implicit conversion of %s into Hash", rb_obj_classname(other));
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
rb_hash_foreach(hash, swiss_hash_merge_ruby_hash_i, (VALUE)ctx);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
static VALUE swiss_hash_merge_bang(int argc, VALUE *argv, VALUE self) {
|
|
1043
|
+
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
1044
|
+
MergeCtx ctx = {self, sh, rb_block_given_p()};
|
|
1045
|
+
|
|
1046
|
+
for (int i = 0; i < argc; i++) {
|
|
1047
|
+
swiss_hash_merge_one(&ctx, argv[i]);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
return self;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
static VALUE swiss_hash_merge(int argc, VALUE *argv, VALUE self) {
|
|
1054
|
+
VALUE copy = rb_obj_dup(self);
|
|
1055
|
+
swiss_hash_merge_bang(argc, argv, copy);
|
|
1056
|
+
return copy;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
static VALUE swiss_hash_replace(VALUE self, VALUE other) {
|
|
1060
|
+
swiss_hash_clear(self);
|
|
1061
|
+
VALUE argv[1] = {other};
|
|
1062
|
+
swiss_hash_merge_bang(1, argv, self);
|
|
1063
|
+
return self;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
static VALUE swiss_hash_to_sh(VALUE self) {
|
|
1067
|
+
return rb_obj_dup(self);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
static VALUE swiss_hash_fetch_values(int argc, VALUE *argv, VALUE self) {
|
|
1071
|
+
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
1072
|
+
VALUE ary = rb_ary_new_capa((long)argc);
|
|
1073
|
+
int has_block = rb_block_given_p();
|
|
1074
|
+
|
|
1075
|
+
for (int i = 0; i < argc; i++) {
|
|
1076
|
+
VALUE *val = swiss_lookup(sh, argv[i]);
|
|
1077
|
+
if (val) {
|
|
1078
|
+
rb_ary_push(ary, *val);
|
|
1079
|
+
} else if (has_block) {
|
|
1080
|
+
rb_ary_push(ary, rb_yield(argv[i]));
|
|
1081
|
+
} else {
|
|
1082
|
+
VALUE key_argv[1] = {argv[i]};
|
|
1083
|
+
rb_ary_push(ary, swiss_hash_fetch(1, key_argv, self));
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
return ary;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
static VALUE swiss_hash_slice(int argc, VALUE *argv, VALUE self) {
|
|
1091
|
+
SwissHash *src = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
1092
|
+
VALUE result = swiss_hash_new_like(self, (size_t)argc);
|
|
1093
|
+
SwissHash *dst = (SwissHash *)RTYPEDDATA_DATA(result);
|
|
1094
|
+
|
|
1095
|
+
for (int i = 0; i < argc; i++) {
|
|
1096
|
+
VALUE *val = swiss_lookup(src, argv[i]);
|
|
1097
|
+
if (val) {
|
|
1098
|
+
swiss_hash_store_prepared(dst, argv[i], *val);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
return result;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
static VALUE swiss_hash_except(int argc, VALUE *argv, VALUE self) {
|
|
1106
|
+
VALUE result = rb_obj_dup(self);
|
|
1107
|
+
for (int i = 0; i < argc; i++) {
|
|
1108
|
+
swiss_hash_delete(result, argv[i]);
|
|
1109
|
+
}
|
|
1110
|
+
return result;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
static VALUE swiss_hash_invert(VALUE self) {
|
|
1114
|
+
SwissHash *src;
|
|
1115
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, src);
|
|
1116
|
+
VALUE result = swiss_hash_new_like(self, src->size);
|
|
1117
|
+
SwissHash *dst = (SwissHash *)RTYPEDDATA_DATA(result);
|
|
1118
|
+
|
|
1119
|
+
for (size_t i = 0; i < src->capacity; i++) {
|
|
1120
|
+
uint8_t c = src->ctrl[i];
|
|
1121
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1122
|
+
swiss_hash_store_prepared(dst, src->slots[i].value, src->slots[i].key);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
return result;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
static VALUE swiss_hash_assoc(VALUE self, VALUE object) {
|
|
1130
|
+
SwissHash *sh;
|
|
1131
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1132
|
+
|
|
1133
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1134
|
+
uint8_t c = sh->ctrl[i];
|
|
1135
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1136
|
+
if (RTEST(rb_equal(sh->slots[i].key, object))) {
|
|
1137
|
+
VALUE ary = rb_ary_new_capa(2);
|
|
1138
|
+
rb_ary_push(ary, sh->slots[i].key);
|
|
1139
|
+
rb_ary_push(ary, sh->slots[i].value);
|
|
1140
|
+
return ary;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
return Qnil;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
static VALUE swiss_hash_rassoc(VALUE self, VALUE object) {
|
|
1149
|
+
SwissHash *sh;
|
|
1150
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1151
|
+
|
|
1152
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1153
|
+
uint8_t c = sh->ctrl[i];
|
|
1154
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1155
|
+
if (RTEST(rb_equal(sh->slots[i].value, object))) {
|
|
1156
|
+
VALUE ary = rb_ary_new_capa(2);
|
|
1157
|
+
rb_ary_push(ary, sh->slots[i].key);
|
|
1158
|
+
rb_ary_push(ary, sh->slots[i].value);
|
|
1159
|
+
return ary;
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
return Qnil;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
static VALUE swiss_hash_shift(VALUE self) {
|
|
1168
|
+
SwissHash *sh;
|
|
1169
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1170
|
+
|
|
1171
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1172
|
+
uint8_t c = sh->ctrl[i];
|
|
1173
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1174
|
+
VALUE key = sh->slots[i].key;
|
|
1175
|
+
VALUE value = sh->slots[i].value;
|
|
1176
|
+
sh->ctrl[i] = CTRL_DELETED;
|
|
1177
|
+
sh->slots[i].key = Qnil;
|
|
1178
|
+
sh->slots[i].value = Qnil;
|
|
1179
|
+
sh->size--;
|
|
1180
|
+
sh->tombstone_count++;
|
|
1181
|
+
|
|
1182
|
+
VALUE ary = rb_ary_new_capa(2);
|
|
1183
|
+
rb_ary_push(ary, key);
|
|
1184
|
+
rb_ary_push(ary, value);
|
|
1185
|
+
return ary;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
return Qnil;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
static VALUE swiss_hash_delete_if(VALUE self) {
|
|
1193
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1194
|
+
|
|
1195
|
+
SwissHash *sh;
|
|
1196
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1197
|
+
|
|
1198
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1199
|
+
uint8_t c = sh->ctrl[i];
|
|
1200
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1201
|
+
VALUE key = sh->slots[i].key;
|
|
1202
|
+
VALUE value = sh->slots[i].value;
|
|
1203
|
+
if (RTEST(rb_yield_values(2, key, value))) {
|
|
1204
|
+
swiss_delete(sh, key);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
return self;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
static VALUE swiss_hash_keep_if(VALUE self) {
|
|
1213
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1214
|
+
|
|
1215
|
+
SwissHash *sh;
|
|
1216
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1217
|
+
|
|
1218
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1219
|
+
uint8_t c = sh->ctrl[i];
|
|
1220
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1221
|
+
VALUE key = sh->slots[i].key;
|
|
1222
|
+
VALUE value = sh->slots[i].value;
|
|
1223
|
+
if (!RTEST(rb_yield_values(2, key, value))) {
|
|
1224
|
+
swiss_delete(sh, key);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
return self;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
static VALUE swiss_hash_select(VALUE self) {
|
|
1233
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1234
|
+
|
|
1235
|
+
SwissHash *src;
|
|
1236
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, src);
|
|
1237
|
+
VALUE result = swiss_hash_new_like(self, src->size);
|
|
1238
|
+
SwissHash *dst = (SwissHash *)RTYPEDDATA_DATA(result);
|
|
1239
|
+
|
|
1240
|
+
for (size_t i = 0; i < src->capacity; i++) {
|
|
1241
|
+
uint8_t c = src->ctrl[i];
|
|
1242
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1243
|
+
VALUE key = src->slots[i].key;
|
|
1244
|
+
VALUE value = src->slots[i].value;
|
|
1245
|
+
if (RTEST(rb_yield_values(2, key, value))) {
|
|
1246
|
+
swiss_hash_store_prepared(dst, key, value);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
return result;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
static VALUE swiss_hash_reject(VALUE self) {
|
|
1255
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1256
|
+
|
|
1257
|
+
SwissHash *src;
|
|
1258
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, src);
|
|
1259
|
+
VALUE result = swiss_hash_new_like(self, src->size);
|
|
1260
|
+
SwissHash *dst = (SwissHash *)RTYPEDDATA_DATA(result);
|
|
1261
|
+
|
|
1262
|
+
for (size_t i = 0; i < src->capacity; i++) {
|
|
1263
|
+
uint8_t c = src->ctrl[i];
|
|
1264
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1265
|
+
VALUE key = src->slots[i].key;
|
|
1266
|
+
VALUE value = src->slots[i].value;
|
|
1267
|
+
if (!RTEST(rb_yield_values(2, key, value))) {
|
|
1268
|
+
swiss_hash_store_prepared(dst, key, value);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
return result;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
static VALUE swiss_hash_select_bang(VALUE self) {
|
|
1277
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1278
|
+
|
|
1279
|
+
size_t old_size = ((SwissHash *)RTYPEDDATA_DATA(self))->size;
|
|
1280
|
+
swiss_hash_keep_if(self);
|
|
1281
|
+
return ((SwissHash *)RTYPEDDATA_DATA(self))->size == old_size ? Qnil : self;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
static VALUE swiss_hash_reject_bang(VALUE self) {
|
|
1285
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1286
|
+
|
|
1287
|
+
size_t old_size = ((SwissHash *)RTYPEDDATA_DATA(self))->size;
|
|
1288
|
+
swiss_hash_delete_if(self);
|
|
1289
|
+
return ((SwissHash *)RTYPEDDATA_DATA(self))->size == old_size ? Qnil : self;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
static VALUE swiss_hash_compact(VALUE self) {
|
|
1293
|
+
SwissHash *src;
|
|
1294
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, src);
|
|
1295
|
+
VALUE result = swiss_hash_new_like(self, src->size);
|
|
1296
|
+
SwissHash *dst = (SwissHash *)RTYPEDDATA_DATA(result);
|
|
1297
|
+
|
|
1298
|
+
for (size_t i = 0; i < src->capacity; i++) {
|
|
1299
|
+
uint8_t c = src->ctrl[i];
|
|
1300
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED && !NIL_P(src->slots[i].value)) {
|
|
1301
|
+
swiss_hash_store_prepared(dst, src->slots[i].key, src->slots[i].value);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
return result;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
818
1308
|
static VALUE swiss_hash_compact_bang(VALUE self) {
|
|
819
1309
|
SwissHash *sh;
|
|
820
1310
|
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1311
|
+
size_t old_size = sh->size;
|
|
1312
|
+
|
|
1313
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1314
|
+
uint8_t c = sh->ctrl[i];
|
|
1315
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED && NIL_P(sh->slots[i].value)) {
|
|
1316
|
+
VALUE key = sh->slots[i].key;
|
|
1317
|
+
swiss_delete(sh, key);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
return sh->size == old_size ? Qnil : self;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
static VALUE swiss_hash_transform_values(VALUE self) {
|
|
1325
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1326
|
+
|
|
1327
|
+
SwissHash *src;
|
|
1328
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, src);
|
|
1329
|
+
VALUE result = swiss_hash_new_like(self, src->size);
|
|
1330
|
+
SwissHash *dst = (SwissHash *)RTYPEDDATA_DATA(result);
|
|
1331
|
+
|
|
1332
|
+
for (size_t i = 0; i < src->capacity; i++) {
|
|
1333
|
+
uint8_t c = src->ctrl[i];
|
|
1334
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1335
|
+
swiss_hash_store_prepared(dst, src->slots[i].key, rb_yield(src->slots[i].value));
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
return result;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
static VALUE swiss_hash_transform_values_bang(VALUE self) {
|
|
1343
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1344
|
+
|
|
1345
|
+
SwissHash *sh;
|
|
1346
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
1347
|
+
|
|
1348
|
+
for (size_t i = 0; i < sh->capacity; i++) {
|
|
1349
|
+
uint8_t c = sh->ctrl[i];
|
|
1350
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1351
|
+
sh->slots[i].value = rb_yield(sh->slots[i].value);
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
return self;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
static VALUE swiss_hash_transform_keys(VALUE self) {
|
|
1359
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
1360
|
+
|
|
1361
|
+
SwissHash *src;
|
|
1362
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, src);
|
|
1363
|
+
VALUE result = swiss_hash_new_like(self, src->size);
|
|
1364
|
+
SwissHash *dst = (SwissHash *)RTYPEDDATA_DATA(result);
|
|
1365
|
+
|
|
1366
|
+
for (size_t i = 0; i < src->capacity; i++) {
|
|
1367
|
+
uint8_t c = src->ctrl[i];
|
|
1368
|
+
if (c != CTRL_EMPTY && c != CTRL_DELETED) {
|
|
1369
|
+
swiss_hash_store_prepared(dst, rb_yield(src->slots[i].key), src->slots[i].value);
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
return result;
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
static VALUE swiss_hash_compact_storage_bang(VALUE self) {
|
|
1377
|
+
SwissHash *sh;
|
|
1378
|
+
TypedData_Get_Struct(self, SwissHash, &swiss_hash_type, sh);
|
|
821
1379
|
|
|
822
1380
|
if (sh->tombstone_count > 0) {
|
|
823
1381
|
swiss_compact(sh);
|
|
@@ -843,10 +1401,8 @@ static VALUE swiss_hash_stats(VALUE self) {
|
|
|
843
1401
|
|
|
844
1402
|
#ifdef SWISS_USE_SSE2
|
|
845
1403
|
rb_hash_aset(hash, ID2SYM(rb_intern("simd")), rb_str_new_cstr("SSE2"));
|
|
846
|
-
#elif defined(SWISS_USE_NEON)
|
|
847
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("simd")), rb_str_new_cstr("NEON"));
|
|
848
1404
|
#else
|
|
849
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("simd")), rb_str_new_cstr("
|
|
1405
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("simd")), rb_str_new_cstr("SWAR"));
|
|
850
1406
|
#endif
|
|
851
1407
|
rb_hash_aset(hash, ID2SYM(rb_intern("layout")), rb_str_new_cstr("hybrid"));
|
|
852
1408
|
|
|
@@ -858,9 +1414,11 @@ void Init_swiss_hash(void) {
|
|
|
858
1414
|
|
|
859
1415
|
VALUE mSwissHash = rb_define_module("SwissHash");
|
|
860
1416
|
VALUE cHash = rb_define_class_under(mSwissHash, "Hash", rb_cObject);
|
|
1417
|
+
cSwissHashHash = cHash;
|
|
861
1418
|
|
|
862
1419
|
rb_define_alloc_func(cHash, swiss_hash_alloc);
|
|
863
1420
|
rb_define_method(cHash, "initialize", swiss_hash_initialize, -1);
|
|
1421
|
+
rb_define_method(cHash, "initialize_copy", swiss_hash_initialize_copy, 1);
|
|
864
1422
|
rb_define_method(cHash, "[]=", swiss_hash_aset, 2);
|
|
865
1423
|
rb_define_method(cHash, "store", swiss_hash_aset, 2);
|
|
866
1424
|
rb_define_method(cHash, "[]", swiss_hash_aref, 1);
|
|
@@ -870,11 +1428,48 @@ void Init_swiss_hash(void) {
|
|
|
870
1428
|
rb_define_method(cHash, "empty?", swiss_hash_empty_p, 0);
|
|
871
1429
|
rb_define_method(cHash, "clear", swiss_hash_clear, 0);
|
|
872
1430
|
rb_define_method(cHash, "each", swiss_hash_each, 0);
|
|
1431
|
+
rb_define_method(cHash, "each_pair", swiss_hash_each, 0);
|
|
1432
|
+
rb_define_method(cHash, "each_key", swiss_hash_each_key, 0);
|
|
1433
|
+
rb_define_method(cHash, "each_value", swiss_hash_each_value, 0);
|
|
873
1434
|
rb_define_method(cHash, "keys", swiss_hash_keys, 0);
|
|
874
1435
|
rb_define_method(cHash, "values", swiss_hash_values, 0);
|
|
1436
|
+
rb_define_method(cHash, "to_h", swiss_hash_to_h, 0);
|
|
1437
|
+
rb_define_method(cHash, "to_a", swiss_hash_to_a, 0);
|
|
1438
|
+
rb_define_method(cHash, "fetch", swiss_hash_fetch, -1);
|
|
1439
|
+
rb_define_method(cHash, "values_at", swiss_hash_values_at, -1);
|
|
1440
|
+
rb_define_method(cHash, "fetch_values", swiss_hash_fetch_values, -1);
|
|
1441
|
+
rb_define_method(cHash, "merge!", swiss_hash_merge_bang, -1);
|
|
1442
|
+
rb_define_method(cHash, "update", swiss_hash_merge_bang, -1);
|
|
1443
|
+
rb_define_method(cHash, "merge", swiss_hash_merge, -1);
|
|
1444
|
+
rb_define_method(cHash, "replace", swiss_hash_replace, 1);
|
|
1445
|
+
rb_define_method(cHash, "to_sh", swiss_hash_to_sh, 0);
|
|
1446
|
+
rb_define_method(cHash, "slice", swiss_hash_slice, -1);
|
|
1447
|
+
rb_define_method(cHash, "except", swiss_hash_except, -1);
|
|
1448
|
+
rb_define_method(cHash, "invert", swiss_hash_invert, 0);
|
|
1449
|
+
rb_define_method(cHash, "assoc", swiss_hash_assoc, 1);
|
|
1450
|
+
rb_define_method(cHash, "rassoc", swiss_hash_rassoc, 1);
|
|
1451
|
+
rb_define_method(cHash, "shift", swiss_hash_shift, 0);
|
|
1452
|
+
rb_define_method(cHash, "delete_if", swiss_hash_delete_if, 0);
|
|
1453
|
+
rb_define_method(cHash, "keep_if", swiss_hash_keep_if, 0);
|
|
1454
|
+
rb_define_method(cHash, "select", swiss_hash_select, 0);
|
|
1455
|
+
rb_define_method(cHash, "filter", swiss_hash_select, 0);
|
|
1456
|
+
rb_define_method(cHash, "reject", swiss_hash_reject, 0);
|
|
1457
|
+
rb_define_method(cHash, "select!", swiss_hash_select_bang, 0);
|
|
1458
|
+
rb_define_method(cHash, "filter!", swiss_hash_select_bang, 0);
|
|
1459
|
+
rb_define_method(cHash, "reject!", swiss_hash_reject_bang, 0);
|
|
1460
|
+
rb_define_method(cHash, "compact", swiss_hash_compact, 0);
|
|
1461
|
+
rb_define_method(cHash, "compact!", swiss_hash_compact_bang, 0);
|
|
1462
|
+
rb_define_method(cHash, "transform_values", swiss_hash_transform_values, 0);
|
|
1463
|
+
rb_define_method(cHash, "transform_values!", swiss_hash_transform_values_bang, 0);
|
|
1464
|
+
rb_define_method(cHash, "transform_keys", swiss_hash_transform_keys, 0);
|
|
875
1465
|
rb_define_method(cHash, "key?", swiss_hash_key_p, 1);
|
|
876
1466
|
rb_define_method(cHash, "has_key?", swiss_hash_key_p, 1);
|
|
877
1467
|
rb_define_method(cHash, "include?", swiss_hash_key_p, 1);
|
|
878
|
-
rb_define_method(cHash, "
|
|
1468
|
+
rb_define_method(cHash, "member?", swiss_hash_key_p, 1);
|
|
1469
|
+
rb_define_method(cHash, "value?", swiss_hash_value_p, 1);
|
|
1470
|
+
rb_define_method(cHash, "has_value?", swiss_hash_value_p, 1);
|
|
1471
|
+
rb_define_method(cHash, "key", swiss_hash_key_for_value, 1);
|
|
1472
|
+
rb_define_method(cHash, "compact_storage!", swiss_hash_compact_storage_bang, 0);
|
|
1473
|
+
rb_define_method(cHash, "__compact_storage!", swiss_hash_compact_storage_bang, 0);
|
|
879
1474
|
rb_define_method(cHash, "stats", swiss_hash_stats, 0);
|
|
880
1475
|
}
|