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.
@@ -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 16
136
- #define GROUP_MASK 0xFFFFu
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 SWISS_USE_PORTABLE 1
145
- #define GROUP_SIZE 8
146
- #define GROUP_MASK 0xFFu
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
- #elif defined(SWISS_USE_NEON)
182
+ #else /* SWAR */
174
183
 
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);
182
- }
184
+ #define SWAR_LSB 0x0101010101010101ULL
185
+ #define SWAR_MSB 0x8080808080808080ULL
183
186
 
184
- static inline uint8x8_t ctrl_load(const uint8_t *ctrl) {
185
- return vld1_u8(ctrl);
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
- 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)));
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 = 0x0101010101010101ULL * h2;
195
+ uint64_t broadcast = SWAR_LSB * h2;
212
196
  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;
197
+ uint64_t result = (xored - SWAR_LSB) & ~xored & SWAR_MSB;
198
+ return ctrl_bitmask_from_msb(result);
220
199
  }
221
200
 
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;
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
- 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;
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
- static inline uint32_t ctrl_match_empty(const uint8_t *ctrl) {
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 /* SIMD selection */
217
+ #endif /* control-byte matching selection */
245
218
 
246
- static inline int ctz32(uint32_t v) {
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
- static inline uint64_t compute_hash(VALUE key) {
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
- 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;
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)) return 0;
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) return 1;
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
- static inline VALUE prepare_key(VALUE key) {
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
- static inline ProbeSeq probe_start(uint64_t h1, size_t group_mask) {
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
- static inline void probe_next(ProbeSeq *ps) {
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
- static inline void swiss_insert_rehash(SwissHash *sh, uint64_t hash, VALUE key, VALUE value) {
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
- if (keys_equal(sh->slots[idx].key, key)) {
573
- sh->slots[idx].value = value;
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
- if (keys_equal(sh->slots[idx].key, key)) {
631
- VALUE old_value = sh->slots[idx].value;
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("portable/SWAR"));
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, "compact!", swiss_hash_compact_bang, 0);
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
  }