mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
mruby-string-ext: implement String#split! in C for performance
Co-authored-by: Gemini <gemini@google.com>
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user