Add String#bytesplice

This commit is contained in:
Shugo Maeda 2022-02-22 10:41:56 +09:00
parent b8e72bd2e9
commit 1107839a7f
Notes: git 2022-03-18 11:51:26 +09:00
2 changed files with 115 additions and 0 deletions

View File

@ -6296,6 +6296,76 @@ rb_str_byteslice(int argc, VALUE *argv, VALUE str)
return str_byte_aref(str, argv[0]);
}
/*
* call-seq:
* bytesplice(index, length, str) -> string
* bytesplice(range, str) -> string
*
* Replaces some or all of the content of +self+ with +str+, and returns +str+.
* The portion of the string affected is determined using
* the same criteria as String#byteslice, except that +length+ cannot be omitted.
* If the replacement string is not the same length as the text it is replacing,
* the string will be adjusted accordingly.
* The form that take an Integer will raise an IndexError if the value is out
* of range; the Range form will raise a RangeError.
* If the beginning or ending offset does not land on character (codepoint)
* boundary, an IndexError will be raised.
*/
static VALUE
rb_str_bytesplice(int argc, VALUE *argv, VALUE str)
{
long beg, end, len, slen;
VALUE val;
rb_encoding *enc;
int cr;
rb_check_arity(argc, 2, 3);
if (argc == 2) {
if (!rb_range_beg_len(argv[0], &beg, &len, RSTRING_LEN(str), 2)) {
rb_raise(rb_eTypeError, "wrong argument type %s (expected Range)",
rb_builtin_class_name(argv[0]));
}
val = argv[1];
}
else {
beg = NUM2LONG(argv[0]);
len = NUM2LONG(argv[1]);
val = argv[2];
}
if (len < 0) rb_raise(rb_eIndexError, "negative length %ld", len);
slen = RSTRING_LEN(str);
if ((slen < beg) || ((beg < 0) && (beg + slen < 0))) {
rb_raise(rb_eIndexError, "index %ld out of string", beg);
}
if (beg < 0) {
beg += slen;
}
assert(beg >= 0);
assert(beg <= slen);
if (len > slen - beg) {
len = slen - beg;
}
end = beg + len;
if (!str_check_byte_pos(str, beg)) {
rb_raise(rb_eIndexError,
"offset %ld does not land on character boundary", beg);
}
if (!str_check_byte_pos(str, end)) {
rb_raise(rb_eIndexError,
"offset %ld does not land on character boundary", end);
}
StringValue(val);
enc = rb_enc_check(str, val);
str_modify_keep_cr(str);
rb_str_splice_0(str, beg, len, val);
rb_enc_associate(str, enc);
cr = ENC_CODERANGE_AND(ENC_CODERANGE(str), ENC_CODERANGE(val));
if (cr != ENC_CODERANGE_BROKEN)
ENC_CODERANGE_SET(str, cr);
return val;
}
/*
* call-seq:
* reverse -> string
@ -12560,6 +12630,7 @@ Init_String(void)
rb_define_method(rb_cString, "getbyte", rb_str_getbyte, 1);
rb_define_method(rb_cString, "setbyte", rb_str_setbyte, 2);
rb_define_method(rb_cString, "byteslice", rb_str_byteslice, -1);
rb_define_method(rb_cString, "bytesplice", rb_str_bytesplice, -1);
rb_define_method(rb_cString, "scrub", str_scrub, -1);
rb_define_method(rb_cString, "scrub!", str_scrub_bang, -1);
rb_define_method(rb_cString, "freeze", rb_str_freeze, 0);

View File

@ -3395,6 +3395,50 @@ CODE
assert_nil(S("").byterindex(S("こんにちは")))
assert_nil(S("").byterindex(S("こんにちは")))
end
def test_bytesplice
assert_bytesplice_raise(IndexError, S("hello"), -6, 0, "xxx")
assert_bytesplice_result("xxxhello", S("hello"), -5, 0, "xxx")
assert_bytesplice_result("xxxhello", S("hello"), 0, 0, "xxx")
assert_bytesplice_result("xxxello", S("hello"), 0, 1, "xxx")
assert_bytesplice_result("xxx", S("hello"), 0, 5, "xxx")
assert_bytesplice_result("xxx", S("hello"), 0, 6, "xxx")
assert_bytesplice_raise(RangeError, S("hello"), -6...-6, "xxx")
assert_bytesplice_result("xxxhello", S("hello"), -5...-5, "xxx")
assert_bytesplice_result("xxxhello", S("hello"), 0...0, "xxx")
assert_bytesplice_result("xxxello", S("hello"), 0..0, "xxx")
assert_bytesplice_result("xxxello", S("hello"), 0...1, "xxx")
assert_bytesplice_result("xxxllo", S("hello"), 0..1, "xxx")
assert_bytesplice_result("xxx", S("hello"), 0..-1, "xxx")
assert_bytesplice_result("xxx", S("hello"), 0...5, "xxx")
assert_bytesplice_result("xxx", S("hello"), 0...6, "xxx")
assert_bytesplice_raise(TypeError, S("hello"), 0, "xxx")
assert_bytesplice_raise(IndexError, S("こんにちは"), -16, 0, "xxx")
assert_bytesplice_result("xxxこんにちは", S("こんにちは"), -15, 0, "xxx")
assert_bytesplice_result("xxxこんにちは", S("こんにちは"), 0, 0, "xxx")
assert_bytesplice_raise(IndexError, S("こんにちは"), 1, 0, "xxx")
assert_bytesplice_raise(IndexError, S("こんにちは"), 0, 1, "xxx")
assert_bytesplice_raise(IndexError, S("こんにちは"), 0, 2, "xxx")
assert_bytesplice_result("xxxんにちは", S("こんにちは"), 0, 3, "xxx")
assert_bytesplice_result("こんにちはxxx", S("こんにちは"), 15, 0, "xxx")
assert_bytesplice_result("", S(""), 0, 0, "")
assert_bytesplice_result("xxx", S(""), 0, 0, "xxx")
end
private
def assert_bytesplice_result(expected, s, *args)
assert_equal(args.last, s.send(:bytesplice, *args))
assert_equal(expected, s)
end
def assert_bytesplice_raise(e, s, *args)
assert_raise(e) { s.send(:bytesplice, *args) }
end
end
class TestString2 < TestString