Ruby 4.1.0dev (2026-10-01 revision 1c03c515fa595e0f5783b81c30e3bfcaf23a8935)
set.c (1c03c515fa595e0f5783b81c30e3bfcaf23a8935)
1/* This implements sets using the same hash table implementation as in
2 st.c, but without a value for each hash entry. This results in the
3 same basic performance characteristics as when using an st table,
4 but uses 1/3 less memory.
5 */
6
7#include "id.h"
8#include "internal.h"
9#include "internal/bits.h"
10#include "internal/error.h"
11#include "internal/hash.h"
12#include "internal/object.h"
13#include "internal/proc.h"
14#include "internal/sanitizers.h"
15#include "internal/set.h"
16#include "internal/set_table.h"
17#include "internal/symbol.h"
18#include "internal/variable.h"
19#include "ruby_assert.h"
20
21#include <stdio.h>
22#ifdef HAVE_STDLIB_H
23#include <stdlib.h>
24#endif
25#include <string.h>
26
27#ifndef SET_DEBUG
28#define SET_DEBUG 0
29#endif
30
31#if SET_DEBUG
32#include "internal/gc.h"
33#endif
34
35static st_index_t
36dbl_to_index(double d)
37{
38 union {double d; st_index_t i;} u;
39 u.d = d;
40 return u.i;
41}
42
43static const uint64_t prime1 = ((uint64_t)0x2e0bb864 << 32) | 0xe9ea7df5;
44static const uint32_t prime2 = 0x830fcab9;
45
46static inline uint64_t
47mult_and_mix(uint64_t m1, uint64_t m2)
48{
49#if defined HAVE_UINT128_T
50 uint128_t r = (uint128_t) m1 * (uint128_t) m2;
51 return (uint64_t) (r >> 64) ^ (uint64_t) r;
52#else
53 uint64_t hm1 = m1 >> 32, hm2 = m2 >> 32;
54 uint64_t lm1 = m1, lm2 = m2;
55 uint64_t v64_128 = hm1 * hm2;
56 uint64_t v32_96 = hm1 * lm2 + lm1 * hm2;
57 uint64_t v1_32 = lm1 * lm2;
58
59 return (v64_128 + (v32_96 >> 32)) ^ ((v32_96 << 32) + v1_32);
60#endif
61}
62
63static inline uint64_t
64key64_hash(uint64_t key, uint32_t seed)
65{
66 return mult_and_mix(key + seed, prime1);
67}
68
69/* Should cast down the result for each purpose */
70#define set_index_hash(index) key64_hash(rb_hash_start(index), prime2)
71
72static st_index_t
73set_ident_hash(st_data_t n)
74{
75#ifdef USE_FLONUM /* RUBY */
76 /*
77 * - flonum (on 64-bit) is pathologically bad, mix the actual
78 * float value in, but do not use the float value as-is since
79 * many integers get interpreted as 2.0 or -2.0 [Bug #10761]
80 */
81 if (FLONUM_P(n)) {
82 n ^= dbl_to_index(rb_float_value(n));
83 }
84#endif
85
86 return (st_index_t)set_index_hash((st_index_t)n);
87}
88
89static const struct st_hash_type identhash = {
90 rb_st_numcmp,
91 set_ident_hash,
92};
93
94static const struct st_hash_type objhash = {
95 rb_any_cmp,
96 rb_any_hash,
97};
98
100static VALUE set_i_compare_by_identity(VALUE set);
101
102#define id_each idEach
103static ID id_each_entry;
104static ID id_any_p;
105static ID id_new;
106static ID id_i_hash;
107static ID id_set_iter_lev;
108static ID id_subclass_compatible;
109static ID id_class_methods;
110
111#define RSET_INITIALIZED FL_USER1
112#define RSET_LEV_MASK (FL_USER13 | FL_USER14 | FL_USER15 | /* FL 13..19 */ \
113 FL_USER16 | FL_USER17 | FL_USER18 | FL_USER19)
114#define RSET_LEV_SHIFT (FL_USHIFT + 13)
115#define RSET_LEV_MAX 127 /* 7 bits */
116
117#define SET_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(SET_DEBUG, expr, #expr)
118
119#define RSET_SIZE(set) set_table_size(RSET_TABLE(set))
120#define RSET_EMPTY(set) (RSET_SIZE(set) == 0)
121#define RSET_SIZE_NUM(set) SIZET2NUM(RSET_SIZE(set))
122#define RSET_IS_MEMBER(set, item) set_table_lookup(RSET_TABLE(set), (st_data_t)(item))
123#define RSET_COMPARE_BY_IDENTITY(set) (RSET_TABLE(set)->type == &identhash)
124
126 set_table table;
127};
128
129static int
130mark_and_pin_key(st_data_t key, st_data_t data)
131{
132 rb_gc_mark((VALUE)key);
133
134 return ST_CONTINUE;
135}
136
137static int
138mark_key(st_data_t key, st_data_t data)
139{
140 rb_gc_mark_movable((VALUE)key);
141
142 return ST_CONTINUE;
143}
144
145static void
146set_mark(void *ptr)
147{
148 struct set_object *sobj = ptr;
149 if (sobj->table.entries) {
150 if (sobj->table.type == &identhash) {
151 set_table_foreach(&sobj->table, mark_and_pin_key, 0);
152 }
153 else {
154 set_table_foreach(&sobj->table, mark_key, 0);
155 }
156 }
157}
158
159static void
160set_free(void *ptr)
161{
162 struct set_object *sobj = ptr;
163 set_free_embedded_table(&sobj->table);
164}
165
166static size_t
167set_size(const void *ptr)
168{
169 const struct set_object *sobj = ptr;
170 /* Do not count the table size twice, as it is embedded */
171 return (unsigned long)set_memsize(&sobj->table) - sizeof(sobj->table);
172}
173
174static int
175set_foreach_replace(st_data_t key, st_data_t argp, int error)
176{
177 if (rb_gc_location((VALUE)key) != (VALUE)key) {
178 return ST_REPLACE;
179 }
180
181 return ST_CONTINUE;
182}
183
184static int
185set_replace_ref(st_data_t *key, st_data_t argp, int existing)
186{
187 rb_gc_mark_and_move((VALUE *)key);
188
189 return ST_CONTINUE;
190}
191
192static void
193set_update_references(void *ptr)
194{
195 struct set_object *sobj = ptr;
196 set_foreach_with_replace(&sobj->table, set_foreach_replace, set_replace_ref, 0);
197}
198
199static const rb_data_type_t set_data_type = {
200 .wrap_struct_name = "set",
201 .function = {
202 .dmark = set_mark,
203 .dfree = set_free,
204 .dsize = set_size,
205 .dcompact = set_update_references,
206 },
207 .flags = RUBY_TYPED_EMBEDDABLE | RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE
208};
209
210static inline set_table *
211RSET_TABLE(VALUE set)
212{
213 struct set_object *sobj;
214 TypedData_Get_Struct(set, struct set_object, &set_data_type, sobj);
215 return &sobj->table;
216}
217
218static unsigned long
219iter_lev_in_ivar(VALUE set)
220{
221 VALUE levval = rb_ivar_get(set, id_set_iter_lev);
222 SET_ASSERT(FIXNUM_P(levval));
223 long lev = FIX2LONG(levval);
224 SET_ASSERT(lev >= 0);
225 return (unsigned long)lev;
226}
227
228void rb_ivar_set_internal(VALUE obj, ID id, VALUE val);
229
230static void
231iter_lev_in_ivar_set(VALUE set, unsigned long lev)
232{
233 SET_ASSERT(lev >= RSET_LEV_MAX);
234 SET_ASSERT(POSFIXABLE(lev)); /* POSFIXABLE means fitting to long */
235 rb_ivar_set_internal(set, id_set_iter_lev, LONG2FIX((long)lev));
236}
237
238static inline unsigned long
239iter_lev_in_flags(VALUE set)
240{
241 return (unsigned long)((RBASIC(set)->flags >> RSET_LEV_SHIFT) & RSET_LEV_MAX);
242}
243
244static inline void
245iter_lev_in_flags_set(VALUE set, unsigned long lev)
246{
247 SET_ASSERT(lev <= RSET_LEV_MAX);
248 RBASIC(set)->flags = ((RBASIC(set)->flags & ~RSET_LEV_MASK) | ((VALUE)lev << RSET_LEV_SHIFT));
249}
250
251static inline bool
252set_iterating_p(VALUE set)
253{
254 return iter_lev_in_flags(set) > 0;
255}
256
257static void
258set_iter_lev_inc(VALUE set)
259{
260 unsigned long lev = iter_lev_in_flags(set);
261 if (lev == RSET_LEV_MAX) {
262 lev = iter_lev_in_ivar(set) + 1;
263 if (!POSFIXABLE(lev)) { /* paranoiac check */
264 rb_raise(rb_eRuntimeError, "too much nested iterations");
265 }
266 }
267 else {
268 lev += 1;
269 iter_lev_in_flags_set(set, lev);
270 if (lev < RSET_LEV_MAX) return;
271 }
272 iter_lev_in_ivar_set(set, lev);
273}
274
275static void
276set_iter_lev_dec(VALUE set)
277{
278 unsigned long lev = iter_lev_in_flags(set);
279 if (lev == RSET_LEV_MAX) {
280 lev = iter_lev_in_ivar(set);
281 if (lev > RSET_LEV_MAX) {
282 iter_lev_in_ivar_set(set, lev-1);
283 return;
284 }
285 rb_attr_delete(set, id_set_iter_lev);
286 }
287 else if (lev == 0) {
288 rb_raise(rb_eRuntimeError, "iteration level underflow");
289 }
290 iter_lev_in_flags_set(set, lev - 1);
291}
292
293static VALUE
294set_foreach_ensure(VALUE set)
295{
296 set_iter_lev_dec(set);
297 return 0;
298}
299
300typedef int set_foreach_func(VALUE, VALUE);
301
303 VALUE set;
304 set_foreach_func *func;
305 VALUE arg;
306};
307
308static int
309set_iter_status_check(int status)
310{
311 if (status == ST_CONTINUE) {
312 return ST_CHECK;
313 }
314
315 return status;
316}
317
318static int
319set_foreach_iter(st_data_t key, st_data_t argp, int error)
320{
321 struct set_foreach_arg *arg = (struct set_foreach_arg *)argp;
322
323 if (error) return ST_STOP;
324
325 set_table *tbl = RSET_TABLE(arg->set);
326 int status = (*arg->func)((VALUE)key, arg->arg);
327
328 if (RSET_TABLE(arg->set) != tbl) {
329 rb_raise(rb_eRuntimeError, "reset occurred during iteration");
330 }
331
332 return set_iter_status_check(status);
333}
334
335static VALUE
336set_foreach_call(VALUE arg)
337{
338 VALUE set = ((struct set_foreach_arg *)arg)->set;
339 int ret = 0;
340 ret = set_foreach_check(RSET_TABLE(set), set_foreach_iter,
341 (st_data_t)arg, (st_data_t)Qundef);
342 if (ret) {
343 rb_raise(rb_eRuntimeError, "ret: %d, set modified during iteration", ret);
344 }
345 return Qnil;
346}
347
348static void
349set_iter(VALUE set, set_foreach_func *func, VALUE farg)
350{
351 struct set_foreach_arg arg;
352
353 if (RSET_EMPTY(set))
354 return;
355 arg.set = set;
356 arg.func = func;
357 arg.arg = farg;
358 if (RB_OBJ_FROZEN(set)) {
359 set_foreach_call((VALUE)&arg);
360 }
361 else {
362 set_iter_lev_inc(set);
363 rb_ensure(set_foreach_call, (VALUE)&arg, set_foreach_ensure, set);
364 }
365}
366
367NORETURN(static void no_new_item(void));
368static void
369no_new_item(void)
370{
371 rb_raise(rb_eRuntimeError, "can't add a new item into set during iteration");
372}
373
374static void
375set_compact_after_delete(VALUE set)
376{
377 if (!set_iterating_p(set)) {
378 set_compact_table(RSET_TABLE(set));
379 }
380}
381
382static int
383set_table_insert_wb(set_table *tab, VALUE set, VALUE key)
384{
385 if (tab->type != &identhash && rb_obj_class(key) == rb_cString && !RB_OBJ_FROZEN(key)) {
386 key = rb_hash_key_str(key);
387 }
388 int ret = set_insert(tab, (st_data_t)key);
389 if (ret == 0) RB_OBJ_WRITTEN(set, Qundef, key);
390 return ret;
391}
392
393static int
394set_insert_wb(VALUE set, VALUE key)
395{
396 return set_table_insert_wb(RSET_TABLE(set), set, key);
397}
398
399static VALUE
400set_alloc_with_size_and_type(VALUE klass, st_index_t size, const struct st_hash_type *type)
401{
402 VALUE set;
403 struct set_object *sobj;
404
405 set = TypedData_Make_Struct(klass, struct set_object, &set_data_type, sobj);
406 set_init_table_with_size(&sobj->table, type, size);
407
408 return set;
409}
410
411static VALUE
412set_alloc_with_size(VALUE klass, st_index_t size)
413{
414 return set_alloc_with_size_and_type(klass, size, &objhash);
415}
416
417static VALUE
418set_s_alloc(VALUE klass)
419{
420 return set_alloc_with_size(klass, 0);
421}
422
423bool
424rb_set_p(VALUE obj)
425{
426 return rb_typeddata_is_instance_of(obj, &set_data_type);
427}
428
429/*
430 * call-seq:
431 * Set[*objects] -> new_set
432 *
433 * Returns a new set populated with the given +objects+:
434 *
435 * Set[1, 'one', :one, 1.0, %w[a b c], {foo: 0, bar: 1}]
436 * # => Set[1, "one", :one, 1.0, ["a", "b", "c"], {foo: 0, bar: 1}]
437 * Set[Set[0, 1, 2], Set[%w[a b c]]]
438 * # => Set[Set[0, 1, 2], Set[["a", "b", "c"]]]
439 * Set[] # => Set[]
440 *
441 * Related: see {Methods for Creating a Set}[rdoc-ref:Set@Methods+for+Creating+a+Set].
442 *
443 */
444static VALUE
445set_s_create(int argc, VALUE *argv, VALUE klass)
446{
447 VALUE set = set_alloc_with_size(klass, argc);
448 set_table *table = RSET_TABLE(set);
449 int i;
450
451 for (i=0; i < argc; i++) {
452 set_table_insert_wb(table, set, argv[i]);
453 }
454
455 return set;
456}
457
458static VALUE
459set_s_inherited(VALUE klass, VALUE subclass)
460{
461 if (klass == rb_cSet) {
462 // When subclassing directly from Set, include the compatibility layer
463 rb_require("set/subclass_compatible.rb");
464 VALUE subclass_compatible = rb_const_get(klass, id_subclass_compatible);
465 rb_include_module(subclass, subclass_compatible);
466 rb_extend_object(subclass, rb_const_get(subclass_compatible, id_class_methods));
467 }
468 return Qnil;
469}
470
471static void
472check_set(VALUE arg)
473{
474 if (!rb_obj_is_kind_of(arg, rb_cSet)) {
475 rb_raise(rb_eArgError, "value must be a set");
476 }
477}
478
479static ID
480enum_method_id(VALUE other)
481{
482 if (rb_respond_to(other, id_each_entry)) {
483 return id_each_entry;
484 }
485 else if (rb_respond_to(other, id_each)) {
486 return id_each;
487 }
488 else {
489 rb_raise(rb_eArgError, "value must be enumerable");
490 }
491}
492
493static VALUE
494set_enum_size(VALUE set, VALUE args, VALUE eobj)
495{
496 return RSET_SIZE_NUM(set);
497}
498
499static VALUE
500set_initialize_without_block(RB_BLOCK_CALL_FUNC_ARGLIST(i, set))
501{
502 VALUE element = i;
503 set_insert_wb(set, element);
504 return element;
505}
506
507static VALUE
508set_initialize_with_block(RB_BLOCK_CALL_FUNC_ARGLIST(i, set))
509{
510 VALUE element = rb_yield(i);
511 set_insert_wb(set, element);
512 return element;
513}
514
515/*
516 * call-seq:
517 * Set.new(object = nil) -> new_set
518 * Set.new(object = nil) {|element| ... } -> new_set
519 *
520 * Returns a new set based on the given +object+,
521 * which must be an Enumerable or +nil+.
522 *
523 * With argument +object+ given as +nil+,
524 * returns a new empty set:
525 *
526 * Set.new # => Set[]
527 * Set.new { fail 'Cannot happen' } # => Set[] # Block not called.
528 *
529 * With no block given and enumerable argument +object+ given,
530 * populates the new set with the elements of +object+:
531 *
532 * Set.new(%w[ a b c ]) # => Set["a", "b", "c"]
533 * Set.new({foo: 0, bar: 1}) # => Set[[:foo, 0], [:bar, 1]]
534 * Set.new(4..10) # => Set[4, 5, 6, 7, 8, 9, 10]
535 * Set.new(Dir.new('lib')).take(5)
536 * # => [".", "..", "bundled_gems.rb", "bundler", "bundler.rb"]
537 * Set.new(File.new('doc/NEWS/NEWS-4.0.0.md')).take(3)
538 * # => ["# NEWS for Ruby 4.0.0\n", "\n", "This document is a list of user-visible feature changes\n"]
539 *
540 * With a block given and enumerable argument +object+ given,
541 * calls the block with each element of +object+;
542 * adds the block's return value to the new set:
543 *
544 * Set.new(4..10) {|i| i * 2 } # => Set[8, 10, 12, 14, 16, 18, 20]
545 *
546 * Related: see {Methods for Creating a Set}[rdoc-ref:Set@Methods+for+Creating+a+Set].
547 *
548 */
549static VALUE
550set_i_initialize(int argc, VALUE *argv, VALUE set)
551{
552 if (RBASIC(set)->flags & RSET_INITIALIZED) {
553 rb_raise(rb_eRuntimeError, "cannot reinitialize set");
554 }
555 RBASIC(set)->flags |= RSET_INITIALIZED;
556
557 VALUE other;
558 rb_check_arity(argc, 0, 1);
559
560 if (argc > 0 && (other = argv[0]) != Qnil) {
561 if (RB_TYPE_P(other, T_ARRAY)) {
562 long i;
563 int block_given = rb_block_given_p();
564 set_table *into = RSET_TABLE(set);
565 for (i=0; i<RARRAY_LEN(other); i++) {
566 VALUE key = RARRAY_AREF(other, i);
567 if (block_given) key = rb_yield(key);
568 set_table_insert_wb(into, set, key);
569 }
570 }
571 else {
572 rb_block_call(other, enum_method_id(other), 0, 0,
573 rb_block_given_p() ? set_initialize_with_block : set_initialize_without_block,
574 set);
575 }
576 }
577
578 return set;
579}
580
581/* :nodoc: */
582static VALUE
583set_i_initialize_copy(VALUE set, VALUE other)
584{
585 if (set == other) return set;
586
587 if (set_iterating_p(set)) {
588 rb_raise(rb_eRuntimeError, "cannot replace set during iteration");
589 }
590
591 struct set_object *sobj;
592 TypedData_Get_Struct(set, struct set_object, &set_data_type, sobj);
593
594 set_free_embedded_table(&sobj->table);
595 set_copy(&sobj->table, RSET_TABLE(other));
596 rb_gc_writebarrier_remember(set);
597
598 return set;
599}
600
601static int
602set_inspect_i(st_data_t key, st_data_t arg)
603{
604 VALUE *args = (VALUE*)arg;
605 VALUE str = args[0];
606 if (args[1] == Qtrue) {
607 rb_str_buf_cat_ascii(str, ", ");
608 }
609 else {
610 args[1] = Qtrue;
611 }
613
614 return ST_CONTINUE;
615}
616
617static VALUE
618set_inspect(VALUE set, VALUE dummy, int recur)
619{
620 VALUE str;
621 VALUE klass_name = rb_class_path(CLASS_OF(set));
622
623 if (recur) {
624 str = rb_sprintf("%"PRIsVALUE"[...]", klass_name);
625 return rb_str_export_to_enc(str, rb_usascii_encoding());
626 }
627
628 str = rb_sprintf("%"PRIsVALUE"[", klass_name);
629 VALUE args[2] = {str, Qfalse};
630 set_iter(set, set_inspect_i, (st_data_t)args);
631 rb_str_buf_cat2(str, "]");
632
633 return str;
634}
635
636/*
637 * call-seq:
638 * inspect -> string
639 *
640 * Returns a string representation of +self+:
641 *
642 * Set[*%w[foo bar], {foo: 0, bar: 1}].inspect
643 * # => "Set[\"foo\", \"bar\", {foo: 0, bar: 1}]"
644 *
645 * Related: see {Methods for Converting}[rdoc-ref:Set@Methods+for+Converting].
646 */
647static VALUE
648set_i_inspect(VALUE set)
649{
650 return rb_exec_recursive(set_inspect, set, 0);
651}
652
653static int
654set_to_a_i(st_data_t key, st_data_t arg)
655{
656 rb_ary_push((VALUE)arg, (VALUE)key);
657 return ST_CONTINUE;
658}
659
660/*
661 * call-seq:
662 * to_a -> array
663 *
664 * Returns an array containing the elements of +self+:
665 *
666 * Set[1, 2].to_a # => [1, 2]
667 * Set[1, 'c', :s].to_a # => [1, "c", :s]
668 *
669 * Related: {Methods for Converting}[rdoc-ref:Set@Methods+for+Converting].
670 */
671static VALUE
672set_i_to_a(VALUE set)
673{
674 st_index_t size = RSET_SIZE(set);
675 VALUE ary = rb_ary_new_capa(size);
676
677 if (size == 0) return ary;
678
679 if (ST_DATA_COMPATIBLE_P(VALUE)) {
680 RARRAY_PTR_USE(ary, ptr, {
681 size = set_keys(RSET_TABLE(set), ptr, size);
682 });
683 rb_gc_writebarrier_remember(ary);
684 rb_ary_set_len(ary, size);
685 }
686 else {
687 set_iter(set, set_to_a_i, (st_data_t)ary);
688 }
689 return ary;
690}
691
692/*
693 * call-seq:
694 * to_set {|element| ... } -> new_set
695 * to_set -> self or new_set
696 *
697 * With a block given, creates and returns a new set;
698 * calls the block with each element of +self+,
699 * and adds the block's returns value to the new set:
700 *
701 * set = Set[*0..9] # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
702 * set.to_set {|i| i * 2 } # => Set[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
703 *
704 * With no block given, when +self+ is an instance of +Set+,
705 * returns +self+:
706 *
707 * set = Set[*0..9]
708 * set.to_set
709 * set.to_set.equal?(set) # => true
710 *
711 * With no block given, when +self+ is an instance of a subclass of +Set+,
712 * returns a set containing the elements of +self+:
713 *
714 * class MySet < Set; end
715 * my_set = MySet[*0..9] # => #<MySet: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}>
716 * set = my_set.to_set # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
717 *
718 * Related: see {Methods for Converting}[rdoc-ref:Set@Methods+for+Converting].
719 */
720static VALUE
721set_i_to_set(VALUE set)
722{
724 return set;
725 }
726
727 return rb_funcall_passing_block(rb_cSet, id_new, 1, &set);
728}
729
730/*
731 * call-seq:
732 * join(separator = $,) -> string
733 *
734 * Returns the string formed by joining the string-converted elements of +self+
735 * with the given +separator+ (defaults to <tt>$,</tt>):
736 *
737 * $, # => nil
738 * Set[*%w[foo bar baz]].join
739 * # => "foobarbaz"
740 * Set[*%w[foo bar baz]].join(', ')
741 * # => "foo, bar, baz"
742 *
743 * Flattens nested arrays:
744 *
745 * Set[[:foo, [:bar, [:baz, :bat]]]].join
746 * # => "foobarbazbat"
747 *
748 * Does not flatten nested sets:
749 *
750 * Set[Set[:foo, Set[:bar, Set[:baz, :bat]]]].join
751 * # => "Set[:foo, Set[:bar, Set[:baz, :bat]]]"
752 *
753 * Related: see {Methods for Converting}[rdoc-ref:Set@Methods+for+Converting].
754 */
755static VALUE
756set_i_join(int argc, VALUE *argv, VALUE set)
757{
758 rb_check_arity(argc, 0, 1);
759 return rb_ary_join(set_i_to_a(set), argc == 0 ? Qnil : argv[0]);
760}
761
762/*
763 * call-seq:
764 * add(object) -> self
765 *
766 * Adds the given +object+ to +self+; returns +self+:
767 *
768 * set = Set[0, 1, 2]
769 * set.add(%w[a b c]) # => Set[0, 1, 2, ["a", "b", "c"]]
770 * set.add(0) # => Set[0, 1, 2, ["a", "b", "c"]]
771 *
772 * Related: see {Methods for Assigning}[rdoc-ref:Set@Methods+for+Assigning].
773 */
774static VALUE
775set_i_add(VALUE set, VALUE item)
776{
777 rb_check_frozen(set);
778 if (set_iterating_p(set)) {
779 if (!set_table_lookup(RSET_TABLE(set), (st_data_t)item)) {
780 no_new_item();
781 }
782 }
783 else {
784 set_insert_wb(set, item);
785 }
786 return set;
787}
788
789/*
790 * call-seq:
791 * add?(object) -> self or nil
792 *
793 * Like #add, but returns +nil+ if the given +object+ is already in +self+:
794 *
795 * set = Set[0, 1, 2]
796 * set.add?(:foo) # => Set[0, 1, 2, :foo]
797 * set.add?(0..9) # => Set[0, 1, 2, :foo, 0..9]
798 * set.add?(2) # => nil
799 *
800 * Related: see {Methods for Assigning}[rdoc-ref:Set@Methods+for+Assigning].
801 */
802static VALUE
803set_i_add_p(VALUE set, VALUE item)
804{
805 rb_check_frozen(set);
806 if (set_iterating_p(set)) {
807 if (!set_table_lookup(RSET_TABLE(set), (st_data_t)item)) {
808 no_new_item();
809 }
810 return Qnil;
811 }
812 else {
813 return set_insert_wb(set, item) ? Qnil : set;
814 }
815}
816
817/*
818 * call-seq:
819 * delete(object) -> self
820 *
821 * Removes the given +object+ from +self+ if +self+ includes the object;
822 * returns +self+:
823 *
824 * set = Set[0, 'zero', :zero]
825 * set.delete(0) # => Set["zero", :zero]
826 * set.delete(:nosuch) # => Set["zero", :zero]
827 *
828 * Related: see {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
829 */
830static VALUE
831set_i_delete(VALUE set, VALUE item)
832{
833 st_data_t item_data = (st_data_t)item;
834 rb_check_frozen(set);
835 if (set_table_delete(RSET_TABLE(set), &item_data)) {
836 set_compact_after_delete(set);
837 }
838 return set;
839}
840
841/*
842 * call-seq:
843 * delete?(object) -> self or nil
844 *
845 * Like #delete, but returns +nil+ if the object is not in +self+:
846 *
847 * set = Set[0, 'zero', :zero]
848 * set.delete?(0) # => Set["zero", :zero]
849 * set.delete?(0) # => nil
850 *
851 * Related: see {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
852 */
853static VALUE
854set_i_delete_p(VALUE set, VALUE item)
855{
856 st_data_t item_data = (st_data_t)item;
857 rb_check_frozen(set);
858 if (set_table_delete(RSET_TABLE(set), &item_data)) {
859 set_compact_after_delete(set);
860 return set;
861 }
862 return Qnil;
863}
864
865static int
866set_delete_if_i(st_data_t key, st_data_t dummy)
867{
868 return RTEST(rb_yield((VALUE)key)) ? ST_DELETE : ST_CONTINUE;
869}
870
871/*
872 * call-seq:
873 * delete_if {|element| ... } -> self
874 * delete_if -> enumerator
875 *
876 * With a block given, calls the block with each element in +self+;
877 * removes the element if the block returns a truthy value:
878 *
879 * set = Set[*0..9]
880 * # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
881 * set.delete_if {|element| element.even? }
882 * # => Set[1, 3, 5, 7, 9]
883 *
884 * With no block given, returns an Enumerator.
885 *
886 * Related: {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
887 */
888static VALUE
889set_i_delete_if(VALUE set)
890{
891 RETURN_SIZED_ENUMERATOR(set, 0, 0, set_enum_size);
892 rb_check_frozen(set);
893 set_iter(set, set_delete_if_i, 0);
894 set_compact_after_delete(set);
895 return set;
896}
897
898/*
899 * call-seq:
900 * reject! {|element| ... } -> self or nil
901 * reject! -> enumerator
902 *
903 * With a block given, like #delete_if, but returns +nil+ if no changes were made:
904 *
905 * set = Set[*0..9] # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
906 * set.reject! {|element| element.even? } # => Set[1, 3, 5, 7, 9]
907 * set.reject! {|element| element.even? } # => nil
908 * set.reject! {|element| element.odd? } # => Set[]
909 *
910 * With no block given, returns an Enumerator.
911 *
912 * Related: see {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
913 */
914static VALUE
915set_i_reject(VALUE set)
916{
917 RETURN_SIZED_ENUMERATOR(set, 0, 0, set_enum_size);
918 rb_check_frozen(set);
919
920 set_table *table = RSET_TABLE(set);
921 size_t n = set_table_size(table);
922 set_iter(set, set_delete_if_i, 0);
923
924 if (n == set_table_size(table)) return Qnil;
925
926 set_compact_after_delete(set);
927 return set;
928}
929
930static int
931set_classify_i(st_data_t key, st_data_t tmp)
932{
933 VALUE* args = (VALUE*)tmp;
934 VALUE hash = args[0];
935 VALUE hash_key = rb_yield(key);
936 VALUE set = rb_hash_lookup2(hash, hash_key, Qundef);
937 if (set == Qundef) {
938 set = set_s_alloc(args[1]);
939 if (RTEST(args[2])) {
940 set_i_compare_by_identity(set);
941 }
942 rb_hash_aset(hash, hash_key, set);
943 }
944 set_i_add(set, key);
945
946 return ST_CONTINUE;
947}
948
949/*
950 * call-seq:
951 * classify {|element| ... } -> hash
952 * classify -> enumerator
953 *
954 * With a block given, calls the block with each element of +self+;
955 * returns a hash whose keys are the block's return values.
956 * The value for each key is a set containing the elements
957 * for which the block returned that key.
958 *
959 * This example classifies elements by their classes:
960 *
961 * set = Set[*(5..7), *%w[foo bar]] # => Set[5, 6, 7, "foo", "bar"]
962 * set.classify {|element| element.class }
963 * # => {Integer => Set[5, 6, 7], String => Set["foo", "bar"]}
964 *
965 * With no block given, returns an Enumerator.
966 *
967 * Related: see {Methods for Converting}[rdoc-ref:Set@Methods+for+Converting].
968 */
969static VALUE
970set_i_classify(VALUE set)
971{
972 RETURN_SIZED_ENUMERATOR(set, 0, 0, set_enum_size);
973 VALUE args[3];
974 args[0] = rb_hash_new();
975 args[1] = rb_obj_class(set);
976 args[2] = RBOOL(RSET_COMPARE_BY_IDENTITY(set));
977 set_iter(set, set_classify_i, (st_data_t)args);
978 return args[0];
979}
980
981// Union-find with path compression
982static long
983set_divide_union_find_root(long *uf_parents, long index, long *tmp_array)
984{
985 long root = uf_parents[index];
986 long update_size = 0;
987 while (root != index) {
988 tmp_array[update_size++] = index;
989 index = root;
990 root = uf_parents[index];
991 }
992 for (long j = 0; j < update_size; j++) {
993 long idx = tmp_array[j];
994 uf_parents[idx] = root;
995 }
996 return root;
997}
998
999static void
1000set_divide_union_find_merge(long *uf_parents, long i, long j, long *tmp_array)
1001{
1002 long root_i = set_divide_union_find_root(uf_parents, i, tmp_array);
1003 long root_j = set_divide_union_find_root(uf_parents, j, tmp_array);
1004 if (root_i != root_j) uf_parents[root_j] = root_i;
1005}
1006
1007static VALUE
1008set_divide_arity2(VALUE set)
1009{
1010 VALUE tmp, uf;
1011 long size, *uf_parents, *tmp_array;
1012 VALUE set_class = rb_obj_class(set);
1013 VALUE items = set_i_to_a(set);
1014 rb_ary_freeze(items);
1015 size = RARRAY_LEN(items);
1016 tmp_array = ALLOCV_N(long, tmp, size);
1017 uf_parents = ALLOCV_N(long, uf, size);
1018 for (long i = 0; i < size; i++) {
1019 uf_parents[i] = i;
1020 }
1021 for (long i = 0; i < size - 1; i++) {
1022 VALUE item1 = RARRAY_AREF(items, i);
1023 for (long j = i + 1; j < size; j++) {
1024 VALUE item2 = RARRAY_AREF(items, j);
1025 if (RTEST(rb_yield_values(2, item1, item2)) &&
1026 RTEST(rb_yield_values(2, item2, item1))) {
1027 set_divide_union_find_merge(uf_parents, i, j, tmp_array);
1028 }
1029 }
1030 }
1031 VALUE final_set = set_s_create(0, 0, rb_cSet);
1032 VALUE hash = rb_hash_new();
1033 for (long i = 0; i < size; i++) {
1034 VALUE v = RARRAY_AREF(items, i);
1035 long root = set_divide_union_find_root(uf_parents, i, tmp_array);
1036 VALUE subset = rb_hash_aref(hash, LONG2FIX(root));
1037 if (subset == Qnil) {
1038 subset = set_s_alloc(set_class);
1039 if (RSET_COMPARE_BY_IDENTITY(set)) {
1040 set_i_compare_by_identity(subset);
1041 }
1042 rb_hash_aset(hash, LONG2FIX(root), subset);
1043 set_i_add(final_set, subset);
1044 }
1045 set_i_add(subset, v);
1046 }
1047 ALLOCV_END(tmp);
1048 ALLOCV_END(uf);
1049 return final_set;
1050}
1051
1052static void set_merge_enum_into(VALUE set, VALUE arg);
1053
1054/*
1055 * call-seq:
1056 * divide {|ele| ... } -> new_set
1057 * divide {|ele0, ele1| ... } -> new_set
1058 * divide -> enumerator
1059 *
1060 * With a block given, returns a set of sets.
1061 *
1062 * For a block that accepts one argument,
1063 * calls the block with each element;
1064 * creates a set for each distinct block return value:
1065 *
1066 * set = Set[*0..9]
1067 * # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1068 * # Divide into mod 3 sets.
1069 * set.divide {|ele| ele % 3 }
1070 * # => Set[Set[0, 3, 6, 9], Set[1, 4, 7], Set[2, 5, 8]]
1071 * # Divide into mod 5 sets.
1072 * set.divide {|ele| ele % 5 }
1073 * # => Set[Set[0, 5], Set[1, 6], Set[2, 7], Set[3, 8], Set[4, 9]]
1074 *
1075 * Set[0].divide {|ele| anything } # => Set[Set[0]]
1076 * Set[].divide {|ele| not called } # => Set[]
1077 *
1078 * For a block that accepts two arguments,
1079 * divides +self+ into connected components based on the binary
1080 * relation defined by the block, calling the block with each 2-element
1081 * permutation of the elements of +self+:
1082 *
1083 * set = Set[*0..9]
1084 * # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1085 * # Divide into mod 2 sets.
1086 * set.divide {|i, j| (i - j) % 2 == 0 }
1087 * # => Set[Set[0, 2, 4, 6, 8], Set[1, 3, 5, 7, 9]]
1088 * # Divide into mod 3 sets.
1089 * set.divide {|i, j| (i - j) % 3 == 0 }
1090 * # => Set[Set[0, 3, 6, 9], Set[1, 4, 7], Set[2, 5, 8]]
1091 *
1092 * Set[0].divide {|i, j| not called } # => Set[Set[0]]
1093 * Set[].divide {|i, j| not called } # => Set[]
1094 *
1095 * With no block given, returns an Enumerator.
1096 *
1097 * Related: see {Methods for Converting}[rdoc-ref:Set@Methods+for+Converting].
1098 */
1099static VALUE
1100set_i_divide(VALUE set)
1101{
1102 RETURN_SIZED_ENUMERATOR(set, 0, 0, set_enum_size);
1103
1104 if (rb_block_arity() == 2) {
1105 return set_divide_arity2(set);
1106 }
1107
1108 VALUE values = rb_hash_values(set_i_classify(set));
1109 set = set_alloc_with_size(rb_cSet, RARRAY_LEN(values));
1110 set_merge_enum_into(set, values);
1111 return set;
1112}
1113
1114static int
1115set_clear_i(st_data_t key, st_data_t dummy)
1116{
1117 return ST_DELETE;
1118}
1119
1120/*
1121 * call-seq:
1122 * clear -> self
1123 *
1124 * Returns +self+ with all elements removed:
1125 *
1126 * Set[1, :one, 'one', 1.0].clear # => Set[]
1127 *
1128 * Related: see {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
1129 */
1130static VALUE
1131set_i_clear(VALUE set)
1132{
1133 rb_check_frozen(set);
1134 if (RSET_SIZE(set) == 0) return set;
1135 if (set_iterating_p(set)) {
1136 set_iter(set, set_clear_i, 0);
1137 }
1138 else {
1139 set_table_clear(RSET_TABLE(set));
1140 set_compact_after_delete(set);
1141 }
1142 return set;
1143}
1144
1146 VALUE set;
1147 VALUE other_set;
1148 VALUE new_set;
1149 set_table *into;
1150 set_table *other;
1151};
1152
1153static int
1154set_intersection_i(st_data_t key, st_data_t tmp)
1155{
1156 struct set_intersection_data *data = MEMO_FOR(struct set_intersection_data, tmp);
1157 if (set_table_lookup(data->other, key)) {
1158 set_table_insert_wb(data->into, data->set, key);
1159 }
1160
1161 return ST_CONTINUE;
1162}
1163
1164static VALUE
1165set_intersection_block(RB_BLOCK_CALL_FUNC_ARGLIST(i, data))
1166{
1167 set_intersection_i((st_data_t)i, (st_data_t)data);
1168 return i;
1169}
1170
1171/*
1172 * call-seq:
1173 * self & enumerable -> new_set
1174 *
1175 * Returns a new set containing the {intersection}[https://en.wikipedia.org/wiki/Intersection_(set_theory)]
1176 * of +self+ and +enumerable+;
1177 * that is, containing all elements common to both, with no duplicates.
1178 * Argument +enumerable+ must be an Enumerable object:
1179 *
1180 * set = Set[*(0..6), *%w[ a b c]] # => Set[0, 1, 2, 3, 4, 5, 6, "a", "b", "c"]
1181 * set & ['c', 6, 8, 4] # => Set["c", 6, 4]
1182 * set & [:foo, :bar] # => Set[] # No elements in common.
1183 *
1184 * Related: see {Methods for Set Operations}[rdoc-ref:Set@Methods+for+Set+Operations].
1185 */
1186static VALUE
1187set_i_intersection(VALUE set, VALUE other)
1188{
1189 VALUE new_set = set_s_alloc(rb_obj_class(set));
1190 if (RSET_COMPARE_BY_IDENTITY(set)) {
1191 set_i_compare_by_identity(new_set);
1192 }
1193 set_table *stable = RSET_TABLE(set);
1194 set_table *ntable = RSET_TABLE(new_set);
1195 VALUE data;
1196
1197 if (rb_obj_is_kind_of(other, rb_cSet)) {
1198 set_table *otable = RSET_TABLE(other);
1199 if (set_table_size(stable) >= set_table_size(otable)) {
1200 /* Swap so we iterate over the smaller set */
1201 otable = stable;
1202 set = other;
1203 }
1204
1205 *NEW_PARTIAL_MEMO_FOR(struct set_intersection_data, data, into) = (struct set_intersection_data) {
1206 .set = new_set,
1207 .other_set = other,
1208 .new_set = new_set,
1209 .into = ntable,
1210 .other = otable,
1211 };
1212 set_iter(set, set_intersection_i, (st_data_t)data);
1213 }
1214 else {
1215 *NEW_PARTIAL_MEMO_FOR(struct set_intersection_data, data, into) = (struct set_intersection_data) {
1216 .set = new_set,
1217 .other_set = other,
1218 .new_set = new_set,
1219 .into = ntable,
1220 .other = stable,
1221 };
1222 rb_block_call(other, enum_method_id(other), 0, 0, set_intersection_block, data);
1223 }
1224 RB_GC_GUARD(data);
1225
1226 return new_set;
1227}
1228
1229/*
1230 * call-seq:
1231 * include?(object) -> true or false
1232 *
1233 * Returns whether the given +object+ is an element of +self+:
1234 *
1235 * set = Set[0, :zero, '0']
1236 * set.include?('0') # => true
1237 * set.include?('zero') # => false
1238 *
1239 * Tests equality using `hash` and `eql?`.
1240 *
1241 * Aliased as #===, which means that sets may be used in +case+ expressions:
1242 *
1243 * case :apple
1244 * when Set[:potato, :carrot]
1245 * 'vegetable'
1246 * when Set[:apple, :banana]
1247 * 'fruit'
1248 * else
1249 * 'unknown'
1250 * end
1251 * # => "fruit"
1252 *
1253 * Related: see {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
1254 */
1255static VALUE
1256set_i_include(VALUE set, VALUE item)
1257{
1258 return RBOOL(RSET_IS_MEMBER(set, item));
1259}
1260
1262 VALUE set;
1263 set_table *into;
1264};
1265
1266static int
1267set_merge_i(st_data_t key, st_data_t data)
1268{
1269 struct set_merge_args *args = (struct set_merge_args *)data;
1270 set_table_insert_wb(args->into, args->set, key);
1271 return ST_CONTINUE;
1272}
1273
1274static VALUE
1275set_merge_block(RB_BLOCK_CALL_FUNC_ARGLIST(key, set))
1276{
1277 VALUE element = key;
1278 set_insert_wb(set, element);
1279 return element;
1280}
1281
1282static void
1283set_merge_enum_into(VALUE set, VALUE arg)
1284{
1285 if (rb_obj_is_kind_of(arg, rb_cSet)) {
1286 struct set_merge_args args = {
1287 .set = set,
1288 .into = RSET_TABLE(set)
1289 };
1290 set_iter(arg, set_merge_i, (st_data_t)&args);
1291 }
1292 else if (RB_TYPE_P(arg, T_ARRAY)) {
1293 long i;
1294 set_table *into = RSET_TABLE(set);
1295 for (i=0; i<RARRAY_LEN(arg); i++) {
1296 set_table_insert_wb(into, set, RARRAY_AREF(arg, i));
1297 }
1298 RB_GC_GUARD(arg);
1299 }
1300 else {
1301 rb_block_call(arg, enum_method_id(arg), 0, 0, set_merge_block, set);
1302 }
1303}
1304
1305/*
1306 * call-seq:
1307 * merge(*enumerables, **nil) -> self
1308 *
1309 * Adds each element of each of the given +enumerables+ to +self+;
1310 * returns +self+:
1311 *
1312 * set = Set[*0..2] # => Set[0, 1, 2]
1313 * set.merge('a'..'c', %w[foo bar]) # => Set[0, 1, 2, "a", "b", "c", "foo", "bar"]
1314 * set.merge('a'..'c', %w[foo bar]) # => Set[0, 1, 2, "a", "b", "c", "foo", "bar"]
1315 *
1316 * Related: see {Methods for Assigning}[rdoc-ref:Set@Methods+for+Assigning].
1317 *
1318 */
1319static VALUE
1320set_i_merge(int argc, VALUE *argv, VALUE set)
1321{
1322 if (rb_keyword_given_p()) {
1323 rb_raise(rb_eArgError, "no keywords accepted");
1324 }
1325
1326 if (set_iterating_p(set)) {
1327 rb_raise(rb_eRuntimeError, "cannot add to set during iteration");
1328 }
1329
1330 rb_check_frozen(set);
1331
1332 int i;
1333
1334 for (i=0; i < argc; i++) {
1335 set_merge_enum_into(set, argv[i]);
1336 }
1337
1338 return set;
1339}
1340
1341static VALUE
1342set_reset_table_with_type(VALUE set, const struct st_hash_type *type)
1343{
1344 rb_check_frozen(set);
1345
1346 struct set_object *sobj;
1347 TypedData_Get_Struct(set, struct set_object, &set_data_type, sobj);
1348 set_table *old = &sobj->table;
1349
1350 size_t size = set_table_size(old);
1351 if (size > 0) {
1352 set_table *new = set_init_table_with_size(NULL, type, size);
1353 struct set_merge_args args = {
1354 .set = set,
1355 .into = new
1356 };
1357 set_iter(set, set_merge_i, (st_data_t)&args);
1358 set_free_embedded_table(&sobj->table);
1359 memcpy(&sobj->table, new, sizeof(*new));
1360 SIZED_FREE(new);
1361 }
1362 else {
1363 sobj->table.type = type;
1364 }
1365
1366 return set;
1367}
1368
1369/*
1370 * call-seq:
1371 * compare_by_identity -> self
1372 *
1373 * Sets +self+ to compare by object identity
1374 * (rather than by object content, which is the initial setting);
1375 * returns +self+:
1376 *
1377 * set = Set.new
1378 * set.compare_by_identity
1379 * str = +"foo"
1380 * set.add(str)
1381 * # => Set["foo"]
1382 * set.include?(str)
1383 * # => true
1384 * set.add(str)
1385 * # => Set["foo"])
1386 * set.include?(+"foo")
1387 * # => false
1388 * set.add(+"foo")
1389 * # => Set["foo", "foo"])
1390 *
1391 * Once set, the compare-by-identity property may not be unset.
1392 *
1393 * Related: #compare_by_identity?.
1394 */
1395static VALUE
1396set_i_compare_by_identity(VALUE set)
1397{
1398 if (RSET_COMPARE_BY_IDENTITY(set)) return set;
1399
1400 if (set_iterating_p(set)) {
1401 rb_raise(rb_eRuntimeError, "compare_by_identity during iteration");
1402 }
1403
1404 return set_reset_table_with_type(set, &identhash);
1405}
1406
1407/*
1408 * call-seq:
1409 * compare_by_identity? -> true or false
1410 *
1411 * Returns whether +self+ compares elements by object identity
1412 * (rather than by content):
1413 *
1414 * set = Set[]
1415 * set.compare_by_identity? # => false
1416 * set.compare_by_identity
1417 * set.compare_by_identity? # => true
1418 *
1419 * Related: #compare_by_identity;
1420 * see also {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
1421 */
1422static VALUE
1423set_i_compare_by_identity_p(VALUE set)
1424{
1425 return RBOOL(RSET_COMPARE_BY_IDENTITY(set));
1426}
1427
1428/*
1429 * call-seq:
1430 * size -> integer
1431 *
1432 * Returns the number of elements in +self+:
1433 *
1434 * Set[*0..9].size # => 10
1435 *
1436 * Related: see {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
1437 */
1438static VALUE
1439set_i_size(VALUE set)
1440{
1441 return RSET_SIZE_NUM(set);
1442}
1443
1444/*
1445 * call-seq:
1446 * empty? -> true or false
1447 *
1448 * Returns whether +self+ contains no elements:
1449 *
1450 * Set[].empty? # => true
1451 * Set[0].empty? # => false
1452 *
1453 * Related: see {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
1454 */
1455static VALUE
1456set_i_empty(VALUE set)
1457{
1458 return RBOOL(RSET_EMPTY(set));
1459}
1460
1461static int
1462set_xor_i(st_data_t key, st_data_t data)
1463{
1464 VALUE element = (VALUE)key;
1465 VALUE set = (VALUE)data;
1466 set_table *table = RSET_TABLE(set);
1467 if (set_table_insert_wb(table, set, element)) {
1468 set_table_delete(table, &element);
1469 }
1470 return ST_CONTINUE;
1471}
1472
1473/*
1474 * call-seq:
1475 * self ^ enumerable -> new_set
1476 *
1477 * Returns a new set containing
1478 * the {exclusive OR}[https://en.wikipedia.org/wiki/Exclusive_or]
1479 * of +self+ and the given +enumerable+;
1480 * that is, containing each element that is in either +self+ or +enumerable+,
1481 * but not in both:
1482 *
1483 * set = Set[0, 1, 2]
1484 * set ^ Set[1, 2, 3] # => Set[0, 3]
1485 * set ^ Set[2, 1] # => Set[0]
1486 * set ^ Set[2, *('a'..'c')] # => Set[0, 1, "a", "b", "c"]
1487 * set ^ Set[2, 1, 0] # => Set[]
1488 *
1489 * For \Set +set+ and \Enumerable +enumerable+, these expressions are equivalent:
1490 *
1491 * set ^ enumerable
1492 * ((set | enumerable) - (set & enumerable))
1493 *
1494 * Related: see {Methods for Set Operations}[rdoc-ref:Set@Methods+for+Set+Operations].
1495 */
1496static VALUE
1497set_i_xor(VALUE set, VALUE other)
1498{
1499 VALUE new_set = rb_obj_dup(set);
1500
1501 if (rb_obj_is_kind_of(other, rb_cSet)) {
1502 set_iter(other, set_xor_i, (st_data_t)new_set);
1503 }
1504 else {
1505 VALUE tmp = set_s_alloc(rb_obj_class(new_set));
1506 if (RSET_COMPARE_BY_IDENTITY(new_set)) {
1507 set_i_compare_by_identity(tmp);
1508 }
1509 set_merge_enum_into(tmp, other);
1510 set_iter(tmp, set_xor_i, (st_data_t)new_set);
1511 }
1512 set_compact_after_delete(set);
1513
1514 return new_set;
1515}
1516
1517/*
1518 * call-seq:
1519 * self | enumerable -> new_set
1520 *
1521 * Returns a new set containing
1522 * the {union}[https://en.wikipedia.org/wiki/Union_(set_theory)]
1523 * of +self+ and the given +enumerable+;
1524 * that is, containing the elements of both +self+ and +enumerable+.
1525 *
1526 * set = Set[0, 1, 2]
1527 * set | Set[2, 1, 'a'] # => Set[0, 1, 2, "a"]
1528 * set | set # => Set[0, 1, 2]
1529 *
1530 * Related: see {Methods for Set Operations}[rdoc-ref:Set@Methods+for+Set+Operations].
1531 */
1532static VALUE
1533set_i_union(VALUE set, VALUE other)
1534{
1535 set = rb_obj_dup(set);
1536 set_merge_enum_into(set, other);
1537 return set;
1538}
1539
1540static int
1541set_remove_i(st_data_t key, st_data_t from)
1542{
1543 st_data_t key_data = (st_data_t)key;
1544 set_table_delete((struct set_table *)from, &key_data);
1545 return ST_CONTINUE;
1546}
1547
1548static VALUE
1549set_remove_block(RB_BLOCK_CALL_FUNC_ARGLIST(key, set))
1550{
1551 st_data_t key_data = (st_data_t)key;
1552 rb_check_frozen(set);
1553 set_table_delete(RSET_TABLE(set), &key_data);
1554 return (VALUE)key_data;
1555}
1556
1557static void
1558set_remove_enum_from(VALUE set, VALUE arg)
1559{
1560 if (rb_obj_is_kind_of(arg, rb_cSet)) {
1561 set_iter(arg, set_remove_i, (st_data_t)RSET_TABLE(set));
1562 }
1563 else {
1564 rb_block_call(arg, enum_method_id(arg), 0, 0, set_remove_block, set);
1565 }
1566 set_compact_after_delete(set);
1567}
1568
1569/*
1570 * call-seq:
1571 * subtract(enumerable) -> self
1572 *
1573 * Deletes from +self+ every element found in the given +enumerable+;
1574 * returns +self+:
1575 *
1576 * set = Set[*0..9] # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1577 * set.subtract(5..14) # => Set[0, 1, 2, 3, 4]
1578 * set.subtract(Set[6, 2]) # => Set[0, 1, 3, 4]
1579 *
1580 * Related: see {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
1581 */
1582static VALUE
1583set_i_subtract(VALUE set, VALUE other)
1584{
1585 rb_check_frozen(set);
1586 set_remove_enum_from(set, other);
1587 return set;
1588}
1589
1590/*
1591 * call-seq:
1592 * self - enumerable -> new_set
1593 *
1594 * Returns a new set containing the
1595 * {difference}[https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement]
1596 * of +self+ and argument +enumerable+;
1597 * that is, containing all elements in +self+ that are not in +enumerable+.
1598 *
1599 *
1600 * set = Set[*(0..6), *%w[ a b c]] # => Set[0, 1, 2, 3, 4, 5, 6, "a", "b", "c"]
1601 * set - ['b', 6, 4, 1] # => Set[0, 2, 3, 5, "a", "c"]
1602 * set - ['d', 7, 9] # => Set[0, 1, 2, 3, 4, 5, 6, "a", "b", "c"]
1603 *
1604 * Related: see {Methods for Set Operations}[rdoc-ref:Set@Methods+for+Set+Operations].
1605 */
1606static VALUE
1607set_i_difference(VALUE set, VALUE other)
1608{
1609 return set_i_subtract(rb_obj_dup(set), other);
1610}
1611
1612static int
1613set_each_i(st_data_t key, st_data_t dummy)
1614{
1615 rb_yield(key);
1616 return ST_CONTINUE;
1617}
1618
1619/*
1620 * call-seq:
1621 * each {|element| ... } -> self
1622 * each -> enumerator
1623 *
1624 * With a block given, calls the block once for each element in the set,
1625 * passing the element as a parameter;
1626 * returns +self+:
1627 *
1628 * sum = 0
1629 * Set[1, 2, 3].each {|i| sum += i }
1630 * sum # => 6
1631 *
1632 * With no block given, returns an Enumerator.
1633 */
1634static VALUE
1635set_i_each(VALUE set)
1636{
1637 RETURN_SIZED_ENUMERATOR(set, 0, 0, set_enum_size);
1638 set_iter(set, set_each_i, 0);
1639 return set;
1640}
1641
1642static int
1643set_collect_i(st_data_t key, st_data_t data)
1644{
1645 set_insert_wb((VALUE)data, rb_yield((VALUE)key));
1646 return ST_CONTINUE;
1647}
1648
1649/*
1650 * call-seq:
1651 * collect! {|element| ... } -> self
1652 * collect! -> enumerator
1653 *
1654 * With a block given, calls the block with each element in +self+;
1655 * replaces the element with the block's return value:
1656 *
1657 * Set[1, :one, 'one', 1.0].collect! {|element| element.class }
1658 * # => Set[Integer, Symbol, String, Float]
1659 *
1660 * With no block given, returns an Enumerator.
1661 *
1662 * Related: see {Methods for Converting}[rdoc-ref:Set@Methods+for+Converting].
1663 */
1664static VALUE
1665set_i_collect(VALUE set)
1666{
1667 RETURN_SIZED_ENUMERATOR(set, 0, 0, set_enum_size);
1668 rb_check_frozen(set);
1669
1670 VALUE new_set = set_s_alloc(rb_obj_class(set));
1671 if (RSET_COMPARE_BY_IDENTITY(set)) {
1672 set_i_compare_by_identity(new_set);
1673 }
1674 set_iter(set, set_collect_i, (st_data_t)new_set);
1675 set_i_initialize_copy(set, new_set);
1676
1677 return set;
1678}
1679
1680static int
1681set_keep_if_i(st_data_t key, st_data_t into)
1682{
1683 if (!RTEST(rb_yield((VALUE)key))) {
1684 set_table_delete((set_table *)into, &key);
1685 }
1686 return ST_CONTINUE;
1687}
1688
1689/*
1690 * call-seq:
1691 * keep_if {|element| ... } -> self
1692 * keep_if -> enumerator
1693 *
1694 * With a block given,
1695 * calls the block with each element in +self+,
1696 * deleting the element if the block returns +false+ or +nil+;
1697 * returns +self+:
1698 *
1699 * set = Set[*0..9] # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1700 * set.keep_if {|i| i.even? } # => Set[0, 2, 4, 6, 8]
1701 * set.keep_if {|i| i.odd? } # => Set[]
1702 *
1703 * With no block given, returns an Enumerator.
1704 *
1705 * Related: see {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
1706 */
1707static VALUE
1708set_i_keep_if(VALUE set)
1709{
1710 RETURN_SIZED_ENUMERATOR(set, 0, 0, set_enum_size);
1711 rb_check_frozen(set);
1712
1713 set_iter(set, set_keep_if_i, (st_data_t)RSET_TABLE(set));
1714 set_compact_after_delete(set);
1715
1716 return set;
1717}
1718
1719/*
1720 * call-seq:
1721 * select! {|element| ... } -> self or nil
1722 * select! -> enumerator
1723 *
1724 * With a block given, like #keep_if, but returns +nil+ if no changes were made:
1725 *
1726 * set = Set[*0..9] # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1727 * set.select! {|i| i.even? } # => Set[0, 2, 4, 6, 8]
1728 * set.select! {|i| i.even? } # => nil
1729 * set.select! {|i| i.odd? } # => Set[]
1730 *
1731 * With no block given, returns an Enumerator.
1732 *
1733 * Related: see {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
1734 */
1735static VALUE
1736set_i_select(VALUE set)
1737{
1738 RETURN_SIZED_ENUMERATOR(set, 0, 0, set_enum_size);
1739 rb_check_frozen(set);
1740
1741 set_table *table = RSET_TABLE(set);
1742 size_t n = set_table_size(table);
1743 set_iter(set, set_keep_if_i, (st_data_t)table);
1744 set_compact_after_delete(set);
1745
1746 return (n == set_table_size(table)) ? Qnil : set;
1747}
1748
1749/*
1750 * call-seq:
1751 * replace(enumerable) -> self
1752 *
1753 * Replaces the contents +self+ with the contents of the given +enumerable+;
1754 * returns +self+:
1755 *
1756 * set = Set[1, 'c', :s] # => Set[1, "c", :s]
1757 * set.replace([1, 2]) # => Set[1, 2]
1758 *
1759 * Related: see {Methods for Assigning}[rdoc-ref:Set@Methods+for+Assigning].
1760 */
1761static VALUE
1762set_i_replace(VALUE set, VALUE other)
1763{
1764 rb_check_frozen(set);
1765
1766 if (rb_obj_is_kind_of(other, rb_cSet)) {
1767 set_i_initialize_copy(set, other);
1768 }
1769 else {
1770 if (set_iterating_p(set)) {
1771 rb_raise(rb_eRuntimeError, "cannot replace set during iteration");
1772 }
1773
1774 // make sure enum is enumerable before calling clear
1775 enum_method_id(other);
1776
1777 set_table_clear(RSET_TABLE(set));
1778 set_merge_enum_into(set, other);
1779 }
1780 set_compact_after_delete(set);
1781
1782 return set;
1783}
1784
1785/*
1786 * call-seq:
1787 * reset -> self
1788 *
1789 * Resets the internal state of +self+; returns +self+.
1790 *
1791 * A set relies on the #hash results of each element being consistent.
1792 * Modifying an element in a way that changes the results of #hash
1793 * may allow duplicate elements in the set:
1794 *
1795 * array = [1]
1796 * set = Set[array] # => Set[[1]]
1797 * array << 2
1798 * set.add(array) # => Set[[1, 2], [1, 2]]
1799 *
1800 * Calling #reset will recalculate all of the hash values and remove
1801 * duplicate elements:
1802 *
1803 * set.reset # => Set[[1, 2]]
1804 *
1805 */
1806static VALUE
1807set_i_reset(VALUE set)
1808{
1809 if (set_iterating_p(set)) {
1810 rb_raise(rb_eRuntimeError, "reset during iteration");
1811 }
1812
1813 return set_reset_table_with_type(set, RSET_TABLE(set)->type);
1814}
1815
1816static void set_flatten_merge(VALUE set, VALUE from, VALUE seen);
1817
1818static int
1819set_flatten_merge_i(st_data_t item, st_data_t arg)
1820{
1821 VALUE *args = (VALUE *)arg;
1822 VALUE set = args[0];
1823 if (rb_obj_is_kind_of(item, rb_cSet)) {
1824 VALUE e_id = rb_obj_id(item);
1825 VALUE hash = args[2];
1826 switch(rb_hash_aref(hash, e_id)) {
1827 case Qfalse:
1828 return ST_CONTINUE;
1829 case Qtrue:
1830 rb_raise(rb_eArgError, "tried to flatten recursive Set");
1831 default:
1832 break;
1833 }
1834
1835 rb_hash_aset(hash, e_id, Qtrue);
1836 set_flatten_merge(set, item, hash);
1837 rb_hash_aset(hash, e_id, Qfalse);
1838 }
1839 else {
1840 set_i_add(set, item);
1841 }
1842 return ST_CONTINUE;
1843}
1844
1845static void
1846set_flatten_merge(VALUE set, VALUE from, VALUE hash)
1847{
1848 VALUE args[3] = {set, from, hash};
1849 set_iter(from, set_flatten_merge_i, (st_data_t)args);
1850}
1851
1852/*
1853 * call-seq:
1854 * flatten -> new_set
1855 *
1856 * Returns a new set that is a copy of +self+,
1857 * but with +self+ and its nested sets flattened;
1858 * that is, their elements become elements of +self+:
1859 *
1860 * Set[Set[0, 1], Set[2, 3]].flatten
1861 * # => Set[0, 1, 2, 3]
1862 * Set[Set[0, 1], Set[Set[2, 3], Set[3, 4]]].flatten
1863 * # => Set[0, 1, 2, 3, 4]
1864 *
1865 * Does not flatten nested arrays or hashes:
1866 *
1867 * Set[%w[foo bar]].flatten # => Set[["foo", "bar"]]
1868 * Set[{foo: 0, bar: 1}].flatten # => Set[{foo: 0, bar: 1}]
1869 *
1870 * Related: see {Methods for Converting}[rdoc-ref:Set@Methods+for+Converting].
1871 */
1872static VALUE
1873set_i_flatten(VALUE set)
1874{
1875 VALUE new_set = set_s_alloc(rb_obj_class(set));
1876 if (RSET_COMPARE_BY_IDENTITY(set)) {
1877 set_i_compare_by_identity(new_set);
1878 }
1879 set_flatten_merge(new_set, set, rb_hash_new());
1880 return new_set;
1881}
1882
1883static int
1884set_contains_set_i(st_data_t item, st_data_t arg)
1885{
1886 if (rb_obj_is_kind_of(item, rb_cSet)) {
1887 *(bool *)arg = true;
1888 return ST_STOP;
1889 }
1890 return ST_CONTINUE;
1891}
1892
1893/*
1894 * call-seq:
1895 * flatten! -> self or nil
1896 *
1897 * Like #flatten, but if any changes were made
1898 * replaces +self+ with the result and returns +self+:
1899 *
1900 * Set[Set[0, 1], Set[2, 3]].flatten!
1901 * # => Set[0, 1, 2, 3]
1902 * Set[Set[0, 1], Set[Set[2, 3], Set[3, 4]]].flatten!
1903 * # => Set[0, 1, 2, 3, 4]
1904 *
1905 * Returns +nil+ if no changes were made:
1906 *
1907 * Set[0, 1, 2].flatten! # => nil
1908 *
1909 * Related: see {Methods for Assigning}[rdoc-ref:Set@Methods+for+Assigning].
1910 */
1911static VALUE
1912set_i_flatten_bang(VALUE set)
1913{
1914 bool contains_set = false;
1915 set_iter(set, set_contains_set_i, (st_data_t)&contains_set);
1916 if (!contains_set) return Qnil;
1917 rb_check_frozen(set);
1918 return set_i_replace(set, set_i_flatten(set));
1919}
1920
1922 set_table *table;
1923 VALUE result;
1924};
1925
1926static int
1927set_le_i(st_data_t key, st_data_t arg)
1928{
1929 struct set_subset_data *data = (struct set_subset_data *)arg;
1930 if (set_table_lookup(data->table, key)) return ST_CONTINUE;
1931 data->result = Qfalse;
1932 return ST_STOP;
1933}
1934
1935static VALUE
1936set_le(VALUE set, VALUE other)
1937{
1938 struct set_subset_data data = {
1939 .table = RSET_TABLE(other),
1940 .result = Qtrue
1941 };
1942 set_iter(set, set_le_i, (st_data_t)&data);
1943 return data.result;
1944}
1945
1946/*
1947 * call-seq:
1948 * proper_subset?(other_set) -> true or false
1949 *
1950 * Returns whether +self+ is
1951 * a {proper subset}[https://en.wikipedia.org/wiki/Subset]
1952 * of the given +other_set+:
1953 *
1954 * set = Set[*'b'..'e']
1955 * set.proper_subset?(set) # => false
1956 * set.proper_subset?(Set[*'a'..'f']) # => true
1957 *
1958 * Related: {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
1959 */
1960static VALUE
1961set_i_proper_subset(VALUE set, VALUE other)
1962{
1963 check_set(other);
1964 if (RSET_SIZE(set) >= RSET_SIZE(other)) return Qfalse;
1965 return set_le(set, other);
1966}
1967
1968/*
1969 * call-seq:
1970 * subset?(other_set) -> true or false
1971 *
1972 * Returns whether +self+ is a {subset}[https://en.wikipedia.org/wiki/Subset]
1973 * of the given +other_set+:
1974 *
1975 * set = Set[*'b'..'e']
1976 * set.subset?(set) # => true
1977 * set.subset?(Set[*'a'..'f']) # => true
1978 * set.subset?(Set[*'c'..'e']) # => false
1979 *
1980 * Related: {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
1981 */
1982static VALUE
1983set_i_subset(VALUE set, VALUE other)
1984{
1985 check_set(other);
1986 if (RSET_SIZE(set) > RSET_SIZE(other)) return Qfalse;
1987 return set_le(set, other);
1988}
1989
1990/*
1991 * call-seq:
1992 * proper_superset?(other_set) -> true or false
1993 *
1994 * Returns whether +self+ is
1995 * a {proper superset}[https://en.wikipedia.org/wiki/Subset]
1996 * of the given +other_set+:
1997 *
1998 * set = Set[*'a'..'f']
1999 * set.proper_superset?(set) # => false
2000 * set.proper_superset?(Set[*'b'..'e']) # => true
2001 *
2002 * Related: {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
2003 */
2004static VALUE
2005set_i_proper_superset(VALUE set, VALUE other)
2006{
2007 check_set(other);
2008 if (RSET_SIZE(set) <= RSET_SIZE(other)) return Qfalse;
2009 return set_le(other, set);
2010}
2011
2012/*
2013 * call-seq:
2014 * superset?(other_set) -> true or false
2015 *
2016 * Returns whether +self+ is a {superset}[https://en.wikipedia.org/wiki/Subset]
2017 * of the given +other_set+:
2018 *
2019 * set = Set[*'a'..'f'] # => Set["a", "b", "c", "d", "e", "f"]
2020 * set.superset?(set) # => true
2021 * set.superset?(Set[*'b'..'e']) # => true
2022 * set.superset?(Set[*'b'..'x']) # => false
2023 *
2024 * Related: {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
2025 */
2026static VALUE
2027set_i_superset(VALUE set, VALUE other)
2028{
2029 check_set(other);
2030 if (RSET_SIZE(set) < RSET_SIZE(other)) return Qfalse;
2031 return set_le(other, set);
2032}
2033
2034static int
2035set_intersect_i(st_data_t key, st_data_t arg)
2036{
2037 VALUE *args = (VALUE *)arg;
2038 if (set_table_lookup((set_table *)args[0], key)) {
2039 args[1] = Qtrue;
2040 return ST_STOP;
2041 }
2042 return ST_CONTINUE;
2043}
2044
2045/*
2046 * call-seq:
2047 * intersect?(enumerable) -> true or false
2048 *
2049 * Returns whether +self+ and +enumerable+ have any elements in common:
2050 *
2051 * set = Set[0, 'zero', :zero]
2052 * set.intersect?([0, 1, 2]) # => true
2053 * set.intersect?(%w[zero one two]) # => true
2054 * set.intersect?(Set[3]) # => false
2055 *
2056 * Related: see {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
2057 */
2058static VALUE
2059set_i_intersect(VALUE set, VALUE other)
2060{
2061 if (rb_obj_is_kind_of(other, rb_cSet)) {
2062 size_t set_size = RSET_SIZE(set);
2063 size_t other_size = RSET_SIZE(other);
2064 VALUE args[2];
2065 args[1] = Qfalse;
2066 VALUE iter_arg;
2067
2068 if (set_size < other_size) {
2069 iter_arg = set;
2070 args[0] = (VALUE)RSET_TABLE(other);
2071 }
2072 else {
2073 iter_arg = other;
2074 args[0] = (VALUE)RSET_TABLE(set);
2075 }
2076 set_iter(iter_arg, set_intersect_i, (st_data_t)args);
2077 return args[1];
2078 }
2079 else if (rb_obj_is_kind_of(other, rb_mEnumerable)) {
2080 return rb_funcall(other, id_any_p, 1, set);
2081 }
2082 else {
2083 rb_raise(rb_eArgError, "value must be enumerable");
2084 }
2085}
2086
2087/*
2088 * call-seq:
2089 * disjoint?(enumerable) -> true or false
2090 *
2091 * Returns whether no element of +enumerable+ is present in +self+:
2092 *
2093 * set = Set[0, 'zero', :zero]
2094 * set.disjoint?([1, 2, 3]) # => true
2095 * set.disjoint?([0, 1, 2, 3]) # => false
2096 *
2097 * Related: see {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
2098 */
2099static VALUE
2100set_i_disjoint(VALUE set, VALUE other)
2101{
2102 return RBOOL(!RTEST(set_i_intersect(set, other)));
2103}
2104
2105/*
2106 * call-seq:
2107 * self <=> object -> -1, 0, 1, or nil
2108 *
2109 * Compares +self+ and +object+.
2110 *
2111 * If +object+ is another set, returns:
2112 *
2113 * - +-1+, if +self+ is a proper subset of +object+.
2114 * - +0+, if +self+ and +object+ have the same elements.
2115 * - +1+, if +self+ is a proper superset of +object+.
2116 * - +nil+, if none of the above;
2117 * that is, if +self+ and +object+ each have one or more elements
2118 * not included in the other.
2119 *
2120 * Examples:
2121 *
2122 * set = Set[0, 1, 2]
2123 * set <=> Set[3, 2, 1, 0] # => -1
2124 * set <=> Set[2, 1, 0] # => 0
2125 * set <=> Set[1, 0] # => 1
2126 * set <=> Set[1, 0, 3] # => nil
2127 *
2128 * Returns +nil+ if +object+ is not a set:
2129 *
2130 * set <=> [2, 1, 0] # => nil # Array, not Set.
2131 *
2132 * Related: see {Methods for Comparing}[rdoc-ref:Set@Methods+for+Comparing].
2133 */
2134static VALUE
2135set_i_compare(VALUE set, VALUE other)
2136{
2137 if (rb_obj_is_kind_of(other, rb_cSet)) {
2138 size_t set_size = RSET_SIZE(set);
2139 size_t other_size = RSET_SIZE(other);
2140
2141 if (set_size < other_size) {
2142 if (set_le(set, other) == Qtrue) {
2143 return INT2NUM(-1);
2144 }
2145 }
2146 else if (set_size > other_size) {
2147 if (set_le(other, set) == Qtrue) {
2148 return INT2NUM(1);
2149 }
2150 }
2151 else if (set_le(set, other) == Qtrue) {
2152 return INT2NUM(0);
2153 }
2154 }
2155
2156 return Qnil;
2157}
2158
2160 VALUE result;
2161 VALUE set;
2162};
2163
2164static int
2165set_eql_i(st_data_t item, st_data_t arg)
2166{
2167 struct set_equal_data *data = (struct set_equal_data *)arg;
2168
2169 if (!set_table_lookup(RSET_TABLE(data->set), item)) {
2170 data->result = Qfalse;
2171 return ST_STOP;
2172 }
2173 return ST_CONTINUE;
2174}
2175
2176static VALUE
2177set_recursive_eql(VALUE set, VALUE dt, int recur)
2178{
2179 if (recur) return Qtrue;
2180 struct set_equal_data *data = (struct set_equal_data*)dt;
2181 data->result = Qtrue;
2182 set_iter(set, set_eql_i, dt);
2183 return data->result;
2184}
2185
2186/*
2187 * call-seq:
2188 * self == object -> true or false
2189 *
2190 * Returns whether +object+ is a set, and has the same elements as +self+:
2191 *
2192 * set = Set[0, 1, 2]
2193 * set == Set[1, 2, 0] # => true
2194 * set == [1, 2, 3] # => false
2195 * set == Set[1, 2, '3'] # => false
2196 *
2197 * Related: see {Methods for Comparing}[rdoc-ref:Set@Methods+for+Comparing].
2198 */
2199static VALUE
2200set_i_eq(VALUE set, VALUE other)
2201{
2202 if (!rb_obj_is_kind_of(other, rb_cSet)) return Qfalse;
2203 if (set == other) return Qtrue;
2204
2205 set_table *stable = RSET_TABLE(set);
2206 set_table *otable = RSET_TABLE(other);
2207 size_t ssize = set_table_size(stable);
2208 size_t osize = set_table_size(otable);
2209
2210 if (ssize != osize) return Qfalse;
2211 if (ssize == 0 && osize == 0) return Qtrue;
2212 if (stable->type != otable->type) return Qfalse;
2213
2214 struct set_equal_data data;
2215 data.set = other;
2216 return rb_exec_recursive_paired(set_recursive_eql, set, other, (VALUE)&data);
2217}
2218
2219static int
2220set_hash_i(st_data_t item, st_data_t(arg))
2221{
2222 st_index_t *hval = (st_index_t *)arg;
2223 st_index_t ival = rb_hash(item);
2224 *hval ^= rb_st_hash(&ival, sizeof(st_index_t), 0);
2225 return ST_CONTINUE;
2226}
2227
2228/*
2229 * call-seq:
2230 * hash -> integer
2231 *
2232 * Returns the integer hash value for +self+.
2233 *
2234 * Two sets with the same content have the same hash value.
2235 *
2236 * Set[0, 1].hash == Set[1, 0].hash # => true
2237 * Set[0, 1].hash == Set[0].hash # => false
2238 */
2239static VALUE
2240set_i_hash(VALUE set)
2241{
2242 st_index_t size = RSET_SIZE(set);
2243 st_index_t hval = rb_st_hash_start(size);
2244 hval = rb_hash_uint(hval, (st_index_t)set_i_hash);
2245 if (size) {
2246 set_iter(set, set_hash_i, (VALUE)&hval);
2247 }
2248 hval = rb_st_hash_end(hval);
2249 return ST2FIX(hval);
2250}
2251
2252/* :nodoc: */
2253static int
2254set_to_hash_i(st_data_t key, st_data_t arg)
2255{
2256 rb_hash_aset((VALUE)arg, (VALUE)key, Qtrue);
2257 return ST_CONTINUE;
2258}
2259
2260static VALUE
2261set_i_to_h(VALUE set)
2262{
2263 long size = RSET_SIZE(set);
2264 VALUE hash;
2265 if (RSET_COMPARE_BY_IDENTITY(set)) {
2266 hash = rb_ident_hash_new_capa(size);
2267 }
2268 else {
2269 hash = rb_hash_new_capa(size);
2270 }
2271 rb_hash_set_default(hash, Qfalse);
2272
2273 if (size == 0) return hash;
2274
2275 set_iter(set, set_to_hash_i, (st_data_t)hash);
2276 return hash;
2277}
2278
2279static VALUE
2280compat_dumper(VALUE set)
2281{
2282 VALUE dumper = rb_class_allocate_instance_capa(rb_cObject, 1);
2283 rb_ivar_set(dumper, id_i_hash, set_i_to_h(set));
2284 return dumper;
2285}
2286
2287static int
2288set_i_from_hash_i(st_data_t key, st_data_t val, st_data_t set)
2289{
2290 if ((VALUE)val != Qtrue) {
2291 rb_raise(rb_eRuntimeError, "expect true as Set value: %"PRIsVALUE, rb_obj_class((VALUE)val));
2292 }
2293 set_i_add((VALUE)set, (VALUE)key);
2294 return ST_CONTINUE;
2295}
2296
2297static VALUE
2298set_i_from_hash(VALUE set, VALUE hash)
2299{
2300 Check_Type(hash, T_HASH);
2301 if (rb_hash_compare_by_id_p(hash)) set_i_compare_by_identity(set);
2302 rb_hash_stlike_foreach(hash, set_i_from_hash_i, (st_data_t)set);
2303 return set;
2304}
2305
2306static VALUE
2307compat_loader(VALUE self, VALUE a)
2308{
2309 return set_i_from_hash(self, rb_ivar_get(a, id_i_hash));
2310}
2311
2312/* Internal C-API functions */
2313
2314VALUE
2315rb_ident_set_new(void)
2316{
2317 return set_alloc_with_size_and_type(rb_cSet, 0, &identhash);
2318}
2319
2320bool
2321rb_set_add_no_check(VALUE set, VALUE element)
2322{
2323 if (set_insert(RSET_TABLE(set), (st_data_t)element) == 0) {
2324 RB_OBJ_WRITTEN(set, Qundef, element);
2325 return true;
2326 }
2327 return false;
2328}
2329
2330bool
2331rb_set_delete_no_check(VALUE set, VALUE element)
2332{
2333 st_data_t element_data = (st_data_t)element;
2334 return set_table_delete(RSET_TABLE(set), &element_data) != 0;
2335}
2336
2337VALUE
2338rb_set_to_a(VALUE set)
2339{
2340 return set_i_to_a(set);
2341}
2342
2343/* C-API functions */
2344
2345void
2346rb_set_foreach(VALUE set, int (*func)(VALUE element, VALUE arg), VALUE arg)
2347{
2348 set_iter(set, func, arg);
2349}
2350
2351VALUE
2353{
2354 return set_alloc_with_size(rb_cSet, 0);
2355}
2356
2357VALUE
2359{
2360 return set_alloc_with_size(rb_cSet, (st_index_t)capa);
2361}
2362
2363bool
2365{
2366 return RSET_IS_MEMBER(set, element);
2367}
2368
2369bool
2371{
2372 return set_i_add_p(set, element) != Qnil;
2373}
2374
2375VALUE
2377{
2378 return set_i_clear(set);
2379}
2380
2381bool
2383{
2384 return set_i_delete_p(set, element) != Qnil;
2385}
2386
2387size_t
2389{
2390 return RSET_SIZE(set);
2391}
2392
2393/*
2394 * Document-class: Set
2395 *
2396 * An instance of class \Set contains a collection
2397 * of objects (elements), with no duplicates.
2398 *
2399 * By default:
2400 *
2401 * - Set determines equality via Object#eql? and Object#hash,
2402 * and assumes that these values do not change for a stored element.
2403 * If these values do change, the set enters an unreliable state;
2404 * see #reset.
2405 * - A String instance added to a set is stored as a frozen copy of the string,
2406 * unless it is already frozen.
2407 *
2408 * Calling #compare_by_identity causes:
2409 *
2410 * - All following determinations of equality
2411 * to use object identity instead of the methods mentioned above.
2412 * - A String added to a set is stored "as is", whether or not frozen.
2413 *
2414 * \Set includes module Enumerable, and is easy to use with other enumerable objects.
2415 * Many of its methods accept enumerable objects as arguments;
2416 * any enumerable object may be converted to a set via #to_set.
2417 *
2418 * == Contact
2419 *
2420 * - Akinori MUSHA <knu@iDaemons.org> (current maintainer)
2421 *
2422 * == Inheriting from \Set
2423 *
2424 * Before Ruby 4.0 (released in December, 2025),
2425 * class \Set had a different, less efficient implementation.
2426 * In Ruby 4.0, the class was reimplemented in C,
2427 * and the behaviors of some methods were adjusted.
2428 *
2429 * When compatibility with the older implementation is needed,
2430 * a \Set subclass should inherit directly from class +Set+;
2431 * this automatically includes module +Set::SubclassCompatible+,
2432 * which makes behaviors closer to those in the older implementation.
2433 *
2434 * A difference may be seen as follows:
2435 *
2436 * Set[[1, 2, 3]] # => Set[[1, 2, 3]]
2437 * class MySet < Set; end
2438 * MySet[[1, 2, 3]] # => #<MySet: {[1, 2, 3]}> # Same as in Ruby 3.4.
2439 *
2440 * When backward compatibility is not needed,
2441 * a \Set subclass should inherit from +Set::CoreSet+,
2442 * which avoids including the compatibility layer:
2443 *
2444 * class MyCoreSet < Set::CoreSet; end
2445 * MyCoreSet[[1, 2, 3]] # => MyCoreSet[[1, 2, 3]]
2446 *
2447 * == What's Here
2448 *
2449 * First, what's elsewhere. \Class \Set:
2450 *
2451 * - Inherits from {class Object}[rdoc-ref:Object@Whats+Here].
2452 * - Includes {module Enumerable}[rdoc-ref:Enumerable@Whats+Here],
2453 * which provides dozens of additional methods.
2454 *
2455 * In particular, class \Set does not have many methods of its own
2456 * for fetching or for iterating.
2457 * Instead, it relies on those in \Enumerable.
2458 *
2459 * Here, class \Set provides methods that are useful for:
2460 *
2461 * - {Creating a Set}[rdoc-ref:Set@Methods+for+Creating+a+Set]
2462 * - {Set Operations}[rdoc-ref:Set@Methods+for+Set+Operations]
2463 * - {Comparing}[rdoc-ref:Set@Methods+for+Comparing]
2464 * - {Querying}[rdoc-ref:Set@Methods+for+Querying]
2465 * - {Assigning}[rdoc-ref:Set@Methods+for+Assigning]
2466 * - {Deleting}[rdoc-ref:Set@Methods+for+Deleting]
2467 * - {Converting}[rdoc-ref:Set@Methods+for+Converting]
2468 * - {And more....}[rdoc-ref:Set@Other+Methods]
2469 *
2470 * === Methods for Creating a \Set
2471 *
2472 * - ::[]:
2473 * Returns a new set populated with the given objects.
2474 * - ::new:
2475 * Returns a new set based on the given object (if no block given),
2476 * or on the return values from the called block (if a block given).
2477 *
2478 * === Methods for \Set Operations
2479 *
2480 * - #& (aliased as #intersection):
2481 * Returns a new set containing the intersection of +self+ and the given enumerable.
2482 * - #- (aliased as #difference):
2483 * Returns a new set containing the difference of +self+ and the given enumerable.
2484 * - #^: Returns a new set containing the exclusive OR of +self+ and the given enumerable.
2485 * - #| (aliased as #union and #+):
2486 * Returns a new set containing the union of +self+ and the given enumerable.
2487 *
2488 * === Methods for Comparing
2489 *
2490 * - #<=>: Returns -1, 0, or 1 as +self+ is less than, equal to,
2491 * or greater than a given object.
2492 * - #==: Returns whether +self+ and a given enumerable are equal,
2493 * as determined by Object#eql?.
2494 * - #compare_by_identity?:
2495 * Returns whether +self+ considers only identity
2496 * when comparing elements.
2497 * - #proper_subset? (aliased as #<):
2498 * Returns whether the given enumerable is a proper subset of +self+.
2499 * - #proper_superset? (aliased as #>):
2500 * Returns whether the given enumerable is a proper superset of +self+.
2501 * - #subset? (aliased as #<=):
2502 * Returns whether the given object is a subset of +self+.
2503 * - #superset? (aliased as #>=):
2504 * Returns whether the given enumerable is a superset of +self+.
2505 *
2506 * === Methods for Querying
2507 *
2508 * - #disjoint?:
2509 * Returns whether no element of the given enumerable is present in +self+.
2510 * - #empty?:
2511 * Returns whether +self+ contains no elements.
2512 * - #include? (aliased as #member? and #===):
2513 * Returns whether the given object is an element of +self+.
2514 * - #intersect?:
2515 * Returns whether +self+ and the given enumerable have any elements in common.
2516 * - #size (aliased as #length):
2517 * Returns the number of elements in +self+.
2518 *
2519 * === Methods for Assigning
2520 *
2521 * - #add (aliased as #<<):
2522 * Adds the given object to +self+; returns +self+.
2523 * - #add?:
2524 * Like #add, but returns +nil+ if the given object is already in +self+.
2525 * - #merge:
2526 * Adds each element of each of the given enumerables to +self+; returns +self+.
2527 * - #replace:
2528 * Replaces the contents of +self+ with the contents of the given enumerable;
2529 * returns +self+.
2530 *
2531 * === Methods for Deleting
2532 *
2533 * - #clear:
2534 * Removes all elements from +self+; returns +self+.
2535 * - #delete:
2536 * Removes the given object from +self+ if +self+ includes the object; returns +self+.
2537 * - #delete?:
2538 * Like #delete, but returns +nil+ if the object is not in +self+.
2539 * - #delete_if:
2540 * Calls the block with each element in +self+;
2541 * removes the element if the block returns a truthy value.
2542 * - #keep_if:
2543 * Calls the block with each element in +self+,
2544 * deleting the element if the block returns +false+ or +nil+; returns +self+.
2545 * - #reject!
2546 * Like #delete_if, but returns +nil+ if no changes were made.
2547 * - #select! (aliased as #filter!):
2548 * Like #keep_if, but returns +nil+ if no changes were made.
2549 * - #subtract:
2550 * Deletes from +self+ every element found in the given enumerable; returns +self+:
2551 *
2552 * === Methods for Converting
2553 *
2554 * - #classify:
2555 * Returns a hash that partitions the elements,
2556 * as determined by the given block.
2557 * - #collect! (aliased as #map!):
2558 * Replaces each element with a block return-value.
2559 * - #divide:
2560 * Returns a set of sets that partition the elements,
2561 * as determined by the given block.
2562 * - #flatten:
2563 * Returns a new set that is a recursive flattening of +self+.
2564 * - #flatten!: Like #flatten, but if any changes were made
2565 * replaces +self+ with the result and returns +self+.
2566 * - #inspect (aliased as #to_s):
2567 * Returns a string representation of +self+.
2568 * - #join:
2569 * Returns the string formed by joining the string-converted elements of +self+
2570 * with the given separator.
2571 * - #to_a:
2572 * Returns an array containing the elements of +self+.
2573 * - #to_set:
2574 * With a block given, creates and returns a new set;
2575 * calls the block with each element of +self+,
2576 * and adds the block's returns value to the new set.
2577 *
2578 * === Other Methods
2579 *
2580 * - #compare_by_identity:
2581 * Sets +self+ to compare by object identity (rather than by object content).
2582 * - #each:
2583 * Calls the block with each successive element of +self+; returns +self+.
2584 * - #reset:
2585 * Resets the internal state of +self+; returns +self+.
2586 * Useful if an element has been modified while an element in the set.
2587 *
2588 */
2589void
2590Init_Set(void)
2591{
2592 rb_cSet = rb_define_class("Set", rb_cObject);
2594
2595 id_each_entry = rb_intern_const("each_entry");
2596 id_any_p = rb_intern_const("any?");
2597 id_new = rb_intern_const("new");
2598 id_i_hash = rb_intern_const("@hash");
2599 id_subclass_compatible = rb_intern_const("SubclassCompatible");
2600 id_class_methods = rb_intern_const("ClassMethods");
2601 id_set_iter_lev = rb_make_internal_id();
2602
2603 rb_define_alloc_func(rb_cSet, set_s_alloc);
2604 rb_define_singleton_method(rb_cSet, "[]", set_s_create, -1);
2605
2606 rb_define_method(rb_cSet, "initialize", set_i_initialize, -1);
2607 rb_define_method(rb_cSet, "initialize_copy", set_i_initialize_copy, 1);
2608
2609 rb_define_method(rb_cSet, "&", set_i_intersection, 1);
2610 rb_define_alias(rb_cSet, "intersection", "&");
2611 rb_define_method(rb_cSet, "-", set_i_difference, 1);
2612 rb_define_alias(rb_cSet, "difference", "-");
2613 rb_define_method(rb_cSet, "^", set_i_xor, 1);
2614 rb_define_method(rb_cSet, "|", set_i_union, 1);
2615 rb_define_alias(rb_cSet, "+", "|");
2616 rb_define_alias(rb_cSet, "union", "|");
2617 rb_define_method(rb_cSet, "<=>", set_i_compare, 1);
2618 rb_define_method(rb_cSet, "==", set_i_eq, 1);
2619 rb_define_alias(rb_cSet, "eql?", "==");
2620 rb_define_method(rb_cSet, "add", set_i_add, 1);
2621 rb_define_alias(rb_cSet, "<<", "add");
2622 rb_define_method(rb_cSet, "add?", set_i_add_p, 1);
2623 rb_define_method(rb_cSet, "classify", set_i_classify, 0);
2624 rb_define_method(rb_cSet, "clear", set_i_clear, 0);
2625 rb_define_method(rb_cSet, "collect!", set_i_collect, 0);
2626 rb_define_alias(rb_cSet, "map!", "collect!");
2627 rb_define_method(rb_cSet, "compare_by_identity", set_i_compare_by_identity, 0);
2628 rb_define_method(rb_cSet, "compare_by_identity?", set_i_compare_by_identity_p, 0);
2629 rb_define_method(rb_cSet, "delete", set_i_delete, 1);
2630 rb_define_method(rb_cSet, "delete?", set_i_delete_p, 1);
2631 rb_define_method(rb_cSet, "delete_if", set_i_delete_if, 0);
2632 rb_define_method(rb_cSet, "disjoint?", set_i_disjoint, 1);
2633 rb_define_method(rb_cSet, "divide", set_i_divide, 0);
2634 rb_define_method(rb_cSet, "each", set_i_each, 0);
2635 rb_define_method(rb_cSet, "empty?", set_i_empty, 0);
2636 rb_define_method(rb_cSet, "flatten", set_i_flatten, 0);
2637 rb_define_method(rb_cSet, "flatten!", set_i_flatten_bang, 0);
2638 rb_define_method(rb_cSet, "hash", set_i_hash, 0);
2639 rb_define_method(rb_cSet, "include?", set_i_include, 1);
2640 rb_define_alias(rb_cSet, "member?", "include?");
2641 rb_define_alias(rb_cSet, "===", "include?");
2642 rb_define_method(rb_cSet, "inspect", set_i_inspect, 0);
2643 rb_define_alias(rb_cSet, "to_s", "inspect");
2644 rb_define_method(rb_cSet, "intersect?", set_i_intersect, 1);
2645 rb_define_method(rb_cSet, "join", set_i_join, -1);
2646 rb_define_method(rb_cSet, "keep_if", set_i_keep_if, 0);
2647 rb_define_method(rb_cSet, "merge", set_i_merge, -1);
2648 rb_define_method(rb_cSet, "proper_subset?", set_i_proper_subset, 1);
2649 rb_define_alias(rb_cSet, "<", "proper_subset?");
2650 rb_define_method(rb_cSet, "proper_superset?", set_i_proper_superset, 1);
2651 rb_define_alias(rb_cSet, ">", "proper_superset?");
2652 rb_define_method(rb_cSet, "reject!", set_i_reject, 0);
2653 rb_define_method(rb_cSet, "replace", set_i_replace, 1);
2654 rb_define_method(rb_cSet, "reset", set_i_reset, 0);
2655 rb_define_method(rb_cSet, "size", set_i_size, 0);
2656 rb_define_alias(rb_cSet, "length", "size");
2657 rb_define_method(rb_cSet, "select!", set_i_select, 0);
2658 rb_define_alias(rb_cSet, "filter!", "select!");
2659 rb_define_method(rb_cSet, "subset?", set_i_subset, 1);
2660 rb_define_alias(rb_cSet, "<=", "subset?");
2661 rb_define_method(rb_cSet, "subtract", set_i_subtract, 1);
2662 rb_define_method(rb_cSet, "superset?", set_i_superset, 1);
2663 rb_define_alias(rb_cSet, ">=", "superset?");
2664 rb_define_method(rb_cSet, "to_a", set_i_to_a, 0);
2665 rb_define_method(rb_cSet, "to_set", set_i_to_set, 0);
2666
2667 /* :nodoc: */
2668 VALUE compat = rb_define_class_under(rb_cSet, "compatible", rb_cObject);
2669 rb_marshal_define_compat(rb_cSet, compat, compat_dumper, compat_loader);
2670
2671 // Create Set::CoreSet before defining inherited, so it does not include
2672 // the backwards compatibility layer.
2673 rb_define_class_under(rb_cSet, "CoreSet", rb_cSet);
2674 rb_define_private_method(rb_singleton_class(rb_cSet), "inherited", set_s_inherited, 1);
2675
2676 rb_provide("set.rb");
2677}
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
#define rb_define_private_method(klass, mid, func, arity)
Defines klass#mid and makes it private.
static bool RB_OBJ_FROZEN(VALUE obj)
Checks if an object is frozen.
Definition fl_type.h:714
void rb_include_module(VALUE klass, VALUE module)
Includes a module to a class.
Definition class.c:1769
void rb_extend_object(VALUE obj, VALUE module)
Extend the object with the module.
Definition eval.c:1911
VALUE rb_singleton_class(VALUE obj)
Finds or creates the singleton class of the passed object.
Definition class.c:3051
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
Definition class.c:3094
int rb_keyword_given_p(void)
Determines if the current method is given a keyword argument.
Definition eval.c:1048
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition eval.c:1035
#define rb_str_buf_cat2
Old name of rb_usascii_str_new_cstr.
Definition string.h:1707
#define Qundef
Old name of RUBY_Qundef.
#define CLASS_OF
Old name of rb_class_of.
Definition globals.h:205
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
#define T_HASH
Old name of RUBY_T_HASH.
Definition value_type.h:65
#define FLONUM_P
Old name of RB_FLONUM_P.
#define Qtrue
Old name of RUBY_Qtrue.
#define ST2FIX
Old name of RB_ST2FIX.
Definition st_data_t.h:33
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define FIX2LONG
Old name of RB_FIX2LONG.
Definition long.h:46
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define ALLOCV_N
Old name of RB_ALLOCV_N.
Definition memory.h:405
#define POSFIXABLE
Old name of RB_POSFIXABLE.
Definition fixnum.h:29
#define FIXNUM_P
Old name of RB_FIXNUM_P.
#define ALLOCV_END
Old name of RB_ALLOCV_END.
Definition memory.h:406
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1471
VALUE rb_cSet
Set class.
Definition set.c:99
VALUE rb_cObject
Object class.
Definition object.c:60
VALUE rb_mEnumerable
Enumerable module.
Definition enum.c:28
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:234
VALUE rb_obj_dup(VALUE obj)
Duplicates the given object.
Definition object.c:556
VALUE rb_inspect(VALUE obj)
Generates a human-readable textual representation of the given object.
Definition object.c:669
VALUE rb_obj_is_instance_of(VALUE obj, VALUE klass)
Queries if the given object is a direct instance of the given class.
Definition object.c:850
VALUE rb_obj_is_kind_of(VALUE obj, VALUE klass)
Queries if the given object is an instance (of possibly descendants) of the given class.
Definition object.c:906
VALUE rb_cString
String class.
Definition string.c:85
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
Definition gc.h:504
VALUE rb_str_export_to_enc(VALUE obj, rb_encoding *enc)
Identical to rb_str_export(), except it additionally takes an encoding.
Definition string.c:1484
VALUE rb_funcall_passing_block(VALUE recv, ID mid, int argc, const VALUE *argv)
Identical to rb_funcallv_public(), except you can pass the passed block.
Definition vm_eval.c:1186
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1123
VALUE rb_ary_new_capa(long capa)
Identical to rb_ary_new(), except it additionally specifies how many rooms of objects it should alloc...
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
VALUE rb_ary_freeze(VALUE obj)
Freeze an array, preventing further modifications.
VALUE rb_ary_join(VALUE ary, VALUE sep)
Recursively stringises the elements of the passed array, flattens that result, then joins the sequenc...
#define RETURN_SIZED_ENUMERATOR(obj, argc, argv, size_fn)
This roughly resembles return enum_for(__callee__) unless block_given?.
Definition enumerator.h:208
static int rb_check_arity(int argc, int min, int max)
Ensures that the passed integer is in the passed range.
Definition error.h:284
void rb_provide(const char *feature)
Declares that the given feature is already provided by someone else.
Definition load.c:710
void rb_marshal_define_compat(VALUE newclass, VALUE oldclass, VALUE(*dumper)(VALUE), VALUE(*loader)(VALUE, VALUE))
Marshal format compatibility layer.
Definition marshal.c:139
size_t rb_set_size(VALUE set)
Returns the number of elements in the set.
Definition set.c:2388
VALUE rb_set_clear(VALUE set)
Removes all entries from set.
Definition set.c:2376
bool rb_set_delete(VALUE set, VALUE element)
Removes the element from from set.
Definition set.c:2382
bool rb_set_add(VALUE set, VALUE element)
Adds element to set.
Definition set.c:2370
void rb_set_foreach(VALUE set, int(*func)(VALUE element, VALUE arg), VALUE arg)
Iterates over a set.
Definition set.c:2346
bool rb_set_lookup(VALUE set, VALUE element)
Whether the set contains the given element.
Definition set.c:2364
VALUE rb_set_new(void)
Creates a new, empty set object.
Definition set.c:2352
VALUE rb_set_new_capa(size_t capa)
Identical to rb_set_new(), except it additionally specifies how many elements it is expected to conta...
Definition set.c:2358
#define rb_hash_uint(h, i)
Just another name of st_hash_uint.
Definition string.h:967
VALUE rb_str_buf_append(VALUE dst, VALUE src)
Identical to rb_str_cat_cstr(), except it takes Ruby's string instead of C's.
Definition string.c:3879
VALUE rb_str_buf_cat_ascii(VALUE dst, const char *src)
Identical to rb_str_cat_cstr(), except it additionally assumes the source string be a NUL terminated ...
Definition string.c:3855
VALUE rb_exec_recursive(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h)
"Recursion" API entry point.
VALUE rb_exec_recursive_paired(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE p, VALUE h)
Identical to rb_exec_recursive(), except it checks for the recursion on the ordered pair of { g,...
VALUE rb_const_get(VALUE space, ID name)
Identical to rb_const_defined(), except it returns the actual defined value.
Definition variable.c:3505
VALUE rb_ivar_set(VALUE obj, ID name, VALUE val)
Identical to rb_iv_set(), except it accepts the name as an ID instead of a C string.
Definition variable.c:2141
VALUE rb_ivar_get(VALUE obj, ID name)
Identical to rb_iv_get(), except it accepts the name as an ID instead of a C string.
Definition variable.c:1641
VALUE rb_class_path(VALUE mod)
Identical to rb_mod_name(), except it returns #<Class: ...> style inspection for anonymous modules.
Definition variable.c:398
int rb_respond_to(VALUE obj, ID mid)
Queries if the object responds to the method.
Definition vm_method.c:3693
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:285
int capa
Designed capacity of the buffer.
Definition io.h:11
#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)
Shim for block function parameters.
Definition iterator.h:58
VALUE rb_yield_values(int n,...)
Identical to rb_yield(), except it takes variadic number of parameters and pass them to the block.
Definition vm_eval.c:1401
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c:1378
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
VALUE rb_block_call(VALUE q, ID w, int e, const VALUE *r, type *t, VALUE y)
Call a method with a block.
VALUE type(ANYARGS)
ANYARGS-ed function type.
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:50
#define RARRAY_PTR_USE(ary, ptr_name, expr)
Declares a section of code where raw pointers are used.
Definition rarray.h:347
#define RARRAY_AREF(a, i)
Definition rarray.h:402
#define RBASIC(obj)
Convenient casting macro.
Definition rbasic.h:40
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:773
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
Definition rtypeddata.h:604
VALUE rb_require(const char *feature)
Identical to rb_require_string(), except it takes C's string instead of Ruby's.
Definition load.c:1528
#define RTEST
This is an old name of RB_TEST.
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:242
const char * wrap_struct_name
Name of structs of this kind.
Definition rtypeddata.h:249
set_table_entry * entries
Array of size 2^entry_power.
Definition set_table.h:31
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static void Check_Type(VALUE v, enum ruby_value_type t)
Identical to RB_TYPE_P(), except it raises exceptions on predication failure.
Definition value_type.h:425
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376