mruby-array-ext: implement Array#insert in C

This commit also corrects the behavior of `Array#insert` when a negative
index is out of bounds. It now raises an `IndexError`, which is
consistent with CRuby.

Co-authored-by: Gemini <gemini@google.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-06-30 08:19:55 +09:00
parent f8451045fe
commit 87c39d3c0b
3 changed files with 113 additions and 23 deletions
-18
View File
@@ -233,25 +233,7 @@ class Array
self.replace(result)
end
##
# call-seq:
# ary.insert(index, obj...) -> ary
#
# Inserts the given values before the element with the given +index+.
#
# Negative indices count backwards from the end of the array, where +-1+ is
# the last element.
#
# a = %w{ a b c d }
# a.insert(2, 99) #=> ["a", "b", 99, "c", "d"]
# a.insert(-2, 1, 2, 3) #=> ["a", "b", 99, "c", 1, 2, 3, "d"]
def insert(idx, *args)
idx = idx.__to_int
idx += self.size + 1 if idx < 0
self[idx, 0] = args
self
end
##
# call-seq:
+57
View File
@@ -1,4 +1,5 @@
#include <mruby.h>
#include <string.h>
#include <mruby/value.h>
#include <mruby/array.h>
#include <mruby/range.h>
@@ -1136,6 +1137,61 @@ ary_flatten_bang(mrb_state *mrb, mrb_value self)
return self;
}
/*
* call-seq:
* ary.insert(index, obj...) -> ary
*
* Inserts the given values before the element with the given index.
*
* Negative indices count backwards from the end of the array, where -1
* is the last element. If a negative index is used, the elements are
* inserted after that element.
*
* If the index is greater than the length of the array, the array is
* extended with nil elements.
*
* a = %w{ a b c d }
* a.insert(2, 99) #=> ["a", "b", 99, "c", "d"]
* a.insert(-2, 1, 2, 3) #=> ["a", "b", 99, "c", 1, 2, 3, "d"]
*/
static mrb_value
ary_insert(mrb_state *mrb, mrb_value self)
{
mrb_int idx;
const mrb_value *argv;
mrb_int argc;
mrb_get_args(mrb, "i*", &idx, &argv, &argc);
if (argc == 0) {
return self;
}
mrb_int len = RARRAY_LEN(self);
if (idx < 0) {
idx += len + 1;
if (idx < 0) {
mrb_raisef(mrb, E_INDEX_ERROR, "index %i outside of array bounds", idx - (len + 1));
}
}
mrb_ary_modify(mrb, mrb_ary_ptr(self));
mrb_int new_len = (idx > len ? idx : len) + argc;
mrb_ary_resize(mrb, self, new_len);
if (idx < len) {
memmove(RARRAY_PTR(self) + idx + argc, RARRAY_PTR(self) + idx, (len - idx) * sizeof(mrb_value));
}
for (mrb_int i = 0; i < argc; i++) {
mrb_ary_set(mrb, self, idx + i, argv[i]);
}
return self;
}
void
mrb_mruby_array_ext_gem_init(mrb_state* mrb)
{
@@ -1163,6 +1219,7 @@ mrb_mruby_array_ext_gem_init(mrb_state* mrb)
mrb_define_method_id(mrb, a, MRB_SYM_B(__uniq), ary_uniq_bang, MRB_ARGS_NONE());
mrb_define_method_id(mrb, a, MRB_SYM(flatten), ary_flatten, MRB_ARGS_OPT(1));
mrb_define_method_id(mrb, a, MRB_SYM_B(flatten), ary_flatten_bang, MRB_ARGS_OPT(1));
mrb_define_method_id(mrb, a, MRB_SYM(insert), ary_insert, MRB_ARGS_ARG(1, -1));
}
void
+56 -5
View File
@@ -461,12 +461,63 @@ assert("Array#reject!") do
end
assert("Array#insert") do
a = ["a", "b", "c", "d"]
assert_equal ["a", "b", 99, "c", "d"], a.insert(2, 99)
assert_equal ["a", "b", 99, "c", 1, 2, 3, "d"], a.insert(-2, 1, 2, 3)
# Basic insertion
a = [1, 2, 3]
assert_same a, a.insert(1, 99)
assert_equal [1, 99, 2, 3], a
b = ["a", "b", "c", "d"]
assert_equal ["a", "b", "c", "d", nil, nil, 99], b.insert(6, 99)
# Multiple elements
a = [1, 2, 3]
a.insert(2, 'a', 'b')
assert_equal [1, 2, 'a', 'b', 3], a
# Negative index
a = [1, 2, 3, 4]
a.insert(-2, 99)
assert_equal [1, 2, 3, 99, 4], a
# Negative index out of bounds
a = [1, 2, 3]
assert_raise(IndexError) { a.insert(-5, 99) }
assert_equal [1, 2, 3], a
# Insertion beyond bounds (creates nils)
a = [1, 2]
a.insert(5, 99)
assert_equal [1, 2, nil, nil, nil, 99], a
# Insertion at the end
a = [1, 2, 3]
a.insert(3, 99)
assert_equal [1, 2, 3, 99], a
# Insertion into an empty array
a = []
a.insert(0, 1, 2)
assert_equal [1, 2], a
# Insertion into an empty array at a non-zero index
a = []
a.insert(2, 99)
assert_equal [nil, nil, 99], a
# No-op (inserting zero elements)
a = [1, 2, 3]
a.insert(1)
assert_equal [1, 2, 3], a
# Return value is self
a = [1, 2, 3]
b = a.insert(1, 99)
assert_same a, b
# Large array insertion
a = (0...1000).to_a
a.insert(500, "x")
assert_equal 1001, a.size
assert_equal "x", a[500]
assert_equal 499, a[499]
assert_equal 500, a[501]
end
assert("Array#bsearch") do