1930 Commits

Author SHA1 Message Date
John Hawthorn
98ba0a86a3 Don't write str2's code range back in rb_str_buf_append
Since rb_enc_str_coderange(str2) is called first, str2_cr is never
UNKNOWN and rb_enc_cr_str_buf_cat returns it unchanged, so writing it
back to str2 is redundant. It is also an unsynchronized write to str2,
which may be a frozen string shared with other Ractors.

When str and str2 are the same string, the write-back also overwrote
the result's code range with the pre-append one, so `s << s` could
leave a valid string marked BROKEN.

[Bug #22451]

(cherry picked from commit b740f7d553e6ee4abdff8847760394b4e6ea389c)
2026-11-06 22:14:51 +09:00
nagachika
0aeb21f336 merge revision(s) fef67df22408cd8de42592a0f8a8dc8ed17f0b91:
[PATCH] Do not drop substring before match in middle of char
2026-11-04 21:52:51 +09:00
Erik Berlin
26a4d835f7 Fix Range#include? of an empty range of single characters
A range of single character Strings told that it includes its end
even when it is empty, since the end was not compared with the begin.

    ("z".."a").to_a          #=> []
    ("z".."a").include?("a") #=> true

(cherry picked from commit 1bf91309438d071359aa521328074cc6ec293299)
2026-10-17 13:21:36 +09:00
nagachika
f5cef120e9 merge revision(s) 2d08b5f42f2cbed4a4e6ecf8d62987a4f21f98c1: [Backport #22423]
[PATCH] [Bug #22423] Preserve encoding with `String` capacity
2026-10-12 14:22:20 +09:00
Randy Stauner
cc3e2b9687 [Backport 3.4] [Bug #22387] Fix termlen in str_shared_replace
The buffer is copied over based on the new string's termlen
so we can just set the new encoding raw
instead of recalculating based on the old encoding's termlen.
2026-10-12 13:24:35 +09:00
nagachika
c17e24d88d merge revision(s) 88bfa3005cdc58515630ad4e2b3e9f9eb09563d4:
[PATCH] Fix buffer overflow in String#rpartition
	MIME-Version: 1.0
	Content-Type: text/plain; charset=UTF-8
	Content-Transfer-Encoding: 8bit

	The conversion of the pattern to string can cause the string to be modified.
	This can cause a buffer overflow since it uses the original length of the
	string. For example, the following script reports a buffer overflow with
	ASAN:

	    s = "héllo" * 1000
	    obj = Object.new
	    obj.define_singleton_method(:to_str) do
	      s.replace("hé")
	      "l"
	    end
	    p s.rpartition(obj)
2026-09-22 18:39:10 +09:00
nagachika
044f08d26d merge revision(s) edce07a8c93895be8eeb2d27400aa138cc8a3cf9:
[PATCH] Fix use-after-free in String#% when array modified

	If the array object passed into String#% is modified during conversion
	(such as via to_s, to_i, to_f, etc.) then it can be a use-after-free if
	the buffer of the array is freed.

	This commit changes it to detect if the buffer has been changed and raises
	an error when that happens.

	The following script reproduces the crash:

	    count = 1_000
	    obj = Object.new
	    ary = [obj, *Array.new(count - 1) { "x" }]
	    obj.define_singleton_method(:to_s) do
	      ary.replace([])
	      "X"
	    end
	    str = "%s" * count
	    puts (str % ary).length
2026-09-21 17:18:30 +09:00
nagachika
d8ada47f21 merge revision(s) d4d078672943ffd3c344c431c631a8666a5a3226:
[PATCH] Fix out-of-bounds access in String#byteindex/byterindex
	MIME-Version: 1.0
	Content-Type: text/plain; charset=UTF-8
	Content-Transfer-Encoding: 8bit

	If the source string is modified when converting offset to an integer,
	then there could be an out-of-bounds access because the length of the
	string is captured before to_int is called. For example, the following
	script triggers an ASAN error:

	    s = "héllo" * 1_000_000
	    obj = Object.new
	    obj.define_singleton_method(:to_int) do
	      s.replace("é" * 1000)
	      450000
	    end
	    p s.byteindex("l", obj)
2026-09-21 17:16:16 +09:00
nagachika
083198e9fb merge revision(s) 78b7646bdb91285873ac26bca060591e06c45afe, b4a62a1ca949d93332ad8bce0fcc273581160cc5: [Backport #21842]
[PATCH] [Bug #21842] Let `rb_interned_str` return US-ASCII if possible

	[PATCH] [DOC] Update docs for rb_interned_str and related functions (#15897)

	Related to [Bug #21842].

	* rb_interned_str: document what decides whether the returned string is
	  in US-ASCII or BINARY encoding.
	* rb_interned_str_cstr: include the same description as rb_interned_str
	  for the encoding. This one was still missing the update for US-ASCII
	  and erroneously said the returned string was alwasy in BINARY encoding
	* rb_str_to_interned_str: document how the encoding of the result is
	  defined.

	Co-authored-by: Herwin <herwinw@users.noreply.github.com>
2026-08-09 14:17:55 +09:00
ZHIJIE XIE
46f51cb3a2 string.c: guard tmp in rb_str_format_m (GH-16280)
[Bug #21931]

Keep tmp alive while RARRAY_CONST_PTR(tmp) is used by rb_str_format.

[alan: sunk the guard below usage]
Reviewed-by: Alan Wu <XrXr@users.noreply.github.com>
2026-03-05 20:57:24 -05:00
Hiroshi SHIBATA
2bbc06e21d merge revision(s) d209e6f1c0a93ad3ce1cc64dd165a6b67672614d: [Backport #21715]
[PATCH] search_nonascii(): Replace UB pointer cast with memcpy

	Casting a pointer to create an unaligned one is undefined behavior in C
	standards. Use memcpy to express the unaligned load instead to play by
	the rules.

	Practically, this yields the same binary output in many situations
	while fixing the crash in [Bug #21715].
2026-01-21 16:52:39 +09:00
John Hawthorn
9e426489f0
Don't modify fstrings in rb_str_tmp_frozen_no_embed_acquire (#15104)
[Bug #21671]
2025-11-07 19:44:56 -08:00
Takashi Kokubun
18e176659e merge revision(s) 2bb6fe3854e2a4854bb89bfce4eaaea9d848fd1b: [Backport #21629]
[PATCH] [Bug #21629] Initialize `struct RString`

which appears to be missed in the previous commit for some reason.
2025-10-08 16:50:58 -07:00
Takashi Kokubun
596126ee5a merge revision(s) b42afa1dbcbb91e89852b3b3bc72484d7f0a5528, f1f0cc14cc7d3f9be35b203e5583f9224b1e2387, 543e3a1896ae2fe3b5b954f6497d261ab5663a15, ed2806117a0b76e4439ce1a061fae21d9e116d69, 46e4c8673747de96838d2c5dec37446d23d99d88: [Backport #21500]
Suppress gcc 15 unterminated-string-initialization warnings

	Separate `__has_attribute` from `defined(__has_attribute)`

	Fix Visual C warnings:
	```
	regenc.h(121): warning C4067: unexpected tokens following preprocessor directive - expected a newline
	```

	Cast up `int` instruction code to `VALUE`

	Fix Visual C warnings:
	```
	iseq.c(3793): warning C4312: 'type cast': conversion from 'int' to 'void *' of greater size
	iseq.c(3794): warning C4312: 'type cast': conversion from 'int' to 'void *' of greater size
	```

	Do not let files depend on a phony target

	Detect `clock_gettime` and `clock_getres` for winpthreads
2025-07-14 14:50:56 -07:00
Takashi Kokubun
fee92000fe merge revision(s) fa85d23ff4a02985ebfe0716b0ff768f5b4fe13d: [Backport #21380]
[Bug #21380] Prohibit modification in String#split block

	Reported at https://hackerone.com/reports/3163876
2025-07-14 14:23:45 -07:00
Jean Boussier
161cffb399 Fix a race condition with interned strings sweeping.
[Bug #21172]

This fixes a rare CI failure.

The timeline of the race condition is:

- A `"foo" oid=1` string is interned.
- `"foo" oid=1` is no longer referenced and will be swept in the future.
- Another `"foo" oid=2` string is interned.
- `register_fstring` finds `"foo" oid=1`, but since it is about to be swept,
  removes it from `fstring_table` and insert `"foo" oid=2` instead.
- `"foo" oid=1` is swept, since it has the `RSTRING_FSTR` flag,
  a `st_delete` is issued in `fstring_table` which removes `"foo" oid=2`.

I don't know how to reproduce this bug consistently in a single test
case.
2025-03-06 09:24:37 -08:00
Alan Wu
880a90cf2e [DOC] [Feature #20205] Document the new power of String#+@ 2024-12-13 14:25:32 -05:00
Jean Boussier
26d020cb6e Optimize rb_must_asciicompat
While profiling `strscan`, I noticed `rb_must_asciicompat` was quite
slow, as more than 5% of the benchmark was spent in it: https://share.firefox.dev/49bOcTn

By checking for the common 3 ASCII compatible encoding index first,
we can skip a lot of expensive operations in the happy path.
2024-11-27 14:50:07 +01:00
Nobuyoshi Nakada
6b4f8945d6 Many of Oniguruma functions need valid encoding strings 2024-11-26 11:46:34 +09:00
Nobuyoshi Nakada
02b70256b5 Check negative integer underflow 2024-11-26 11:46:34 +09:00
Matt Valentine-House
551be8219e Place all non-default GC API behind USE_SHARED_GC
So that it doesn't get included in the generated binaries for builds
that don't support loading shared GC modules

Co-Authored-By: Peter Zhu <peter@peterzhu.ca>
2024-11-25 13:05:23 +00:00
Peter Zhu
41a9460227 [DOC] Fix typo in comment for STR_PRECOMPUTED_HASH 2024-11-20 11:16:10 -05:00
Kouhei Yanagita
eb2b0c2a0d [DOC] Fix the default limit of String#split
We can't pass `nil` as the second parameter of `String#split`.
Therefore, descriptions like "if limit is nil, ..." are not appropriate.
2024-11-19 12:15:48 +09:00
Randy Stauner
beafae9750
YJIT: Specialize String#[] (String#slice) with fixnum arguments (#12069)
* YJIT: Specialize `String#[]` (`String#slice`) with fixnum arguments

String#[] is in the top few C calls of several YJIT benchmarks:
liquid-compile rubocop mail sudoku

This speeds up these benchmarks by 1-2%.

* YJIT: Try harder to get type info for `String#[]`

In the large generated code of the mail gem the context doesn't have
the type info.  In that case if we peek at the stack and add a guard
we can still apply the specialization
and it speeds up the mail benchmark by 5%.

Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>
Co-authored-by: Takashi Kokubun (k0kubun) <takashikkbn@gmail.com>

---------

Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>
Co-authored-by: Takashi Kokubun (k0kubun) <takashikkbn@gmail.com>
2024-11-13 12:25:09 -05:00
Jean byroot Boussier
6deeec5d45
Mark strings returned by Symbol#to_s as chilled (#12065)
* Use FL_USER0 for ELTS_SHARED

This makes space in RString for two bits for chilled strings.

* Mark strings returned by `Symbol#to_s` as chilled

[Feature #20350]

`STR_CHILLED` now spans on two user flags. If one bit is set it
marks a chilled string literal, if it's the other it marks a
`Symbol#to_s` chilled string.

Since it's not possible, and doesn't make much sense to include
debug info when `--debug-frozen-string-literal` is set, we can't
include allocation source, but we can safely include the symbol
name in the warning message, making it much easier to find the source
of the issue.

Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>

---------

Co-authored-by: Étienne Barrié <etienne.barrie@gmail.com>
Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
2024-11-13 09:20:00 -05:00
Jean Boussier
37a16c7812 string.c: preserve coderange when interning a string
Since `str_do_hash` will most likely scan the string to
compute the coderange, we might as well copy it over in the
interned string in case it's useful later.
2024-11-13 14:14:24 +01:00
Jean Boussier
fae86a701e string.c: Directly create strings with the correct encoding
While profiling msgpack-ruby I noticed a very substantial amout of time
spent in `rb_enc_associate_index`, called by `rb_utf8_str_new`.

On that benchmark, `rb_utf8_str_new` is 33% of the total runtime,
in big part because it cause GC to trigger often, but even then
`5.3%` of the total runtime is spent in `rb_enc_associate_index`
called by `rb_utf8_str_new`.

After closer inspection, it appears that it's performing a lot of
safety check we can assert we don't need, and other extra useless
operations, because strings are first created and filled as ASCII-8BIT
and then later reassociated to the desired encoding.

By directly allocating the string with the right encoding, it allow
to skip a lot of duplicated and useless operations.

After this change, the time spent in `rb_utf8_str_new` is down
to `28.4%` of total runtime, and most of that is GC.
2024-11-13 13:32:32 +01:00
Jean Boussier
bfb4783c01 Move Symbol#name into symbol.rb
This allows to declare it as leaf just like `Symbol#to_s`.

Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>
2024-11-13 10:29:07 +01:00
Étienne Barrié
84a8b911c1 Store precomputed hash when there's capacity
Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
2024-11-06 12:57:17 +01:00
Étienne Barrié
1e037108a1 Precompute hash only once when interning string literals
When a fake string is interned, use the capa field to store the string
hash. This lets us compute it once for hash lookup and embedding the
hash in the interned string.

Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
2024-11-04 14:37:14 +01:00
Yusuke Endoh
a83c91dd7a Fix an off-by-one error of own memrchr implementation
and make it support `search_len == 0`, just for the case

Ref [Bug #20796]
2024-10-21 20:40:42 +09:00
Étienne Barrié
257f78fb67 Show where mutated chilled strings were allocated
[Feature #20205]

The warning now suggests running with --debug-frozen-string-literal:

```
test.rb:3: warning: literal string will be frozen in the future (run with --debug-frozen-string-literal for more information)
```

When using --debug-frozen-string-literal, the location where the string
was created is shown:

```
test.rb:3: warning: literal string will be frozen in the future
test.rb:1: info: the string was created here
```

When resurrecting strings and debug mode is not enabled, the overhead is a simple FL_TEST_RAW.
When mutating chilled strings and deprecation warnings are not enabled,
the overhead is a simple warning category enabled check.

Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
2024-10-21 12:33:02 +02:00
Holger Just
7081838d2a
[DOC] String#sub! and String#gsub! return nil if no replacement occured 2024-10-07 17:20:03 +09:00
Peter Zhu
e956ce32c8 Use rb_bug instead of UNREACHABLE for assertions
UNREACHABLE uses __builtin_unreachable which is not intended to be used
as an assertion.
2024-09-24 14:54:55 -04:00
Peter Zhu
c51d8ff458 Fix undefined behavior in String#append_as_bytes
The UNREACHABLE macro calls __builtin_unreachable, which according to
the [GCC docs](https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005funreachable):

> If control flow reaches the point of the __builtin_unreachable, the
> program is undefined.

But it can reach this point with the following script:

    "123".append_as_bytes("123")

This can crash on some platforms with a `Trace/BPT trap: 5`.
2024-09-24 14:54:55 -04:00
Jeremy Evans
8dc0d2904a Update exception message in string_for_symbol
This is a static function only called in two places (rb_to_id and
rb_to_symbol), and in both places, both symbols and strings are
allowed.  This makes the error message consistent with rb_check_id
and rb_check_symbol.

Fixes [Bug #20607]
2024-09-18 21:29:07 -07:00
Jean Boussier
16f241f0aa Implement String#append_as_bytes(String | Integer, ...)
[Feature #20594]

A handy method to construct a string out of multiple chunks.

Contrary to `String#concat`, it doesn't do any encoding negociation,
and simply append the content as bytes regardless of whether this
result in a broken string or not.

It's the caller responsibility to check for `String#valid_encoding?`
in cases where it's needed.

When passed integers, only the lower byte is considered, like in
`String#setbyte`.
2024-09-09 15:04:51 +02:00
Jean Boussier
036ca726bb Fix documentation for String#index and String#byterindex 2024-09-04 11:26:17 +02:00
Nobuyoshi Nakada
ade240e578
Adjust indents [ci skip] 2024-09-04 10:28:52 +09:00
Jean Boussier
b7fa2dd0d0 rb_enc_str_asciionly_p: avoid always fetching the encoding
Profiling of `JSON.dump` shows a significant amount of time spent
in `rb_enc_str_asciionly_p`, in large part because it fetches the
encoding.

It can be made twice as fast in this scenario by first checking the
coderange and only falling back to fetching the encoding if the
coderange is unknown.

Additionally we can skip fetching the encoding for the common
popular encodings.
2024-09-03 12:21:36 +02:00
Zack Deveau
e7cb70be4e Improve String#rindex performance on OSX
On OSX, String#rindex is slow due to the lack of `memrchr`.
The fallback implementation finds a match by instead doing
a `memcmp` on every single character in the search string
looking for a substring match.

For OSX hosts, this changeset introduces a simple `memrchr`
implementation, `rb_memrchr`, that can be used instead. An
example benchmark below demonstrates an 8000 char long
search string with a 10 char substring near the end.

```
ruby-master | substring near the end | osx

UTF-8
       user     system      total        real
index  0.000111   0.000000   0.000111 (  0.000110)
rindex  0.000446   0.000005   0.000451 (  0.000454)
```

```
ruby-patched | substring near the end | osx

UTF-8
       user     system      total        real
index  0.000112   0.000000   0.000112 (  0.000111)
rindex  0.000057   0.000001   0.000058 (  0.000057)
```
2024-09-03 14:25:25 +09:00
Jean Boussier
4e85b6b4c4 rb_str_bytesplice: skip encoding check if encodings are the same
If both strings have the same encoding, all this work is useless.
2024-08-09 22:06:44 +02:00
Jean Boussier
3bac5f6af5 string.c: add fastpath in str_ensure_byte_pos
If the string only contain single byte characters we can
skips all the costly checks.
2024-08-09 22:06:44 +02:00
Jean Boussier
a332367dad string.c: Add fastpath to single_byte_optimizable
`rb_enc_from_index` is a costly operation so it is worth avoiding
to call it for the common encodings.

Also in the case of UTF-8, it's more efficient to scan the
coderange if it is unknown that to fallback to the slower
algorithms.
2024-08-09 22:06:44 +02:00
Jean Boussier
2bd5dc47ac string.c: str_capacity don't check for immediates
`STR_EMBED_P` uses `FL_TEST_RAW` meaning we already assume `str`
isn't an immediate, so we can use `FL_TEST_RAW` here too.
2024-08-09 15:20:58 +02:00
Jean Boussier
af44af238b str_independent: add a fastpath with a single flag check
If we assume that most strings we modify are not frozen and
are independent, then we can optimize this case by replacing
multiple flag checks by a single mask check.
2024-08-09 15:20:58 +02:00
Kevin Menard
04a6165ac0
YJIT: Enhance the String#<< method substitution to handle integer codepoint values. (#11032)
* Document why we need to explicitly spill registers.

* Simplify passing a byte value to `str_buf_cat`.

* YJIT: Enhance the `String#<<` method substitution to handle integer codepoint values.

* YJIT: Move runtime type check into YJIT.

Performing the check in YJIT means we can make assumptions about the type. It also improves correctness of stack traces in cases where the codepoint argument is not a String or a Fixnum.
2024-08-02 15:45:22 -04:00
Jean Boussier
83f57ca3d2 String.new(capacity:) don't substract termlen
[Bug #20585]

This was changed in 36a06efdd9f0604093dccbaf96d4e2cb17874dc8 because
`String.new(1024)` would end up allocating `1025` bytes, but the problem
with this change is that the caller may be trying to right size a String.

So instead, we should just better document the behavior of `capacity:`.
2024-06-19 15:11:07 +02:00
Kevin Menard
a119b5f879 Add a fast path implementation for appending single byte values to US-ASCII strings. 2024-06-17 09:44:48 -07:00
Kevin Menard
27e13fbc58 Add a fast path implementation for appending single byte values to binary strings.
Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
2024-06-17 09:44:48 -07:00