mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
mruby-array-ext: implement Array#fill in C
Implemented shared C argument parser and separate fill logic to eliminate code duplication while maximizing performance. The implementation uses C implemented __fill_parse_args for unified argument handling and __fill_exec for fast C-based value filling. Added comprehensive test coverage for both shared argument parsing and C fill implementation, including range arguments, block handling, and array extension scenarios. Co-authored-by: Atlassian Rovo Dev Co-authored-by: Gemini <gemini@google.com>
This commit is contained in:
@@ -209,65 +209,21 @@ class Array
|
||||
raise ArgumentError, "wrong number of arguments (given 0, expected 1..3)"
|
||||
end
|
||||
|
||||
beg = len = 0
|
||||
if block
|
||||
if arg0.nil? && arg1.nil? && arg2.nil?
|
||||
# ary.fill { |index| block } -> ary
|
||||
beg = 0
|
||||
len = self.size
|
||||
elsif !arg0.nil? && arg0.kind_of?(Range)
|
||||
# ary.fill(range) { |index| block } -> ary
|
||||
beg = arg0.begin
|
||||
beg += self.size if beg < 0
|
||||
len = arg0.end
|
||||
len += self.size if len < 0
|
||||
len += 1 unless arg0.exclude_end?
|
||||
elsif !arg0.nil?
|
||||
# ary.fill(start [, length] ) { |index| block } -> ary
|
||||
beg = arg0.__to_int
|
||||
beg += self.size if beg < 0
|
||||
if arg1.nil?
|
||||
len = self.size
|
||||
else
|
||||
len = beg + arg1.__to_int
|
||||
end
|
||||
end
|
||||
else
|
||||
if !arg0.nil? && arg1.nil? && arg2.nil?
|
||||
# ary.fill(obj) -> ary
|
||||
beg = 0
|
||||
len = self.size
|
||||
elsif !arg0.nil? && !arg1.nil? && arg1.kind_of?(Range)
|
||||
# ary.fill(obj, range ) -> ary
|
||||
beg = arg1.begin
|
||||
beg += self.size if beg < 0
|
||||
len = arg1.end
|
||||
len += self.size if len < 0
|
||||
len += 1 unless arg1.exclude_end?
|
||||
elsif !arg0.nil? && !arg1.nil?
|
||||
# ary.fill(obj, start [, length]) -> ary
|
||||
beg = arg1.__to_int
|
||||
beg += self.size if beg < 0
|
||||
if arg2.nil?
|
||||
len = self.size
|
||||
else
|
||||
len = beg + arg2.__to_int
|
||||
end
|
||||
end
|
||||
end
|
||||
# Use shared C argument parser for all cases
|
||||
start, length = __fill_parse_arg(arg0, arg1, arg2, &block)
|
||||
|
||||
i = beg
|
||||
if block
|
||||
while i < len
|
||||
# Block-based filling in Ruby
|
||||
i = start
|
||||
while i < start + length
|
||||
self[i] = block.call(i)
|
||||
i += 1
|
||||
end
|
||||
else
|
||||
while i < len
|
||||
self[i] = arg0
|
||||
i += 1
|
||||
end
|
||||
# Use fast C implementation for value filling
|
||||
__fill_exec(start, length, arg0)
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
|
||||
@@ -806,6 +806,132 @@ ary_intersect_p(mrb_state *mrb, mrb_value self)
|
||||
return mrb_false_value();
|
||||
}
|
||||
|
||||
/*
|
||||
* Shared argument parser for Array#fill that handles all the complex
|
||||
* argument parsing logic including ranges, negative indices, etc.
|
||||
* Returns normalized start and length values.
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
ary_fill_parse_arg(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value arg0 = mrb_nil_value(), arg1 = mrb_nil_value(), arg2 = mrb_nil_value();
|
||||
mrb_value block = mrb_nil_value();
|
||||
mrb_int argc;
|
||||
|
||||
argc = mrb_get_args(mrb, "|ooo&", &arg0, &arg1, &arg2, &block);
|
||||
|
||||
struct RArray *ary = mrb_ary_ptr(self);
|
||||
mrb_int ary_len = ARY_LEN(ary);
|
||||
mrb_int start = 0, length = 0;
|
||||
|
||||
if (!mrb_nil_p(block)) {
|
||||
if (argc == 0 || (argc >= 1 && mrb_nil_p(arg0))) {
|
||||
/* fill { |index| block } */
|
||||
start = 0;
|
||||
length = ary_len;
|
||||
}
|
||||
else if (argc >= 1 && mrb_range_p(arg0)) {
|
||||
/* fill(range) { |index| block } */
|
||||
mrb_int range_beg, range_end;
|
||||
|
||||
if (mrb_range_beg_len(mrb, arg0, &range_beg, &range_end, ary_len, 1)) {
|
||||
start = range_beg;
|
||||
length = range_end;
|
||||
}
|
||||
}
|
||||
else if (argc >= 1 && !mrb_nil_p(arg0)) {
|
||||
/* fill(start [, length]) { |index| block } */
|
||||
start = mrb_int(mrb, arg0);
|
||||
if (start < 0) start += ary_len;
|
||||
if (start < 0) start = 0;
|
||||
|
||||
if (argc == 1 || mrb_nil_p(arg1)) {
|
||||
length = ary_len - start;
|
||||
}
|
||||
else {
|
||||
length = mrb_int(mrb, arg1);
|
||||
if (length < 0) length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (argc >= 1 && !mrb_nil_p(arg0)) {
|
||||
if (argc == 1 || (argc >= 2 && mrb_nil_p(arg1) && mrb_nil_p(arg2))) {
|
||||
/* fill(obj) */
|
||||
start = 0;
|
||||
length = ary_len;
|
||||
}
|
||||
else if (argc >= 2 && mrb_range_p(arg1)) {
|
||||
/* fill(obj, range) */
|
||||
mrb_int range_beg, range_end;
|
||||
|
||||
if (mrb_range_beg_len(mrb, arg1, &range_beg, &range_end, ary_len, 1)) {
|
||||
start = range_beg;
|
||||
length = range_end;
|
||||
}
|
||||
}
|
||||
else if (argc >= 2 && !mrb_nil_p(arg1)) {
|
||||
/* fill(obj, start [, length]) */
|
||||
start = mrb_int(mrb, arg1);
|
||||
if (start < 0) start += ary_len;
|
||||
if (start < 0) start = 0;
|
||||
|
||||
if (argc == 2 || mrb_nil_p(arg2)) {
|
||||
length = ary_len - start;
|
||||
}
|
||||
else {
|
||||
length = mrb_int(mrb, arg2);
|
||||
if (length < 0) length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return [start, length] array */
|
||||
mrb_value result = mrb_ary_new_capa(mrb, 2);
|
||||
mrb_ary_push(mrb, result, mrb_fixnum_value(start));
|
||||
mrb_ary_push(mrb, result, mrb_fixnum_value(length));
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fast C implementation that fills a specific range of the array
|
||||
* with the given object. Handles array extension if necessary.
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
ary_fill_exec(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value obj;
|
||||
mrb_int start, length;
|
||||
|
||||
mrb_get_args(mrb, "iio", &start, &length, &obj);
|
||||
|
||||
struct RArray *ary = mrb_ary_ptr(self);
|
||||
mrb_int ary_len = ARY_LEN(ary);
|
||||
|
||||
/* Extend array if necessary */
|
||||
if (start + length > ary_len) {
|
||||
mrb_ary_resize(mrb, self, start + length);
|
||||
ary = mrb_ary_ptr(self); /* refresh pointer after resize */
|
||||
}
|
||||
|
||||
/* Ensure we don't go beyond array bounds */
|
||||
if (start >= ARY_LEN(ary) || length <= 0) return self;
|
||||
if (start + length > ARY_LEN(ary)) {
|
||||
length = ARY_LEN(ary) - start;
|
||||
}
|
||||
|
||||
/* Fill the array */
|
||||
mrb_value *ptr = ARY_PTR(ary) + start;
|
||||
for (mrb_int i = 0; i < length; i++) {
|
||||
ptr[i] = obj;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
mrb_mruby_array_ext_gem_init(mrb_state* mrb)
|
||||
{
|
||||
@@ -827,6 +953,8 @@ mrb_mruby_array_ext_gem_init(mrb_state* mrb)
|
||||
mrb_define_method_id(mrb, a, MRB_OPSYM(and), ary_intersection, MRB_ARGS_REQ(1));
|
||||
mrb_define_method_id(mrb, a, MRB_SYM(intersection), ary_intersection_multi, MRB_ARGS_ANY());
|
||||
mrb_define_method_id(mrb, a, MRB_SYM_Q(intersect), ary_intersect_p, MRB_ARGS_REQ(1));
|
||||
mrb_define_method_id(mrb, a, MRB_SYM(__fill_parse_arg), ary_fill_parse_arg, MRB_ARGS_ARG(0,4));
|
||||
mrb_define_method_id(mrb, a, MRB_SYM(__fill_exec), ary_fill_exec, MRB_ARGS_REQ(3));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -395,8 +395,14 @@ assert("Array#fill") do
|
||||
assert_equal [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6].fill('x', -2...-2)
|
||||
assert_equal [1, 2, 3, 4, 'x', 6], [1, 2, 3, 4, 5, 6].fill('x', -2..-2)
|
||||
assert_equal [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6].fill('x', -2..0)
|
||||
|
||||
# Test extending array
|
||||
a = [1, 2]
|
||||
assert_equal [1, 2, nil, nil, "x"], a.fill("x", 4, 1)
|
||||
end
|
||||
|
||||
|
||||
|
||||
assert("Array#reverse_each") do
|
||||
a = [ "a", "b", "c", "d" ]
|
||||
b = []
|
||||
|
||||
Reference in New Issue
Block a user