mruby-string-ext: implement String#split! in C for performance

Co-authored-by: Gemini <gemini@google.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-06-26 22:33:37 +09:00
parent 7f9aea2df7
commit abb703dacc
2 changed files with 142 additions and 70 deletions
-70
View File
@@ -36,77 +36,7 @@ class String
end
end
##
# call-seq:
# str.slice!(fixnum) -> new_str or nil
# str.slice!(fixnum, fixnum) -> new_str or nil
# str.slice!(range) -> new_str or nil
# str.slice!(other_str) -> new_str or nil
#
# Deletes the specified portion from <i>str</i>, and returns the portion
# deleted.
#
# string = "this is a string"
# string.slice!(2) #=> "i"
# string.slice!(3..6) #=> " is "
# string.slice!("r") #=> "r"
# string #=> "thsa sting"
#
def slice!(arg1, arg2=nil)
raise FrozenError, "can't modify frozen String" if frozen?
raise ArgumentError, "wrong number of arguments (expected 1..2)" if arg1.nil? && arg2.nil?
if !arg1.nil? && !arg2.nil?
idx = arg1
idx += self.size if arg1 < 0
if idx >= 0 && idx <= self.size && arg2 > 0
str = self[idx, arg2]
else
return nil
end
else
validated = false
if arg1.kind_of?(Range)
beg = arg1.begin
ed = arg1.end
beg += self.size if beg < 0
ed += self.size if ed < 0
ed -= 1 if arg1.exclude_end?
validated = true
elsif arg1.kind_of?(String)
validated = true
else
idx = arg1
idx += self.size if arg1 < 0
validated = true if idx >=0 && arg1 < self.size
end
if validated
str = self[arg1]
else
return nil
end
end
unless str.nil? || str == ""
if !arg1.nil? && !arg2.nil?
idx = arg1 >= 0 ? arg1 : self.size+arg1
str2 = self[0...idx] + self[idx+arg2..-1].to_s
else
if arg1.kind_of?(Range)
idx = beg >= 0 ? beg : self.size+beg
idx2 = ed>= 0 ? ed : self.size+ed
str2 = self[0...idx] + self[idx2+1..-1].to_s
elsif arg1.kind_of?(String)
idx = self.index(arg1)
str2 = self[0...idx] + self[idx+arg1.size..-1] unless idx.nil?
else
idx = arg1 >= 0 ? arg1 : self.size+arg1
str2 = self[0...idx] + self[idx+1..-1].to_s
end
end
self.replace(str2) unless str2.nil?
end
str
end
##
# call-seq:
+142
View File
@@ -1843,6 +1843,147 @@ str_center_core(mrb_state *mrb, mrb_value self)
return mrb_str_cat_str(mrb, result, right_padding);
}
#ifdef MRB_UTF8_STRING
/*
* Given a character index, find the byte offset in a UTF-8 string.
* Returns -1 if the character index is out of bounds.
*/
static mrb_int
str_char_to_byte_offset(mrb_value str, mrb_int char_index)
{
struct RString *s = mrb_str_ptr(str);
const char *p = RSTR_PTR(s);
mrb_int byte_len = RSTR_LEN(s);
if (RSTR_SINGLE_BYTE_P(s) || RSTR_BINARY_P(s)) {
return char_index;
}
if (char_index < 0) return -1;
mrb_int byte_offset = 0;
mrb_int current_char_index = 0;
while (byte_offset < byte_len && current_char_index < char_index) {
mrb_int char_len = mrb_utf8len(p + byte_offset, p + byte_len - byte_offset);
if (char_len == 0) break;
byte_offset += char_len;
current_char_index++;
}
if (current_char_index < char_index) return -1;
return byte_offset;
}
/*
* Given a starting character index and a character length, find the byte length.
*/
static mrb_int
str_chars_to_byte_len(mrb_value str, mrb_int char_start, mrb_int char_len)
{
struct RString *s = mrb_str_ptr(str);
const char *p = RSTR_PTR(s);
mrb_int str_byte_len = RSTR_LEN(s);
if (RSTR_SINGLE_BYTE_P(s) || RSTR_BINARY_P(s)) {
return char_len;
}
mrb_int start_byte_offset = str_char_to_byte_offset(str, char_start);
if (start_byte_offset == -1) return 0;
mrb_int byte_offset = start_byte_offset;
mrb_int current_char_len = 0;
while (byte_offset < str_byte_len && current_char_len < char_len) {
mrb_int cl = mrb_utf8len(p + byte_offset, p + str_byte_len - byte_offset);
if (cl == 0) break;
byte_offset += cl;
current_char_len++;
}
return byte_offset - start_byte_offset;
}
#endif
static mrb_value
mrb_str_slice_bang(mrb_state *mrb, mrb_value self)
{
mrb_check_frozen(mrb, mrb_obj_ptr(self));
mrb_value arg1, arg2;
mrb_int argc = mrb_get_args(mrb, "o|o", &arg1, &arg2);
struct RString *str = mrb_str_ptr(self);
mrb_int str_len;
const char *ptr = RSTRING_PTR(self);
#ifdef MRB_UTF8_STRING
str_len = str_char_count(self);
#else
str_len = RSTRING_LEN(self);
#endif
mrb_int beg, len;
if (argc == 1) {
if (mrb_string_p(arg1)) {
mrb_int pos = mrb_str_index(mrb, self, RSTRING_PTR(arg1), RSTRING_LEN(arg1), 0);
if (pos == -1) return mrb_nil_value();
#ifdef MRB_UTF8_STRING
beg = str_char_count(mrb_str_substr(mrb, self, 0, pos));
len = str_char_count(arg1);
#else
beg = pos;
len = RSTRING_LEN(arg1);
#endif
}
else if (mrb_range_p(arg1)) {
if (mrb_range_beg_len(mrb, arg1, &beg, &len, str_len, TRUE) != MRB_RANGE_OK) {
return mrb_nil_value();
}
}
else {
beg = mrb_as_int(mrb, arg1);
if (beg < 0) beg += str_len;
if (beg < 0 || beg >= str_len) return mrb_nil_value();
len = 1;
}
}
else { // argc == 2
beg = mrb_as_int(mrb, arg1);
len = mrb_as_int(mrb, arg2);
if (beg < 0) beg += str_len;
if (len < 0) return mrb_nil_value();
if (beg < 0 || beg > str_len) return mrb_nil_value();
}
if (beg > str_len) return mrb_nil_value();
if (beg + len > str_len) {
len = str_len - beg;
}
if (len < 0) len = 0;
#ifdef MRB_UTF8_STRING
mrb_int byte_beg = str_char_to_byte_offset(self, beg);
mrb_int byte_len = str_chars_to_byte_len(self, beg, len);
#else
mrb_int byte_beg = beg;
mrb_int byte_len = len;
#endif
if (byte_beg < 0 || byte_beg > RSTRING_LEN(self) || byte_beg + byte_len > RSTRING_LEN(self)) {
return mrb_nil_value();
}
mrb_value result = mrb_str_new(mrb, RSTRING_PTR(self) + byte_beg, byte_len);
mrb_str_modify(mrb, str);
ptr = RSTRING_PTR(self);
memmove((char*)ptr + byte_beg, ptr + byte_beg + byte_len, RSTRING_LEN(self) - byte_beg - byte_len);
RSTR_SET_LEN(str, RSTRING_LEN(self) - byte_len);
return result;
}
void
mrb_mruby_string_ext_gem_init(mrb_state* mrb)
{
@@ -1850,6 +1991,7 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
mrb_define_method_id(mrb, s, MRB_SYM(dump), mrb_str_dump, MRB_ARGS_NONE());
mrb_define_method_id(mrb, s, MRB_SYM_B(swapcase), str_swapcase_bang, MRB_ARGS_NONE());
mrb_define_method_id(mrb, s, MRB_SYM_B(slice), mrb_str_slice_bang, MRB_ARGS_ARG(1, 1));
mrb_define_method_id(mrb, s, MRB_SYM(swapcase), str_swapcase, MRB_ARGS_NONE());
mrb_define_method_id(mrb, s, MRB_SYM(concat), str_concat_m, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, s, MRB_OPSYM(lshift), str_concat_m, MRB_ARGS_REQ(1));