swiss_hash 0.1.1 → 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/README.md +164 -83
- data/ext/swiss_hash/swiss_hash.c +672 -106
- data/lib/swiss_hash/version.rb +1 -1
- data/lib/swiss_hash.rb +37 -9
- metadata +4 -7
data/ext/swiss_hash/swiss_hash.c
CHANGED
|
@@ -12,6 +12,20 @@
|
|
|
12
12
|
#endif
|
|
13
13
|
#endif
|
|
14
14
|
|
|
15
|
+
#ifndef RB_LIKELY
|
|
16
|
+
#if defined(__GNUC__) || defined(__clang__)
|
|
17
|
+
#define RB_LIKELY(x) __builtin_expect(!!(x), 1)
|
|
18
|
+
#else
|
|
19
|
+
#define RB_LIKELY(x) (x)
|
|
20
|
+
#endif
|
|
21
|
+
#endif
|
|
22
|
+
|
|
23
|
+
#if defined(__GNUC__) || defined(__clang__)
|
|
24
|
+
#define SH_ALWAYS_INLINE static inline __attribute__((always_inline))
|
|
25
|
+
#else
|
|
26
|
+
#define SH_ALWAYS_INLINE static inline
|
|
27
|
+
#endif
|
|
28
|
+
|
|
15
29
|
#if defined(__GNUC__) || defined(__clang__)
|
|
16
30
|
#define SH_PREFETCH(p) __builtin_prefetch((const void *)(p), 0, 1)
|
|
17
31
|
#else
|
|
@@ -20,6 +34,7 @@
|
|
|
20
34
|
|
|
21
35
|
static uint64_t swiss_hash_seed0;
|
|
22
36
|
static uint64_t swiss_hash_seed1;
|
|
37
|
+
static VALUE cSwissHashHash;
|
|
23
38
|
|
|
24
39
|
static void init_hash_seed(void) {
|
|
25
40
|
VALUE seed_val = rb_hash(INT2FIX(0));
|
|
@@ -132,18 +147,12 @@ static inline uint64_t wyhash(const void *data, size_t len, uint64_t seed) {
|
|
|
132
147
|
#define SWISS_USE_SSE2 1
|
|
133
148
|
#include <emmintrin.h>
|
|
134
149
|
|
|
135
|
-
#define GROUP_SIZE
|
|
136
|
-
#define GROUP_MASK
|
|
137
|
-
#elif defined(__aarch64__) || defined(_M_ARM64)
|
|
138
|
-
#define SWISS_USE_NEON 1
|
|
139
|
-
#include <arm_neon.h>
|
|
140
|
-
|
|
141
|
-
#define GROUP_SIZE 8
|
|
142
|
-
#define GROUP_MASK 0xFFu
|
|
150
|
+
#define GROUP_SIZE 16
|
|
151
|
+
#define GROUP_MASK 0xFFFFu
|
|
143
152
|
#else
|
|
144
|
-
#define
|
|
145
|
-
#define GROUP_SIZE
|
|
146
|
-
#define GROUP_MASK
|
|
153
|
+
#define SWISS_USE_SWAR 1
|
|
154
|
+
#define GROUP_SIZE 8
|
|
155
|
+
#define GROUP_MASK 0xFFu
|
|
147
156
|
#endif
|
|
148
157
|
|
|
149
158
|
#ifdef SWISS_USE_SSE2
|
|
@@ -170,80 +179,44 @@ static inline uint32_t ctrl_match_empty(const uint8_t *ctrl) {
|
|
|
170
179
|
return ctrl_match_empty_vec(ctrl_load(ctrl));
|
|
171
180
|
}
|
|
172
181
|
|
|
173
|
-
#
|
|
182
|
+
#else /* SWAR */
|
|
174
183
|
|
|
175
|
-
|
|
176
|
-
|
|
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);
|
|
182
|
-
}
|
|
184
|
+
#define SWAR_LSB 0x0101010101010101ULL
|
|
185
|
+
#define SWAR_MSB 0x8080808080808080ULL
|
|
183
186
|
|
|
184
|
-
|
|
185
|
-
|
|
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);
|
|
186
190
|
}
|
|
187
191
|
|
|
188
|
-
|
|
189
|
-
return neon_movemask(vceq_u8(cv, vdup_n_u8(h2)));
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
static inline uint32_t ctrl_match_empty_vec(uint8x8_t cv) {
|
|
193
|
-
return neon_movemask(vceq_u8(cv, vdup_n_u8(CTRL_EMPTY)));
|
|
194
|
-
}
|
|
195
|
-
|
|
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);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
static inline uint32_t ctrl_match_empty(const uint8_t *ctrl) {
|
|
203
|
-
return ctrl_match_empty_vec(ctrl_load(ctrl));
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
#else /* portable */
|
|
207
|
-
|
|
208
|
-
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) {
|
|
209
193
|
uint64_t c;
|
|
210
194
|
memcpy(&c, ctrl, 8);
|
|
211
|
-
uint64_t broadcast =
|
|
195
|
+
uint64_t broadcast = SWAR_LSB * h2;
|
|
212
196
|
uint64_t xored = c ^ broadcast;
|
|
213
|
-
uint64_t result = (xored -
|
|
214
|
-
|
|
215
|
-
for (int i = 0; i < 8; i++) {
|
|
216
|
-
if (result & (0x80ULL << (i * 8)))
|
|
217
|
-
mask |= (1u << i);
|
|
218
|
-
}
|
|
219
|
-
return mask;
|
|
197
|
+
uint64_t result = (xored - SWAR_LSB) & ~xored & SWAR_MSB;
|
|
198
|
+
return ctrl_bitmask_from_msb(result);
|
|
220
199
|
}
|
|
221
200
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
mask |= (1u << i);
|
|
227
|
-
}
|
|
228
|
-
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);
|
|
229
205
|
}
|
|
230
206
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
mask |= (1u << i);
|
|
236
|
-
}
|
|
237
|
-
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);
|
|
238
211
|
}
|
|
239
212
|
|
|
240
|
-
|
|
213
|
+
SH_ALWAYS_INLINE uint32_t ctrl_match_empty(const uint8_t *ctrl) {
|
|
241
214
|
return ctrl_match_empty_raw(ctrl);
|
|
242
215
|
}
|
|
243
216
|
|
|
244
|
-
#endif /*
|
|
217
|
+
#endif /* control-byte matching selection */
|
|
245
218
|
|
|
246
|
-
|
|
219
|
+
SH_ALWAYS_INLINE int ctz32(uint32_t v) {
|
|
247
220
|
#if defined(__GNUC__) || defined(__clang__)
|
|
248
221
|
return __builtin_ctz(v);
|
|
249
222
|
#elif defined(_MSC_VER)
|
|
@@ -293,7 +266,7 @@ typedef struct {
|
|
|
293
266
|
|
|
294
267
|
#define FIBONACCI_HASH_C 0x9E3779B97F4A7C15ULL
|
|
295
268
|
|
|
296
|
-
|
|
269
|
+
SH_ALWAYS_INLINE uint64_t compute_hash(VALUE key) {
|
|
297
270
|
uint64_t v;
|
|
298
271
|
|
|
299
272
|
if (FIXNUM_P(key)) {
|
|
@@ -327,16 +300,20 @@ static inline uint64_t compute_hash(VALUE key) {
|
|
|
327
300
|
#define H1(hash) ((hash) >> 7)
|
|
328
301
|
#define H2(hash) ((uint8_t)((hash) & H2_MASK))
|
|
329
302
|
|
|
330
|
-
|
|
331
|
-
if (a == b)
|
|
332
|
-
|
|
303
|
+
SH_ALWAYS_INLINE int keys_equal(VALUE a, VALUE b) {
|
|
304
|
+
if (a == b)
|
|
305
|
+
return 1;
|
|
306
|
+
if (FIXNUM_P(a) || SYMBOL_P(a) || SPECIAL_CONST_P(a))
|
|
307
|
+
return 0;
|
|
333
308
|
|
|
334
309
|
if (RB_TYPE_P(a, T_STRING) && RB_TYPE_P(b, T_STRING)) {
|
|
335
310
|
long la = RSTRING_LEN(a);
|
|
336
|
-
if (la != RSTRING_LEN(b))
|
|
311
|
+
if (la != RSTRING_LEN(b))
|
|
312
|
+
return 0;
|
|
337
313
|
const char *pa = RSTRING_PTR(a);
|
|
338
314
|
const char *pb = RSTRING_PTR(b);
|
|
339
|
-
if (pa == pb)
|
|
315
|
+
if (pa == pb)
|
|
316
|
+
return 1;
|
|
340
317
|
|
|
341
318
|
int ea = ENCODING_GET(a);
|
|
342
319
|
int eb = ENCODING_GET(b);
|
|
@@ -344,8 +321,7 @@ static inline int keys_equal(VALUE a, VALUE b) {
|
|
|
344
321
|
return memcmp(pa, pb, (size_t)la) == 0;
|
|
345
322
|
}
|
|
346
323
|
|
|
347
|
-
if (ENC_CODERANGE(a) == ENC_CODERANGE_7BIT &&
|
|
348
|
-
ENC_CODERANGE(b) == ENC_CODERANGE_7BIT) {
|
|
324
|
+
if (ENC_CODERANGE(a) == ENC_CODERANGE_7BIT && ENC_CODERANGE(b) == ENC_CODERANGE_7BIT) {
|
|
349
325
|
return memcmp(pa, pb, (size_t)la) == 0;
|
|
350
326
|
}
|
|
351
327
|
|
|
@@ -358,12 +334,17 @@ static inline int keys_equal(VALUE a, VALUE b) {
|
|
|
358
334
|
return rb_eql(a, b);
|
|
359
335
|
}
|
|
360
336
|
|
|
361
|
-
|
|
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) {
|
|
362
342
|
if (RB_TYPE_P(key, T_STRING)) {
|
|
343
|
+
#if SWISS_HASH_COPY_STRING_KEYS
|
|
363
344
|
if (!OBJ_FROZEN(key)) {
|
|
364
345
|
key = rb_str_new_frozen(key);
|
|
365
346
|
}
|
|
366
|
-
|
|
347
|
+
#endif
|
|
367
348
|
rb_enc_str_coderange(key);
|
|
368
349
|
}
|
|
369
350
|
return key;
|
|
@@ -414,7 +395,7 @@ typedef struct {
|
|
|
414
395
|
size_t group_mask;
|
|
415
396
|
} ProbeSeq;
|
|
416
397
|
|
|
417
|
-
|
|
398
|
+
SH_ALWAYS_INLINE ProbeSeq probe_start(uint64_t h1, size_t group_mask) {
|
|
418
399
|
ProbeSeq ps;
|
|
419
400
|
ps.group_idx = (size_t)(h1)&group_mask;
|
|
420
401
|
ps.stride = 0;
|
|
@@ -422,7 +403,7 @@ static inline ProbeSeq probe_start(uint64_t h1, size_t group_mask) {
|
|
|
422
403
|
return ps;
|
|
423
404
|
}
|
|
424
405
|
|
|
425
|
-
|
|
406
|
+
SH_ALWAYS_INLINE void probe_next(ProbeSeq *ps) {
|
|
426
407
|
ps->stride++;
|
|
427
408
|
ps->group_idx = (ps->group_idx + ps->stride) & ps->group_mask;
|
|
428
409
|
}
|
|
@@ -442,11 +423,6 @@ static VALUE *swiss_lookup(SwissHash *sh, VALUE key) {
|
|
|
442
423
|
SH_PREFETCH(&sh->slots[off]);
|
|
443
424
|
uint32_t match = ctrl_match_h2_vec(cv, h2);
|
|
444
425
|
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
426
|
#else
|
|
451
427
|
SH_PREFETCH(&sh->slots[off]);
|
|
452
428
|
uint32_t match = ctrl_match_h2_raw(sh->ctrl + off, h2);
|
|
@@ -455,7 +431,7 @@ static VALUE *swiss_lookup(SwissHash *sh, VALUE key) {
|
|
|
455
431
|
while (match) {
|
|
456
432
|
int slot = ctz32(match);
|
|
457
433
|
Slot *s = &sh->slots[off + slot];
|
|
458
|
-
if (keys_equal(s->key, key)) {
|
|
434
|
+
if (RB_LIKELY(s->key == key) || keys_equal(s->key, key)) {
|
|
459
435
|
return &s->value;
|
|
460
436
|
}
|
|
461
437
|
match &= match - 1;
|
|
@@ -470,7 +446,7 @@ static VALUE *swiss_lookup(SwissHash *sh, VALUE key) {
|
|
|
470
446
|
static void swiss_grow(SwissHash *sh);
|
|
471
447
|
static void swiss_compact(SwissHash *sh);
|
|
472
448
|
|
|
473
|
-
|
|
449
|
+
SH_ALWAYS_INLINE void swiss_insert_rehash(SwissHash *sh, uint64_t hash, VALUE key, VALUE value) {
|
|
474
450
|
uint8_t h2 = H2(hash);
|
|
475
451
|
ProbeSeq ps = probe_start(H1(hash), sh->group_mask);
|
|
476
452
|
|
|
@@ -555,13 +531,8 @@ static VALUE swiss_insert(SwissHash *sh, VALUE key, VALUE value) {
|
|
|
555
531
|
uint32_t match = ctrl_match_h2_vec(cv, h2);
|
|
556
532
|
uint32_t empty = ctrl_match_empty_vec(cv);
|
|
557
533
|
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);
|
|
564
534
|
#else
|
|
535
|
+
SH_PREFETCH(&sh->slots[off]);
|
|
565
536
|
uint32_t match = ctrl_match_h2_raw(sh->ctrl + off, h2);
|
|
566
537
|
uint32_t empty = ctrl_match_empty_raw(sh->ctrl + off);
|
|
567
538
|
uint32_t avail = ctrl_match_empty_or_deleted_raw(sh->ctrl + off);
|
|
@@ -569,8 +540,9 @@ static VALUE swiss_insert(SwissHash *sh, VALUE key, VALUE value) {
|
|
|
569
540
|
while (match) {
|
|
570
541
|
int slot = ctz32(match);
|
|
571
542
|
size_t idx = off + slot;
|
|
572
|
-
|
|
573
|
-
|
|
543
|
+
Slot *s = &sh->slots[idx];
|
|
544
|
+
if (RB_LIKELY(s->key == key) || keys_equal(s->key, key)) {
|
|
545
|
+
s->value = value;
|
|
574
546
|
return value;
|
|
575
547
|
}
|
|
576
548
|
match &= match - 1;
|
|
@@ -614,11 +586,6 @@ static VALUE swiss_delete(SwissHash *sh, VALUE key) {
|
|
|
614
586
|
SH_PREFETCH(&sh->slots[off]);
|
|
615
587
|
uint32_t match = ctrl_match_h2_vec(cv, h2);
|
|
616
588
|
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
589
|
#else
|
|
623
590
|
SH_PREFETCH(&sh->slots[off]);
|
|
624
591
|
uint32_t match = ctrl_match_h2_raw(sh->ctrl + off, h2);
|
|
@@ -627,8 +594,9 @@ static VALUE swiss_delete(SwissHash *sh, VALUE key) {
|
|
|
627
594
|
while (match) {
|
|
628
595
|
int slot = ctz32(match);
|
|
629
596
|
size_t idx = off + slot;
|
|
630
|
-
|
|
631
|
-
|
|
597
|
+
Slot *s = &sh->slots[idx];
|
|
598
|
+
if (RB_LIKELY(s->key == key) || keys_equal(s->key, key)) {
|
|
599
|
+
VALUE old_value = s->value;
|
|
632
600
|
|
|
633
601
|
MUTATE_GUARD_BEGIN(sh);
|
|
634
602
|
sh->ctrl[idx] = CTRL_DELETED;
|
|
@@ -750,6 +718,36 @@ static VALUE swiss_hash_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
750
718
|
return self;
|
|
751
719
|
}
|
|
752
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
|
+
|
|
753
751
|
static VALUE swiss_hash_aset(VALUE self, VALUE key, VALUE value) {
|
|
754
752
|
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
755
753
|
if (RB_UNLIKELY(!(FIXNUM_P(key) || SYMBOL_P(key)))) {
|
|
@@ -838,15 +836,546 @@ static VALUE swiss_hash_values(VALUE self) {
|
|
|
838
836
|
return ary;
|
|
839
837
|
}
|
|
840
838
|
|
|
839
|
+
static VALUE swiss_hash_each_key(VALUE self) {
|
|
840
|
+
SwissHash *sh;
|
|
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
|
+
|
|
841
977
|
static VALUE swiss_hash_key_p(VALUE self, VALUE key) {
|
|
842
978
|
SwissHash *sh = (SwissHash *)RTYPEDDATA_DATA(self);
|
|
843
979
|
VALUE *val = swiss_lookup(sh, key);
|
|
844
980
|
return val ? Qtrue : Qfalse;
|
|
845
981
|
}
|
|
846
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
|
+
|
|
847
1308
|
static VALUE swiss_hash_compact_bang(VALUE self) {
|
|
848
1309
|
SwissHash *sh;
|
|
849
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);
|
|
850
1379
|
|
|
851
1380
|
if (sh->tombstone_count > 0) {
|
|
852
1381
|
swiss_compact(sh);
|
|
@@ -872,10 +1401,8 @@ static VALUE swiss_hash_stats(VALUE self) {
|
|
|
872
1401
|
|
|
873
1402
|
#ifdef SWISS_USE_SSE2
|
|
874
1403
|
rb_hash_aset(hash, ID2SYM(rb_intern("simd")), rb_str_new_cstr("SSE2"));
|
|
875
|
-
#elif defined(SWISS_USE_NEON)
|
|
876
|
-
rb_hash_aset(hash, ID2SYM(rb_intern("simd")), rb_str_new_cstr("NEON"));
|
|
877
1404
|
#else
|
|
878
|
-
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"));
|
|
879
1406
|
#endif
|
|
880
1407
|
rb_hash_aset(hash, ID2SYM(rb_intern("layout")), rb_str_new_cstr("hybrid"));
|
|
881
1408
|
|
|
@@ -887,9 +1414,11 @@ void Init_swiss_hash(void) {
|
|
|
887
1414
|
|
|
888
1415
|
VALUE mSwissHash = rb_define_module("SwissHash");
|
|
889
1416
|
VALUE cHash = rb_define_class_under(mSwissHash, "Hash", rb_cObject);
|
|
1417
|
+
cSwissHashHash = cHash;
|
|
890
1418
|
|
|
891
1419
|
rb_define_alloc_func(cHash, swiss_hash_alloc);
|
|
892
1420
|
rb_define_method(cHash, "initialize", swiss_hash_initialize, -1);
|
|
1421
|
+
rb_define_method(cHash, "initialize_copy", swiss_hash_initialize_copy, 1);
|
|
893
1422
|
rb_define_method(cHash, "[]=", swiss_hash_aset, 2);
|
|
894
1423
|
rb_define_method(cHash, "store", swiss_hash_aset, 2);
|
|
895
1424
|
rb_define_method(cHash, "[]", swiss_hash_aref, 1);
|
|
@@ -899,11 +1428,48 @@ void Init_swiss_hash(void) {
|
|
|
899
1428
|
rb_define_method(cHash, "empty?", swiss_hash_empty_p, 0);
|
|
900
1429
|
rb_define_method(cHash, "clear", swiss_hash_clear, 0);
|
|
901
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);
|
|
902
1434
|
rb_define_method(cHash, "keys", swiss_hash_keys, 0);
|
|
903
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);
|
|
904
1465
|
rb_define_method(cHash, "key?", swiss_hash_key_p, 1);
|
|
905
1466
|
rb_define_method(cHash, "has_key?", swiss_hash_key_p, 1);
|
|
906
1467
|
rb_define_method(cHash, "include?", swiss_hash_key_p, 1);
|
|
907
|
-
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);
|
|
908
1474
|
rb_define_method(cHash, "stats", swiss_hash_stats, 0);
|
|
909
1475
|
}
|