Skip to content

string.c: String#chop! takes a byte off a binary string - #7084

Merged
matz merged 1 commit into
mruby:masterfrom
takumin:string-binary-chop
Aug 10, 2026
Merged

string.c: String#chop! takes a byte off a binary string#7084
matz merged 1 commit into
mruby:masterfrom
takumin:string-binary-chop

Conversation

@takumin

@takumin takumin commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

mrb_str_chop_bang() walks the string with mrb_utf8len() to find where the
last character starts. A string marked by String#b is read as bytes
everywhere else and ends in a byte rather than in a character, and the walk
never asked about the flag, so it took off the whole of a multi-byte sequence.

"\u{1F600}".b.dup.chop!   # F0 9F 98 80: ""   <- CRuby: "\xF0\x9F\x98"
"a\u{1F600}".b.dup.chop!  # "a"                <- CRuby: "a\xF0\x9F\x98"

A string that held one character came back empty, which is the right answer
for a one-character string read as UTF-8 and was reached here by a rule that
does not apply to a string of bytes.

The change

mrb_str_chop_bang() takes the last byte when RSTR_BINARY_P() says the
string is byte-indexed. The \r\n pair is still taken together, since that
test runs on the byte before the end either way.

Related, not required

String#chop copies the string first and str_replace() does not carry
MRB_STR_BINARY to the copy, so it answers as before; #7080 makes the flag
survive a copy and the copying form follows. #7081 fixes the length for the
same kind of string, and String#inspect has a walk of its own. Each is a
separate fix and none depends on another landing first.

Testing

rake test is green on full-core with gcc on x86_64-linux: 2215 asserts,
no failures. The new test fails without the change:

Fail: String#chop! on a binary string removes one byte
  KO: 1   ->   KO: 0

The test also pins what must not move: a string read as UTF-8 still loses the
whole character, and "a\r\n".b still loses both bytes of the pair. Checked
against CRuby 4.0.6.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed String#chop! for binary strings in UTF-8 builds. It now removes exactly the final byte without incorrectly interpreting multi-byte data as text.
    • Preserved character-based behavior for regular UTF-8 strings, including correct handling of line endings.
  • Tests

    • Added coverage for binary data, multi-byte UTF-8 strings, and CRLF line endings.

@takumin
takumin requested a review from matz as a code owner August 10, 2026 16:27
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8ee9715c-be02-493f-ab26-e7aaedba4b7d

📥 Commits

Reviewing files that changed from the base of the PR and between 6c9721d and f981565.

📒 Files selected for processing (1)
  • mrbgems/mruby-string-ext/test/string.rb

📝 Walkthrough

Walkthrough

String#chop! now removes one byte from binary strings under UTF-8 builds. Regular UTF-8 strings retain character-based removal. Tests cover multi-byte data, UTF-8 strings, and CRLF input.

Changes

Binary String Chop Handling

Layer / File(s) Summary
String#chop! behavior and regression coverage
src/string.c, mrbgems/mruby-string-ext/test/string.rb
Binary strings remove their final byte, while regular UTF-8 strings remove the final character boundary. Tests cover multi-byte data and CRLF handling.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • mruby/mruby#7081: Both changes modify mrb_str_chop_bang() for binary strings under UTF-8 builds.

Suggested reviewers: matz

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change to String#chop! for binary strings.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

`mrb_str_chop_bang()` walks the string with `mrb_utf8len()` to find where the
last character starts. A string marked by `String#b` is read as bytes
everywhere else and ends in a byte rather than in a character, and the walk
never asked about the flag, so it took off the whole of a multi-byte sequence.

```ruby
"\u{1F600}".b.dup.chop!   # F0 9F 98 80: was "", CRuby: "\xF0\x9F\x98"
"a\u{1F600}".b.dup.chop!  # was "a",       CRuby: "a\xF0\x9F\x98"
```

A string that held one character came back empty, which is the same answer
`chop!` gives for a one-character string read as UTF-8 and was reached here by
a rule that does not apply.

Read the flag and take the last byte. The `\r\n` pair is still taken together,
since that test runs on the byte before the end either way.

`String#chop` copies the string first and `str_replace()` does not carry
`MRB_STR_BINARY` to the copy, so it answers as before until the flag survives
a copy.
@takumin
takumin force-pushed the string-binary-chop branch from 6c9721d to f981565 Compare August 10, 2026 16:31

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/string.c`:
- Around line 1870-1882: Update mrb_str_chop and its duplication path so the
copied receiver retains MRB_STR_BINARY before invoking the chop helper, ensuring
String#chop matches chop! for binary strings by removing one byte. Add a
regression test covering String#chop on a binary string under MRB_UTF8_STRING.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3cb19dae-e035-459e-a591-b0932bbff0ab

📥 Commits

Reviewing files that changed from the base of the PR and between 7184392 and 6c9721d.

📒 Files selected for processing (2)
  • mrbgems/mruby-encoding/test/string.rb
  • src/string.c

Comment thread src/string.c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants