Skip to content

Conversation

@FrancescoK
Copy link
Contributor

With latest Ruby 3.4.x, using marcel emits the following warning:
lib/ruby/gems/3.4.0/gems/marcel-1.0.4/lib/marcel/magic.rb:120: warning: literal string will be frozen in the future

This occurs because the code was creating an empty string literal used as mutable buffer for I/O operations.

This pull request replaces "".dup.encode(Encoding::BINARY) with (+"").encode(Encoding::BINARY) in the buffer creation. Using the unary plus operator to explicitly create a mutable string benchmarked slightly faster than .dup.encode() on my local machine.

Benchmark Results

String Creation Performance (1M iterations)

Method Time (seconds) Relative Performance
"".dup.encode() 0.135 baseline
(+"").encode() 0.136 similar
String.new(encoding:) 0.173 28% slower

Memory Allocation (100K iterations)

Method Objects Allocated Relative Memory
"".dup.encode() 200,005 baseline
(+"").encode() 200,002 same
String.new(encoding:) 300,003 50% more

Real-world Usage (10K magic detection operations)

Method Time (seconds) Performance Improvement
"".dup.encode() 0.0099 baseline
(+"").encode() 0.0072 27% faster
String.new(encoding:) 0.0079 20% faster

@FrancescoK
Copy link
Contributor Author

Tests pass on my machine and GH Actions, the Buildkite build erroring out seems unrelated and not fixable from this PR.

@FrancescoK FrancescoK marked this pull request as draft May 25, 2025 17:03
io.binmode if io.respond_to?(:binmode)
io.set_encoding(Encoding::BINARY) if io.respond_to?(:set_encoding)
buffer = "".encode(Encoding::BINARY)
buffer = (+"").encode(Encoding::BINARY)

Choose a reason for hiding this comment

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

Perhaps this is better for readability:

Suggested change
buffer = (+"").encode(Encoding::BINARY)
buffer = String.new.encode(Encoding::BINARY)

Choose a reason for hiding this comment

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

I see from the bench-marking that you did already consider that option. Also this is more succinct hence better.

Suggested change
buffer = (+"").encode(Encoding::BINARY)
buffer = String.new(encoding: Encoding::BINARY)

Choose a reason for hiding this comment

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

Please disregard!

Choose a reason for hiding this comment

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

I think what you want here is to declare a mutable string with binary encoding. That's

String.new(encoding: Encoding::BINARY)

@FrancescoK
Copy link
Contributor Author

I’m not sure whether this actually fixes the issue I was trying to fix — tried to bundle install marcel from my repo and it still appeared — but this should still be a slight improvement

@FrancescoK FrancescoK marked this pull request as ready for review June 1, 2025 15:36
simonbaird added a commit to tiddlyhost/tiddlyhost-com that referenced this pull request Jun 2, 2025
As mentioned in the comments,
rails/marcel#123 PR should make this go
away.

This is just for cosmetic reasons. The warning makes the test suite
output look untidy.
io.binmode if io.respond_to?(:binmode)
io.set_encoding(Encoding::BINARY) if io.respond_to?(:set_encoding)
buffer = "".encode(Encoding::BINARY)
buffer = (+"").encode(Encoding::BINARY)

Choose a reason for hiding this comment

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

Suggested change
buffer = (+"").encode(Encoding::BINARY)
buffer = String.new(encoding: Encoding::BINARY)

@patelvp
Copy link

patelvp commented Aug 13, 2025

Hey, can we try to get this merged in? A lot of our tests are failing on ruby 3.4.4 because of this. And instead of recording deprecations, I would like this to be fixed. Let me know if I can help in any way
Thanks for maintaining this library

@kyrylo
Copy link

kyrylo commented Sep 15, 2025

It’d be great to get this fixed. I’m seeing a lot of deprecation warnings while running my tests.

Screenshot 2025-09-15 at 2 36 18 PM

@rafaelfranca rafaelfranca merged commit 56f5946 into rails:main Sep 16, 2025
9 of 10 checks passed
@kyrylo
Copy link

kyrylo commented Sep 17, 2025

Thanks for merging this and releasing a new version. I’m still seeing this warning in marcel-1.1.0:

Screenshot 2025-09-17 at 12 08 58 PM

@Roupiye
Copy link

Roupiye commented Sep 30, 2025

still getting warnings even after this patch

@iHiD
Copy link

iHiD commented Oct 9, 2025

Bumping this as there's not been a response to the "still broken" comments after merge and they may have been missed. I'm still seeing these with 1.1.0 too:

/Users/iHiD/.gem/ruby/3.4.4/gems/marcel-1.1.0/lib/marcel/magic.rb:120: warning: literal string will be frozen in the future (run with --debug-frozen-string-literal for more information)

andynu added a commit to andynu/marcel that referenced this pull request Oct 31, 2025
PR rails#123 fixed frozen string warnings on line 127 (buffer creation)
but missed the warnings triggered by calling binmode/set_encoding on
StringIO objects at lines 123-124.

StringIO is always binary-capable and doesn't need these method calls.
Calling them triggers Ruby 3.4 deprecation warnings when running with
verbose warnings (-W2), which Rails test suites use by default.

This change skips binmode/set_encoding for StringIO objects while
maintaining the existing defensive respond_to? checks for File objects.
andynu added a commit to andynu/marcel that referenced this pull request Oct 31, 2025
PR rails#123 fixed frozen string warnings on line 127 (buffer creation)
but missed the warnings triggered by calling binmode/set_encoding on
StringIO objects at lines 123-124.

These methods modify StringIO's internal string encoding, which
triggers Ruby 3.4 deprecation warnings when running with verbose
warnings (-W2), which Rails test suites use by default.

Skip these calls for StringIO objects since marcel's byte-reading
operations work correctly without the encoding modification, as
demonstrated by all 384 tests passing.

This maintains the existing defensive respond_to? checks for File objects.
andynu added a commit to andynu/marcel that referenced this pull request Oct 31, 2025
PR rails#123 fixed frozen string warnings on line 127 (buffer creation)
but missed the warnings triggered by calling binmode/set_encoding on
StringIO objects at lines 120-121.

These methods modify StringIO's internal string encoding, which
triggers Ruby 3.4 deprecation warnings when running with verbose
warnings (-W2), which Rails test suites use by default.

Skip these calls for StringIO objects since marcel's byte-reading
operations work correctly without the encoding modification, as
demonstrated by all 384 tests passing.

This maintains the existing defensive respond_to? checks for File objects.
andynu added a commit to andynu/marcel that referenced this pull request Oct 31, 2025
PR rails#123 fixed frozen string warnings on line 127 (buffer creation)
but missed the warnings triggered by calling binmode/set_encoding on
StringIO objects.

These methods modify StringIO's internal string encoding, which
triggers Ruby 3.4 deprecation warnings when running with verbose
warnings (-W2), which Rails test suites use by default.

Solution: Make StringIO readonly before encoding modifications by
calling close_write. This prevents the internal string modification
that triggers the warning, while still allowing binmode/set_encoding
to execute normally. Only affects StringIO objects (which have
closed_write? method) - File objects are unaffected.

This workaround should only be needed until Ruby 3.5, which will
include an upstream fix: https://redmine.ruby-lang.org/issues/21280
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants