From 10ea05d3aa8e0f731aac168ccb4dcadb07a5d7ce Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Fri, 20 Jun 2025 13:34:32 +0900 Subject: [PATCH] mruby-set: refactor Set#union (+, |) The code is written by Atlassian Rovodev. --- mrbgems/mruby-set/mrblib/set.rb | 19 +++++++++++++ mrbgems/mruby-set/src/set.c | 48 ++++++++++++++++++++++----------- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/mrbgems/mruby-set/mrblib/set.rb b/mrbgems/mruby-set/mrblib/set.rb index 4f196c852..9a34eb387 100644 --- a/mrbgems/mruby-set/mrblib/set.rb +++ b/mrbgems/mruby-set/mrblib/set.rb @@ -46,6 +46,25 @@ class Set # Alias for #intersection alias & intersection + # Returns a new set built by merging the set and the elements of the given + # enumerable object. + # + # @param [Enumerable] enum The enumerable object to merge with + # @return [Set] A new set containing all elements from both + def union(enum) + if enum.is_a?(Set) + # Fast path: Call C-implemented function for Set-to-Set union + __set_union(enum) + else + # General path: Create a duplicate and merge the enumerable + dup.merge(enum) + end + end + + # Aliases for #union + alias | union + alias + union + # Returns a new set containing elements exclusive between the set and the given # enumerable object. # diff --git a/mrbgems/mruby-set/src/set.c b/mrbgems/mruby-set/src/set.c index 8d2dfa9ee..e28b426b8 100644 --- a/mrbgems/mruby-set/src/set.c +++ b/mrbgems/mruby-set/src/set.c @@ -410,23 +410,41 @@ set_subtract(mrb_state *mrb, mrb_value self) } /* - * call-seq: - * set | enum -> new_set - * set + enum -> new_set - * set.union(enum) -> new_set - * - * Returns a new set built by merging the set and the elements of the given - * enumerable object. + * Core implementation of Set-to-Set union + * This is an internal method that will be called from Ruby */ static mrb_value -set_union(mrb_state *mrb, mrb_value self) +set_core_union(mrb_state *mrb, mrb_value self) { - mrb_value enum_obj; - mrb_value new_set; + mrb_value other; + mrb_value result_set; + khash_t(set) *result_kh, *self_kh, *other_kh; - mrb_get_args(mrb, "o", &enum_obj); - new_set = mrb_funcall_id(mrb, self, MRB_SYM(dup), 0); - return mrb_funcall_id(mrb, new_set, MRB_SYM(merge), 1, enum_obj); + mrb_get_args(mrb, "o", &other); + + /* Create a new set by duplicating self */ + result_set = mrb_obj_dup(mrb, self); + result_kh = set_get_khash(mrb, result_set); + if (!result_kh) { + /* If self is empty, create a new empty set */ + result_kh = kh_init(set, mrb); + set_set_khash(mrb, result_set, result_kh); + } + + /* Add all elements from other set */ + other_kh = set_get_khash(mrb, other); + if (other_kh) { + khiter_t k; + int ai = mrb_gc_arena_save(mrb); + for (k = kh_begin(other_kh); k != kh_end(other_kh); k++) { + if (kh_exist(other_kh, k)) { + kh_put(set, mrb, result_kh, kh_key(other_kh, k)); + mrb_gc_arena_restore(mrb, ai); + } + } + } + + return result_set; } /* @@ -925,9 +943,7 @@ mrb_mruby_set_gem_init(mrb_state *mrb) mrb_define_method(mrb, set, "merge", set_merge, MRB_ARGS_REQ(1)); mrb_define_method(mrb, set, "subtract", set_subtract, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, set, "|", set_union, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, set, "+", set_union, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, set, "union", set_union, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, set, "__set_union", set_core_union, MRB_ARGS_REQ(1)); mrb_define_method(mrb, set, "-", set_difference, MRB_ARGS_REQ(1)); mrb_define_method(mrb, set, "difference", set_difference, MRB_ARGS_REQ(1));