Ruby 4.1.0dev (2026-09-27 revision f6ff9e7d02e46360f8930b280a3dd921cccbda29)
set.c (f6ff9e7d02e46360f8930b280a3dd921cccbda29)
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 rb_check_frozen(set);
834 if (set_table_delete(RSET_TABLE(set), (st_data_t *)&item)) {
835 set_compact_after_delete(set);
836 }
837 return set;
838}
839
840/*
841 * call-seq:
842 * delete?(object) -> self or nil
843 *
844 * Like #delete, but returns +nil+ if the object is not in +self+:
845 *
846 * set = Set[0, 'zero', :zero]
847 * set.delete?(0) # => Set["zero", :zero]
848 * set.delete?(0) # => nil
849 *
850 * Related: see {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
851 */
852static VALUE
853set_i_delete_p(VALUE set, VALUE item)
854{
855 rb_check_frozen(set);
856 if (set_table_delete(RSET_TABLE(set), (st_data_t *)&item)) {
857 set_compact_after_delete(set);
858 return set;
859 }
860 return Qnil;
861}
862
863static int
864set_delete_if_i(st_data_t key, st_data_t dummy)
865{
866 return RTEST(rb_yield((VALUE)key)) ? ST_DELETE : ST_CONTINUE;
867}
868
869/*
870 * call-seq:
871 * delete_if {|element| ... } -> self
872 * delete_if -> enumerator
873 *
874 * With a block given, calls the block with each element in +self+;
875 * removes the element if the block returns a truthy value:
876 *
877 * set = Set[*0..9]
878 * # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
879 * set.delete_if {|element| element.even? }
880 * # => Set[1, 3, 5, 7, 9]
881 *
882 * With no block given, returns an Enumerator.
883 *
884 * Related: {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
885 */
886static VALUE
887set_i_delete_if(VALUE set)
888{
889 RETURN_SIZED_ENUMERATOR(set, 0, 0, set_enum_size);
890 rb_check_frozen(set);
891 set_iter(set, set_delete_if_i, 0);
892 set_compact_after_delete(set);
893 return set;
894}
895
896/*
897 * call-seq:
898 * reject! {|element| ... } -> self or nil
899 * reject! -> enumerator
900 *
901 * With a block given, like #delete_if, but returns +nil+ if no changes were made:
902 *
903 * set = Set[*0..9] # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
904 * set.reject! {|element| element.even? } # => Set[1, 3, 5, 7, 9]
905 * set.reject! {|element| element.even? } # => nil
906 * set.reject! {|element| element.odd? } # => Set[]
907 *
908 * With no block given, returns an Enumerator.
909 *
910 * Related: see {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
911 */
912static VALUE
913set_i_reject(VALUE set)
914{
915 RETURN_SIZED_ENUMERATOR(set, 0, 0, set_enum_size);
916 rb_check_frozen(set);
917
918 set_table *table = RSET_TABLE(set);
919 size_t n = set_table_size(table);
920 set_iter(set, set_delete_if_i, 0);
921
922 if (n == set_table_size(table)) return Qnil;
923
924 set_compact_after_delete(set);
925 return set;
926}
927
928static int
929set_classify_i(st_data_t key, st_data_t tmp)
930{
931 VALUE* args = (VALUE*)tmp;
932 VALUE hash = args[0];
933 VALUE hash_key = rb_yield(key);
934 VALUE set = rb_hash_lookup2(hash, hash_key, Qundef);
935 if (set == Qundef) {
936 set = set_s_alloc(args[1]);
937 if (RTEST(args[2])) {
938 set_i_compare_by_identity(set);
939 }
940 rb_hash_aset(hash, hash_key, set);
941 }
942 set_i_add(set, key);
943
944 return ST_CONTINUE;
945}
946
947/*
948 * call-seq:
949 * classify {|element| ... } -> hash
950 * classify -> enumerator
951 *
952 * With a block given, calls the block with each element of +self+;
953 * returns a hash whose keys are the block's return values.
954 * The value for each key is a set containing the elements
955 * for which the block returned that key.
956 *
957 * This example classifies elements by their classes:
958 *
959 * set = Set[*(5..7), *%w[foo bar]] # => Set[5, 6, 7, "foo", "bar"]
960 * set.classify {|element| element.class }
961 * # => {Integer => Set[5, 6, 7], String => Set["foo", "bar"]}
962 *
963 * With no block given, returns an Enumerator.
964 *
965 * Related: see {Methods for Converting}[rdoc-ref:Set@Methods+for+Converting].
966 */
967static VALUE
968set_i_classify(VALUE set)
969{
970 RETURN_SIZED_ENUMERATOR(set, 0, 0, set_enum_size);
971 VALUE args[3];
972 args[0] = rb_hash_new();
973 args[1] = rb_obj_class(set);
974 args[2] = RBOOL(RSET_COMPARE_BY_IDENTITY(set));
975 set_iter(set, set_classify_i, (st_data_t)args);
976 return args[0];
977}
978
979// Union-find with path compression
980static long
981set_divide_union_find_root(long *uf_parents, long index, long *tmp_array)
982{
983 long root = uf_parents[index];
984 long update_size = 0;
985 while (root != index) {
986 tmp_array[update_size++] = index;
987 index = root;
988 root = uf_parents[index];
989 }
990 for (long j = 0; j < update_size; j++) {
991 long idx = tmp_array[j];
992 uf_parents[idx] = root;
993 }
994 return root;
995}
996
997static void
998set_divide_union_find_merge(long *uf_parents, long i, long j, long *tmp_array)
999{
1000 long root_i = set_divide_union_find_root(uf_parents, i, tmp_array);
1001 long root_j = set_divide_union_find_root(uf_parents, j, tmp_array);
1002 if (root_i != root_j) uf_parents[root_j] = root_i;
1003}
1004
1005static VALUE
1006set_divide_arity2(VALUE set)
1007{
1008 VALUE tmp, uf;
1009 long size, *uf_parents, *tmp_array;
1010 VALUE set_class = rb_obj_class(set);
1011 VALUE items = set_i_to_a(set);
1012 rb_ary_freeze(items);
1013 size = RARRAY_LEN(items);
1014 tmp_array = ALLOCV_N(long, tmp, size);
1015 uf_parents = ALLOCV_N(long, uf, size);
1016 for (long i = 0; i < size; i++) {
1017 uf_parents[i] = i;
1018 }
1019 for (long i = 0; i < size - 1; i++) {
1020 VALUE item1 = RARRAY_AREF(items, i);
1021 for (long j = i + 1; j < size; j++) {
1022 VALUE item2 = RARRAY_AREF(items, j);
1023 if (RTEST(rb_yield_values(2, item1, item2)) &&
1024 RTEST(rb_yield_values(2, item2, item1))) {
1025 set_divide_union_find_merge(uf_parents, i, j, tmp_array);
1026 }
1027 }
1028 }
1029 VALUE final_set = set_s_create(0, 0, rb_cSet);
1030 VALUE hash = rb_hash_new();
1031 for (long i = 0; i < size; i++) {
1032 VALUE v = RARRAY_AREF(items, i);
1033 long root = set_divide_union_find_root(uf_parents, i, tmp_array);
1034 VALUE subset = rb_hash_aref(hash, LONG2FIX(root));
1035 if (subset == Qnil) {
1036 subset = set_s_alloc(set_class);
1037 if (RSET_COMPARE_BY_IDENTITY(set)) {
1038 set_i_compare_by_identity(subset);
1039 }
1040 rb_hash_aset(hash, LONG2FIX(root), subset);
1041 set_i_add(final_set, subset);
1042 }
1043 set_i_add(subset, v);
1044 }
1045 ALLOCV_END(tmp);
1046 ALLOCV_END(uf);
1047 return final_set;
1048}
1049
1050static void set_merge_enum_into(VALUE set, VALUE arg);
1051
1052/*
1053 * call-seq:
1054 * divide {|ele| ... } -> new_set
1055 * divide {|ele0, ele1| ... } -> new_set
1056 * divide -> enumerator
1057 *
1058 * With a block given, returns a set of sets.
1059 *
1060 * For a block that accepts one argument,
1061 * calls the block with each element;
1062 * creates a set for each distinct block return value:
1063 *
1064 * set = Set[*0..9]
1065 * # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1066 * # Divide into mod 3 sets.
1067 * set.divide {|ele| ele % 3 }
1068 * # => Set[Set[0, 3, 6, 9], Set[1, 4, 7], Set[2, 5, 8]]
1069 * # Divide into mod 5 sets.
1070 * set.divide {|ele| ele % 5 }
1071 * # => Set[Set[0, 5], Set[1, 6], Set[2, 7], Set[3, 8], Set[4, 9]]
1072 *
1073 * Set[0].divide {|ele| anything } # => Set[Set[0]]
1074 * Set[].divide {|ele| not called } # => Set[]
1075 *
1076 * For a block that accepts two arguments,
1077 * divides +self+ into connected components based on the binary
1078 * relation defined by the block, calling the block with each 2-element
1079 * permutation of the elements of +self+:
1080 *
1081 * set = Set[*0..9]
1082 * # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1083 * # Divide into mod 2 sets.
1084 * set.divide {|i, j| (i - j) % 2 == 0 }
1085 * # => Set[Set[0, 2, 4, 6, 8], Set[1, 3, 5, 7, 9]]
1086 * # Divide into mod 3 sets.
1087 * set.divide {|i, j| (i - j) % 3 == 0 }
1088 * # => Set[Set[0, 3, 6, 9], Set[1, 4, 7], Set[2, 5, 8]]
1089 *
1090 * Set[0].divide {|i, j| not called } # => Set[Set[0]]
1091 * Set[].divide {|i, j| not called } # => Set[]
1092 *
1093 * With no block given, returns an Enumerator.
1094 *
1095 * Related: see {Methods for Converting}[rdoc-ref:Set@Methods+for+Converting].
1096 */
1097static VALUE
1098set_i_divide(VALUE set)
1099{
1100 RETURN_SIZED_ENUMERATOR(set, 0, 0, set_enum_size);
1101
1102 if (rb_block_arity() == 2) {
1103 return set_divide_arity2(set);
1104 }
1105
1106 VALUE values = rb_hash_values(set_i_classify(set));
1107 set = set_alloc_with_size(rb_cSet, RARRAY_LEN(values));
1108 set_merge_enum_into(set, values);
1109 return set;
1110}
1111
1112static int
1113set_clear_i(st_data_t key, st_data_t dummy)
1114{
1115 return ST_DELETE;
1116}
1117
1118/*
1119 * call-seq:
1120 * clear -> self
1121 *
1122 * Returns +self+ with all elements removed:
1123 *
1124 * Set[1, :one, 'one', 1.0].clear # => Set[]
1125 *
1126 * Related: see {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
1127 */
1128static VALUE
1129set_i_clear(VALUE set)
1130{
1131 rb_check_frozen(set);
1132 if (RSET_SIZE(set) == 0) return set;
1133 if (set_iterating_p(set)) {
1134 set_iter(set, set_clear_i, 0);
1135 }
1136 else {
1137 set_table_clear(RSET_TABLE(set));
1138 set_compact_after_delete(set);
1139 }
1140 return set;
1141}
1142
1144 VALUE set;
1145 set_table *into;
1146 set_table *other;
1147};
1148
1149static int
1150set_intersection_i(st_data_t key, st_data_t tmp)
1151{
1152 struct set_intersection_data *data = (struct set_intersection_data *)tmp;
1153 if (set_table_lookup(data->other, key)) {
1154 set_table_insert_wb(data->into, data->set, key);
1155 }
1156
1157 return ST_CONTINUE;
1158}
1159
1160static VALUE
1161set_intersection_block(RB_BLOCK_CALL_FUNC_ARGLIST(i, data))
1162{
1163 set_intersection_i((st_data_t)i, (st_data_t)data);
1164 return i;
1165}
1166
1167/*
1168 * call-seq:
1169 * self & enumerable -> new_set
1170 *
1171 * Returns a new set containing the {intersection}[https://en.wikipedia.org/wiki/Intersection_(set_theory)]
1172 * of +self+ and +enumerable+;
1173 * that is, containing all elements common to both, with no duplicates.
1174 * Argument +enumerable+ must be an Enumerable object:
1175 *
1176 * set = Set[*(0..6), *%w[ a b c]] # => Set[0, 1, 2, 3, 4, 5, 6, "a", "b", "c"]
1177 * set & ['c', 6, 8, 4] # => Set["c", 6, 4]
1178 * set & [:foo, :bar] # => Set[] # No elements in common.
1179 *
1180 * Related: see {Methods for Set Operations}[rdoc-ref:Set@Methods+for+Set+Operations].
1181 */
1182static VALUE
1183set_i_intersection(VALUE set, VALUE other)
1184{
1185 VALUE new_set = set_s_alloc(rb_obj_class(set));
1186 if (RSET_COMPARE_BY_IDENTITY(set)) {
1187 set_i_compare_by_identity(new_set);
1188 }
1189 set_table *stable = RSET_TABLE(set);
1190 set_table *ntable = RSET_TABLE(new_set);
1191
1192 if (rb_obj_is_kind_of(other, rb_cSet)) {
1193 set_table *otable = RSET_TABLE(other);
1194 if (set_table_size(stable) >= set_table_size(otable)) {
1195 /* Swap so we iterate over the smaller set */
1196 otable = stable;
1197 set = other;
1198 }
1199
1200 struct set_intersection_data data = {
1201 .set = new_set,
1202 .into = ntable,
1203 .other = otable
1204 };
1205 set_iter(set, set_intersection_i, (st_data_t)&data);
1206 }
1207 else {
1208 struct set_intersection_data data = {
1209 .set = new_set,
1210 .into = ntable,
1211 .other = stable
1212 };
1213 rb_block_call(other, enum_method_id(other), 0, 0, set_intersection_block, (VALUE)&data);
1214 }
1215
1216 return new_set;
1217}
1218
1219/*
1220 * call-seq:
1221 * include?(object) -> true or false
1222 *
1223 * Returns whether the given +object+ is an element of +self+:
1224 *
1225 * set = Set[0, :zero, '0']
1226 * set.include?('0') # => true
1227 * set.include?('zero') # => false
1228 *
1229 * Tests equality using `hash` and `eql?`.
1230 *
1231 * Aliased as #===, which means that sets may be used in +case+ expressions:
1232 *
1233 * case :apple
1234 * when Set[:potato, :carrot]
1235 * 'vegetable'
1236 * when Set[:apple, :banana]
1237 * 'fruit'
1238 * else
1239 * 'unknown'
1240 * end
1241 * # => "fruit"
1242 *
1243 * Related: see {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
1244 */
1245static VALUE
1246set_i_include(VALUE set, VALUE item)
1247{
1248 return RBOOL(RSET_IS_MEMBER(set, item));
1249}
1250
1252 VALUE set;
1253 set_table *into;
1254};
1255
1256static int
1257set_merge_i(st_data_t key, st_data_t data)
1258{
1259 struct set_merge_args *args = (struct set_merge_args *)data;
1260 set_table_insert_wb(args->into, args->set, key);
1261 return ST_CONTINUE;
1262}
1263
1264static VALUE
1265set_merge_block(RB_BLOCK_CALL_FUNC_ARGLIST(key, set))
1266{
1267 VALUE element = key;
1268 set_insert_wb(set, element);
1269 return element;
1270}
1271
1272static void
1273set_merge_enum_into(VALUE set, VALUE arg)
1274{
1275 if (rb_obj_is_kind_of(arg, rb_cSet)) {
1276 struct set_merge_args args = {
1277 .set = set,
1278 .into = RSET_TABLE(set)
1279 };
1280 set_iter(arg, set_merge_i, (st_data_t)&args);
1281 }
1282 else if (RB_TYPE_P(arg, T_ARRAY)) {
1283 long i;
1284 set_table *into = RSET_TABLE(set);
1285 for (i=0; i<RARRAY_LEN(arg); i++) {
1286 set_table_insert_wb(into, set, RARRAY_AREF(arg, i));
1287 }
1288 }
1289 else {
1290 rb_block_call(arg, enum_method_id(arg), 0, 0, set_merge_block, (VALUE)set);
1291 }
1292}
1293
1294/*
1295 * call-seq:
1296 * merge(*enumerables, **nil) -> self
1297 *
1298 * Adds each element of each of the given +enumerables+ to +self+;
1299 * returns +self+:
1300 *
1301 * set = Set[*0..2] # => Set[0, 1, 2]
1302 * set.merge('a'..'c', %w[foo bar]) # => Set[0, 1, 2, "a", "b", "c", "foo", "bar"]
1303 * set.merge('a'..'c', %w[foo bar]) # => Set[0, 1, 2, "a", "b", "c", "foo", "bar"]
1304 *
1305 * Related: see {Methods for Assigning}[rdoc-ref:Set@Methods+for+Assigning].
1306 *
1307 */
1308static VALUE
1309set_i_merge(int argc, VALUE *argv, VALUE set)
1310{
1311 if (rb_keyword_given_p()) {
1312 rb_raise(rb_eArgError, "no keywords accepted");
1313 }
1314
1315 if (set_iterating_p(set)) {
1316 rb_raise(rb_eRuntimeError, "cannot add to set during iteration");
1317 }
1318
1319 rb_check_frozen(set);
1320
1321 int i;
1322
1323 for (i=0; i < argc; i++) {
1324 set_merge_enum_into(set, argv[i]);
1325 }
1326
1327 return set;
1328}
1329
1330static VALUE
1331set_reset_table_with_type(VALUE set, const struct st_hash_type *type)
1332{
1333 rb_check_frozen(set);
1334
1335 struct set_object *sobj;
1336 TypedData_Get_Struct(set, struct set_object, &set_data_type, sobj);
1337 set_table *old = &sobj->table;
1338
1339 size_t size = set_table_size(old);
1340 if (size > 0) {
1341 set_table *new = set_init_table_with_size(NULL, type, size);
1342 struct set_merge_args args = {
1343 .set = set,
1344 .into = new
1345 };
1346 set_iter(set, set_merge_i, (st_data_t)&args);
1347 set_free_embedded_table(&sobj->table);
1348 memcpy(&sobj->table, new, sizeof(*new));
1349 SIZED_FREE(new);
1350 }
1351 else {
1352 sobj->table.type = type;
1353 }
1354
1355 return set;
1356}
1357
1358/*
1359 * call-seq:
1360 * compare_by_identity -> self
1361 *
1362 * Sets +self+ to compare by object identity
1363 * (rather than by object content, which is the initial setting);
1364 * returns +self+:
1365 *
1366 * set = Set.new
1367 * set.compare_by_identity
1368 * str = +"foo"
1369 * set.add(str)
1370 * # => Set["foo"]
1371 * set.include?(str)
1372 * # => true
1373 * set.add(str)
1374 * # => Set["foo"])
1375 * set.include?(+"foo")
1376 * # => false
1377 * set.add(+"foo")
1378 * # => Set["foo", "foo"])
1379 *
1380 * Once set, the compare-by-identity property may not be unset.
1381 *
1382 * Related: #compare_by_identity?.
1383 */
1384static VALUE
1385set_i_compare_by_identity(VALUE set)
1386{
1387 if (RSET_COMPARE_BY_IDENTITY(set)) return set;
1388
1389 if (set_iterating_p(set)) {
1390 rb_raise(rb_eRuntimeError, "compare_by_identity during iteration");
1391 }
1392
1393 return set_reset_table_with_type(set, &identhash);
1394}
1395
1396/*
1397 * call-seq:
1398 * compare_by_identity? -> true or false
1399 *
1400 * Returns whether +self+ compares elements by object identity
1401 * (rather than by content):
1402 *
1403 * set = Set[]
1404 * set.compare_by_identity? # => false
1405 * set.compare_by_identity
1406 * set.compare_by_identity? # => true
1407 *
1408 * Related: #compare_by_identity;
1409 * see also {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
1410 */
1411static VALUE
1412set_i_compare_by_identity_p(VALUE set)
1413{
1414 return RBOOL(RSET_COMPARE_BY_IDENTITY(set));
1415}
1416
1417/*
1418 * call-seq:
1419 * size -> integer
1420 *
1421 * Returns the number of elements in +self+:
1422 *
1423 * Set[*0..9].size # => 10
1424 *
1425 * Related: see {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
1426 */
1427static VALUE
1428set_i_size(VALUE set)
1429{
1430 return RSET_SIZE_NUM(set);
1431}
1432
1433/*
1434 * call-seq:
1435 * empty? -> true or false
1436 *
1437 * Returns whether +self+ contains no elements:
1438 *
1439 * Set[].empty? # => true
1440 * Set[0].empty? # => false
1441 *
1442 * Related: see {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
1443 */
1444static VALUE
1445set_i_empty(VALUE set)
1446{
1447 return RBOOL(RSET_EMPTY(set));
1448}
1449
1450static int
1451set_xor_i(st_data_t key, st_data_t data)
1452{
1453 VALUE element = (VALUE)key;
1454 VALUE set = (VALUE)data;
1455 set_table *table = RSET_TABLE(set);
1456 if (set_table_insert_wb(table, set, element)) {
1457 set_table_delete(table, &element);
1458 }
1459 return ST_CONTINUE;
1460}
1461
1462/*
1463 * call-seq:
1464 * self ^ enumerable -> new_set
1465 *
1466 * Returns a new set containing
1467 * the {exclusive OR}[https://en.wikipedia.org/wiki/Exclusive_or]
1468 * of +self+ and the given +enumerable+;
1469 * that is, containing each element that is in either +self+ or +enumerable+,
1470 * but not in both:
1471 *
1472 * set = Set[0, 1, 2]
1473 * set ^ Set[1, 2, 3] # => Set[0, 3]
1474 * set ^ Set[2, 1] # => Set[0]
1475 * set ^ Set[2, *('a'..'c')] # => Set[0, 1, "a", "b", "c"]
1476 * set ^ Set[2, 1, 0] # => Set[]
1477 *
1478 * For \Set +set+ and \Enumerable +enumerable+, these expressions are equivalent:
1479 *
1480 * set ^ enumerable
1481 * ((set | enumerable) - (set & enumerable))
1482 *
1483 * Related: see {Methods for Set Operations}[rdoc-ref:Set@Methods+for+Set+Operations].
1484 */
1485static VALUE
1486set_i_xor(VALUE set, VALUE other)
1487{
1488 VALUE new_set = rb_obj_dup(set);
1489
1490 if (rb_obj_is_kind_of(other, rb_cSet)) {
1491 set_iter(other, set_xor_i, (st_data_t)new_set);
1492 }
1493 else {
1494 VALUE tmp = set_s_alloc(rb_obj_class(new_set));
1495 if (RSET_COMPARE_BY_IDENTITY(new_set)) {
1496 set_i_compare_by_identity(tmp);
1497 }
1498 set_merge_enum_into(tmp, other);
1499 set_iter(tmp, set_xor_i, (st_data_t)new_set);
1500 }
1501 set_compact_after_delete(set);
1502
1503 return new_set;
1504}
1505
1506/*
1507 * call-seq:
1508 * self | enumerable -> new_set
1509 *
1510 * Returns a new set containing
1511 * the {union}[https://en.wikipedia.org/wiki/Union_(set_theory)]
1512 * of +self+ and the given +enumerable+;
1513 * that is, containing the elements of both +self+ and +enumerable+.
1514 *
1515 * set = Set[0, 1, 2]
1516 * set | Set[2, 1, 'a'] # => Set[0, 1, 2, "a"]
1517 * set | set # => Set[0, 1, 2]
1518 *
1519 * Related: see {Methods for Set Operations}[rdoc-ref:Set@Methods+for+Set+Operations].
1520 */
1521static VALUE
1522set_i_union(VALUE set, VALUE other)
1523{
1524 set = rb_obj_dup(set);
1525 set_merge_enum_into(set, other);
1526 return set;
1527}
1528
1529static int
1530set_remove_i(st_data_t key, st_data_t from)
1531{
1532 set_table_delete((struct set_table *)from, (st_data_t *)&key);
1533 return ST_CONTINUE;
1534}
1535
1536static VALUE
1537set_remove_block(RB_BLOCK_CALL_FUNC_ARGLIST(key, set))
1538{
1539 rb_check_frozen(set);
1540 set_table_delete(RSET_TABLE(set), (st_data_t *)&key);
1541 return key;
1542}
1543
1544static void
1545set_remove_enum_from(VALUE set, VALUE arg)
1546{
1547 if (rb_obj_is_kind_of(arg, rb_cSet)) {
1548 set_iter(arg, set_remove_i, (st_data_t)RSET_TABLE(set));
1549 }
1550 else {
1551 rb_block_call(arg, enum_method_id(arg), 0, 0, set_remove_block, (VALUE)set);
1552 }
1553 set_compact_after_delete(set);
1554}
1555
1556/*
1557 * call-seq:
1558 * subtract(enumerable) -> self
1559 *
1560 * Deletes from +self+ every element found in the given +enumerable+;
1561 * returns +self+:
1562 *
1563 * set = Set[*0..9] # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1564 * set.subtract(5..14) # => Set[0, 1, 2, 3, 4]
1565 * set.subtract(Set[6, 2]) # => Set[0, 1, 3, 4]
1566 *
1567 * Related: see {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
1568 */
1569static VALUE
1570set_i_subtract(VALUE set, VALUE other)
1571{
1572 rb_check_frozen(set);
1573 set_remove_enum_from(set, other);
1574 return set;
1575}
1576
1577/*
1578 * call-seq:
1579 * self - enumerable -> new_set
1580 *
1581 * Returns a new set containing the
1582 * {difference}[https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement]
1583 * of +self+ and argument +enumerable+;
1584 * that is, containing all elements in +self+ that are not in +enumerable+.
1585 *
1586 *
1587 * set = Set[*(0..6), *%w[ a b c]] # => Set[0, 1, 2, 3, 4, 5, 6, "a", "b", "c"]
1588 * set - ['b', 6, 4, 1] # => Set[0, 2, 3, 5, "a", "c"]
1589 * set - ['d', 7, 9] # => Set[0, 1, 2, 3, 4, 5, 6, "a", "b", "c"]
1590 *
1591 * Related: see {Methods for Set Operations}[rdoc-ref:Set@Methods+for+Set+Operations].
1592 */
1593static VALUE
1594set_i_difference(VALUE set, VALUE other)
1595{
1596 return set_i_subtract(rb_obj_dup(set), other);
1597}
1598
1599static int
1600set_each_i(st_data_t key, st_data_t dummy)
1601{
1602 rb_yield(key);
1603 return ST_CONTINUE;
1604}
1605
1606/*
1607 * call-seq:
1608 * each {|element| ... } -> self
1609 * each -> enumerator
1610 *
1611 * With a block given, calls the block once for each element in the set,
1612 * passing the element as a parameter;
1613 * returns +self+:
1614 *
1615 * sum = 0
1616 * Set[1, 2, 3].each {|i| sum += i }
1617 * sum # => 6
1618 *
1619 * With no block given, returns an Enumerator.
1620 */
1621static VALUE
1622set_i_each(VALUE set)
1623{
1624 RETURN_SIZED_ENUMERATOR(set, 0, 0, set_enum_size);
1625 set_iter(set, set_each_i, 0);
1626 return set;
1627}
1628
1629static int
1630set_collect_i(st_data_t key, st_data_t data)
1631{
1632 set_insert_wb((VALUE)data, rb_yield((VALUE)key));
1633 return ST_CONTINUE;
1634}
1635
1636/*
1637 * call-seq:
1638 * collect! {|element| ... } -> self
1639 * collect! -> enumerator
1640 *
1641 * With a block given, calls the block with each element in +self+;
1642 * replaces the element with the block's return value:
1643 *
1644 * Set[1, :one, 'one', 1.0].collect! {|element| element.class }
1645 * # => Set[Integer, Symbol, String, Float]
1646 *
1647 * With no block given, returns an Enumerator.
1648 *
1649 * Related: see {Methods for Converting}[rdoc-ref:Set@Methods+for+Converting].
1650 */
1651static VALUE
1652set_i_collect(VALUE set)
1653{
1654 RETURN_SIZED_ENUMERATOR(set, 0, 0, set_enum_size);
1655 rb_check_frozen(set);
1656
1657 VALUE new_set = set_s_alloc(rb_obj_class(set));
1658 if (RSET_COMPARE_BY_IDENTITY(set)) {
1659 set_i_compare_by_identity(new_set);
1660 }
1661 set_iter(set, set_collect_i, (st_data_t)new_set);
1662 set_i_initialize_copy(set, new_set);
1663
1664 return set;
1665}
1666
1667static int
1668set_keep_if_i(st_data_t key, st_data_t into)
1669{
1670 if (!RTEST(rb_yield((VALUE)key))) {
1671 set_table_delete((set_table *)into, &key);
1672 }
1673 return ST_CONTINUE;
1674}
1675
1676/*
1677 * call-seq:
1678 * keep_if {|element| ... } -> self
1679 * keep_if -> enumerator
1680 *
1681 * With a block given,
1682 * calls the block with each element in +self+,
1683 * deleting the element if the block returns +false+ or +nil+;
1684 * returns +self+:
1685 *
1686 * set = Set[*0..9] # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1687 * set.keep_if {|i| i.even? } # => Set[0, 2, 4, 6, 8]
1688 * set.keep_if {|i| i.odd? } # => Set[]
1689 *
1690 * With no block given, returns an Enumerator.
1691 *
1692 * Related: see {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
1693 */
1694static VALUE
1695set_i_keep_if(VALUE set)
1696{
1697 RETURN_SIZED_ENUMERATOR(set, 0, 0, set_enum_size);
1698 rb_check_frozen(set);
1699
1700 set_iter(set, set_keep_if_i, (st_data_t)RSET_TABLE(set));
1701 set_compact_after_delete(set);
1702
1703 return set;
1704}
1705
1706/*
1707 * call-seq:
1708 * select! {|element| ... } -> self or nil
1709 * select! -> enumerator
1710 *
1711 * With a block given, like #keep_if, but returns +nil+ if no changes were made:
1712 *
1713 * set = Set[*0..9] # => Set[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1714 * set.select! {|i| i.even? } # => Set[0, 2, 4, 6, 8]
1715 * set.select! {|i| i.even? } # => nil
1716 * set.select! {|i| i.odd? } # => Set[]
1717 *
1718 * With no block given, returns an Enumerator.
1719 *
1720 * Related: see {Methods for Deleting}[rdoc-ref:Set@Methods+for+Deleting].
1721 */
1722static VALUE
1723set_i_select(VALUE set)
1724{
1725 RETURN_SIZED_ENUMERATOR(set, 0, 0, set_enum_size);
1726 rb_check_frozen(set);
1727
1728 set_table *table = RSET_TABLE(set);
1729 size_t n = set_table_size(table);
1730 set_iter(set, set_keep_if_i, (st_data_t)table);
1731 set_compact_after_delete(set);
1732
1733 return (n == set_table_size(table)) ? Qnil : set;
1734}
1735
1736/*
1737 * call-seq:
1738 * replace(enumerable) -> self
1739 *
1740 * Replaces the contents +self+ with the contents of the given +enumerable+;
1741 * returns +self+:
1742 *
1743 * set = Set[1, 'c', :s] # => Set[1, "c", :s]
1744 * set.replace([1, 2]) # => Set[1, 2]
1745 *
1746 * Related: see {Methods for Assigning}[rdoc-ref:Set@Methods+for+Assigning].
1747 */
1748static VALUE
1749set_i_replace(VALUE set, VALUE other)
1750{
1751 rb_check_frozen(set);
1752
1753 if (rb_obj_is_kind_of(other, rb_cSet)) {
1754 set_i_initialize_copy(set, other);
1755 }
1756 else {
1757 if (set_iterating_p(set)) {
1758 rb_raise(rb_eRuntimeError, "cannot replace set during iteration");
1759 }
1760
1761 // make sure enum is enumerable before calling clear
1762 enum_method_id(other);
1763
1764 set_table_clear(RSET_TABLE(set));
1765 set_merge_enum_into(set, other);
1766 }
1767 set_compact_after_delete(set);
1768
1769 return set;
1770}
1771
1772/*
1773 * call-seq:
1774 * reset -> self
1775 *
1776 * Resets the internal state of +self+; returns +self+.
1777 *
1778 * A set relies on the #hash results of each element being consistent.
1779 * Modifying an element in a way that changes the results of #hash
1780 * may allow duplicate elements in the set:
1781 *
1782 * array = [1]
1783 * set = Set[array] # => Set[[1]]
1784 * array << 2
1785 * set.add(array) # => Set[[1, 2], [1, 2]]
1786 *
1787 * Calling #reset will recalculate all of the hash values and remove
1788 * duplicate elements:
1789 *
1790 * set.reset # => Set[[1, 2]]
1791 *
1792 */
1793static VALUE
1794set_i_reset(VALUE set)
1795{
1796 if (set_iterating_p(set)) {
1797 rb_raise(rb_eRuntimeError, "reset during iteration");
1798 }
1799
1800 return set_reset_table_with_type(set, RSET_TABLE(set)->type);
1801}
1802
1803static void set_flatten_merge(VALUE set, VALUE from, VALUE seen);
1804
1805static int
1806set_flatten_merge_i(st_data_t item, st_data_t arg)
1807{
1808 VALUE *args = (VALUE *)arg;
1809 VALUE set = args[0];
1810 if (rb_obj_is_kind_of(item, rb_cSet)) {
1811 VALUE e_id = rb_obj_id(item);
1812 VALUE hash = args[2];
1813 switch(rb_hash_aref(hash, e_id)) {
1814 case Qfalse:
1815 return ST_CONTINUE;
1816 case Qtrue:
1817 rb_raise(rb_eArgError, "tried to flatten recursive Set");
1818 default:
1819 break;
1820 }
1821
1822 rb_hash_aset(hash, e_id, Qtrue);
1823 set_flatten_merge(set, item, hash);
1824 rb_hash_aset(hash, e_id, Qfalse);
1825 }
1826 else {
1827 set_i_add(set, item);
1828 }
1829 return ST_CONTINUE;
1830}
1831
1832static void
1833set_flatten_merge(VALUE set, VALUE from, VALUE hash)
1834{
1835 VALUE args[3] = {set, from, hash};
1836 set_iter(from, set_flatten_merge_i, (st_data_t)args);
1837}
1838
1839/*
1840 * call-seq:
1841 * flatten -> new_set
1842 *
1843 * Returns a new set that is a copy of +self+,
1844 * but with +self+ and its nested sets flattened;
1845 * that is, their elements become elements of +self+:
1846 *
1847 * Set[Set[0, 1], Set[2, 3]].flatten
1848 * # => Set[0, 1, 2, 3]
1849 * Set[Set[0, 1], Set[Set[2, 3], Set[3, 4]]].flatten
1850 * # => Set[0, 1, 2, 3, 4]
1851 *
1852 * Does not flatten nested arrays or hashes:
1853 *
1854 * Set[%w[foo bar]].flatten # => Set[["foo", "bar"]]
1855 * Set[{foo: 0, bar: 1}].flatten # => Set[{foo: 0, bar: 1}]
1856 *
1857 * Related: see {Methods for Converting}[rdoc-ref:Set@Methods+for+Converting].
1858 */
1859static VALUE
1860set_i_flatten(VALUE set)
1861{
1862 VALUE new_set = set_s_alloc(rb_obj_class(set));
1863 if (RSET_COMPARE_BY_IDENTITY(set)) {
1864 set_i_compare_by_identity(new_set);
1865 }
1866 set_flatten_merge(new_set, set, rb_hash_new());
1867 return new_set;
1868}
1869
1870static int
1871set_contains_set_i(st_data_t item, st_data_t arg)
1872{
1873 if (rb_obj_is_kind_of(item, rb_cSet)) {
1874 *(bool *)arg = true;
1875 return ST_STOP;
1876 }
1877 return ST_CONTINUE;
1878}
1879
1880/*
1881 * call-seq:
1882 * flatten! -> self or nil
1883 *
1884 * Like #flatten, but if any changes were made
1885 * replaces +self+ with the result and returns +self+:
1886 *
1887 * Set[Set[0, 1], Set[2, 3]].flatten!
1888 * # => Set[0, 1, 2, 3]
1889 * Set[Set[0, 1], Set[Set[2, 3], Set[3, 4]]].flatten!
1890 * # => Set[0, 1, 2, 3, 4]
1891 *
1892 * Returns +nil+ if no changes were made:
1893 *
1894 * Set[0, 1, 2].flatten! # => nil
1895 *
1896 * Related: see {Methods for Assigning}[rdoc-ref:Set@Methods+for+Assigning].
1897 */
1898static VALUE
1899set_i_flatten_bang(VALUE set)
1900{
1901 bool contains_set = false;
1902 set_iter(set, set_contains_set_i, (st_data_t)&contains_set);
1903 if (!contains_set) return Qnil;
1904 rb_check_frozen(set);
1905 return set_i_replace(set, set_i_flatten(set));
1906}
1907
1909 set_table *table;
1910 VALUE result;
1911};
1912
1913static int
1914set_le_i(st_data_t key, st_data_t arg)
1915{
1916 struct set_subset_data *data = (struct set_subset_data *)arg;
1917 if (set_table_lookup(data->table, key)) return ST_CONTINUE;
1918 data->result = Qfalse;
1919 return ST_STOP;
1920}
1921
1922static VALUE
1923set_le(VALUE set, VALUE other)
1924{
1925 struct set_subset_data data = {
1926 .table = RSET_TABLE(other),
1927 .result = Qtrue
1928 };
1929 set_iter(set, set_le_i, (st_data_t)&data);
1930 return data.result;
1931}
1932
1933/*
1934 * call-seq:
1935 * proper_subset?(other_set) -> true or false
1936 *
1937 * Returns whether +self+ is
1938 * a {proper subset}[https://en.wikipedia.org/wiki/Subset]
1939 * of the given +other_set+:
1940 *
1941 * set = Set[*'b'..'e']
1942 * set.proper_subset?(set) # => false
1943 * set.proper_subset?(Set[*'a'..'f']) # => true
1944 *
1945 * Related: {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
1946 */
1947static VALUE
1948set_i_proper_subset(VALUE set, VALUE other)
1949{
1950 check_set(other);
1951 if (RSET_SIZE(set) >= RSET_SIZE(other)) return Qfalse;
1952 return set_le(set, other);
1953}
1954
1955/*
1956 * call-seq:
1957 * subset?(other_set) -> true or false
1958 *
1959 * Returns whether +self+ is a {subset}[https://en.wikipedia.org/wiki/Subset]
1960 * of the given +other_set+:
1961 *
1962 * set = Set[*'b'..'e']
1963 * set.subset?(set) # => true
1964 * set.subset?(Set[*'a'..'f']) # => true
1965 * set.subset?(Set[*'c'..'e']) # => false
1966 *
1967 * Related: {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
1968 */
1969static VALUE
1970set_i_subset(VALUE set, VALUE other)
1971{
1972 check_set(other);
1973 if (RSET_SIZE(set) > RSET_SIZE(other)) return Qfalse;
1974 return set_le(set, other);
1975}
1976
1977/*
1978 * call-seq:
1979 * proper_superset?(other_set) -> true or false
1980 *
1981 * Returns whether +self+ is
1982 * a {proper superset}[https://en.wikipedia.org/wiki/Subset]
1983 * of the given +other_set+:
1984 *
1985 * set = Set[*'a'..'f']
1986 * set.proper_superset?(set) # => false
1987 * set.proper_superset?(Set[*'b'..'e']) # => true
1988 *
1989 * Related: {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
1990 */
1991static VALUE
1992set_i_proper_superset(VALUE set, VALUE other)
1993{
1994 check_set(other);
1995 if (RSET_SIZE(set) <= RSET_SIZE(other)) return Qfalse;
1996 return set_le(other, set);
1997}
1998
1999/*
2000 * call-seq:
2001 * superset?(other_set) -> true or false
2002 *
2003 * Returns whether +self+ is a {superset}[https://en.wikipedia.org/wiki/Subset]
2004 * of the given +other_set+:
2005 *
2006 * set = Set[*'a'..'f'] # => Set["a", "b", "c", "d", "e", "f"]
2007 * set.superset?(set) # => true
2008 * set.superset?(Set[*'b'..'e']) # => true
2009 * set.superset?(Set[*'b'..'x']) # => false
2010 *
2011 * Related: {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
2012 */
2013static VALUE
2014set_i_superset(VALUE set, VALUE other)
2015{
2016 check_set(other);
2017 if (RSET_SIZE(set) < RSET_SIZE(other)) return Qfalse;
2018 return set_le(other, set);
2019}
2020
2021static int
2022set_intersect_i(st_data_t key, st_data_t arg)
2023{
2024 VALUE *args = (VALUE *)arg;
2025 if (set_table_lookup((set_table *)args[0], key)) {
2026 args[1] = Qtrue;
2027 return ST_STOP;
2028 }
2029 return ST_CONTINUE;
2030}
2031
2032/*
2033 * call-seq:
2034 * intersect?(enumerable) -> true or false
2035 *
2036 * Returns whether +self+ and +enumerable+ have any elements in common:
2037 *
2038 * set = Set[0, 'zero', :zero]
2039 * set.intersect?([0, 1, 2]) # => true
2040 * set.intersect?(%w[zero one two]) # => true
2041 * set.intersect?(Set[3]) # => false
2042 *
2043 * Related: see {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
2044 */
2045static VALUE
2046set_i_intersect(VALUE set, VALUE other)
2047{
2048 if (rb_obj_is_kind_of(other, rb_cSet)) {
2049 size_t set_size = RSET_SIZE(set);
2050 size_t other_size = RSET_SIZE(other);
2051 VALUE args[2];
2052 args[1] = Qfalse;
2053 VALUE iter_arg;
2054
2055 if (set_size < other_size) {
2056 iter_arg = set;
2057 args[0] = (VALUE)RSET_TABLE(other);
2058 }
2059 else {
2060 iter_arg = other;
2061 args[0] = (VALUE)RSET_TABLE(set);
2062 }
2063 set_iter(iter_arg, set_intersect_i, (st_data_t)args);
2064 return args[1];
2065 }
2066 else if (rb_obj_is_kind_of(other, rb_mEnumerable)) {
2067 return rb_funcall(other, id_any_p, 1, set);
2068 }
2069 else {
2070 rb_raise(rb_eArgError, "value must be enumerable");
2071 }
2072}
2073
2074/*
2075 * call-seq:
2076 * disjoint?(enumerable) -> true or false
2077 *
2078 * Returns whether no element of +enumerable+ is present in +self+:
2079 *
2080 * set = Set[0, 'zero', :zero]
2081 * set.disjoint?([1, 2, 3]) # => true
2082 * set.disjoint?([0, 1, 2, 3]) # => false
2083 *
2084 * Related: see {Methods for Querying}[rdoc-ref:Set@Methods+for+Querying].
2085 */
2086static VALUE
2087set_i_disjoint(VALUE set, VALUE other)
2088{
2089 return RBOOL(!RTEST(set_i_intersect(set, other)));
2090}
2091
2092/*
2093 * call-seq:
2094 * self <=> object -> -1, 0, 1, or nil
2095 *
2096 * Compares +self+ and +object+.
2097 *
2098 * If +object+ is another set, returns:
2099 *
2100 * - +-1+, if +self+ is a proper subset of +object+.
2101 * - +0+, if +self+ and +object+ have the same elements.
2102 * - +1+, if +self+ is a proper superset of +object+.
2103 * - +nil+, if none of the above;
2104 * that is, if +self+ and +object+ each have one or more elements
2105 * not included in the other.
2106 *
2107 * Examples:
2108 *
2109 * set = Set[0, 1, 2]
2110 * set <=> Set[3, 2, 1, 0] # => -1
2111 * set <=> Set[2, 1, 0] # => 0
2112 * set <=> Set[1, 0] # => 1
2113 * set <=> Set[1, 0, 3] # => nil
2114 *
2115 * Returns +nil+ if +object+ is not a set:
2116 *
2117 * set <=> [2, 1, 0] # => nil # Array, not Set.
2118 *
2119 * Related: see {Methods for Comparing}[rdoc-ref:Set@Methods+for+Comparing].
2120 */
2121static VALUE
2122set_i_compare(VALUE set, VALUE other)
2123{
2124 if (rb_obj_is_kind_of(other, rb_cSet)) {
2125 size_t set_size = RSET_SIZE(set);
2126 size_t other_size = RSET_SIZE(other);
2127
2128 if (set_size < other_size) {
2129 if (set_le(set, other) == Qtrue) {
2130 return INT2NUM(-1);
2131 }
2132 }
2133 else if (set_size > other_size) {
2134 if (set_le(other, set) == Qtrue) {
2135 return INT2NUM(1);
2136 }
2137 }
2138 else if (set_le(set, other) == Qtrue) {
2139 return INT2NUM(0);
2140 }
2141 }
2142
2143 return Qnil;
2144}
2145
2147 VALUE result;
2148 VALUE set;
2149};
2150
2151static int
2152set_eql_i(st_data_t item, st_data_t arg)
2153{
2154 struct set_equal_data *data = (struct set_equal_data *)arg;
2155
2156 if (!set_table_lookup(RSET_TABLE(data->set), item)) {
2157 data->result = Qfalse;
2158 return ST_STOP;
2159 }
2160 return ST_CONTINUE;
2161}
2162
2163static VALUE
2164set_recursive_eql(VALUE set, VALUE dt, int recur)
2165{
2166 if (recur) return Qtrue;
2167 struct set_equal_data *data = (struct set_equal_data*)dt;
2168 data->result = Qtrue;
2169 set_iter(set, set_eql_i, dt);
2170 return data->result;
2171}
2172
2173/*
2174 * call-seq:
2175 * self == object -> true or false
2176 *
2177 * Returns whether +object+ is a set, and has the same elements as +self+:
2178 *
2179 * set = Set[0, 1, 2]
2180 * set == Set[1, 2, 0] # => true
2181 * set == [1, 2, 3] # => false
2182 * set == Set[1, 2, '3'] # => false
2183 *
2184 * Related: see {Methods for Comparing}[rdoc-ref:Set@Methods+for+Comparing].
2185 */
2186static VALUE
2187set_i_eq(VALUE set, VALUE other)
2188{
2189 if (!rb_obj_is_kind_of(other, rb_cSet)) return Qfalse;
2190 if (set == other) return Qtrue;
2191
2192 set_table *stable = RSET_TABLE(set);
2193 set_table *otable = RSET_TABLE(other);
2194 size_t ssize = set_table_size(stable);
2195 size_t osize = set_table_size(otable);
2196
2197 if (ssize != osize) return Qfalse;
2198 if (ssize == 0 && osize == 0) return Qtrue;
2199 if (stable->type != otable->type) return Qfalse;
2200
2201 struct set_equal_data data;
2202 data.set = other;
2203 return rb_exec_recursive_paired(set_recursive_eql, set, other, (VALUE)&data);
2204}
2205
2206static int
2207set_hash_i(st_data_t item, st_data_t(arg))
2208{
2209 st_index_t *hval = (st_index_t *)arg;
2210 st_index_t ival = rb_hash(item);
2211 *hval ^= rb_st_hash(&ival, sizeof(st_index_t), 0);
2212 return ST_CONTINUE;
2213}
2214
2215/*
2216 * call-seq:
2217 * hash -> integer
2218 *
2219 * Returns the integer hash value for +self+.
2220 *
2221 * Two sets with the same content have the same hash value.
2222 *
2223 * Set[0, 1].hash == Set[1, 0].hash # => true
2224 * Set[0, 1].hash == Set[0].hash # => false
2225 */
2226static VALUE
2227set_i_hash(VALUE set)
2228{
2229 st_index_t size = RSET_SIZE(set);
2230 st_index_t hval = rb_st_hash_start(size);
2231 hval = rb_hash_uint(hval, (st_index_t)set_i_hash);
2232 if (size) {
2233 set_iter(set, set_hash_i, (VALUE)&hval);
2234 }
2235 hval = rb_st_hash_end(hval);
2236 return ST2FIX(hval);
2237}
2238
2239/* :nodoc: */
2240static int
2241set_to_hash_i(st_data_t key, st_data_t arg)
2242{
2243 rb_hash_aset((VALUE)arg, (VALUE)key, Qtrue);
2244 return ST_CONTINUE;
2245}
2246
2247static VALUE
2248set_i_to_h(VALUE set)
2249{
2250 long size = RSET_SIZE(set);
2251 VALUE hash;
2252 if (RSET_COMPARE_BY_IDENTITY(set)) {
2253 hash = rb_ident_hash_new_capa(size);
2254 }
2255 else {
2256 hash = rb_hash_new_capa(size);
2257 }
2258 rb_hash_set_default(hash, Qfalse);
2259
2260 if (size == 0) return hash;
2261
2262 set_iter(set, set_to_hash_i, (st_data_t)hash);
2263 return hash;
2264}
2265
2266static VALUE
2267compat_dumper(VALUE set)
2268{
2269 VALUE dumper = rb_class_allocate_instance_capa(rb_cObject, 1);
2270 rb_ivar_set(dumper, id_i_hash, set_i_to_h(set));
2271 return dumper;
2272}
2273
2274static int
2275set_i_from_hash_i(st_data_t key, st_data_t val, st_data_t set)
2276{
2277 if ((VALUE)val != Qtrue) {
2278 rb_raise(rb_eRuntimeError, "expect true as Set value: %"PRIsVALUE, rb_obj_class((VALUE)val));
2279 }
2280 set_i_add((VALUE)set, (VALUE)key);
2281 return ST_CONTINUE;
2282}
2283
2284static VALUE
2285set_i_from_hash(VALUE set, VALUE hash)
2286{
2287 Check_Type(hash, T_HASH);
2288 if (rb_hash_compare_by_id_p(hash)) set_i_compare_by_identity(set);
2289 rb_hash_stlike_foreach(hash, set_i_from_hash_i, (st_data_t)set);
2290 return set;
2291}
2292
2293static VALUE
2294compat_loader(VALUE self, VALUE a)
2295{
2296 return set_i_from_hash(self, rb_ivar_get(a, id_i_hash));
2297}
2298
2299/* Internal C-API functions */
2300
2301VALUE
2302rb_ident_set_new(void)
2303{
2304 return set_alloc_with_size_and_type(rb_cSet, 0, &identhash);
2305}
2306
2307bool
2308rb_set_add_no_check(VALUE set, VALUE element)
2309{
2310 if (set_insert(RSET_TABLE(set), (st_data_t)element) == 0) {
2311 RB_OBJ_WRITTEN(set, Qundef, element);
2312 return true;
2313 }
2314 return false;
2315}
2316
2317bool
2318rb_set_delete_no_check(VALUE set, VALUE element)
2319{
2320 return set_table_delete(RSET_TABLE(set), (st_data_t *)&element) != 0;
2321}
2322
2323VALUE
2324rb_set_to_a(VALUE set)
2325{
2326 return set_i_to_a(set);
2327}
2328
2329/* C-API functions */
2330
2331void
2332rb_set_foreach(VALUE set, int (*func)(VALUE element, VALUE arg), VALUE arg)
2333{
2334 set_iter(set, func, arg);
2335}
2336
2337VALUE
2339{
2340 return set_alloc_with_size(rb_cSet, 0);
2341}
2342
2343VALUE
2345{
2346 return set_alloc_with_size(rb_cSet, (st_index_t)capa);
2347}
2348
2349bool
2351{
2352 return RSET_IS_MEMBER(set, element);
2353}
2354
2355bool
2357{
2358 return set_i_add_p(set, element) != Qnil;
2359}
2360
2361VALUE
2363{
2364 return set_i_clear(set);
2365}
2366
2367bool
2369{
2370 return set_i_delete_p(set, element) != Qnil;
2371}
2372
2373size_t
2375{
2376 return RSET_SIZE(set);
2377}
2378
2379/*
2380 * Document-class: Set
2381 *
2382 * An instance of class \Set contains a collection
2383 * of objects (elements), with no duplicates.
2384 *
2385 * By default:
2386 *
2387 * - Set determines equality via Object#eql? and Object#hash,
2388 * and assumes that these values do not change for a stored element.
2389 * If these values do change, the set enters an unreliable state;
2390 * see #reset.
2391 * - A String instance added to a set is stored as a frozen copy of the string,
2392 * unless it is already frozen.
2393 *
2394 * Calling #compare_by_identity causes:
2395 *
2396 * - All following determinations of equality
2397 * to use object identity instead of the methods mentioned above.
2398 * - A String added to a set is stored "as is", whether or not frozen.
2399 *
2400 * \Set includes module Enumerable, and is easy to use with other enumerable objects.
2401 * Many of its methods accept enumerable objects as arguments;
2402 * any enumerable object may be converted to a set via #to_set.
2403 *
2404 * == Contact
2405 *
2406 * - Akinori MUSHA <knu@iDaemons.org> (current maintainer)
2407 *
2408 * == Inheriting from \Set
2409 *
2410 * Before Ruby 4.0 (released in December, 2025),
2411 * class \Set had a different, less efficient implementation.
2412 * In Ruby 4.0, the class was reimplemented in C,
2413 * and the behaviors of some methods were adjusted.
2414 *
2415 * When compatibility with the older implementation is needed,
2416 * a \Set subclass should inherit directly from class +Set+;
2417 * this automatically includes module +Set::SubclassCompatible+,
2418 * which makes behaviors closer to those in the older implementation.
2419 *
2420 * A difference may be seen as follows:
2421 *
2422 * Set[[1, 2, 3]] # => Set[[1, 2, 3]]
2423 * class MySet < Set; end
2424 * MySet[[1, 2, 3]] # => #<MySet: {[1, 2, 3]}> # Same as in Ruby 3.4.
2425 *
2426 * When backward compatibility is not needed,
2427 * a \Set subclass should inherit from +Set::CoreSet+,
2428 * which avoids including the compatibility layer:
2429 *
2430 * class MyCoreSet < Set::CoreSet; end
2431 * MyCoreSet[[1, 2, 3]] # => MyCoreSet[[1, 2, 3]]
2432 *
2433 * == What's Here
2434 *
2435 * First, what's elsewhere. \Class \Set:
2436 *
2437 * - Inherits from {class Object}[rdoc-ref:Object@Whats+Here].
2438 * - Includes {module Enumerable}[rdoc-ref:Enumerable@Whats+Here],
2439 * which provides dozens of additional methods.
2440 *
2441 * In particular, class \Set does not have many methods of its own
2442 * for fetching or for iterating.
2443 * Instead, it relies on those in \Enumerable.
2444 *
2445 * Here, class \Set provides methods that are useful for:
2446 *
2447 * - {Creating a Set}[rdoc-ref:Set@Methods+for+Creating+a+Set]
2448 * - {Set Operations}[rdoc-ref:Set@Methods+for+Set+Operations]
2449 * - {Comparing}[rdoc-ref:Set@Methods+for+Comparing]
2450 * - {Querying}[rdoc-ref:Set@Methods+for+Querying]
2451 * - {Assigning}[rdoc-ref:Set@Methods+for+Assigning]
2452 * - {Deleting}[rdoc-ref:Set@Methods+for+Deleting]
2453 * - {Converting}[rdoc-ref:Set@Methods+for+Converting]
2454 * - {And more....}[rdoc-ref:Set@Other+Methods]
2455 *
2456 * === Methods for Creating a \Set
2457 *
2458 * - ::[]:
2459 * Returns a new set populated with the given objects.
2460 * - ::new:
2461 * Returns a new set based on the given object (if no block given),
2462 * or on the return values from the called block (if a block given).
2463 *
2464 * === Methods for \Set Operations
2465 *
2466 * - #& (aliased as #intersection):
2467 * Returns a new set containing the intersection of +self+ and the given enumerable.
2468 * - #- (aliased as #difference):
2469 * Returns a new set containing the difference of +self+ and the given enumerable.
2470 * - #^: Returns a new set containing the exclusive OR of +self+ and the given enumerable.
2471 * - #| (aliased as #union and #+):
2472 * Returns a new set containing the union of +self+ and the given enumerable.
2473 *
2474 * === Methods for Comparing
2475 *
2476 * - #<=>: Returns -1, 0, or 1 as +self+ is less than, equal to,
2477 * or greater than a given object.
2478 * - #==: Returns whether +self+ and a given enumerable are equal,
2479 * as determined by Object#eql?.
2480 * - #compare_by_identity?:
2481 * Returns whether +self+ considers only identity
2482 * when comparing elements.
2483 * - #proper_subset? (aliased as #<):
2484 * Returns whether the given enumerable is a proper subset of +self+.
2485 * - #proper_superset? (aliased as #>):
2486 * Returns whether the given enumerable is a proper superset of +self+.
2487 * - #subset? (aliased as #<=):
2488 * Returns whether the given object is a subset of +self+.
2489 * - #superset? (aliased as #>=):
2490 * Returns whether the given enumerable is a superset of +self+.
2491 *
2492 * === Methods for Querying
2493 *
2494 * - #disjoint?:
2495 * Returns whether no element of the given enumerable is present in +self+.
2496 * - #empty?:
2497 * Returns whether +self+ contains no elements.
2498 * - #include? (aliased as #member? and #===):
2499 * Returns whether the given object is an element of +self+.
2500 * - #intersect?:
2501 * Returns whether +self+ and the given enumerable have any elements in common.
2502 * - #size (aliased as #length):
2503 * Returns the number of elements in +self+.
2504 *
2505 * === Methods for Assigning
2506 *
2507 * - #add (aliased as #<<):
2508 * Adds the given object to +self+; returns +self+.
2509 * - #add?:
2510 * Like #add, but returns +nil+ if the given object is already in +self+.
2511 * - #merge:
2512 * Adds each element of each of the given enumerables to +self+; returns +self+.
2513 * - #replace:
2514 * Replaces the contents of +self+ with the contents of the given enumerable;
2515 * returns +self+.
2516 *
2517 * === Methods for Deleting
2518 *
2519 * - #clear:
2520 * Removes all elements from +self+; returns +self+.
2521 * - #delete:
2522 * Removes the given object from +self+ if +self+ includes the object; returns +self+.
2523 * - #delete?:
2524 * Like #delete, but returns +nil+ if the object is not in +self+.
2525 * - #delete_if:
2526 * Calls the block with each element in +self+;
2527 * removes the element if the block returns a truthy value.
2528 * - #keep_if:
2529 * Calls the block with each element in +self+,
2530 * deleting the element if the block returns +false+ or +nil+; returns +self+.
2531 * - #reject!
2532 * Like #delete_if, but returns +nil+ if no changes were made.
2533 * - #select! (aliased as #filter!):
2534 * Like #keep_if, but returns +nil+ if no changes were made.
2535 * - #subtract:
2536 * Deletes from +self+ every element found in the given enumerable; returns +self+:
2537 *
2538 * === Methods for Converting
2539 *
2540 * - #classify:
2541 * Returns a hash that partitions the elements,
2542 * as determined by the given block.
2543 * - #collect! (aliased as #map!):
2544 * Replaces each element with a block return-value.
2545 * - #divide:
2546 * Returns a set of sets that partition the elements,
2547 * as determined by the given block.
2548 * - #flatten:
2549 * Returns a new set that is a recursive flattening of +self+.
2550 * - #flatten!: Like #flatten, but if any changes were made
2551 * replaces +self+ with the result and returns +self+.
2552 * - #inspect (aliased as #to_s):
2553 * Returns a string representation of +self+.
2554 * - #join:
2555 * Returns the string formed by joining the string-converted elements of +self+
2556 * with the given separator.
2557 * - #to_a:
2558 * Returns an array containing the elements of +self+.
2559 * - #to_set:
2560 * With a block given, creates and returns a new set;
2561 * calls the block with each element of +self+,
2562 * and adds the block's returns value to the new set.
2563 *
2564 * === Other Methods
2565 *
2566 * - #compare_by_identity:
2567 * Sets +self+ to compare by object identity (rather than by object content).
2568 * - #each:
2569 * Calls the block with each successive element of +self+; returns +self+.
2570 * - #reset:
2571 * Resets the internal state of +self+; returns +self+.
2572 * Useful if an element has been modified while an element in the set.
2573 *
2574 */
2575void
2576Init_Set(void)
2577{
2578 rb_cSet = rb_define_class("Set", rb_cObject);
2580
2581 id_each_entry = rb_intern_const("each_entry");
2582 id_any_p = rb_intern_const("any?");
2583 id_new = rb_intern_const("new");
2584 id_i_hash = rb_intern_const("@hash");
2585 id_subclass_compatible = rb_intern_const("SubclassCompatible");
2586 id_class_methods = rb_intern_const("ClassMethods");
2587 id_set_iter_lev = rb_make_internal_id();
2588
2589 rb_define_alloc_func(rb_cSet, set_s_alloc);
2590 rb_define_singleton_method(rb_cSet, "[]", set_s_create, -1);
2591
2592 rb_define_method(rb_cSet, "initialize", set_i_initialize, -1);
2593 rb_define_method(rb_cSet, "initialize_copy", set_i_initialize_copy, 1);
2594
2595 rb_define_method(rb_cSet, "&", set_i_intersection, 1);
2596 rb_define_alias(rb_cSet, "intersection", "&");
2597 rb_define_method(rb_cSet, "-", set_i_difference, 1);
2598 rb_define_alias(rb_cSet, "difference", "-");
2599 rb_define_method(rb_cSet, "^", set_i_xor, 1);
2600 rb_define_method(rb_cSet, "|", set_i_union, 1);
2601 rb_define_alias(rb_cSet, "+", "|");
2602 rb_define_alias(rb_cSet, "union", "|");
2603 rb_define_method(rb_cSet, "<=>", set_i_compare, 1);
2604 rb_define_method(rb_cSet, "==", set_i_eq, 1);
2605 rb_define_alias(rb_cSet, "eql?", "==");
2606 rb_define_method(rb_cSet, "add", set_i_add, 1);
2607 rb_define_alias(rb_cSet, "<<", "add");
2608 rb_define_method(rb_cSet, "add?", set_i_add_p, 1);
2609 rb_define_method(rb_cSet, "classify", set_i_classify, 0);
2610 rb_define_method(rb_cSet, "clear", set_i_clear, 0);
2611 rb_define_method(rb_cSet, "collect!", set_i_collect, 0);
2612 rb_define_alias(rb_cSet, "map!", "collect!");
2613 rb_define_method(rb_cSet, "compare_by_identity", set_i_compare_by_identity, 0);
2614 rb_define_method(rb_cSet, "compare_by_identity?", set_i_compare_by_identity_p, 0);
2615 rb_define_method(rb_cSet, "delete", set_i_delete, 1);
2616 rb_define_method(rb_cSet, "delete?", set_i_delete_p, 1);
2617 rb_define_method(rb_cSet, "delete_if", set_i_delete_if, 0);
2618 rb_define_method(rb_cSet, "disjoint?", set_i_disjoint, 1);
2619 rb_define_method(rb_cSet, "divide", set_i_divide, 0);
2620 rb_define_method(rb_cSet, "each", set_i_each, 0);
2621 rb_define_method(rb_cSet, "empty?", set_i_empty, 0);
2622 rb_define_method(rb_cSet, "flatten", set_i_flatten, 0);
2623 rb_define_method(rb_cSet, "flatten!", set_i_flatten_bang, 0);
2624 rb_define_method(rb_cSet, "hash", set_i_hash, 0);
2625 rb_define_method(rb_cSet, "include?", set_i_include, 1);
2626 rb_define_alias(rb_cSet, "member?", "include?");
2627 rb_define_alias(rb_cSet, "===", "include?");
2628 rb_define_method(rb_cSet, "inspect", set_i_inspect, 0);
2629 rb_define_alias(rb_cSet, "to_s", "inspect");
2630 rb_define_method(rb_cSet, "intersect?", set_i_intersect, 1);
2631 rb_define_method(rb_cSet, "join", set_i_join, -1);
2632 rb_define_method(rb_cSet, "keep_if", set_i_keep_if, 0);
2633 rb_define_method(rb_cSet, "merge", set_i_merge, -1);
2634 rb_define_method(rb_cSet, "proper_subset?", set_i_proper_subset, 1);
2635 rb_define_alias(rb_cSet, "<", "proper_subset?");
2636 rb_define_method(rb_cSet, "proper_superset?", set_i_proper_superset, 1);
2637 rb_define_alias(rb_cSet, ">", "proper_superset?");
2638 rb_define_method(rb_cSet, "reject!", set_i_reject, 0);
2639 rb_define_method(rb_cSet, "replace", set_i_replace, 1);
2640 rb_define_method(rb_cSet, "reset", set_i_reset, 0);
2641 rb_define_method(rb_cSet, "size", set_i_size, 0);
2642 rb_define_alias(rb_cSet, "length", "size");
2643 rb_define_method(rb_cSet, "select!", set_i_select, 0);
2644 rb_define_alias(rb_cSet, "filter!", "select!");
2645 rb_define_method(rb_cSet, "subset?", set_i_subset, 1);
2646 rb_define_alias(rb_cSet, "<=", "subset?");
2647 rb_define_method(rb_cSet, "subtract", set_i_subtract, 1);
2648 rb_define_method(rb_cSet, "superset?", set_i_superset, 1);
2649 rb_define_alias(rb_cSet, ">=", "superset?");
2650 rb_define_method(rb_cSet, "to_a", set_i_to_a, 0);
2651 rb_define_method(rb_cSet, "to_set", set_i_to_set, 0);
2652
2653 /* :nodoc: */
2654 VALUE compat = rb_define_class_under(rb_cSet, "compatible", rb_cObject);
2655 rb_marshal_define_compat(rb_cSet, compat, compat_dumper, compat_loader);
2656
2657 // Create Set::CoreSet before defining inherited, so it does not include
2658 // the backwards compatibility layer.
2659 rb_define_class_under(rb_cSet, "CoreSet", rb_cSet);
2660 rb_define_private_method(rb_singleton_class(rb_cSet), "inherited", set_s_inherited, 1);
2661
2662 rb_provide("set.rb");
2663}
#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:1683
#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:2374
VALUE rb_set_clear(VALUE set)
Removes all entries from set.
Definition set.c:2362
bool rb_set_delete(VALUE set, VALUE element)
Removes the element from from set.
Definition set.c:2368
bool rb_set_add(VALUE set, VALUE element)
Adds element to set.
Definition set.c:2356
void rb_set_foreach(VALUE set, int(*func)(VALUE element, VALUE arg), VALUE arg)
Iterates over a set.
Definition set.c:2332
bool rb_set_lookup(VALUE set, VALUE element)
Whether the set contains the given element.
Definition set.c:2350
VALUE rb_set_new(void)
Creates a new, empty set object.
Definition set.c:2338
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:2344
#define rb_hash_uint(h, i)
Just another name of st_hash_uint.
Definition string.h:943
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:3872
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:3848
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:3683
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
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