From b0b9f7201acab05c2a3ad92c3043a1f01df3e17f Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 25 Jul 2022 10:21:38 +0200 Subject: [PATCH] rb_str_resize: Only clear coderange on truncation If we are expanding the string or only stripping extra capacity then coderange won't change, so clearing it is wasteful. --- file.c | 3 +++ sprintf.c | 5 ++++- string.c | 13 ++++++------- test/ruby/test_sprintf.rb | 10 ++++++++++ 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/file.c b/file.c index 5265d3a3a5..92338bb754 100644 --- a/file.c +++ b/file.c @@ -4090,6 +4090,9 @@ static VALUE str_shrink(VALUE str) { rb_str_resize(str, RSTRING_LEN(str)); + // expand_path on Windows can sometimes mutate the string + // without clearing its coderange + ENC_CODERANGE_CLEAR(str); return str; } diff --git a/sprintf.c b/sprintf.c index 2b2b34b5b4..1ee293b6d9 100644 --- a/sprintf.c +++ b/sprintf.c @@ -937,6 +937,8 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) if (RTEST(ruby_verbose)) rb_warn("%s", mesg); } rb_str_resize(result, blen); + // rb_str_format mutates the string without updating coderange + ENC_CODERANGE_CLEAR(result); return result; } @@ -1163,6 +1165,8 @@ ruby_vsprintf0(VALUE result, char *p, const char *fmt, va_list ap) buffer.value = 0; BSD_vfprintf(&f, fmt, ap); RBASIC_SET_CLASS_RAW(result, klass); + // vfprintf mutates the string without updating coderange + ENC_CODERANGE_CLEAR(result); rb_str_resize(result, (char *)f._p - RSTRING_PTR(result)); #undef f } @@ -1183,7 +1187,6 @@ rb_enc_vsprintf(rb_encoding *enc, const char *fmt, va_list ap) rb_enc_associate(result, enc); } ruby_vsprintf0(result, RSTRING_PTR(result), fmt, ap); - return result; } diff --git a/string.c b/string.c index e74783cf92..6f211b2005 100644 --- a/string.c +++ b/string.c @@ -2498,7 +2498,6 @@ rb_str_modify_expand(VALUE str, long expand) else if (expand > 0) { RESIZE_CAPA_TERM(str, len + expand, termlen); } - ENC_CODERANGE_CLEAR(str); } /* As rb_str_modify(), but don't clear coderange */ @@ -3073,16 +3072,16 @@ rb_str_set_len(VALUE str, long len) VALUE rb_str_resize(VALUE str, long len) { - long slen; - int independent; - if (len < 0) { rb_raise(rb_eArgError, "negative string size (or size too big)"); } - independent = str_independent(str); - ENC_CODERANGE_CLEAR(str); - slen = RSTRING_LEN(str); + int independent = str_independent(str); + long slen = RSTRING_LEN(str); + + if (slen > len && ENC_CODERANGE(str) != ENC_CODERANGE_7BIT) { + ENC_CODERANGE_CLEAR(str); + } { long capa; diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb index f2e73eb58d..b05f4f3e44 100644 --- a/test/ruby/test_sprintf.rb +++ b/test/ruby/test_sprintf.rb @@ -507,6 +507,16 @@ class TestSprintf < Test::Unit::TestCase end end + def test_coderange + format_str = "wrong constant name %s" + interpolated_str = "\u3042" + assert_predicate format_str, :ascii_only? + refute_predicate interpolated_str, :ascii_only? + + str = format_str % interpolated_str + refute_predicate str, :ascii_only? + end + def test_named_default h = Hash.new('world') assert_equal("hello world", "hello %{location}" % h)