Project

General

Profile

Actions

Feature #22238

closed

String#tr to take a Hash for multi-character replacements

Feature #22238: String#tr to take a Hash for multi-character replacements
1

Added by byroot (Jean Boussier) about 2 months ago. Updated 27 days ago.

Status:
Closed
Target version:
-
[ruby-core:126356]

Description

This is @matz's counter proposal to [Feature #22229] from the last dev-meeting

matz: I will counter-propose String#tr-with-hash style.

'Hello </script>'.tr(">" => '\u003e', "<" => '\u003c', "&" => '\u0026') # matz: OK

'Hello </script>'.tr("abc" => 'ABC') # should raise an exception
'Hello </script>'.tr("abc" => 'ABC', "ab" => "XY") # should raise an exception

"fée".tr("é" => "€") # it should work

"Hello".tr("l" => "ABC", "o" => "XYZ") #=> "HeABCABCXYZ"

"Hello".gsub(/./m) { hash[$&] || $& } # gsub equivalent

Notes:

  • Hash keys MUST be single characters (but can be multi-byte).
  • Values can be multiple characters.

Related issues 1 (0 open — 1 closed)

Related to Ruby - Feature #22229: Allow GCI.escapeHTML to take a custom escape tableRejectedActions

Updated by byroot (Jean Boussier) about 2 months ago Actions #1

  • Related to Feature #22229: Allow GCI.escapeHTML to take a custom escape table added

Updated by nobu (Nobuyoshi Nakada) about 2 months ago 1Actions #2

byroot (Jean Boussier) wrote:

  • Hash keys MUST be single characters (but can be multi-byte).

I think it would be worth to clarify: "character" means "codepoint" here not including combined characters, as well as String#tr does now.

Updated by Anonymous about 2 months ago Actions #3

  • Status changed from Open to Closed

Applied in changeset git|9144c914a1a51176d461c06e11bc78723c2c24cb.


Fix GC compaction re-embedding for arrays and strings

Don't re-embed after compaction for these types if they are pinned because if these objects
aren't embedded we want to keep their xmalloc's backing storage intact so that RARRAY_PTR
and RSTRING_PTR don't point to freed memory across a compaction event. If the object is pinned
we want to be able to trust that these pointers can't go stale across an allocation.

Fixes [Bug #22238]

Updated by luke-gru (Luke Gruber) about 2 months ago Actions #4 [ruby-core:126367]

  • Status changed from Closed to Open

The commit in the previous message has the wrong Issue ID, it is unrelated.

Updated by sowieso (So Wieso) about 2 months ago Actions #5 [ruby-core:126389]

Can you explain what

"Hello".gsub(/./m) { hash[$&] || $& } # gsub equivalent

is supposed to do? I can't wrap my head around this code.

Updated by byroot (Jean Boussier) about 2 months ago Actions #6 [ruby-core:126390]

It's a per character replacement:

hash = { "e" => "€", "l" => "EL", "o" => "Ø" }
str = "Hello".gsub(/./m) { hash[$&] || $& } # gsub equivalent
puts str
H€ELELØ

Updated by sowieso (So Wieso) about 2 months ago Actions #7 [ruby-core:126392]

Ah, of course. Thank you! I thought hash was Kernel#hash🤦

Updated by byroot (Jean Boussier) about 1 month ago Actions #8

  • Status changed from Open to Closed

Applied in changeset git|522f6ea22744b8ae92a50cee26785ddd5b64b7e7.


String#tr: take a Hash for multi-character replacements

[Feature #22238]

Updated by matz (Yukihiro Matsumoto) 27 days ago Actions #9 [ruby-core:126650]

  • Status changed from Closed to Open
  • Assignee set to byroot (Jean Boussier)

Reopened. I am satisfied with the spec, but the current implementation (master 3742091) has bugs.

"hello".tr("x" => "y")   #=> nil (should be "hello")
"hello".tr({})           #=> nil (should be "hello")

s = +"hello"
s.tr!("x" => "y")        #=> nil
s                        #=> "" (the receiver is emptied)

"0123456789abcdefghij".tr("h" => "H", "2" => "@")
#=> "0123456789abcdefgHij" ("2" is not replaced)
  • When nothing is replaced, tr returns nil, and tr! empties the receiver. The spec for tr! only checks the return value, so it passes.
  • On strings of 16 bytes or longer, the SIMD search only finds the first key. The loop that merges the matches of other keys starts with for (i = i; ...) and never runs. With more than 16 keys, the SSE2 version finds nothing.

Also:

  • The call-seq and document of tr do not mention the Hash form.
  • A broken string raises ArgumentError in non-fastpath encodings, but not in UTF-8/US-ASCII/BINARY. It should be consistent (the 2-argument form raises).

Please add tests for these cases as well.

Matz.

Updated by byroot (Jean Boussier) 27 days ago Actions #10

  • Status changed from Open to Closed

Applied in changeset git|0adfd0ec77e4acda94b7ec3643b3998c621f71df.


string.c: Fix String#tr edge cases and improve coverage

[Feature #22238]

"hello".tr("x" => "y")   #=> nil (should be "hello")
"hello".tr({})           #=> nil (should be "hello")

s = +"hello"
s.tr!("x" => "y")        #=> nil
s                        #=> "" (the receiver is emptied)

"0123456789abcdefghij".tr("h" => "H", "2" => "@")

Updated by byroot (Jean Boussier) 27 days ago Actions #11 [ruby-core:126663]

Thanks for catching that. I believe I addressed all points.

Actions

Also available in: PDF Atom