Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions mrbgems/mruby-string-ext/test/string.rb
Original file line number Diff line number Diff line change
Expand Up @@ -891,8 +891,8 @@ def assert_upto(exp, receiver, *args)
end if UTF8STRING

assert('String#chop! on a binary string removes one byte') do
# `chop!` walks to the last character, and a byte-indexed string ends in a
# byte rather than in a character. Walking it as UTF-8 took the whole of a
# `chop!` cuts at the last character, and a byte-indexed string ends in a
# byte rather than in a character. Reading it as UTF-8 took the whole of a
# multi-byte sequence off, or all of a string that held only one.
if UTF8STRING
s = "\u{1F600}".b # F0 9F 98 80: four bytes, one character
Expand Down
14 changes: 6 additions & 8 deletions src/string.c
Original file line number Diff line number Diff line change
Expand Up @@ -2045,14 +2045,12 @@ mrb_str_chop_bang(mrb_state *mrb, mrb_value str)
len = RSTR_LEN(s) - 1;
}
else {
const char* t = RSTR_PTR(s), *p = t;
const char* e = p + RSTR_LEN(s);
while (p<e) {
mrb_int clen = mrb_utf8len(p, e);
if (p + clen>=e) break;
p += clen;
}
len = p - t;
/* The last character starts at the head of the one covering the last
byte, which is read backwards from there rather than by walking the
whole string. */
const char* t = RSTR_PTR(s);
const char* e = t + RSTR_LEN(s);
len = mrb_utf8_char_head(t, e-1, e) - t;
}
#else
len = RSTR_LEN(s) - 1;
Expand Down
16 changes: 16 additions & 0 deletions test/t/string.rb
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,22 @@ def [](*args)
assert_equal c, 'あいう'
end if UTF8STRING

assert('String#chop! cuts where String#length counts a character') do
# The last character starts where the character covering the last byte
# starts, and a byte that no lead byte reaches is a character of its own.
assert_equal "あ", "あ\x82".chop
assert_equal "\x80\x80", "\x80\x80\x80".chop
# A sequence RFC 3629 forbids spells no character, so its bytes stand alone.
assert_equal "\xC0", "\xC0\x80".chop
assert_equal "\xED\xA0", "\xED\xA0\x80".chop
# A lead byte the string end cuts short reaches none of the bytes after it.
assert_equal "a\xE3", "a\xE3\x81".chop
# a whole character still goes at once, however many bytes it spells
assert_equal "", "\u{1F600}".chop
# and the \r\n pair is still taken together after one
assert_equal "あ", "あ\r\n".chop
end if UTF8STRING

assert('String#downcase', '15.2.10.5.13') do
a = 'ABC'.downcase
b = 'ABC'
Expand Down
Loading