diff options
Diffstat (limited to 'string.c')
| -rw-r--r-- | string.c | 1818 |
1 files changed, 1009 insertions, 809 deletions
@@ -45,6 +45,7 @@ #include "ruby/re.h" #include "ruby/thread.h" #include "ruby/util.h" +#include "ruby/ractor.h" #include "ruby_assert.h" #include "shape.h" #include "vm_sync.h" @@ -59,9 +60,6 @@ # define HAVE_CRYPT_R 1 #endif -#define BEG(no) (regs->beg[(no)]) -#define END(no) (regs->end[(no)]) - #undef rb_str_new #undef rb_usascii_str_new #undef rb_utf8_str_new @@ -145,27 +143,7 @@ VALUE rb_cSymbol; RSTRING(str)->len = (n); \ } while (0) -static inline bool -str_encindex_fastpath(int encindex) -{ - // The overwhelming majority of strings are in one of these 3 encodings. - switch (encindex) { - case ENCINDEX_ASCII_8BIT: - case ENCINDEX_UTF_8: - case ENCINDEX_US_ASCII: - return true; - default: - return false; - } -} - -static inline bool -str_enc_fastpath(VALUE str) -{ - return str_encindex_fastpath(ENCODING_GET_INLINED(str)); -} - -#define TERM_LEN(str) (str_enc_fastpath(str) ? 1 : rb_enc_mbminlen(rb_enc_from_index(ENCODING_GET(str)))) +#define TERM_LEN(str) (rb_str_enc_fastpath(str) ? 1 : rb_enc_mbminlen(rb_enc_from_index(ENCODING_GET(str)))) #define TERM_FILL(ptr, termlen) do {\ char *const term_fill_ptr = (ptr);\ const int term_fill_len = (termlen);\ @@ -183,7 +161,7 @@ str_enc_fastpath(VALUE str) if (str_embed_capa(str) < capacity + termlen) {\ char *const tmp = ALLOC_N(char, (size_t)(capacity) + (termlen));\ const long tlen = RSTRING_LEN(str);\ - memcpy(tmp, RSTRING_PTR(str), tlen);\ + memcpy(tmp, RSTRING_PTR(str), str_embed_capa(str));\ RSTRING(str)->as.heap.ptr = tmp;\ RSTRING(str)->len = tlen;\ STR_SET_NOEMBED(str);\ @@ -204,6 +182,7 @@ str_enc_fastpath(VALUE str) RUBY_ASSERT(RSTRING_PTR(str) <= RSTRING_PTR(shared_str) + RSTRING_LEN(shared_str)); \ RB_OBJ_WRITE((str), &RSTRING(str)->as.heap.aux.shared, (shared_str)); \ FL_SET((str), STR_SHARED); \ + rb_gc_register_pinning_obj(str); \ FL_SET((shared_str), STR_SHARED_ROOT); \ if (RBASIC_CLASS((shared_str)) == 0) /* for CoW-friendliness */ \ FL_SET_RAW((shared_str), STR_BORROWED); \ @@ -216,15 +195,30 @@ str_enc_fastpath(VALUE str) #define STR_ENC_GET(str) get_encoding(str) +static inline bool +zero_filled(const char *s, int n) +{ + for (; n > 0; --n) { + if (*s++) return false; + } + return true; +} + #if !defined SHARABLE_MIDDLE_SUBSTRING # define SHARABLE_MIDDLE_SUBSTRING 0 #endif -#if !SHARABLE_MIDDLE_SUBSTRING -#define SHARABLE_SUBSTRING_P(beg, len, end) ((beg) + (len) == (end)) + +static inline bool +SHARABLE_SUBSTRING_P(VALUE str, long beg, long len) +{ +#if SHARABLE_MIDDLE_SUBSTRING + return true; #else -#define SHARABLE_SUBSTRING_P(beg, len, end) 1 + long end = beg + len; + long source_len = RSTRING_LEN(str); + return end == source_len || zero_filled(RSTRING_PTR(str) + end, TERM_LEN(str)); #endif - +} static inline long str_embed_capa(VALUE str) @@ -239,9 +233,11 @@ rb_str_reembeddable_p(VALUE str) } static inline size_t -rb_str_embed_size(long capa) +rb_str_embed_size(long capa, long termlen) { - return offsetof(struct RString, as.embed.ary) + capa; + size_t size = offsetof(struct RString, as.embed.ary) + capa + termlen; + if (size < sizeof(struct RString)) size = sizeof(struct RString); + return size; } size_t @@ -249,28 +245,30 @@ rb_str_size_as_embedded(VALUE str) { size_t real_size; if (STR_EMBED_P(str)) { - real_size = rb_str_embed_size(RSTRING(str)->len) + TERM_LEN(str); + size_t capa = RSTRING(str)->len; + if (FL_TEST_RAW(str, STR_PRECOMPUTED_HASH)) capa += sizeof(st_index_t); + + real_size = rb_str_embed_size(capa, TERM_LEN(str)); } /* if the string is not currently embedded, but it can be embedded, how * much space would it require */ else if (rb_str_reembeddable_p(str)) { - real_size = rb_str_embed_size(RSTRING(str)->as.heap.aux.capa) + TERM_LEN(str); + size_t capa = RSTRING(str)->as.heap.aux.capa; + if (FL_TEST_RAW(str, STR_PRECOMPUTED_HASH)) capa += sizeof(st_index_t); + + real_size = rb_str_embed_size(capa, TERM_LEN(str)); } else { real_size = sizeof(struct RString); } - if (FL_TEST_RAW(str, STR_PRECOMPUTED_HASH)) { - real_size += sizeof(st_index_t); - } - return real_size; } static inline bool STR_EMBEDDABLE_P(long len, long termlen) { - return rb_gc_size_allocatable_p(rb_str_embed_size(len + termlen)); + return rb_gc_size_allocatable_p(rb_str_embed_size(len, termlen)); } static VALUE str_replace_shared_without_enc(VALUE str2, VALUE str); @@ -307,7 +305,9 @@ rb_str_make_embedded(VALUE str) RUBY_ASSERT(rb_str_reembeddable_p(str)); RUBY_ASSERT(!STR_EMBED_P(str)); + int termlen = TERM_LEN(str); char *buf = RSTRING(str)->as.heap.ptr; + long old_capa = RSTRING(str)->as.heap.aux.capa + termlen; long len = RSTRING(str)->len; STR_SET_EMBED(str); @@ -315,10 +315,10 @@ rb_str_make_embedded(VALUE str) if (len > 0) { memcpy(RSTRING_PTR(str), buf, len); - ruby_xfree(buf); + SIZED_FREE_N(buf, old_capa); } - TERM_FILL(RSTRING(str)->as.embed.ary + len, TERM_LEN(str)); + TERM_FILL(RSTRING(str)->as.embed.ary + len, termlen); } void @@ -367,7 +367,7 @@ static VALUE register_fstring(VALUE str, bool copy, bool force_precompute_hash); static inline bool BARE_STRING_P(VALUE str) { - return RBASIC_CLASS(str) == rb_cString && !rb_shape_obj_has_ivars(str); + return RBASIC_CLASS(str) == rb_cString && !rb_obj_shape_has_ivars(str); } static inline st_index_t @@ -537,11 +537,14 @@ fstring_concurrent_set_create(VALUE str, void *data) ENC_CODERANGE_SET(str, coderange); RBASIC(str)->flags |= RSTRING_FSTR; - + if (!RB_OBJ_SHAREABLE_P(str)) { + RB_OBJ_SET_SHAREABLE(str); + } + RUBY_ASSERT((rb_gc_verify_shareable(str), 1)); RUBY_ASSERT(RB_TYPE_P(str, T_STRING)); RUBY_ASSERT(OBJ_FROZEN(str)); RUBY_ASSERT(!FL_TEST_RAW(str, STR_FAKESTR)); - RUBY_ASSERT(!rb_obj_exivar_p(str)); + RUBY_ASSERT(!rb_obj_shape_has_ivars(str)); RUBY_ASSERT(RBASIC_CLASS(str) == rb_cString); RUBY_ASSERT(!rb_objspace_garbage_object_p(str)); @@ -583,6 +586,8 @@ register_fstring(VALUE str, bool copy, bool force_precompute_hash) RUBY_ASSERT(!rb_objspace_garbage_object_p(result)); RUBY_ASSERT(RB_TYPE_P(result, T_STRING)); RUBY_ASSERT(OBJ_FROZEN(result)); + RUBY_ASSERT(RB_OBJ_SHAREABLE_P(result)); + RUBY_ASSERT((rb_gc_verify_shareable(result), 1)); RUBY_ASSERT(!FL_TEST_RAW(result, STR_FAKESTR)); RUBY_ASSERT(RBASIC_CLASS(result) == rb_cString); @@ -600,8 +605,11 @@ rb_obj_is_fstring_table(VALUE obj) void rb_gc_free_fstring(VALUE obj) { - // Assume locking and barrier (which there is no assert for) - ASSERT_vm_locking(); + ASSERT_vm_locking_with_barrier(); + + RUBY_ASSERT(FL_TEST(obj, RSTRING_FSTR)); + RUBY_ASSERT(OBJ_FROZEN(obj)); + RUBY_ASSERT(!FL_TEST(obj, STR_SHARED)); rb_concurrent_set_delete_by_identity(fstring_table_obj, obj); @@ -622,7 +630,7 @@ static VALUE setup_fake_str(struct RString *fake_str, const char *name, long len, int encidx) { fake_str->basic.flags = T_STRING|RSTRING_NOEMBED|STR_NOFREE|STR_FAKESTR; - RBASIC_SET_SHAPE_ID((VALUE)fake_str, ROOT_SHAPE_ID); + RBASIC_SET_SHAPE_ID((VALUE)fake_str, ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_OTHER); if (!name) { RUBY_ASSERT_ALWAYS(len == 0); @@ -655,14 +663,14 @@ rb_setup_fake_str(struct RString *fake_str, const char *name, long len, rb_encod VALUE rb_fstring_new(const char *ptr, long len) { - struct RString fake_str; + struct RString fake_str = {RBASIC_INIT}; return register_fstring(setup_fake_str(&fake_str, ptr, len, ENCINDEX_US_ASCII), false, false); } VALUE rb_fstring_enc_new(const char *ptr, long len, rb_encoding *enc) { - struct RString fake_str; + struct RString fake_str = {RBASIC_INIT}; return register_fstring(rb_setup_fake_str(&fake_str, ptr, len, enc), false, false); } @@ -703,7 +711,11 @@ VALUE rb_fs; static inline const char * search_nonascii(const char *p, const char *e) { - const uintptr_t *s, *t; + const char *s, *t; + + if (p < e && !ISASCII(*p)) { + return p; + } #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) # if SIZEOF_UINTPTR_T == 8 @@ -747,17 +759,19 @@ search_nonascii(const char *p, const char *e) #define aligned_ptr(value) \ __builtin_assume_aligned((value), sizeof(uintptr_t)) #else -#define aligned_ptr(value) (uintptr_t *)(value) +#define aligned_ptr(value) (value) #endif s = aligned_ptr(p); - t = (uintptr_t *)(e - (SIZEOF_VOIDP-1)); + t = (e - (SIZEOF_VOIDP-1)); #undef aligned_ptr - for (;s < t; s++) { - if (*s & NONASCII_MASK) { + for (;s < t; s += sizeof(uintptr_t)) { + uintptr_t word; + memcpy(&word, s, sizeof(word)); + if (word & NONASCII_MASK) { #ifdef WORDS_BIGENDIAN - return (const char *)s + (nlz_intptr(*s&NONASCII_MASK)>>3); + return (const char *)s + (nlz_intptr(word&NONASCII_MASK)>>3); #else - return (const char *)s + (ntz_intptr(*s&NONASCII_MASK)>>3); + return (const char *)s + (ntz_intptr(word&NONASCII_MASK)>>3); #endif } } @@ -929,22 +943,31 @@ rb_enc_str_coderange_scan(VALUE str, rb_encoding *enc) } int +rbimpl_enc_str_coderange_scan(VALUE str) +{ + int cr = enc_coderange_scan(str, get_encoding(str)); + ENC_CODERANGE_SET(str, cr); + return cr; +} + +#undef rb_enc_str_coderange +int rb_enc_str_coderange(VALUE str) { int cr = ENC_CODERANGE(str); if (cr == ENC_CODERANGE_UNKNOWN) { - cr = enc_coderange_scan(str, get_encoding(str)); - ENC_CODERANGE_SET(str, cr); + cr = rbimpl_enc_str_coderange_scan(str); } return cr; } +#define rb_enc_str_coderange rb_enc_str_coderange_inline static inline bool rb_enc_str_asciicompat(VALUE str) { int encindex = ENCODING_GET_INLINED(str); - return str_encindex_fastpath(encindex) || rb_enc_asciicompat(rb_enc_get_from_index(encindex)); + return rb_str_encindex_fastpath(encindex) || rb_enc_asciicompat(rb_enc_get_from_index(encindex)); } int @@ -999,12 +1022,14 @@ must_not_null(const char *ptr) static inline VALUE str_alloc_embed(VALUE klass, size_t capa) { - size_t size = rb_str_embed_size(capa); + size_t size = rb_str_embed_size(capa, 0); RUBY_ASSERT(size > 0); RUBY_ASSERT(rb_gc_size_allocatable_p(size)); - NEWOBJ_OF(str, struct RString, klass, - T_STRING | (RGENGC_WB_PROTECTED_STRING ? FL_WB_PROTECTED : 0), size, 0); + NEWOBJ_OF(str, struct RString, klass, T_STRING, size); + + str->len = 0; + str->as.embed.ary[0] = 0; return (VALUE)str; } @@ -1012,8 +1037,11 @@ str_alloc_embed(VALUE klass, size_t capa) static inline VALUE str_alloc_heap(VALUE klass) { - NEWOBJ_OF(str, struct RString, klass, - T_STRING | STR_NOEMBED | (RGENGC_WB_PROTECTED_STRING ? FL_WB_PROTECTED : 0), sizeof(struct RString), 0); + NEWOBJ_OF(str, struct RString, klass, T_STRING | STR_NOEMBED, sizeof(struct RString)); + + str->len = 0; + str->as.heap.aux.capa = 0; + str->as.heap.ptr = NULL; return (VALUE)str; } @@ -1066,6 +1094,9 @@ str_enc_new(VALUE klass, const char *ptr, long len, rb_encoding *enc) if (ptr) { memcpy(RSTRING_PTR(str), ptr, len); } + else { + memset(RSTRING_PTR(str), 0, len); + } STR_SET_LEN(str, len); TERM_FILL(RSTRING_PTR(str) + len, termlen); @@ -1456,7 +1487,7 @@ str_replace_shared_without_enc(VALUE str2, VALUE str) } char *ptr2 = STR_HEAP_PTR(str2); if (ptr2 != ptr) { - ruby_sized_xfree(ptr2, STR_HEAP_SIZE(str2)); + SIZED_FREE_N(ptr2, STR_HEAP_SIZE(str2)); } } FL_SET(str2, STR_NOEMBED); @@ -1535,7 +1566,7 @@ rb_str_tmp_frozen_no_embed_acquire(VALUE orig) * allocated. If the string is shared then the shared root must be * embedded, so we want to create a copy. If the string is a shared root * then it must be embedded, so we want to create a copy. */ - if (STR_EMBED_P(orig) || FL_TEST_RAW(orig, STR_SHARED | STR_SHARED_ROOT)) { + if (STR_EMBED_P(orig) || FL_TEST_RAW(orig, STR_SHARED | STR_SHARED_ROOT | RSTRING_FSTR)) { RSTRING(str)->as.heap.ptr = rb_xmalloc_mul_add_mul(sizeof(char), capa, sizeof(char), TERM_LEN(orig)); memcpy(RSTRING(str)->as.heap.ptr, RSTRING_PTR(orig), capa); } @@ -1546,10 +1577,14 @@ rb_str_tmp_frozen_no_embed_acquire(VALUE orig) RBASIC(str)->flags |= RBASIC(orig)->flags & STR_NOFREE; RBASIC(orig)->flags &= ~STR_NOFREE; STR_SET_SHARED(orig, str); + if (RB_OBJ_SHAREABLE_P(orig)) { + RB_OBJ_SET_SHAREABLE(str); + RUBY_ASSERT((rb_gc_verify_shareable(str), 1)); + } } RSTRING(str)->len = RSTRING(orig)->len; - RSTRING(str)->as.heap.aux.capa = capa; + RSTRING(str)->as.heap.aux.capa = capa + (TERM_LEN(orig) - TERM_LEN(str)); return str; } @@ -1595,6 +1630,7 @@ heap_str_make_shared(VALUE klass, VALUE orig) { RUBY_ASSERT(!STR_EMBED_P(orig)); RUBY_ASSERT(!STR_SHARED_P(orig)); + RUBY_ASSERT(!RB_OBJ_SHAREABLE_P(orig)); VALUE str = str_alloc_heap(klass); STR_SET_LEN(str, RSTRING_LEN(orig)); @@ -1604,7 +1640,7 @@ heap_str_make_shared(VALUE klass, VALUE orig) RBASIC(orig)->flags &= ~STR_NOFREE; STR_SET_SHARED(orig, str); if (klass == 0) - FL_UNSET_RAW(str, STR_BORROWED); + FL_UNSET_RAW(str, STR_BORROWED); return str; } @@ -1654,7 +1690,12 @@ str_new_frozen_buffer(VALUE klass, VALUE orig, int copy_encoding) TERM_FILL(RSTRING_END(str), TERM_LEN(orig)); } else { - str = heap_str_make_shared(klass, orig); + if (RB_OBJ_SHAREABLE_P(orig)) { + str = str_new(klass, RSTRING_PTR(orig), RSTRING_LEN(orig)); + } + else { + str = heap_str_make_shared(klass, orig); + } } } @@ -1725,7 +1766,7 @@ rb_str_free(VALUE str) } else { RB_DEBUG_COUNTER_INC(obj_str_ptr); - ruby_sized_xfree(STR_HEAP_PTR(str), STR_HEAP_SIZE(str)); + SIZED_FREE_N(STR_HEAP_PTR(str), STR_HEAP_SIZE(str)); } } @@ -1856,12 +1897,13 @@ str_replace(VALUE str, VALUE str2) static inline VALUE ec_str_alloc_embed(struct rb_execution_context_struct *ec, VALUE klass, size_t capa) { - size_t size = rb_str_embed_size(capa); + size_t size = rb_str_embed_size(capa, 0); RUBY_ASSERT(size > 0); RUBY_ASSERT(rb_gc_size_allocatable_p(size)); - NEWOBJ_OF(str, struct RString, klass, - T_STRING | (RGENGC_WB_PROTECTED_STRING ? FL_WB_PROTECTED : 0), size, ec); + EC_NEWOBJ_OF(str, struct RString, klass, T_STRING, size, ec); + + str->len = 0; return (VALUE)str; } @@ -1869,8 +1911,10 @@ ec_str_alloc_embed(struct rb_execution_context_struct *ec, VALUE klass, size_t c static inline VALUE ec_str_alloc_heap(struct rb_execution_context_struct *ec, VALUE klass) { - NEWOBJ_OF(str, struct RString, klass, - T_STRING | STR_NOEMBED | (RGENGC_WB_PROTECTED_STRING ? FL_WB_PROTECTED : 0), sizeof(struct RString), ec); + EC_NEWOBJ_OF(str, struct RString, klass, T_STRING | STR_NOEMBED, sizeof(struct RString), ec); + + str->as.heap.aux.capa = 0; + str->as.heap.ptr = NULL; return (VALUE)str; } @@ -1897,8 +1941,8 @@ str_duplicate_setup_embed(VALUE klass, VALUE str, VALUE dup) long len = RSTRING_LEN(str); RUBY_ASSERT(STR_EMBED_P(dup)); - RUBY_ASSERT(str_embed_capa(dup) >= len + 1); - MEMCPY(RSTRING(dup)->as.embed.ary, RSTRING(str)->as.embed.ary, char, len + 1); + RUBY_ASSERT(str_embed_capa(dup) >= len + TERM_LEN(str)); + MEMCPY(RSTRING(dup)->as.embed.ary, RSTRING(str)->as.embed.ary, char, len + TERM_LEN(str)); STR_SET_LEN(dup, RSTRING_LEN(str)); return str_duplicate_setup_encoding(str, dup, flags); } @@ -1919,8 +1963,8 @@ str_duplicate_setup_heap(VALUE klass, VALUE str, VALUE dup) RUBY_ASSERT(RB_OBJ_FROZEN_RAW(root)); RSTRING(dup)->as.heap.ptr = RSTRING_PTR(str); - FL_SET(root, STR_SHARED_ROOT); - RB_OBJ_WRITE(dup, &RSTRING(dup)->as.heap.aux.shared, root); + FL_SET_RAW(dup, RSTRING_NOEMBED); + STR_SET_SHARED(dup, root); flags |= RSTRING_NOEMBED | STR_SHARED; STR_SET_LEN(dup, RSTRING_LEN(str)); @@ -2257,26 +2301,22 @@ enc_strlen(const char *p, const char *e, rb_encoding *enc, int cr) c = 0; if (ENC_CODERANGE_CLEAN_P(cr)) { while (p < e) { - if (ISASCII(*p)) { - q = search_nonascii(p, e); - if (!q) - return c + (e - p); - c += q - p; - p = q; - } + q = search_nonascii(p, e); + if (!q) + return c + (e - p); + c += q - p; + p = q; p += rb_enc_fast_mbclen(p, e, enc); c++; } } else { while (p < e) { - if (ISASCII(*p)) { - q = search_nonascii(p, e); - if (!q) - return c + (e - p); - c += q - p; - p = q; - } + q = search_nonascii(p, e); + if (!q) + return c + (e - p); + c += q - p; + p = q; p += rb_enc_mbclen(p, e, enc); c++; } @@ -2314,15 +2354,13 @@ rb_enc_strlen_cr(const char *p, const char *e, rb_encoding *enc, int *cr) else if (rb_enc_asciicompat(enc)) { c = 0; while (p < e) { - if (ISASCII(*p)) { - q = search_nonascii(p, e); - if (!q) { - if (!*cr) *cr = ENC_CODERANGE_7BIT; - return c + (e - p); - } - c += q - p; - p = q; + q = search_nonascii(p, e); + if (!q) { + if (!*cr) *cr = ENC_CODERANGE_7BIT; + return c + (e - p); } + c += q - p; + p = q; ret = rb_enc_precise_mbclen(p, e, enc); if (MBCLEN_CHARFOUND_P(ret)) { *cr |= ENC_CODERANGE_VALID; @@ -2580,7 +2618,7 @@ rb_str_times(VALUE str, VALUE times) * * Returns the result of formatting +object+ into the format specifications * contained in +self+ - * (see {Format Specifications}[rdoc-ref:format_specifications.rdoc]): + * (see {Format Specifications}[rdoc-ref:language/format_specifications.rdoc]): * * '%05d' % 123 # => "00123" * @@ -2600,7 +2638,9 @@ rb_str_format_m(VALUE str, VALUE arg) VALUE tmp = rb_check_array_type(arg); if (!NIL_P(tmp)) { - return rb_str_format(RARRAY_LENINT(tmp), RARRAY_CONST_PTR(tmp), str); + VALUE result = rb_str_format(RARRAY_LENINT(tmp), RARRAY_CONST_PTR(tmp), str); + RB_GC_GUARD(tmp); + return result; } return rb_str_format(1, &arg, str); } @@ -2682,7 +2722,7 @@ str_make_independent_expand(VALUE str, long len, long expand, const int termlen) memcpy(ptr, oldptr, len); } if (FL_TEST_RAW(str, STR_NOEMBED|STR_NOFREE|STR_SHARED) == STR_NOEMBED) { - xfree(oldptr); + SIZED_FREE_N(oldptr, STR_HEAP_SIZE(str)); } STR_SET_NOEMBED(str); FL_UNSET(str, STR_SHARED|STR_NOFREE); @@ -2740,7 +2780,7 @@ str_discard(VALUE str) { str_modifiable(str); if (!STR_EMBED_P(str) && !FL_TEST(str, STR_SHARED|STR_NOFREE)) { - ruby_sized_xfree(STR_HEAP_PTR(str), STR_HEAP_SIZE(str)); + SIZED_FREE_N(STR_HEAP_PTR(str), STR_HEAP_SIZE(str)); RSTRING(str)->as.heap.ptr = 0; STR_SET_LEN(str, 0); } @@ -2755,7 +2795,7 @@ rb_must_asciicompat(VALUE str) rb_raise(rb_eTypeError, "not encoding capable object"); } - if (RB_LIKELY(str_encindex_fastpath(encindex))) { + if (RB_LIKELY(rb_str_encindex_fastpath(encindex))) { return; } @@ -2785,15 +2825,6 @@ rb_string_value_ptr(volatile VALUE *ptr) return RSTRING_PTR(str); } -static int -zero_filled(const char *s, int n) -{ - for (; n > 0; --n) { - if (*s++) return 0; - } - return 1; -} - static const char * str_null_char(const char *s, long len, const int minlen, rb_encoding *enc) { @@ -2856,16 +2887,21 @@ str_null_check(VALUE str, int *w) { char *s = RSTRING_PTR(str); long len = RSTRING_LEN(str); - rb_encoding *enc = rb_enc_get(str); - const int minlen = rb_enc_mbminlen(enc); + int minlen = 1; + + if (RB_UNLIKELY(!rb_str_enc_fastpath(str))) { + rb_encoding *enc = rb_str_enc_get(str); + minlen = rb_enc_mbminlen(enc); - if (minlen > 1) { - *w = 1; - if (str_null_char(s, len, minlen, enc)) { - return NULL; + if (minlen > 1) { + *w = 1; + if (str_null_char(s, len, minlen, enc)) { + return NULL; + } + return str_fill_term(str, s, len, minlen); } - return str_fill_term(str, s, len, minlen); } + *w = 0; if (!s || memchr(s, 0, len)) { return NULL; @@ -2876,6 +2912,34 @@ str_null_check(VALUE str, int *w) return s; } +const char * +rb_str_null_check(VALUE str) +{ + RUBY_ASSERT(RB_TYPE_P(str, T_STRING)); + + char *s; + long len; + RSTRING_GETMEM(str, s, len); + + if (RB_LIKELY(rb_str_enc_fastpath(str))) { + if (!s || memchr(s, 0, len)) { + rb_raise(rb_eArgError, "string contains null byte"); + } + } + else { + int w; + const char *s = str_null_check(str, &w); + if (!s) { + if (w) { + rb_raise(rb_eArgError, "string contains null char"); + } + rb_raise(rb_eArgError, "string contains null byte"); + } + } + + return s; +} + char * rb_str_to_cstr(VALUE str) { @@ -2954,16 +3018,14 @@ str_nth_len(const char *p, const char *e, long *nthp, rb_encoding *enc) *nthp = nth; return (char *)e; } - if (ISASCII(*p)) { - p2 = search_nonascii(p, e2); - if (!p2) { - nth -= e2 - p; - *nthp = nth; - return (char *)e2; - } - nth -= p2 - p; - p = p2; + p2 = search_nonascii(p, e2); + if (!p2) { + nth -= e2 - p; + *nthp = nth; + return (char *)e2; } + nth -= p2 - p; + p = p2; n = rb_enc_mbclen(p, e, enc); p += n; nth--; @@ -3080,8 +3142,11 @@ str_subseq(VALUE str, long beg, long len) RUBY_ASSERT(beg+len <= RSTRING_LEN(str)); const int termlen = TERM_LEN(str); - if (!SHARABLE_SUBSTRING_P(beg, len, RSTRING_LEN(str))) { - str2 = rb_str_new(RSTRING_PTR(str) + beg, len); + if (!SHARABLE_SUBSTRING_P(str, beg, len)) { + str2 = rb_enc_str_new(RSTRING_PTR(str) + beg, len, rb_str_enc_get(str)); + if (ENC_CODERANGE(str) == ENC_CODERANGE_7BIT) { + ENC_CODERANGE_SET(str2, ENC_CODERANGE_7BIT); + } RB_GC_GUARD(str); return str2; } @@ -3094,12 +3159,19 @@ str_subseq(VALUE str, long beg, long len) TERM_FILL(ptr2+len, termlen); STR_SET_LEN(str2, len); + if (ENC_CODERANGE(str) == ENC_CODERANGE_7BIT) { + ENC_CODERANGE_SET(str2, ENC_CODERANGE_7BIT); + } + RB_GC_GUARD(str); } else { str_replace_shared(str2, str); RUBY_ASSERT(!STR_EMBED_P(str2)); - ENC_CODERANGE_CLEAR(str2); + if (ENC_CODERANGE(str) != ENC_CODERANGE_7BIT) { + ENC_CODERANGE_CLEAR(str2); + } + RSTRING(str2)->as.heap.ptr += beg; if (RSTRING_LEN(str2) > len) { STR_SET_LEN(str2, len); @@ -3255,7 +3327,7 @@ rb_str_freeze(VALUE str) * * Otherwise returns <tt>self.dup</tt>, which is not frozen. * - * Related: see {Freezing/Unfreezing}[rdoc-ref:String@Freezing-2FUnfreezing]. + * Related: see {Freezing/Unfreezing}[rdoc-ref:String@FreezingUnfreezing]. */ static VALUE str_uplus(VALUE str) @@ -3300,7 +3372,7 @@ str_uplus(VALUE str) * * 'foo'.dedup.gsub!('o') * - * Related: see {Freezing/Unfreezing}[rdoc-ref:String@Freezing-2FUnfreezing]. + * Related: see {Freezing/Unfreezing}[rdoc-ref:String@FreezingUnfreezing]. */ static VALUE str_uminus(VALUE str) @@ -3420,13 +3492,16 @@ rb_str_resize(VALUE str, long len) str_make_independent_expand(str, slen, len - slen, termlen); } else if (str_embed_capa(str) >= len + termlen) { + capa = RSTRING(str)->as.heap.aux.capa; char *ptr = STR_HEAP_PTR(str); STR_SET_EMBED(str); if (slen > len) slen = len; if (slen > 0) MEMCPY(RSTRING(str)->as.embed.ary, ptr, char, slen); TERM_FILL(RSTRING(str)->as.embed.ary + len, termlen); STR_SET_LEN(str, len); - if (independent) ruby_xfree(ptr); + if (independent) { + SIZED_FREE_N(ptr, capa + termlen); + } return str; } else if (!independent) { @@ -3724,7 +3799,7 @@ rb_str_buf_append(VALUE str, VALUE str2) { int str2_cr = rb_enc_str_coderange(str2); - if (str_enc_fastpath(str)) { + if (rb_str_enc_fastpath(str)) { switch (str2_cr) { case ENC_CODERANGE_7BIT: // If RHS is 7bit we can do simple concatenation @@ -3941,7 +4016,7 @@ rb_str_append_as_bytes(int argc, VALUE *argv, VALUE str) clear_cr: // If no fast path was hit, we clear the coderange. - // append_as_bytes is predominently meant to be used in + // append_as_bytes is predominantly meant to be used in // buffering situation, hence it's likely the coderange // will never be scanned, so it's not worth spending time // precomputing the coderange except for simple and common @@ -4071,15 +4146,14 @@ rb_ascii8bit_appendable_encoding_index(rb_encoding *enc, unsigned int code) /* * call-seq: - * prepend(*other_strings) -> string + * prepend(*other_strings) -> new_string * - * Prepends each string in +other_strings+ to +self+ and returns +self+: + * Prefixes to +self+ the concatenation of the given +other_strings+; returns +self+: * - * s = 'foo' - * s.prepend('bar', 'baz') # => "barbazfoo" - * s # => "barbazfoo" + * 'baz'.prepend('foo', 'bar') # => "foobarbaz" + * + * Related: see {Modifying}[rdoc-ref:String@Modifying]. * - * Related: String#concat. */ static VALUE @@ -4133,10 +4207,8 @@ rb_str_hash_cmp(VALUE str1, VALUE str2) * call-seq: * hash -> integer * - * Returns the integer hash value for +self+. - * The value is based on the length, content and encoding of +self+. + * :include: doc/string/hash.rdoc * - * Related: Object#hash. */ static VALUE @@ -4201,11 +4273,11 @@ rb_str_cmp(VALUE str1, VALUE str2) /* * call-seq: - * self == object -> true or false + * self == other -> true or false * - * Returns whether +object+ is equal to +self+. + * Returns whether +other+ is equal to +self+. * - * When +object+ is a string, returns whether +object+ has the same length and content as +self+: + * When +other+ is a string, returns whether +other+ has the same length and content as +self+: * * s = 'foo' * s == 'foo' # => true @@ -4216,11 +4288,11 @@ rb_str_cmp(VALUE str1, VALUE str2) * * "\u{e4 f6 fc}".encode(Encoding::ISO_8859_1) == ("\u{c4 d6 dc}") # => false * - * When +object+ is not a string: + * When +other+ is not a string: * - * - If +object+ responds to method <tt>to_str</tt>, - * <tt>object == self</tt> is called and its return value is returned. - * - If +object+ does not respond to <tt>to_str</tt>, + * - If +other+ responds to method <tt>to_str</tt>, + * <tt>other == self</tt> is called and its return value is returned. + * - If +other+ does not respond to <tt>to_str</tt>, * +false+ is returned. * * Related: {Comparing}[rdoc-ref:String@Comparing]. @@ -4257,23 +4329,29 @@ rb_str_eql(VALUE str1, VALUE str2) /* * call-seq: - * self <=> other_string -> -1, 0, 1, or nil + * self <=> other -> -1, 0, 1, or nil * - * Compares +self+ and +other_string+, returning: + * Compares +self+ and +other+, + * evaluating their _contents_, not their _lengths_. * - * - -1 if +other_string+ is larger. - * - 0 if the two are equal. - * - 1 if +other_string+ is smaller. - * - +nil+ if the two are incomparable. + * Returns: + * + * - +-1+, if +self+ is smaller. + * - +0+, if the two are equal. + * - +1+, if +self+ is larger. + * - +nil+, if the two are incomparable. * * Examples: * - * 'foo' <=> 'foo' # => 0 - * 'foo' <=> 'food' # => -1 - * 'food' <=> 'foo' # => 1 - * 'FOO' <=> 'foo' # => -1 - * 'foo' <=> 'FOO' # => 1 - * 'foo' <=> 1 # => nil + * 'a' <=> 'b' # => -1 + * 'a' <=> 'ab' # => -1 + * 'a' <=> 'a' # => 0 + * 'b' <=> 'a' # => 1 + * 'ab' <=> 'a' # => 1 + * 'a' <=> :a # => nil + * + * \Class \String includes module Comparable, + * each of whose methods uses String#<=> for comparison. * * Related: see {Comparing}[rdoc-ref:String@Comparing]. */ @@ -4319,7 +4397,7 @@ static VALUE str_casecmp_p(VALUE str1, VALUE str2); * Related: see {Comparing}[rdoc-ref:String@Comparing]. */ -static VALUE +VALUE rb_str_casecmp(VALUE str1, VALUE str2) { VALUE s = rb_check_string_type(str2); @@ -4381,9 +4459,9 @@ str_casecmp(VALUE str1, VALUE str2) p2 += l2; } } - if (RSTRING_LEN(str1) == RSTRING_LEN(str2)) return INT2FIX(0); - if (RSTRING_LEN(str1) > RSTRING_LEN(str2)) return INT2FIX(1); - return INT2FIX(-1); + if (p1 == p1end && p2 == p2end) return INT2FIX(0); + if (p1 == p1end) return INT2FIX(-1); + return INT2FIX(1); } /* @@ -4501,8 +4579,7 @@ rb_strseq_index(VALUE str, VALUE sub, long offset, int in_byte) /* * call-seq: - * index(substring, offset = 0) -> integer or nil - * index(regexp, offset = 0) -> integer or nil + * index(pattern, offset = 0) -> integer or nil * * :include: doc/string/index.rdoc * @@ -4536,8 +4613,7 @@ rb_str_index_m(int argc, VALUE *argv, VALUE str) if (rb_reg_search(sub, str, pos, 0) >= 0) { VALUE match = rb_backref_get(); - struct re_registers *regs = RMATCH_REGS(match); - pos = rb_str_sublen(str, BEG(0)); + pos = rb_str_sublen(str, RMATCH_BEG(match, 0)); return LONG2NUM(pos); } } @@ -4663,8 +4739,7 @@ rb_str_byteindex_m(int argc, VALUE *argv, VALUE str) if (RB_TYPE_P(sub, T_REGEXP)) { if (rb_reg_search(sub, str, pos, 0) >= 0) { VALUE match = rb_backref_get(); - struct re_registers *regs = RMATCH_REGS(match); - pos = BEG(0); + pos = RMATCH_BEG(match, 0); return LONG2NUM(pos); } } @@ -4760,59 +4835,10 @@ rb_str_rindex(VALUE str, VALUE sub, long pos) /* * call-seq: - * rindex(substring, offset = self.length) -> integer or nil - * rindex(regexp, offset = self.length) -> integer or nil - * - * Returns the Integer index of the _last_ occurrence of the given +substring+, - * or +nil+ if none found: - * - * 'foo'.rindex('f') # => 0 - * 'foo'.rindex('o') # => 2 - * 'foo'.rindex('oo') # => 1 - * 'foo'.rindex('ooo') # => nil - * - * Returns the Integer index of the _last_ match for the given Regexp +regexp+, - * or +nil+ if none found: - * - * 'foo'.rindex(/f/) # => 0 - * 'foo'.rindex(/o/) # => 2 - * 'foo'.rindex(/oo/) # => 1 - * 'foo'.rindex(/ooo/) # => nil + * rindex(pattern, offset = self.length) -> integer or nil * - * The _last_ match means starting at the possible last position, not - * the last of longest matches. + * :include:doc/string/rindex.rdoc * - * 'foo'.rindex(/o+/) # => 2 - * $~ #=> #<MatchData "o"> - * - * To get the last longest match, needs to combine with negative - * lookbehind. - * - * 'foo'.rindex(/(?<!o)o+/) # => 1 - * $~ #=> #<MatchData "oo"> - * - * Or String#index with negative lookforward. - * - * 'foo'.index(/o+(?!.*o)/) # => 1 - * $~ #=> #<MatchData "oo"> - * - * Integer argument +offset+, if given and non-negative, specifies the maximum starting position in the - * string to _end_ the search: - * - * 'foo'.rindex('o', 0) # => nil - * 'foo'.rindex('o', 1) # => 1 - * 'foo'.rindex('o', 2) # => 2 - * 'foo'.rindex('o', 3) # => 2 - * - * If +offset+ is a negative Integer, the maximum starting position in the - * string to _end_ the search is the sum of the string's length and +offset+: - * - * 'foo'.rindex('o', -1) # => 2 - * 'foo'.rindex('o', -2) # => 1 - * 'foo'.rindex('o', -3) # => nil - * 'foo'.rindex('o', -4) # => nil - * - * Related: String#index. */ static VALUE @@ -4844,8 +4870,7 @@ rb_str_rindex_m(int argc, VALUE *argv, VALUE str) if (rb_reg_search(sub, str, pos, 1) >= 0) { VALUE match = rb_backref_get(); - struct re_registers *regs = RMATCH_REGS(match); - pos = rb_str_sublen(str, BEG(0)); + pos = rb_str_sublen(str, RMATCH_BEG(match, 0)); return LONG2NUM(pos); } } @@ -4906,9 +4931,9 @@ rb_str_byterindex(VALUE str, VALUE sub, long pos) * s.size # => 3 # Three 1-byte characters. * s.bytesize # => 3 # Three bytes. * s.byterindex('f') # => 0 - s.byterindex('o') # => 2 - s.byterindex('oo') # => 1 - s.byterindex('ooo') # => nil + * s.byterindex('o') # => 2 + * s.byterindex('oo') # => 1 + * s.byterindex('ooo') # => nil * * When +object+ is a Regexp, * returns the index of the last found substring matching +object+; @@ -5002,8 +5027,7 @@ rb_str_byterindex_m(int argc, VALUE *argv, VALUE str) if (RB_TYPE_P(sub, T_REGEXP)) { if (rb_reg_search(sub, str, pos, 1) >= 0) { VALUE match = rb_backref_get(); - struct re_registers *regs = RMATCH_REGS(match); - pos = BEG(0); + pos = RMATCH_BEG(match, 0); return LONG2NUM(pos); } } @@ -5017,12 +5041,15 @@ rb_str_byterindex_m(int argc, VALUE *argv, VALUE str) /* * call-seq: - * self =~ object -> integer or nil + * self =~ other -> integer or nil * - * When +object+ is a Regexp, returns the index of the first substring in +self+ - * matched by +object+, - * or +nil+ if no match is found; - * updates {Regexp-related global variables}[rdoc-ref:Regexp@Global+Variables]: + * When +other+ is a Regexp: + * + * - Returns the integer index (in characters) of the first match + * for +self+ and +other+, or +nil+ if none; + * - Updates {Regexp-related global variables}[rdoc-ref:Regexp@Global+Variables]. + * + * Examples: * * 'foo' =~ /f/ # => 0 * $~ # => #<MatchData "f"> @@ -5040,8 +5067,8 @@ rb_str_byterindex_m(int argc, VALUE *argv, VALUE str) * /(?<number>\d+)/ =~ 'no. 9' # => 4 * number # => "9" # Assigned. * - * If +object+ is not a Regexp, returns the value - * returned by <tt>object =~ self</tt>. + * When +other+ is not a Regexp, returns the value + * returned by <tt>other =~ self</tt>. * * Related: see {Querying}[rdoc-ref:String@Querying]. */ @@ -5070,34 +5097,36 @@ static VALUE get_pat(VALUE); * match(pattern, offset = 0) -> matchdata or nil * match(pattern, offset = 0) {|matchdata| ... } -> object * - * Returns a MatchData object (or +nil+) based on +self+ and the given +pattern+. - * - * Note: also updates Regexp@Global+Variables. + * Creates a MatchData object based on +self+ and the given arguments; + * updates {Regexp Global Variables}[rdoc-ref:Regexp@Global+Variables]. * * - Computes +regexp+ by converting +pattern+ (if not already a Regexp). + * * regexp = Regexp.new(pattern) + * * - Computes +matchdata+, which will be either a MatchData object or +nil+ * (see Regexp#match): - * matchdata = regexp.match(self) * - * With no block given, returns the computed +matchdata+: + * matchdata = regexp.match(self[offset..]) * - * 'foo'.match('f') # => #<MatchData "f"> - * 'foo'.match('o') # => #<MatchData "o"> - * 'foo'.match('x') # => nil - * - * If Integer argument +offset+ is given, the search begins at index +offset+: + * With no block given, returns the computed +matchdata+ or +nil+: * + * 'foo'.match('f') # => #<MatchData "f"> + * 'foo'.match('o') # => #<MatchData "o"> + * 'foo'.match('x') # => nil * 'foo'.match('f', 1) # => nil * 'foo'.match('o', 1) # => #<MatchData "o"> * - * With a block given, calls the block with the computed +matchdata+ - * and returns the block's return value: + * With a block given and computed +matchdata+ non-nil, calls the block with +matchdata+; + * returns the block's return value: * * 'foo'.match(/o/) {|matchdata| matchdata } # => #<MatchData "o"> - * 'foo'.match(/x/) {|matchdata| matchdata } # => nil - * 'foo'.match(/f/, 1) {|matchdata| matchdata } # => nil * + * With a block given and +nil+ +matchdata+, does not call the block: + * + * 'foo'.match(/x/) {|matchdata| fail 'Cannot happen' } # => nil + * + * Related: see {Querying}[rdoc-ref:String@Querying]. */ static VALUE @@ -5119,24 +5148,23 @@ rb_str_match_m(int argc, VALUE *argv, VALUE str) * call-seq: * match?(pattern, offset = 0) -> true or false * - * Returns +true+ or +false+ based on whether a match is found for +self+ and +pattern+. + * Returns whether a match is found for +self+ and the given arguments; + * does not update {Regexp Global Variables}[rdoc-ref:Regexp@Global+Variables]. * - * Note: does not update Regexp@Global+Variables. + * Computes +regexp+ by converting +pattern+ (if not already a Regexp): * - * Computes +regexp+ by converting +pattern+ (if not already a Regexp). * regexp = Regexp.new(pattern) * - * Returns +true+ if <tt>self+.match(regexp)</tt> returns a MatchData object, + * Returns +true+ if <tt>self[offset..].match(regexp)</tt> returns a MatchData object, * +false+ otherwise: * * 'foo'.match?(/o/) # => true * 'foo'.match?('o') # => true * 'foo'.match?(/x/) # => false - * - * If Integer argument +offset+ is given, the search begins at index +offset+: * 'foo'.match?('f', 1) # => false * 'foo'.match?('o', 1) # => true * + * Related: see {Querying}[rdoc-ref:String@Querying]. */ static VALUE @@ -5337,57 +5365,7 @@ static VALUE str_succ(VALUE str); * call-seq: * succ -> new_str * - * Returns the successor to +self+. The successor is calculated by - * incrementing characters. - * - * The first character to be incremented is the rightmost alphanumeric: - * or, if no alphanumerics, the rightmost character: - * - * 'THX1138'.succ # => "THX1139" - * '<<koala>>'.succ # => "<<koalb>>" - * '***'.succ # => '**+' - * - * The successor to a digit is another digit, "carrying" to the next-left - * character for a "rollover" from 9 to 0, and prepending another digit - * if necessary: - * - * '00'.succ # => "01" - * '09'.succ # => "10" - * '99'.succ # => "100" - * - * The successor to a letter is another letter of the same case, - * carrying to the next-left character for a rollover, - * and prepending another same-case letter if necessary: - * - * 'aa'.succ # => "ab" - * 'az'.succ # => "ba" - * 'zz'.succ # => "aaa" - * 'AA'.succ # => "AB" - * 'AZ'.succ # => "BA" - * 'ZZ'.succ # => "AAA" - * - * The successor to a non-alphanumeric character is the next character - * in the underlying character set's collating sequence, - * carrying to the next-left character for a rollover, - * and prepending another character if necessary: - * - * s = 0.chr * 3 - * s # => "\x00\x00\x00" - * s.succ # => "\x00\x00\x01" - * s = 255.chr * 3 - * s # => "\xFF\xFF\xFF" - * s.succ # => "\x01\x00\x00\x00" - * - * Carrying can occur between and among mixtures of alphanumeric characters: - * - * s = 'zz99zz99' - * s.succ # => "aaa00aa00" - * s = '99zz99zz' - * s.succ # => "100aa00aa" - * - * The successor to an empty +String+ is a new empty +String+: - * - * ''.succ # => "" + * :include: doc/string/succ.rdoc * */ @@ -5492,7 +5470,9 @@ str_succ(VALUE str) * call-seq: * succ! -> self * - * Equivalent to String#succ, but modifies +self+ in place; returns +self+. + * Like String#succ, but modifies +self+ in place; returns +self+. + * + * Related: see {Modifying}[rdoc-ref:String@Modifying]. */ static VALUE @@ -5525,33 +5505,7 @@ str_upto_i(VALUE str, VALUE arg) * upto(other_string, exclusive = false) {|string| ... } -> self * upto(other_string, exclusive = false) -> new_enumerator * - * With a block given, calls the block with each +String+ value - * returned by successive calls to String#succ; - * the first value is +self+, the next is <tt>self.succ</tt>, and so on; - * the sequence terminates when value +other_string+ is reached; - * returns +self+: - * - * 'a8'.upto('b6') {|s| print s, ' ' } # => "a8" - * Output: - * - * a8 a9 b0 b1 b2 b3 b4 b5 b6 - * - * If argument +exclusive+ is given as a truthy object, the last value is omitted: - * - * 'a8'.upto('b6', true) {|s| print s, ' ' } # => "a8" - * - * Output: - * - * a8 a9 b0 b1 b2 b3 b4 b5 - * - * If +other_string+ would not be reached, does not call the block: - * - * '25'.upto('5') {|s| fail s } - * 'aa'.upto('a') {|s| fail s } - * - * With no block given, returns a new Enumerator: - * - * 'a8'.upto('b6') # => #<Enumerator: "a8":upto("b6")> + * :include: doc/string/upto.rdoc * */ @@ -5792,16 +5746,14 @@ rb_str_aref(VALUE str, VALUE indx) /* * call-seq: - * self[index] -> new_string or nil - * self[start, length] -> new_string or nil + * self[offset] -> new_string or nil + * self[offset, size] -> new_string or nil * self[range] -> new_string or nil * self[regexp, capture = 0] -> new_string or nil * self[substring] -> new_string or nil * - * Returns the substring of +self+ specified by the arguments. - * See examples at {String Slices}[rdoc-ref:String@String+Slices]. + * :include: doc/string/aref.rdoc * - * Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. */ static VALUE @@ -5830,11 +5782,14 @@ rb_str_drop_bytes(VALUE str, long len) nlen = olen - len; if (str_embed_capa(str) >= nlen + TERM_LEN(str)) { char *oldptr = ptr; + size_t old_capa = RSTRING(str)->as.heap.aux.capa + TERM_LEN(str); int fl = (int)(RBASIC(str)->flags & (STR_NOEMBED|STR_SHARED|STR_NOFREE)); STR_SET_EMBED(str); ptr = RSTRING(str)->as.embed.ary; memmove(ptr, oldptr + len, nlen); - if (fl == STR_NOEMBED) xfree(oldptr); + if (fl == STR_NOEMBED) { + SIZED_FREE_N(oldptr, old_capa); + } } else { if (!STR_SHARED_P(str)) { @@ -5949,26 +5904,25 @@ rb_str_subpat_set(VALUE str, VALUE re, VALUE backref, VALUE val) VALUE match; long start, end, len; rb_encoding *enc; - struct re_registers *regs; if (rb_reg_search(re, str, 0, 0) < 0) { rb_raise(rb_eIndexError, "regexp not matched"); } match = rb_backref_get(); nth = rb_reg_backref_number(match, backref); - regs = RMATCH_REGS(match); - if ((nth >= regs->num_regs) || ((nth < 0) && (-nth >= regs->num_regs))) { + int num_regs = RMATCH_NREGS(match); + if ((nth >= num_regs) || ((nth < 0) && (-nth >= num_regs))) { rb_raise(rb_eIndexError, "index %d out of regexp", nth); } if (nth < 0) { - nth += regs->num_regs; + nth += num_regs; } - start = BEG(nth); + start = RMATCH_BEG(match, nth); if (start == -1) { rb_raise(rb_eIndexError, "regexp group %d not matched", nth); } - end = END(nth); + end = RMATCH_END(match, nth); len = end - start; StringValue(val); enc = rb_enc_check_str(str, val); @@ -6015,30 +5969,14 @@ rb_str_aset(VALUE str, VALUE indx, VALUE val) /* * call-seq: - * self[index] = new_string - * self[start, length] = new_string - * self[range] = new_string - * self[regexp, capture = 0] = new_string - * self[substring] = new_string + * self[index] = other_string -> new_string + * self[start, length] = other_string -> new_string + * self[range] = other_string -> new_string + * self[regexp, capture = 0] = other_string -> new_string + * self[substring] = other_string -> new_string * - * Replaces all, some, or none of the contents of +self+; returns +new_string+. - * See {String Slices}[rdoc-ref:String@String+Slices]. + * :include: doc/string/aset.rdoc * - * A few examples: - * - * s = 'foo' - * s[2] = 'rtune' # => "rtune" - * s # => "fortune" - * s[1, 5] = 'init' # => "init" - * s # => "finite" - * s[3..4] = 'al' # => "al" - * s # => "finale" - * s[/e$/] = 'ly' # => "ly" - * s # => "finally" - * s['lly'] = 'ncial' # => "ncial" - * s # => "financial" - * - * Related: see {Modifying}[rdoc-ref:String@Modifying]. */ static VALUE @@ -6059,19 +5997,9 @@ rb_str_aset_m(int argc, VALUE *argv, VALUE str) /* * call-seq: - * insert(index, other_string) -> self - * - * Inserts the given +other_string+ into +self+; returns +self+. - * - * If the Integer +index+ is positive, inserts +other_string+ at offset +index+: + * insert(offset, other_string) -> self * - * 'foo'.insert(1, 'bar') # => "fbaroo" - * - * If the Integer +index+ is negative, counts backward from the end of +self+ - * and inserts +other_string+ at offset <tt>index+1</tt> - * (that is, _after_ <tt>self[index]</tt>): - * - * 'foo'.insert(-2, 'bar') # => "fobaro" + * :include: doc/string/insert.rdoc * */ @@ -6099,18 +6027,20 @@ rb_str_insert(VALUE str, VALUE idx, VALUE str2) * slice!(regexp, capture = 0) -> new_string or nil * slice!(substring) -> new_string or nil * - * Removes and returns the substring of +self+ specified by the arguments. - * See {String Slices}[rdoc-ref:String@String+Slices]. + * Like String#[] (and its alias String#slice), except that: + * + * - Performs substitutions in +self+ (not in a copy of +self+). + * - Returns the removed substring if any modifications were made, +nil+ otherwise. * * A few examples: * - * string = "This is a string" - * string.slice!(2) #=> "i" - * string.slice!(3..6) #=> " is " - * string.slice!(/s.*t/) #=> "sa st" - * string.slice!("r") #=> "r" - * string #=> "Thing" + * s = 'hello' + * s.slice!('e') # => "e" + * s # => "hllo" + * s.slice!('e') # => nil + * s # => "hllo" * + * Related: see {Modifying}[rdoc-ref:String@Modifying]. */ static VALUE @@ -6127,14 +6057,14 @@ rb_str_slice_bang(int argc, VALUE *argv, VALUE str) if (RB_TYPE_P(indx, T_REGEXP)) { if (rb_reg_search(indx, str, 0, 0) < 0) return Qnil; VALUE match = rb_backref_get(); - struct re_registers *regs = RMATCH_REGS(match); + int num_regs = RMATCH_NREGS(match); int nth = 0; if (argc > 1 && (nth = rb_reg_backref_number(match, argv[1])) < 0) { - if ((nth += regs->num_regs) <= 0) return Qnil; + if ((nth += num_regs) <= 0) return Qnil; } - else if (nth >= regs->num_regs) return Qnil; - beg = BEG(nth); - len = END(nth) - beg; + else if (nth >= num_regs) return Qnil; + beg = RMATCH_BEG(match, nth); + len = RMATCH_END(match, nth) - beg; goto subseq; } else if (argc == 2) { @@ -6284,13 +6214,12 @@ rb_pat_search(VALUE pat, VALUE str, long pos, int set_backref_str) * sub!(pattern, replacement) -> self or nil * sub!(pattern) {|match| ... } -> self or nil * - * Replaces the first occurrence (not all occurrences) of the given +pattern+ - * on +self+; returns +self+ if a replacement occurred, +nil+ otherwise. + * Like String#sub, except that: * - * See {Substitution Methods}[rdoc-ref:String@Substitution+Methods]. - * - * Related: String#sub, String#gsub, String#gsub!. + * - Changes are made to +self+, not to copy of +self+. + * - Returns +self+ if any changes are made, +nil+ otherwise. * + * Related: see {Modifying}[rdoc-ref:String@Modifying]. */ static VALUE @@ -6308,9 +6237,11 @@ rb_str_sub_bang(int argc, VALUE *argv, VALUE str) } else { repl = argv[1]; - hash = rb_check_hash_type(argv[1]); - if (NIL_P(hash)) { - StringValue(repl); + if (!RB_TYPE_P(repl, T_STRING)) { + hash = rb_check_hash_type(repl); + if (NIL_P(hash)) { + StringValue(repl); + } } } @@ -6323,20 +6254,18 @@ rb_str_sub_bang(int argc, VALUE *argv, VALUE str) int cr = ENC_CODERANGE(str); long beg0, end0; VALUE match, match0 = Qnil; - struct re_registers *regs; char *p, *rp; long len, rlen; match = rb_backref_get(); - regs = RMATCH_REGS(match); if (RB_TYPE_P(pat, T_STRING)) { beg0 = beg; end0 = beg0 + RSTRING_LEN(pat); match0 = pat; } else { - beg0 = BEG(0); - end0 = END(0); + beg0 = RMATCH_BEG(match, 0); + end0 = RMATCH_END(match, 0); if (iter) match0 = rb_reg_nth_match(0, match); } @@ -6354,7 +6283,7 @@ rb_str_sub_bang(int argc, VALUE *argv, VALUE str) rb_check_frozen(str); } else { - repl = rb_reg_regsub(repl, str, regs, RB_TYPE_P(pat, T_STRING) ? Qnil : pat); + repl = rb_reg_regsub_match(repl, str, match); } enc = rb_enc_compatible(str, repl); @@ -6409,13 +6338,7 @@ rb_str_sub_bang(int argc, VALUE *argv, VALUE str) * sub(pattern, replacement) -> new_string * sub(pattern) {|match| ... } -> new_string * - * Returns a copy of +self+ with only the first occurrence - * (not all occurrences) of the given +pattern+ replaced. - * - * See {Substitution Methods}[rdoc-ref:String@Substitution+Methods]. - * - * Related: String#sub!, String#gsub, String#gsub!. - * + * :include: doc/string/sub.rdoc */ static VALUE @@ -6444,15 +6367,17 @@ str_gsub(int argc, VALUE *argv, VALUE str, int bang) break; case 2: repl = argv[1]; - hash = rb_check_hash_type(argv[1]); - if (NIL_P(hash)) { - StringValue(repl); - } - else if (rb_hash_default_unredefined(hash) && !FL_TEST_RAW(hash, RHASH_PROC_DEFAULT)) { - mode = FAST_MAP; - } - else { - mode = MAP; + if (!RB_TYPE_P(repl, T_STRING)) { + hash = rb_check_hash_type(repl); + if (NIL_P(hash)) { + StringValue(repl); + } + else if (rb_hash_default_unredefined(hash) && !FL_TEST_RAW(hash, RHASH_PROC_DEFAULT)) { + mode = FAST_MAP; + } + else { + mode = MAP; + } } break; default: @@ -6466,6 +6391,7 @@ str_gsub(int argc, VALUE *argv, VALUE str, int bang) if (bang) return Qnil; /* no match, no substitution */ return str_duplicate(rb_cString, str); } + if (bang) str_modify_keep_cr(str); offset = 0; blen = RSTRING_LEN(str) + 30; /* len + margin */ @@ -6478,15 +6404,14 @@ str_gsub(int argc, VALUE *argv, VALUE str, int bang) ENC_CODERANGE_SET(dest, rb_enc_asciicompat(str_enc) ? ENC_CODERANGE_7BIT : ENC_CODERANGE_VALID); do { - struct re_registers *regs = RMATCH_REGS(match); if (RB_TYPE_P(pat, T_STRING)) { beg0 = beg; end0 = beg0 + RSTRING_LEN(pat); match0 = pat; } else { - beg0 = BEG(0); - end0 = END(0); + beg0 = RMATCH_BEG(match, 0); + end0 = RMATCH_END(match, 0); if (mode == ITER) match0 = rb_reg_nth_match(0, match); } @@ -6495,7 +6420,7 @@ str_gsub(int argc, VALUE *argv, VALUE str, int bang) val = rb_obj_as_string(rb_yield(match0)); } else { - struct RString fake_str; + struct RString fake_str = {RBASIC_INIT}; VALUE key; if (mode == FAST_MAP) { // It is safe to use a fake_str here because we established that it won't escape, @@ -6515,7 +6440,7 @@ str_gsub(int argc, VALUE *argv, VALUE str, int bang) } } else if (need_backref_str) { - val = rb_reg_regsub(repl, str, regs, RB_TYPE_P(pat, T_STRING) ? Qnil : pat); + val = rb_reg_regsub_match(repl, str, match); if (need_backref_str < 0) { need_backref_str = val != repl; } @@ -6576,21 +6501,18 @@ str_gsub(int argc, VALUE *argv, VALUE str, int bang) * gsub!(pattern) {|match| ... } -> self or nil * gsub!(pattern) -> an_enumerator * - * Performs the specified substring replacement(s) on +self+; - * returns +self+ if any replacement occurred, +nil+ otherwise. + * Like String#gsub, except that: * - * See {Substitution Methods}[rdoc-ref:String@Substitution+Methods]. - * - * Returns an Enumerator if no +replacement+ and no block given. - * - * Related: String#sub, String#gsub, String#sub!. + * - Performs substitutions in +self+ (not in a copy of +self+). + * - Returns +self+ if any characters are removed, +nil+ otherwise. * + * Related: see {Modifying}[rdoc-ref:String@Modifying]. */ static VALUE rb_str_gsub_bang(int argc, VALUE *argv, VALUE str) { - str_modify_keep_cr(str); + str_modifiable(str); return str_gsub(argc, argv, str, 1); } @@ -6601,14 +6523,41 @@ rb_str_gsub_bang(int argc, VALUE *argv, VALUE str) * gsub(pattern) {|match| ... } -> new_string * gsub(pattern) -> enumerator * - * Returns a copy of +self+ with all occurrences of the given +pattern+ replaced. + * Returns a copy of +self+ with zero or more substrings replaced. + * + * Argument +pattern+ may be a string or a Regexp; + * argument +replacement+ may be a string or a Hash. + * Varying types for the argument values makes this method very versatile. + * + * Below are some simple examples; + * for many more examples, see {Substitution Methods}[rdoc-ref:String@Substitution+Methods]. + * + * With arguments +pattern+ and string +replacement+ given, + * replaces each matching substring with the given +replacement+ string: + * + * s = 'abracadabra' + * s.gsub('ab', 'AB') # => "ABracadABra" + * s.gsub(/[a-c]/, 'X') # => "XXrXXXdXXrX" * - * See {Substitution Methods}[rdoc-ref:String@Substitution+Methods]. + * With arguments +pattern+ and hash +replacement+ given, + * replaces each matching substring with a value from the given +replacement+ hash, + * or removes it: * - * Returns an Enumerator if no +replacement+ and no block given. + * h = {'a' => 'A', 'b' => 'B', 'c' => 'C'} + * s.gsub(/[a-c]/, h) # => "ABrACAdABrA" # 'a', 'b', 'c' replaced. + * s.gsub(/[a-d]/, h) # => "ABrACAABrA" # 'd' removed. * - * Related: String#sub, String#sub!, String#gsub!. + * With argument +pattern+ and a block given, + * calls the block with each matching substring; + * replaces that substring with the block's return value: * + * s.gsub(/[a-d]/) {|substring| substring.upcase } + * # => "ABrACADABrA" + * + * With argument +pattern+ and no block given, + * returns a new Enumerator. + * + * Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. */ static VALUE @@ -6622,11 +6571,13 @@ rb_str_gsub(int argc, VALUE *argv, VALUE str) * call-seq: * replace(other_string) -> self * - * Replaces the contents of +self+ with the contents of +other_string+: + * Replaces the contents of +self+ with the contents of +other_string+; + * returns +self+: * * s = 'foo' # => "foo" * s.replace('bar') # => "bar" * + * Related: see {Modifying}[rdoc-ref:String@Modifying]. */ VALUE @@ -6705,13 +6656,14 @@ rb_str_getbyte(VALUE str, VALUE index) * call-seq: * setbyte(index, integer) -> integer * - * Sets the byte at zero-based +index+ to +integer+; returns +integer+: + * Sets the byte at zero-based offset +index+ to the value of the given +integer+; + * returns +integer+: * - * s = 'abcde' # => "abcde" - * s.setbyte(0, 98) # => 98 - * s # => "bbcde" + * s = 'xyzzy' + * s.setbyte(2, 129) # => 129 + * s # => "xy\x81zy" * - * Related: String#getbyte. + * Related: see {Modifying}[rdoc-ref:String@Modifying]. */ VALUE rb_str_setbyte(VALUE str, VALUE index, VALUE value) @@ -6957,12 +6909,16 @@ rb_str_bytesplice(int argc, VALUE *argv, VALUE str) /* * call-seq: - * reverse -> string + * reverse -> new_string * * Returns a new string with the characters from +self+ in reverse order. * - * 'stressed'.reverse # => "desserts" + * 'drawer'.reverse # => "reward" + * 'reviled'.reverse # => "deliver" + * 'stressed'.reverse # => "desserts" + * 'semordnilaps'.reverse # => "spalindromes" * + * Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. */ static VALUE @@ -7022,10 +6978,12 @@ rb_str_reverse(VALUE str) * * Returns +self+ with its characters reversed: * - * s = 'stressed' - * s.reverse! # => "desserts" - * s # => "desserts" + * 'drawer'.reverse! # => "reward" + * 'reviled'.reverse! # => "deliver" + * 'stressed'.reverse! # => "desserts" + * 'semordnilaps'.reverse! # => "spalindromes" * + * Related: see {Modifying}[rdoc-ref:String@Modifying]. */ static VALUE @@ -7059,13 +7017,17 @@ rb_str_reverse_bang(VALUE str) * call-seq: * include?(other_string) -> true or false * - * Returns +true+ if +self+ contains +other_string+, +false+ otherwise: + * Returns whether +self+ contains +other_string+: * - * s = 'foo' - * s.include?('f') # => true - * s.include?('fo') # => true - * s.include?('food') # => false + * s = 'bar' + * s.include?('ba') # => true + * s.include?('ar') # => true + * s.include?('bar') # => true + * s.include?('a') # => true + * s.include?('') # => true + * s.include?('foo') # => false * + * Related: see {Querying}[rdoc-ref:String@Querying]. */ VALUE @@ -7085,12 +7047,13 @@ rb_str_include(VALUE str, VALUE arg) * to_i(base = 10) -> integer * * Returns the result of interpreting leading characters in +self+ - * as an integer in the given +base+ (which must be in (0, 2..36)): + * as an integer in the given +base+; + * +base+ must be either +0+ or in range <tt>(2..36)</tt>: * * '123456'.to_i # => 123456 * '123def'.to_i(16) # => 1195503 * - * With +base+ zero, string +object+ may contain leading characters + * With +base+ zero given, string +object+ may contain leading characters * to specify the actual base: * * '123def'.to_i(0) # => 123 @@ -7110,6 +7073,7 @@ rb_str_include(VALUE str, VALUE arg) * 'abcdef'.to_i # => 0 * '2'.to_i(2) # => 0 * + * Related: see {Converting to Non-String}[rdoc-ref:String@Converting+to+Non-String]. */ static VALUE @@ -7133,7 +7097,7 @@ rb_str_to_i(int argc, VALUE *argv, VALUE str) * '3.14159'.to_f # => 3.14159 * '1.234e-2'.to_f # => 0.01234 * - * Characters past a leading valid number (in the given +base+) are ignored: + * Characters past a leading valid number are ignored: * * '3.14 (pi to two places)'.to_f # => 3.14 * @@ -7141,6 +7105,7 @@ rb_str_to_i(int argc, VALUE *argv, VALUE str) * * 'abcdef'.to_f # => 0.0 * + * See {Converting to Non-String}[rdoc-ref:String@Converting+to+Non-String]. */ static VALUE @@ -7152,10 +7117,12 @@ rb_str_to_f(VALUE str) /* * call-seq: - * to_s -> self or string + * to_s -> self or new_string * * Returns +self+ if +self+ is a +String+, * or +self+ converted to a +String+ if +self+ is a subclass of +String+. + * + * Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. */ static VALUE @@ -7284,16 +7251,26 @@ rb_str_escape(VALUE str) return result; } +/* Lookup table for the inspect fast path. 1 marks bytes that need + * no escaping. 0 marks bytes that need escape inspection: 0x00-0x1F + * (control), 0x22 ("), 0x23 (#), 0x5C (\), 0x7F (DEL), 0x80-0xFF + * (non-ASCII). */ +static const bool inspect_no_escape[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x0F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x1F */ + 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x20-0x2F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x30-0x3F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40-0x4F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 0x50-0x5F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60-0x6F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 0x70-0x7F */ +}; + /* * call-seq: * inspect -> string * - * Returns a printable version of +self+, enclosed in double-quotes, - * and with special characters escaped: - * - * s = "foo\tbar\tbaz\n" - * s.inspect - * # => "\"foo\\tbar\\tbaz\\n\"" + * :include: doc/string/inspect.rdoc * */ @@ -7304,10 +7281,11 @@ rb_str_inspect(VALUE str) rb_encoding *enc = rb_enc_from_index(encidx); const char *p, *pend, *prev; char buf[CHAR_ESC_LEN + 1]; - VALUE result = rb_str_buf_new(0); + VALUE result = rb_str_buf_new(RSTRING_LEN(str) + 2); /* string content + surrounding quotes */ rb_encoding *resenc = rb_default_internal_encoding(); int unicode_p = rb_enc_unicode_p(enc); int asciicompat = rb_enc_asciicompat(enc); + int cr = rb_enc_str_coderange(str); if (resenc == NULL) resenc = rb_default_external_encoding(); if (!rb_enc_asciicompat(resenc)) resenc = rb_usascii_encoding(); @@ -7320,6 +7298,15 @@ rb_str_inspect(VALUE str) unsigned int c, cc; int n; + /* Fast path: bulk-skip runs of safe ASCII bytes via a lookup table. + * Only well-formed strings (CR=7BIT for any encoding, or UTF-8 VALID) + * are eligible. */ + if (cr == ENC_CODERANGE_7BIT || + (encidx == ENCINDEX_UTF_8 && cr == ENC_CODERANGE_VALID)) { + while (p < pend && inspect_no_escape[(unsigned char)*p]) p++; + if (p >= pend) break; + } + n = rb_enc_precise_mbclen(p, pend, enc); if (!MBCLEN_CHARFOUND_P(n)) { if (p > prev) str_buf_cat(result, prev, p - prev); @@ -7660,10 +7647,6 @@ undump_after_backslash(VALUE undumped, const char **ss, const char *s_end, rb_en } break; case 'x': - if (*utf8) { - rb_raise(rb_eRuntimeError, "hex escape and Unicode escape are mixed"); - } - *binary = true; if (++s >= s_end) { rb_raise(rb_eRuntimeError, "invalid hex escape"); } @@ -7671,6 +7654,12 @@ undump_after_backslash(VALUE undumped, const char **ss, const char *s_end, rb_en if (hexlen != 2) { rb_raise(rb_eRuntimeError, "invalid hex escape"); } + if (!ISASCII(*buf)) { + if (*utf8) { + rb_raise(rb_eRuntimeError, "hex escape and Unicode escape are mixed"); + } + *binary = true; + } rb_str_cat(undumped, (char *)buf, 1); s += hexlen; break; @@ -7686,17 +7675,11 @@ static VALUE rb_str_is_ascii_only_p(VALUE str); /* * call-seq: - * undump -> string - * - * Returns an unescaped version of +self+: - * - * s_orig = "\f\x00\xff\\\"" # => "\f\u0000\xFF\\\"" - * s_dumped = s_orig.dump # => "\"\\f\\x00\\xFF\\\\\\\"\"" - * s_undumped = s_dumped.undump # => "\f\u0000\xFF\\\"" - * s_undumped == s_orig # => true + * undump -> new_string * - * Related: String#dump (inverse of String#undump). + * Inverse of String#dump; returns a copy of +self+ with changes of the kinds made by String#dump "undone." * + * Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. */ static VALUE @@ -7877,7 +7860,7 @@ mapping_buffer_free(void *p) while (current_buffer) { previous_buffer = current_buffer; current_buffer = current_buffer->next; - ruby_sized_xfree(previous_buffer, previous_buffer->capa); + ruby_xfree_sized(previous_buffer, offsetof(mapping_buffer, space) + previous_buffer->capa); } } @@ -8023,19 +8006,12 @@ upcase_single(VALUE str) * call-seq: * upcase!(mapping) -> self or nil * - * Upcases the characters in +self+; - * returns +self+ if any changes were made, +nil+ otherwise: - * - * s = 'Hello World!' # => "Hello World!" - * s.upcase! # => "HELLO WORLD!" - * s # => "HELLO WORLD!" - * s.upcase! # => nil - * - * The casing may be affected by the given +mapping+; - * see {Case Mapping}[rdoc-ref:case_mapping.rdoc]. + * Like String#upcase, except that: * - * Related: String#upcase, String#downcase, String#downcase!. + * - Changes character casings in +self+ (not in a copy of +self+). + * - Returns +self+ if any changes are made, +nil+ otherwise. * + * Related: See {Modifying}[rdoc-ref:String@Modifying]. */ static VALUE @@ -8063,18 +8039,9 @@ rb_str_upcase_bang(int argc, VALUE *argv, VALUE str) /* * call-seq: - * upcase(mapping) -> string - * - * Returns a string containing the upcased characters in +self+: - * - * s = 'Hello World!' # => "Hello World!" - * s.upcase # => "HELLO WORLD!" - * - * The casing may be affected by the given +mapping+; - * see {Case Mapping}[rdoc-ref:case_mapping.rdoc]. - * - * Related: String#upcase!, String#downcase, String#downcase!. + * upcase(mapping = :ascii) -> new_string * + * :include: doc/string/upcase.rdoc */ static VALUE @@ -8158,7 +8125,7 @@ rb_str_downcase_bang(int argc, VALUE *argv, VALUE str) /* * call-seq: - * downcase(mapping) -> string + * downcase(mapping = :ascii) -> new_string * * :include: doc/string/downcase.rdoc * @@ -8224,29 +8191,10 @@ rb_str_capitalize_bang(int argc, VALUE *argv, VALUE str) /* * call-seq: - * capitalize(mapping = :ascii) -> string - * - * Returns a string containing the characters in +self+, - * each with possibly changed case: - * - * - The first character is upcased. - * - All other characters are downcased. - * - * Examples: - * - * 'hello world'.capitalize # => "Hello world" - * 'HELLO WORLD'.capitalize # => "Hello world" - * - * Some characters do not have upcase and downcase, and so are not changed; - * see {Case Mapping}[rdoc-ref:case_mapping.rdoc]: - * - * '1, 2, 3, ...'.capitalize # => "1, 2, 3, ..." + * capitalize(mapping = :ascii) -> new_string * - * The casing is affected by the given +mapping+, - * which may be +:ascii+, +:fold+, or +:turkic+; - * see {Case Mappings}[rdoc-ref:case_mapping.rdoc@Case+Mappings]. + * :include: doc/string/capitalize.rdoc * - * Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. */ static VALUE @@ -8274,20 +8222,12 @@ rb_str_capitalize(int argc, VALUE *argv, VALUE str) * call-seq: * swapcase!(mapping) -> self or nil * - * Upcases each lowercase character in +self+; - * downcases uppercase character; - * returns +self+ if any changes were made, +nil+ otherwise: - * - * s = 'Hello World!' # => "Hello World!" - * s.swapcase! # => "hELLO wORLD!" - * s # => "hELLO wORLD!" - * ''.swapcase! # => nil + * Like String#swapcase, except that: * - * The casing may be affected by the given +mapping+; - * see {Case Mapping}[rdoc-ref:case_mapping.rdoc]. - * - * Related: String#swapcase. + * - Changes are made to +self+, not to copy of +self+. + * - Returns +self+ if any changes are made, +nil+ otherwise. * + * Related: see {Modifying}[rdoc-ref:String@Modifying]. */ static VALUE @@ -8311,19 +8251,9 @@ rb_str_swapcase_bang(int argc, VALUE *argv, VALUE str) /* * call-seq: - * swapcase(mapping) -> string - * - * Returns a string containing the characters in +self+, with cases reversed; - * each uppercase character is downcased; - * each lowercase character is upcased: - * - * s = 'Hello World!' # => "Hello World!" - * s.swapcase # => "hELLO wORLD!" + * swapcase(mapping = :ascii) -> new_string * - * The casing may be affected by the given +mapping+; - * see {Case Mapping}[rdoc-ref:case_mapping.rdoc]. - * - * Related: String#swapcase!. + * :include: doc/string/swapcase.rdoc * */ @@ -8520,7 +8450,7 @@ tr_trans(VALUE str, VALUE src, VALUE repl, int sflag) int r = rb_enc_precise_mbclen((char *)s, (char *)send, e1); if (!MBCLEN_CHARFOUND_P(r)) { - xfree(buf); + SIZED_FREE_N(buf, max + termlen); rb_raise(rb_eArgError, "invalid byte sequence in %s", rb_enc_name(e1)); } clen = MBCLEN_CHARFOUND_LEN(r); @@ -8572,7 +8502,7 @@ tr_trans(VALUE str, VALUE src, VALUE repl, int sflag) t += tlen; } if (!STR_EMBED_P(str)) { - ruby_sized_xfree(STR_HEAP_PTR(str), STR_HEAP_SIZE(str)); + SIZED_FREE_N(STR_HEAP_PTR(str), STR_HEAP_SIZE(str)); } TERM_FILL((char *)t, termlen); RSTRING(str)->as.heap.ptr = (char *)buf; @@ -8608,7 +8538,7 @@ tr_trans(VALUE str, VALUE src, VALUE repl, int sflag) int r = rb_enc_precise_mbclen((char *)s, (char *)send, e1); if (!MBCLEN_CHARFOUND_P(r)) { - xfree(buf); + SIZED_FREE_N(buf, max + termlen); rb_raise(rb_eArgError, "invalid byte sequence in %s", rb_enc_name(e1)); } clen = MBCLEN_CHARFOUND_LEN(r); @@ -8656,7 +8586,7 @@ tr_trans(VALUE str, VALUE src, VALUE repl, int sflag) t += tlen; } if (!STR_EMBED_P(str)) { - ruby_sized_xfree(STR_HEAP_PTR(str), STR_HEAP_SIZE(str)); + SIZED_FREE_N(STR_HEAP_PTR(str), STR_HEAP_SIZE(str)); } TERM_FILL((char *)t, termlen); RSTRING(str)->as.heap.ptr = (char *)buf; @@ -8679,9 +8609,12 @@ tr_trans(VALUE str, VALUE src, VALUE repl, int sflag) * call-seq: * tr!(selector, replacements) -> self or nil * - * Like String#tr, but modifies +self+ in place. - * Returns +self+ if any changes were made, +nil+ otherwise. + * Like String#tr, except: + * + * - Performs substitutions in +self+ (not in a copy of +self+). + * - Returns +self+ if any modifications were made, +nil+ otherwise. * + * Related: {Modifying}[rdoc-ref:String@Modifying]. */ static VALUE @@ -8717,17 +8650,15 @@ rb_str_tr_bang(VALUE str, VALUE src, VALUE repl) * * Arguments +selector+ and +replacements+ must be valid character selectors * (see {Character Selectors}[rdoc-ref:character_selectors.rdoc]), - * and may use any of its valid forms, including negation, ranges, and escaping: + * and may use any of its valid forms, including negation, ranges, and escapes: * - * # Negation. - * 'hello'.tr('^aeiou', '-') # => "-e--o" - * # Ranges. - * 'ibm'.tr('b-z', 'a-z') # => "hal" - * # Escapes. + * 'hello'.tr('^aeiou', '-') # => "-e--o" # Negation. + * 'ibm'.tr('b-z', 'a-z') # => "hal" # Range. * 'hel^lo'.tr('\^aeiou', '-') # => "h-l-l-" # Escaped leading caret. * 'i-b-m'.tr('b\-z', 'a-z') # => "ibabm" # Escaped embedded hyphen. * 'foo\\bar'.tr('ab\\', 'XYZ') # => "fooZYXr" # Escaped backslash. * + * Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. */ static VALUE @@ -8919,8 +8850,12 @@ rb_str_delete(int argc, VALUE *argv, VALUE str) * call-seq: * squeeze!(*selectors) -> self or nil * - * Like String#squeeze, but modifies +self+ in place. - * Returns +self+ if any changes were made, +nil+ otherwise. + * Like String#squeeze, except that: + * + * - Characters are squeezed in +self+ (not in a copy of +self+). + * - Returns +self+ if any changes are made, +nil+ otherwise. + * + * Related: See {Modifying}[rdoc-ref:String@Modifying]. */ static VALUE @@ -9003,16 +8938,7 @@ rb_str_squeeze_bang(int argc, VALUE *argv, VALUE str) * call-seq: * squeeze(*selectors) -> new_string * - * Returns a copy of +self+ with characters specified by +selectors+ "squeezed" - * (see {Multiple Character Selectors}[rdoc-ref:character_selectors.rdoc@Multiple+Character+Selectors]): - * - * "Squeezed" means that each multiple-character run of a selected character - * is squeezed down to a single character; - * with no arguments given, squeezes all characters: - * - * "yellow moon".squeeze #=> "yelow mon" - * " now is the".squeeze(" ") #=> " now is the" - * "putters shoot balls".squeeze("m-z") #=> "puters shot balls" + * :include: doc/string/squeeze.rdoc * */ @@ -9029,10 +8955,12 @@ rb_str_squeeze(int argc, VALUE *argv, VALUE str) * call-seq: * tr_s!(selector, replacements) -> self or nil * - * Like String#tr_s, but modifies +self+ in place. - * Returns +self+ if any changes were made, +nil+ otherwise. + * Like String#tr_s, except: * - * Related: String#squeeze!. + * - Modifies +self+ in place (not a copy of +self+). + * - Returns +self+ if any changes were made, +nil+ otherwise. + * + * Related: {Modifying}[rdoc-ref:String@Modifying]. */ static VALUE @@ -9044,16 +8972,21 @@ rb_str_tr_s_bang(VALUE str, VALUE src, VALUE repl) /* * call-seq: - * tr_s(selector, replacements) -> string + * tr_s(selector, replacements) -> new_string + * + * Like String#tr, except: * - * Like String#tr, but also squeezes the modified portions of the translated string; - * returns a new string (translated and squeezed). + * - Also squeezes the modified portions of the translated string; + * see String#squeeze. + * - Returns the translated and squeezed string. + * + * Examples: * * 'hello'.tr_s('l', 'r') #=> "hero" * 'hello'.tr_s('el', '-') #=> "h-o" * 'hello'.tr_s('el', 'hx') #=> "hhxo" * - * Related: String#squeeze. + * Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. * */ @@ -9233,7 +9166,7 @@ literal_split_pattern(VALUE spat, split_type_t default_type) /* * call-seq: - * split(field_sep = $;, limit = 0) -> array + * split(field_sep = $;, limit = 0) -> array_of_substrings * split(field_sep = $;, limit = 0) {|substring| ... } -> self * * :include: doc/string/split.rdoc @@ -9415,18 +9348,16 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str) if (result) result = rb_ary_new(); long len = RSTRING_LEN(str); long start = beg; - long idx; + int idx; int last_null = 0; - struct re_registers *regs; VALUE match = 0; for (; rb_reg_search(spat, str, start, 0) >= 0; (match ? (rb_match_unbusy(match), rb_backref_set(match)) : (void)0)) { match = rb_backref_get(); if (!result) rb_match_busy(match); - regs = RMATCH_REGS(match); - end = BEG(0); - if (start == end && BEG(0) == END(0)) { + end = RMATCH_BEG(match, 0); + if (start == end && RMATCH_BEG(match, 0) == RMATCH_END(match, 0)) { if (!ptr) { SPLIT_STR(0, 0); break; @@ -9446,13 +9377,13 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str) } else { SPLIT_STR(beg, end-beg); - beg = start = END(0); + beg = start = RMATCH_END(match, 0); } last_null = 0; - for (idx=1; idx < regs->num_regs; idx++) { - if (BEG(idx) == -1) continue; - SPLIT_STR(BEG(idx), END(idx)-BEG(idx)); + for (idx = 1; idx < RMATCH_NREGS(match); idx++) { + if (RMATCH_BEG(match, idx) == -1) continue; + SPLIT_STR(RMATCH_BEG(match, idx), RMATCH_END(match, idx) - RMATCH_BEG(match, idx)); } if (!NIL_P(limit) && lim <= ++i) break; } @@ -9678,11 +9609,53 @@ rb_str_each_line(int argc, VALUE *argv, VALUE str) /* * call-seq: - * lines(Line_sep = $/, chomp: false) -> array_of_strings + * lines(record_separator = $/, chomp: false) -> array_of_strings + * + * Returns substrings ("lines") of +self+ + * according to the given arguments: + * + * s = <<~EOT + * This is the first line. + * This is line two. + * + * This is line four. + * This is line five. + * EOT + * + * With the default argument values: + * + * $/ # => "\n" + * s.lines + * # => + * ["This is the first line.\n", + * "This is line two.\n", + * "\n", + * "This is line four.\n", + * "This is line five.\n"] + * + * With a different +record_separator+: + * + * record_separator = ' is ' + * s.lines(record_separator) + * # => + * ["This is ", + * "the first line.\nThis is ", + * "line two.\n\nThis is ", + * "line four.\nThis is ", + * "line five.\n"] * - * Forms substrings ("lines") of +self+ according to the given arguments - * (see String#each_line for details); returns the lines in an array. + * With keyword argument +chomp+ as +true+, + * removes the trailing newline from each line: * + * s.lines(chomp: true) + * # => + * ["This is the first line.", + * "This is line two.", + * "", + * "This is line four.", + * "This is line five."] + * + * Related: see {Converting to Non-String}[rdoc-ref:String@Converting+to+Non-String]. */ static VALUE @@ -9820,6 +9793,7 @@ rb_str_enumerate_codepoints(VALUE str, VALUE ary) unsigned int c; const char *ptr, *end; rb_encoding *enc; + int enc_asciicompat; if (single_byte_optimizable(str)) return rb_str_enumerate_bytes(str, ary); @@ -9828,9 +9802,15 @@ rb_str_enumerate_codepoints(VALUE str, VALUE ary) ptr = RSTRING_PTR(str); end = RSTRING_END(str); enc = STR_ENC_GET(str); + enc_asciicompat = rb_enc_asciicompat(enc); while (ptr < end) { - c = rb_enc_codepoint_len(ptr, end, &n, enc); + /* Fast path: ASCII byte in an ASCII-compatible encoding is its own codepoint; + * skip rb_enc_codepoint_len and return the byte directly. + */ + n = 1; + c = (enc_asciicompat && ISASCII(*ptr)) ? + (unsigned char)*ptr : rb_enc_codepoint_len(ptr, end, &n, enc); ENUM_ELEM(ary, UINT2NUM(c)); ptr += n; } @@ -10282,6 +10262,22 @@ rb_str_chomp(int argc, VALUE *argv, VALUE str) return rb_str_subseq(str, 0, chompped_length(str, rs)); } +static void +tr_setup_table_multi(char table[TR_TABLE_SIZE], VALUE *tablep, VALUE *ctablep, + VALUE str, int num_selectors, VALUE *selectors) +{ + int i; + + for (i=0; i<num_selectors; i++) { + VALUE selector = selectors[i]; + rb_encoding *enc; + + StringValue(selector); + enc = rb_enc_check(str, selector); + tr_setup_table(selector, table, i==0, tablep, ctablep, enc); + } +} + static long lstrip_offset(VALUE str, const char *s, const char *e, rb_encoding *enc) { @@ -10305,18 +10301,39 @@ lstrip_offset(VALUE str, const char *s, const char *e, rb_encoding *enc) return s - start; } +static long +lstrip_offset_table(VALUE str, const char *s, const char *e, rb_encoding *enc, + char table[TR_TABLE_SIZE], VALUE del, VALUE nodel) +{ + const char *const start = s; + + if (!s || s >= e) return 0; + + /* remove leading characters in the table */ + while (s < e) { + int n; + unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); + + if (!tr_find(cc, table, del, nodel)) break; + s += n; + } + return s - start; +} + /* * call-seq: - * lstrip! -> self or nil + * lstrip!(*selectors) -> self or nil + * + * Like String#lstrip, except that: * - * Like String#lstrip, except that any modifications are made in +self+; - * returns +self+ if any modification are made, +nil+ otherwise. + * - Performs stripping in +self+ (not in a copy of +self+). + * - Returns +self+ if any characters are stripped, +nil+ otherwise. * - * Related: String#rstrip!, String#strip!. + * Related: see {Modifying}[rdoc-ref:String@Modifying]. */ static VALUE -rb_str_lstrip_bang(VALUE str) +rb_str_lstrip_bang(int argc, VALUE *argv, VALUE str) { rb_encoding *enc; char *start, *s; @@ -10325,7 +10342,17 @@ rb_str_lstrip_bang(VALUE str) str_modify_keep_cr(str); enc = STR_ENC_GET(str); RSTRING_GETMEM(str, start, olen); - loffset = lstrip_offset(str, start, start+olen, enc); + if (argc > 0) { + char table[TR_TABLE_SIZE]; + VALUE del = 0, nodel = 0; + + tr_setup_table_multi(table, &del, &nodel, str, argc, argv); + loffset = lstrip_offset_table(str, start, start+olen, enc, table, del, nodel); + } + else { + loffset = lstrip_offset(str, start, start+olen, enc); + } + if (loffset > 0) { long len = olen-loffset; s = start + loffset; @@ -10340,26 +10367,48 @@ rb_str_lstrip_bang(VALUE str) /* * call-seq: - * lstrip -> new_string + * lstrip(*selectors) -> new_string * * Returns a copy of +self+ with leading whitespace removed; * see {Whitespace in Strings}[rdoc-ref:String@Whitespace+in+Strings]: * * whitespace = "\x00\t\n\v\f\r " * s = whitespace + 'abc' + whitespace - * s # => "\u0000\t\n\v\f\r abc\u0000\t\n\v\f\r " - * s.lstrip # => "abc\u0000\t\n\v\f\r " + * # => "\u0000\t\n\v\f\r abc\u0000\t\n\v\f\r " + * s.lstrip + * # => "abc\u0000\t\n\v\f\r " + * + * If +selectors+ are given, removes characters of +selectors+ from the beginning of +self+: + * + * s = "---abc+++" + * s.lstrip("-") # => "abc+++" * - * Related: String#rstrip, String#strip. + * +selectors+ must be valid character selectors (see {Character Selectors}[rdoc-ref:character_selectors.rdoc]), + * and may use any of its valid forms, including negation, ranges, and escapes: + * + * "01234abc56789".lstrip("0-9") # "abc56789" + * "01234abc56789".lstrip("0-9", "^4-6") # "4abc56789" + * + * Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. */ static VALUE -rb_str_lstrip(VALUE str) +rb_str_lstrip(int argc, VALUE *argv, VALUE str) { char *start; long len, loffset; + RSTRING_GETMEM(str, start, len); - loffset = lstrip_offset(str, start, start+len, STR_ENC_GET(str)); + if (argc > 0) { + char table[TR_TABLE_SIZE]; + VALUE del = 0, nodel = 0; + + tr_setup_table_multi(table, &del, &nodel, str, argc, argv); + loffset = lstrip_offset_table(str, start, start+len, STR_ENC_GET(str), table, del, nodel); + } + else { + loffset = lstrip_offset(str, start, start+len, STR_ENC_GET(str)); + } if (loffset <= 0) return str_duplicate(rb_cString, str); return rb_str_subseq(str, loffset, len - loffset); } @@ -10393,18 +10442,44 @@ rstrip_offset(VALUE str, const char *s, const char *e, rb_encoding *enc) return e - t; } +static long +rstrip_offset_table(VALUE str, const char *s, const char *e, rb_encoding *enc, + char table[TR_TABLE_SIZE], VALUE del, VALUE nodel) +{ + const char *t; + char *tp; + + rb_str_check_dummy_enc(enc); + if (rb_enc_str_coderange(str) == ENC_CODERANGE_BROKEN) { + rb_raise(rb_eEncCompatError, "invalid byte sequence in %s", rb_enc_name(enc)); + } + if (!s || s >= e) return 0; + t = e; + + /* remove trailing characters in the table */ + while ((tp = rb_enc_prev_char(s, t, e, enc)) != NULL) { + unsigned int c = rb_enc_codepoint(tp, e, enc); + if (!tr_find(c, table, del, nodel)) break; + t = tp; + } + + return e - t; +} + /* * call-seq: - * rstrip! -> self or nil + * rstrip!(*selectors) -> self or nil * - * Like String#rstrip, except that any modifications are made in +self+; - * returns +self+ if any modification are made, +nil+ otherwise. + * Like String#rstrip, except that: * - * Related: String#lstrip!, String#strip!. + * - Performs stripping in +self+ (not in a copy of +self+). + * - Returns +self+ if any characters are stripped, +nil+ otherwise. + * + * Related: see {Modifying}[rdoc-ref:String@Modifying]. */ static VALUE -rb_str_rstrip_bang(VALUE str) +rb_str_rstrip_bang(int argc, VALUE *argv, VALUE str) { rb_encoding *enc; char *start; @@ -10413,7 +10488,16 @@ rb_str_rstrip_bang(VALUE str) str_modify_keep_cr(str); enc = STR_ENC_GET(str); RSTRING_GETMEM(str, start, olen); - roffset = rstrip_offset(str, start, start+olen, enc); + if (argc > 0) { + char table[TR_TABLE_SIZE]; + VALUE del = 0, nodel = 0; + + tr_setup_table_multi(table, &del, &nodel, str, argc, argv); + roffset = rstrip_offset_table(str, start, start+olen, enc, table, del, nodel); + } + else { + roffset = rstrip_offset(str, start, start+olen, enc); + } if (roffset > 0) { long len = olen - roffset; @@ -10427,9 +10511,9 @@ rb_str_rstrip_bang(VALUE str) /* * call-seq: - * rstrip -> new_string + * rstrip(*selectors) -> new_string * - * Returns a copy of the receiver with trailing whitespace removed; + * Returns a copy of +self+ with trailing whitespace removed; * see {Whitespace in Strings}[rdoc-ref:String@Whitespace+in+Strings]: * * whitespace = "\x00\t\n\v\f\r " @@ -10437,11 +10521,22 @@ rb_str_rstrip_bang(VALUE str) * s # => "\u0000\t\n\v\f\r abc\u0000\t\n\v\f\r " * s.rstrip # => "\u0000\t\n\v\f\r abc" * - * Related: String#lstrip, String#strip. + * If +selectors+ are given, removes characters of +selectors+ from the end of +self+: + * + * s = "---abc+++" + * s.rstrip("+") # => "---abc" + * + * +selectors+ must be valid character selectors (see {Character Selectors}[rdoc-ref:character_selectors.rdoc]), + * and may use any of its valid forms, including negation, ranges, and escapes: + * + * "01234abc56789".rstrip("0-9") # "01234abc" + * "01234abc56789".rstrip("0-9", "^4-6") # "01234abc56" + * + * Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. */ static VALUE -rb_str_rstrip(VALUE str) +rb_str_rstrip(int argc, VALUE *argv, VALUE str) { rb_encoding *enc; char *start; @@ -10449,8 +10544,16 @@ rb_str_rstrip(VALUE str) enc = STR_ENC_GET(str); RSTRING_GETMEM(str, start, olen); - roffset = rstrip_offset(str, start, start+olen, enc); + if (argc > 0) { + char table[TR_TABLE_SIZE]; + VALUE del = 0, nodel = 0; + tr_setup_table_multi(table, &del, &nodel, str, argc, argv); + roffset = rstrip_offset_table(str, start, start+olen, enc, table, del, nodel); + } + else { + roffset = rstrip_offset(str, start, start+olen, enc); + } if (roffset <= 0) return str_duplicate(rb_cString, str); return rb_str_subseq(str, 0, olen-roffset); } @@ -10458,16 +10561,18 @@ rb_str_rstrip(VALUE str) /* * call-seq: - * strip! -> self or nil + * strip!(*selectors) -> self or nil + * + * Like String#strip, except that: * - * Like String#strip, except that any modifications are made in +self+; - * returns +self+ if any modification are made, +nil+ otherwise. + * - Any modifications are made to +self+. + * - Returns +self+ if any modification are made, +nil+ otherwise. * - * Related: String#lstrip!, String#strip!. + * Related: see {Modifying}[rdoc-ref:String@Modifying]. */ static VALUE -rb_str_strip_bang(VALUE str) +rb_str_strip_bang(int argc, VALUE *argv, VALUE str) { char *start; long olen, loffset, roffset; @@ -10476,8 +10581,19 @@ rb_str_strip_bang(VALUE str) str_modify_keep_cr(str); enc = STR_ENC_GET(str); RSTRING_GETMEM(str, start, olen); - loffset = lstrip_offset(str, start, start+olen, enc); - roffset = rstrip_offset(str, start+loffset, start+olen, enc); + + if (argc > 0) { + char table[TR_TABLE_SIZE]; + VALUE del = 0, nodel = 0; + + tr_setup_table_multi(table, &del, &nodel, str, argc, argv); + loffset = lstrip_offset_table(str, start, start+olen, enc, table, del, nodel); + roffset = rstrip_offset_table(str, start+loffset, start+olen, enc, table, del, nodel); + } + else { + loffset = lstrip_offset(str, start, start+olen, enc); + roffset = rstrip_offset(str, start+loffset, start+olen, enc); + } if (loffset > 0 || roffset > 0) { long len = olen-roffset; @@ -10495,29 +10611,52 @@ rb_str_strip_bang(VALUE str) /* * call-seq: - * strip -> new_string + * strip(*selectors) -> new_string * - * Returns a copy of the receiver with leading and trailing whitespace removed; + * Returns a copy of +self+ with leading and trailing whitespace removed; * see {Whitespace in Strings}[rdoc-ref:String@Whitespace+in+Strings]: * * whitespace = "\x00\t\n\v\f\r " * s = whitespace + 'abc' + whitespace - * s # => "\u0000\t\n\v\f\r abc\u0000\t\n\v\f\r " + * # => "\u0000\t\n\v\f\r abc\u0000\t\n\v\f\r " * s.strip # => "abc" * - * Related: String#lstrip, String#rstrip. + * If +selectors+ are given, removes characters of +selectors+ from both ends of +self+: + * + * s = "---abc+++" + * s.strip("-+") # => "abc" + * s.strip("+-") # => "abc" + * + * +selectors+ must be valid character selectors (see {Character Selectors}[rdoc-ref:character_selectors.rdoc]), + * and may use any of its valid forms, including negation, ranges, and escapes: + * + * "01234abc56789".strip("0-9") # "abc" + * "01234abc56789".strip("0-9", "^4-6") # "4abc56" + * + * Related: see {Converting to New String}[rdoc-ref:String@Converting+to+New+String]. */ static VALUE -rb_str_strip(VALUE str) +rb_str_strip(int argc, VALUE *argv, VALUE str) { char *start; long olen, loffset, roffset; rb_encoding *enc = STR_ENC_GET(str); RSTRING_GETMEM(str, start, olen); - loffset = lstrip_offset(str, start, start+olen, enc); - roffset = rstrip_offset(str, start+loffset, start+olen, enc); + + if (argc > 0) { + char table[TR_TABLE_SIZE]; + VALUE del = 0, nodel = 0; + + tr_setup_table_multi(table, &del, &nodel, str, argc, argv); + loffset = lstrip_offset_table(str, start, start+olen, enc, table, del, nodel); + roffset = rstrip_offset_table(str, start+loffset, start+olen, enc, table, del, nodel); + } + else { + loffset = lstrip_offset(str, start, start+olen, enc); + roffset = rstrip_offset(str, start+loffset, start+olen, enc); + } if (loffset <= 0 && roffset <= 0) return str_duplicate(rb_cString, str); return rb_str_subseq(str, loffset, olen-loffset-roffset); @@ -10529,17 +10668,14 @@ scan_once(VALUE str, VALUE pat, long *start, int set_backref_str) VALUE result = Qnil; long end, pos = rb_pat_search(pat, str, *start, set_backref_str); if (pos >= 0) { - VALUE match; - struct re_registers *regs; + VALUE match = Qnil; if (BUILTIN_TYPE(pat) == T_STRING) { - regs = NULL; end = pos + RSTRING_LEN(pat); } else { match = rb_backref_get(); - regs = RMATCH_REGS(match); - pos = BEG(0); - end = END(0); + pos = RMATCH_BEG(match, 0); + end = RMATCH_END(match, 0); } if (pos == end) { @@ -10557,16 +10693,17 @@ scan_once(VALUE str, VALUE pat, long *start, int set_backref_str) *start = end; } - if (!regs || regs->num_regs == 1) { + if (NIL_P(match) || RMATCH_NREGS(match) == 1) { result = rb_str_subseq(str, pos, end - pos); return result; } else { - result = rb_ary_new2(regs->num_regs); - for (int i = 1; i < regs->num_regs; i++) { + int num_regs = RMATCH_NREGS(match); + result = rb_ary_new2(num_regs); + for (int i = 1; i < num_regs; i++) { VALUE s = Qnil; - if (BEG(i) >= 0) { - s = rb_str_subseq(str, BEG(i), END(i)-BEG(i)); + if (RMATCH_BEG(match, i) >= 0) { + s = rb_str_subseq(str, RMATCH_BEG(match, i), RMATCH_END(match, i) - RMATCH_BEG(match, i)); } rb_ary_push(result, s); @@ -10582,40 +10719,10 @@ scan_once(VALUE str, VALUE pat, long *start, int set_backref_str) /* * call-seq: - * scan(string_or_regexp) -> array - * scan(string_or_regexp) {|matches| ... } -> self - * - * Matches a pattern against +self+; the pattern is: - * - * - +string_or_regexp+ itself, if it is a Regexp. - * - <tt>Regexp.quote(string_or_regexp)</tt>, if +string_or_regexp+ is a string. - * - * Iterates through +self+, generating a collection of matching results: + * scan(pattern) -> array_of_results + * scan(pattern) {|result| ... } -> self * - * - If the pattern contains no groups, each result is the - * matched string, <code>$&</code>. - * - If the pattern contains groups, each result is an array - * containing one entry per group. - * - * With no block given, returns an array of the results: - * - * s = 'cruel world' - * s.scan(/\w+/) # => ["cruel", "world"] - * s.scan(/.../) # => ["cru", "el ", "wor"] - * s.scan(/(...)/) # => [["cru"], ["el "], ["wor"]] - * s.scan(/(..)(..)/) # => [["cr", "ue"], ["l ", "wo"]] - * - * With a block given, calls the block with each result; returns +self+: - * - * s.scan(/\w+/) {|w| print "<<#{w}>> " } - * print "\n" - * s.scan(/(.)(.)/) {|x,y| print y, x } - * print "\n" - * - * Output: - * - * <<cruel>> <<world>> - * rceu lowlr + * :include: doc/string/scan.rdoc * */ @@ -10657,18 +10764,46 @@ rb_str_scan(VALUE str, VALUE pat) * call-seq: * hex -> integer * - * Interprets the leading substring of +self+ as a string of hexadecimal digits - * (with an optional sign and an optional <code>0x</code>) and returns the - * corresponding number; - * returns zero if there is no such leading substring: + * Interprets the leading substring of +self+ as hexadecimal, possibly signed; + * returns its value as an integer. + * + * The leading substring is interpreted as hexadecimal when it begins with: + * + * - One or more character representing hexadecimal digits + * (each in one of the ranges <tt>'0'..'9'</tt>, <tt>'a'..'f'</tt>, or <tt>'A'..'F'</tt>); + * the string to be interpreted ends at the first character that does not represent a hexadecimal digit: + * + * 'f'.hex # => 15 + * '11'.hex # => 17 + * 'FFF'.hex # => 4095 + * 'fffg'.hex # => 4095 + * 'foo'.hex # => 15 # 'f' hexadecimal, 'oo' not. + * 'bar'.hex # => 186 # 'ba' hexadecimal, 'r' not. + * 'deadbeef'.hex # => 3735928559 + * + * - <tt>'0x'</tt> or <tt>'0X'</tt>, followed by one or more hexadecimal digits: + * + * '0xfff'.hex # => 4095 + * '0xfffg'.hex # => 4095 + * + * Any of the above may prefixed with <tt>'-'</tt>, which negates the interpreted value: + * + * '-fff'.hex # => -4095 + * '-0xFFF'.hex # => -4095 + * + * For any substring not described above, returns zero: * - * '0x0a'.hex # => 10 - * '-1234'.hex # => -4660 - * '0'.hex # => 0 - * 'non-numeric'.hex # => 0 + * 'xxx'.hex # => 0 + * ''.hex # => 0 * - * Related: String#oct. + * Note that, unlike #oct, this method interprets only hexadecimal, + * and not binary, octal, or decimal notations: * + * '0b111'.hex # => 45329 + * '0o777'.hex # => 0 + * '0d999'.hex # => 55705 + * + * Related: See {Converting to Non-String}[rdoc-ref:String@Converting+to+Non-String]. */ static VALUE @@ -10682,20 +10817,79 @@ rb_str_hex(VALUE str) * call-seq: * oct -> integer * - * Interprets the leading substring of +self+ as a string of octal digits - * (with an optional sign) and returns the corresponding number; - * returns zero if there is no such leading substring: + * Interprets the leading substring of +self+ as octal, binary, decimal, or hexadecimal, possibly signed; + * returns their value as an integer. + * + * In brief: + * + * # Interpreted as octal. + * '777'.oct # => 511 + * '777x'.oct # => 511 + * '0777'.oct # => 511 + * '0o777'.oct # => 511 + * '-777'.oct # => -511 + * # Not interpreted as octal. + * '0b111'.oct # => 7 # Interpreted as binary. + * '0d999'.oct # => 999 # Interpreted as decimal. + * '0xfff'.oct # => 4095 # Interpreted as hexadecimal. + * + * The leading substring is interpreted as octal when it begins with: + * + * - One or more character representing octal digits + * (each in the range <tt>'0'..'7'</tt>); + * the string to be interpreted ends at the first character that does not represent an octal digit: + * + * '7'.oct @ => 7 + * '11'.oct # => 9 + * '777'.oct # => 511 + * '0777'.oct # => 511 + * '7778'.oct # => 511 + * '777x'.oct # => 511 + * + * - <tt>'0o'</tt>, followed by one or more octal digits: + * + * '0o777'.oct # => 511 + * '0o7778'.oct # => 511 + * + * The leading substring is _not_ interpreted as octal when it begins with: + * + * - <tt>'0b'</tt>, followed by one or more characters representing binary digits + * (each in the range <tt>'0'..'1'</tt>); + * the string to be interpreted ends at the first character that does not represent a binary digit. + * the string is interpreted as binary digits (base 2): + * + * '0b111'.oct # => 7 + * '0b1112'.oct # => 7 + * + * - <tt>'0d'</tt>, followed by one or more characters representing decimal digits + * (each in the range <tt>'0'..'9'</tt>); + * the string to be interpreted ends at the first character that does not represent a decimal digit. + * the string is interpreted as decimal digits (base 10): + * + * '0d999'.oct # => 999 + * '0d999x'.oct # => 999 + * + * - <tt>'0x'</tt>, followed by one or more characters representing hexadecimal digits + * (each in one of the ranges <tt>'0'..'9'</tt>, <tt>'a'..'f'</tt>, or <tt>'A'..'F'</tt>); + * the string to be interpreted ends at the first character that does not represent a hexadecimal digit. + * the string is interpreted as hexadecimal digits (base 16): * - * '123'.oct # => 83 - * '-377'.oct # => -255 - * '0377non-numeric'.oct # => 255 - * 'non-numeric'.oct # => 0 + * '0xfff'.oct # => 4095 + * '0xfffg'.oct # => 4095 * - * If +self+ starts with <tt>0</tt>, radix indicators are honored; - * see Kernel#Integer. + * Any of the above may prefixed with <tt>'-'</tt>, which negates the interpreted value: * - * Related: String#hex. + * '-777'.oct # => -511 + * '-0777'.oct # => -511 + * '-0b111'.oct # => -7 + * '-0xfff'.oct # => -4095 * + * For any substring not described above, returns zero: + * + * 'foo'.oct # => 0 + * ''.oct # => 0 + * + * Related: see {Converting to Non-String}[rdoc-ref:String@Converting+to+Non-String]. */ static VALUE @@ -10956,48 +11150,48 @@ rb_str_justify(int argc, VALUE *argv, VALUE str, char jflag) rlen = n - llen; cr = ENC_CODERANGE(str); if (flen > 1) { - llen2 = str_offset(f, f + flen, llen % fclen, enc, singlebyte); - rlen2 = str_offset(f, f + flen, rlen % fclen, enc, singlebyte); + llen2 = str_offset(f, f + flen, llen % fclen, enc, singlebyte); + rlen2 = str_offset(f, f + flen, rlen % fclen, enc, singlebyte); } size = RSTRING_LEN(str); if ((len = llen / fclen + rlen / fclen) >= LONG_MAX / flen || - (len *= flen) >= LONG_MAX - llen2 - rlen2 || - (len += llen2 + rlen2) >= LONG_MAX - size) { - rb_raise(rb_eArgError, "argument too big"); + (len *= flen) >= LONG_MAX - llen2 - rlen2 || + (len += llen2 + rlen2) >= LONG_MAX - size) { + rb_raise(rb_eArgError, "argument too big"); } len += size; res = str_enc_new(rb_cString, 0, len, enc); p = RSTRING_PTR(res); if (flen <= 1) { - memset(p, *f, llen); - p += llen; + memset(p, *f, llen); + p += llen; } else { - while (llen >= fclen) { + while (llen >= fclen) { memcpy(p,f,flen); p += flen; llen -= fclen; } - if (llen > 0) { - memcpy(p, f, llen2); - p += llen2; + if (llen > 0) { + memcpy(p, f, llen2); + p += llen2; } } memcpy(p, RSTRING_PTR(str), size); p += size; if (flen <= 1) { - memset(p, *f, rlen); - p += rlen; + memset(p, *f, rlen); + p += rlen; } else { - while (rlen >= fclen) { + while (rlen >= fclen) { memcpy(p,f,flen); p += flen; rlen -= fclen; } - if (rlen > 0) { - memcpy(p, f, rlen2); - p += rlen2; + if (rlen > 0) { + memcpy(p, f, rlen2); + p += rlen2; } } TERM_FILL(p, termlen); @@ -11015,12 +11209,10 @@ rb_str_justify(int argc, VALUE *argv, VALUE str, char jflag) /* * call-seq: - * ljust(size, pad_string = ' ') -> new_string + * ljust(width, pad_string = ' ') -> new_string * * :include: doc/string/ljust.rdoc * - * Related: String#rjust, String#center. - * */ static VALUE @@ -11031,12 +11223,10 @@ rb_str_ljust(int argc, VALUE *argv, VALUE str) /* * call-seq: - * rjust(size, pad_string = ' ') -> new_string + * rjust(width, pad_string = ' ') -> new_string * * :include: doc/string/rjust.rdoc * - * Related: String#ljust, String#center. - * */ static VALUE @@ -11062,7 +11252,7 @@ rb_str_center(int argc, VALUE *argv, VALUE str) /* * call-seq: - * partition(string_or_regexp) -> [head, match, tail] + * partition(pattern) -> [pre_match, first_match, post_match] * * :include: doc/string/partition.rdoc * @@ -11079,10 +11269,9 @@ rb_str_partition(VALUE str, VALUE sep) goto failed; } VALUE match = rb_backref_get(); - struct re_registers *regs = RMATCH_REGS(match); - pos = BEG(0); - sep = rb_str_subseq(str, pos, END(0) - pos); + pos = RMATCH_BEG(match, 0); + sep = rb_str_subseq(str, pos, RMATCH_END(match, 0) - pos); } else { pos = rb_str_index(str, sep, 0); @@ -11099,7 +11288,7 @@ rb_str_partition(VALUE str, VALUE sep) /* * call-seq: - * rpartition(sep) -> [head, match, tail] + * rpartition(pattern) -> [pre_match, last_match, post_match] * * :include: doc/string/rpartition.rdoc * @@ -11116,10 +11305,9 @@ rb_str_rpartition(VALUE str, VALUE sep) goto failed; } VALUE match = rb_backref_get(); - struct re_registers *regs = RMATCH_REGS(match); - pos = BEG(0); - sep = rb_str_subseq(str, pos, END(0) - pos); + pos = RMATCH_BEG(match, 0); + sep = rb_str_subseq(str, pos, RMATCH_END(match, 0) - pos); } else { pos = rb_str_sublen(str, pos); @@ -11139,7 +11327,7 @@ rb_str_rpartition(VALUE str, VALUE sep) /* * call-seq: - * start_with?(*string_or_regexp) -> true or false + * start_with?(*patterns) -> true or false * * :include: doc/string/start_with_p.rdoc * @@ -11395,6 +11583,21 @@ rb_str_setter(VALUE val, ID id, VALUE *var) } static void +nil_setter_warning(ID id) +{ + rb_warn_deprecated("non-nil '%"PRIsVALUE"'", NULL, rb_id2str(id)); +} + +void +rb_deprecated_str_setter(VALUE val, ID id, VALUE *var) +{ + rb_str_setter(val, id, var); + if (!NIL_P(*var)) { + nil_setter_warning(id); + } +} + +static void rb_fs_setter(VALUE val, ID id, VALUE *var) { val = rb_fs_check(val); @@ -11404,7 +11607,7 @@ rb_fs_setter(VALUE val, ID id, VALUE *var) rb_id2str(id)); } if (!NIL_P(val)) { - rb_warn_deprecated("'$;'", NULL); + nil_setter_warning(id); } *var = val; } @@ -11488,11 +11691,8 @@ rb_str_b(VALUE str) * call-seq: * valid_encoding? -> true or false * - * Returns +true+ if +self+ is encoded correctly, +false+ otherwise: + * :include: doc/string/valid_encoding_p.rdoc * - * "\xc2\xa1".force_encoding(Encoding::UTF_8).valid_encoding? # => true - * "\xc2".force_encoding(Encoding::UTF_8).valid_encoding? # => false - * "\x80".force_encoding(Encoding::UTF_8).valid_encoding? # => false */ static VALUE @@ -11671,6 +11871,12 @@ enc_str_scrub(rb_encoding *enc, VALUE str, VALUE repl, int cr) else if (MBCLEN_CHARFOUND_P(ret)) { cr = ENC_CODERANGE_VALID; p += MBCLEN_CHARFOUND_LEN(ret); + /* After a multibyte character, fast-skip the following ASCII run. */ + p = search_nonascii(p, e); + if (!p) { + p = e; + break; + } } else if (MBCLEN_INVALID_P(ret)) { /* @@ -11844,8 +12050,8 @@ enc_str_scrub(rb_encoding *enc, VALUE str, VALUE repl, int cr) /* * call-seq: - * scrub(replacement_string = default_replacement) -> new_string - * scrub{|bytes| ... } -> new_string + * scrub(replacement_string = default_replacement_string) -> new_string + * scrub{|sequence| ... } -> new_string * * :include: doc/string/scrub.rdoc * @@ -11860,11 +12066,15 @@ str_scrub(int argc, VALUE *argv, VALUE str) /* * call-seq: - * scrub! -> self - * scrub!(replacement_string = default_replacement) -> self - * scrub!{|bytes| ... } -> self + * scrub!(replacement_string = default_replacement_string) -> self + * scrub!{|sequence| ... } -> self + * + * Like String#scrub, except that: * - * Like String#scrub, except that any replacements are made in +self+. + * - Any replacements are made in +self+. + * - Returns +self+. + * + * Related: see {Modifying}[rdoc-ref:String@Modifying]. * */ static VALUE @@ -11899,34 +12109,8 @@ unicode_normalize_common(int argc, VALUE *argv, VALUE str, ID id) * call-seq: * unicode_normalize(form = :nfc) -> string * - * Returns a copy of +self+ with - * {Unicode normalization}[https://unicode.org/reports/tr15] applied. - * - * Argument +form+ must be one of the following symbols - * (see {Unicode normalization forms}[https://unicode.org/reports/tr15/#Norm_Forms]): - * - * - +:nfc+: Canonical decomposition, followed by canonical composition. - * - +:nfd+: Canonical decomposition. - * - +:nfkc+: Compatibility decomposition, followed by canonical composition. - * - +:nfkd+: Compatibility decomposition. - * - * The encoding of +self+ must be one of: - * - * - Encoding::UTF_8 - * - Encoding::UTF_16BE - * - Encoding::UTF_16LE - * - Encoding::UTF_32BE - * - Encoding::UTF_32LE - * - Encoding::GB18030 - * - Encoding::UCS_2BE - * - Encoding::UCS_4BE - * - * Examples: - * - * "a\u0300".unicode_normalize # => "a" - * "\u00E0".unicode_normalize(:nfd) # => "a " + * :include: doc/string/unicode_normalize.rdoc * - * Related: String#unicode_normalize!, String#unicode_normalized?. */ static VALUE rb_str_unicode_normalize(int argc, VALUE *argv, VALUE str) @@ -11939,9 +12123,9 @@ rb_str_unicode_normalize(int argc, VALUE *argv, VALUE str) * unicode_normalize!(form = :nfc) -> self * * Like String#unicode_normalize, except that the normalization - * is performed on +self+. + * is performed on +self+ (not on a copy of +self+). * - * Related String#unicode_normalized?. + * Related: see {Modifying}[rdoc-ref:String@Modifying]. * */ static VALUE @@ -11953,8 +12137,9 @@ rb_str_unicode_normalize_bang(int argc, VALUE *argv, VALUE str) /* call-seq: * unicode_normalized?(form = :nfc) -> true or false * - * Returns +true+ if +self+ is in the given +form+ of Unicode normalization, - * +false+ otherwise. + * Returns whether +self+ is in the given +form+ of Unicode normalization; + * see String#unicode_normalize. + * * The +form+ must be one of +:nfc+, +:nfd+, +:nfkc+, or +:nfkd+. * * Examples: @@ -11968,10 +12153,9 @@ rb_str_unicode_normalize_bang(int argc, VALUE *argv, VALUE str) * Raises an exception if +self+ is not in a Unicode encoding: * * s = "\xE0".force_encoding(Encoding::ISO_8859_1) - * s.unicode_normalized? # Raises Encoding::CompatibilityError. - * - * Related: String#unicode_normalize, String#unicode_normalize!. + * s.unicode_normalized? # Raises Encoding::CompatibilityError * + * Related: see {Querying}[rdoc-ref:String@Querying]. */ static VALUE rb_str_unicode_normalized_p(int argc, VALUE *argv, VALUE str) @@ -12043,8 +12227,8 @@ rb_str_unicode_normalized_p(int argc, VALUE *argv, VALUE str) * * First, what's elsewhere. Class +Symbol+: * - * - Inherits from {class Object}[rdoc-ref:Object@What-27s+Here]. - * - Includes {module Comparable}[rdoc-ref:Comparable@What-27s+Here]. + * - Inherits from {class Object}[rdoc-ref:Object@Whats+Here]. + * - Includes {module Comparable}[rdoc-ref:Comparable@Whats+Here]. * * Here, class +Symbol+ provides methods that are useful for: * @@ -12103,9 +12287,9 @@ rb_str_unicode_normalized_p(int argc, VALUE *argv, VALUE str) /* * call-seq: - * symbol == object -> true or false + * self == other -> true or false * - * Returns +true+ if +object+ is the same object as +self+, +false+ otherwise. + * Returns whether +other+ is the same object as +self+. */ #define sym_equal rb_obj_equal @@ -12263,18 +12447,24 @@ sym_succ(VALUE sym) /* * call-seq: - * symbol <=> object -> -1, 0, +1, or nil + * self <=> other -> -1, 0, 1, or nil + * + * Compares +self+ and +other+, using String#<=>. * - * If +object+ is a symbol, - * returns the equivalent of <tt>symbol.to_s <=> object.to_s</tt>: + * Returns: * - * :bar <=> :foo # => -1 - * :foo <=> :foo # => 0 - * :foo <=> :bar # => 1 + * - <tt>self.to_s <=> other.to_s</tt>, if +other+ is a symbol. + * - +nil+, otherwise. * - * Otherwise, returns +nil+: + * Examples: + * + * :bar <=> :foo # => -1 + * :foo <=> :foo # => 0 + * :foo <=> :bar # => 1 + * :foo <=> 'bar' # => nil * - * :foo <=> 'bar' # => nil + * \Class \Symbol includes module Comparable, + * each of whose methods uses Symbol#<=> for comparison. * * Related: String#<=>. */ @@ -12324,9 +12514,9 @@ sym_casecmp_p(VALUE sym, VALUE other) /* * call-seq: - * symbol =~ object -> integer or nil + * self =~ other -> integer or nil * - * Equivalent to <tt>symbol.to_s =~ object</tt>, + * Equivalent to <tt>self.to_s =~ other</tt>, * including possible updates to global variables; * see String#=~. * @@ -12372,11 +12562,11 @@ sym_match_m_p(int argc, VALUE *argv, VALUE sym) /* * call-seq: - * symbol[index] -> string or nil - * symbol[start, length] -> string or nil - * symbol[range] -> string or nil - * symbol[regexp, capture = 0] -> string or nil - * symbol[substring] -> string or nil + * self[offset] -> string or nil + * self[offset, size] -> string or nil + * self[range] -> string or nil + * self[regexp, capture = 0] -> string or nil + * self[substring] -> string or nil * * Equivalent to <tt>symbol.to_s[]</tt>; see String#[]. * @@ -12584,8 +12774,16 @@ rb_str_to_interned_str(VALUE str) VALUE rb_interned_str(const char *ptr, long len) { - struct RString fake_str; - return register_fstring(setup_fake_str(&fake_str, ptr, len, ENCINDEX_US_ASCII), true, false); + struct RString fake_str = {RBASIC_INIT}; + int encidx = ENCINDEX_US_ASCII; + int coderange = ENC_CODERANGE_7BIT; + if (len > 0 && search_nonascii(ptr, ptr + len)) { + encidx = ENCINDEX_ASCII_8BIT; + coderange = ENC_CODERANGE_VALID; + } + VALUE str = setup_fake_str(&fake_str, ptr, len, encidx); + ENC_CODERANGE_SET(str, coderange); + return register_fstring(str, true, false); } VALUE @@ -12601,7 +12799,7 @@ rb_enc_interned_str(const char *ptr, long len, rb_encoding *enc) rb_enc_autoload(enc); } - struct RString fake_str; + struct RString fake_str = {RBASIC_INIT}; return register_fstring(rb_setup_fake_str(&fake_str, ptr, len, enc), true, false); } @@ -12612,8 +12810,10 @@ rb_enc_literal_str(const char *ptr, long len, rb_encoding *enc) rb_enc_autoload(enc); } - struct RString fake_str; - return register_fstring(rb_setup_fake_str(&fake_str, ptr, len, enc), true, true); + struct RString fake_str = {RBASIC_INIT}; + VALUE str = register_fstring(rb_setup_fake_str(&fake_str, ptr, len, enc), true, true); + RUBY_ASSERT(RB_OBJ_SHAREABLE_P(str) && (rb_gc_verify_shareable(str), 1)); + return str; } VALUE @@ -12622,9 +12822,9 @@ rb_enc_interned_str_cstr(const char *ptr, rb_encoding *enc) return rb_enc_interned_str(ptr, strlen(ptr), enc); } -#if USE_YJIT +#if USE_YJIT || USE_ZJIT void -rb_yjit_str_concat_codepoint(VALUE str, VALUE codepoint) +rb_jit_str_concat_codepoint(VALUE str, VALUE codepoint) { if (RB_LIKELY(ENCODING_GET_INLINED(str) == rb_ascii8bit_encindex())) { ssize_t code = RB_NUM2SSIZE(codepoint); @@ -12659,6 +12859,7 @@ Init_String(void) rb_define_singleton_method(rb_cString, "new", rb_str_s_new, -1); rb_define_singleton_method(rb_cString, "try_convert", rb_str_s_try_convert, 1); rb_define_method(rb_cString, "initialize", rb_str_init, -1); + rb_define_method(rb_cString, "replace", rb_str_replace, 1); rb_define_method(rb_cString, "initialize_copy", rb_str_replace, 1); rb_define_method(rb_cString, "<=>", rb_str_cmp_m, 1); rb_define_method(rb_cString, "==", rb_str_equal, 1); @@ -12689,7 +12890,6 @@ Init_String(void) rb_define_method(rb_cString, "byteindex", rb_str_byteindex_m, -1); rb_define_method(rb_cString, "rindex", rb_str_rindex_m, -1); rb_define_method(rb_cString, "byterindex", rb_str_byterindex_m, -1); - rb_define_method(rb_cString, "replace", rb_str_replace, 1); rb_define_method(rb_cString, "clear", rb_str_clear, 0); rb_define_method(rb_cString, "chr", rb_str_chr, 0); rb_define_method(rb_cString, "getbyte", rb_str_getbyte, 1); @@ -12760,9 +12960,9 @@ Init_String(void) rb_define_method(rb_cString, "gsub", rb_str_gsub, -1); rb_define_method(rb_cString, "chop", rb_str_chop, 0); rb_define_method(rb_cString, "chomp", rb_str_chomp, -1); - rb_define_method(rb_cString, "strip", rb_str_strip, 0); - rb_define_method(rb_cString, "lstrip", rb_str_lstrip, 0); - rb_define_method(rb_cString, "rstrip", rb_str_rstrip, 0); + rb_define_method(rb_cString, "strip", rb_str_strip, -1); + rb_define_method(rb_cString, "lstrip", rb_str_lstrip, -1); + rb_define_method(rb_cString, "rstrip", rb_str_rstrip, -1); rb_define_method(rb_cString, "delete_prefix", rb_str_delete_prefix, 1); rb_define_method(rb_cString, "delete_suffix", rb_str_delete_suffix, 1); @@ -12770,9 +12970,9 @@ Init_String(void) rb_define_method(rb_cString, "gsub!", rb_str_gsub_bang, -1); rb_define_method(rb_cString, "chop!", rb_str_chop_bang, 0); rb_define_method(rb_cString, "chomp!", rb_str_chomp_bang, -1); - rb_define_method(rb_cString, "strip!", rb_str_strip_bang, 0); - rb_define_method(rb_cString, "lstrip!", rb_str_lstrip_bang, 0); - rb_define_method(rb_cString, "rstrip!", rb_str_rstrip_bang, 0); + rb_define_method(rb_cString, "strip!", rb_str_strip_bang, -1); + rb_define_method(rb_cString, "lstrip!", rb_str_lstrip_bang, -1); + rb_define_method(rb_cString, "rstrip!", rb_str_rstrip_bang, -1); rb_define_method(rb_cString, "delete_prefix!", rb_str_delete_prefix_bang, 1); rb_define_method(rb_cString, "delete_suffix!", rb_str_delete_suffix_bang, 1); |
