summaryrefslogtreecommitdiff
path: root/op.c
diff options
context:
space:
mode:
authorKarl Williamson <khw@cpan.org>2020-04-01 06:05:06 -0600
committerKarl Williamson <khw@cpan.org>2020-04-02 09:17:38 -0600
commit9f31bc5d8d184ee704d37590d4271ea43374c6c8 (patch)
treec96da89ee686169982cffc31aea0aa512aa75597 /op.c
parent8bbdcb372d98806ff15f6de9ae9f263b85265432 (diff)
downloadperl-9f31bc5d8d184ee704d37590d4271ea43374c6c8.tar.gz
tr/abc/de/: Properly handle longer lhs in in-place calc
A tr/// can be done in-place if the target string doesn't contain a character whose transliterated representation is longer than the original. Otherwise, writing the new value would destroy the next character we need to read. In general, we can't know if a particular string contains such a character without keeping a list of the problematic characters, and scanning it ahead of time for occurrences of those. Instead, we determine at compilation time if, for a given transliteration, if there exists any possible target string that could have an overwriting problem. If none exist, we edit in place. Otherwise, we first make a copy. Prior to this commit, the code failed to account for the case where the rhs is shorter than the left, so that any unmatched lhs characters map to the final rhs one. The reason the code didn't consider this is that I didn't think of this possibility when writing it. This fixes #17654 and #17643
Diffstat (limited to 'op.c')
-rw-r--r--op.c7
1 files changed, 6 insertions, 1 deletions
diff --git a/op.c b/op.c
index 75a38d37cb..175703968b 100644
--- a/op.c
+++ b/op.c
@@ -7475,7 +7475,12 @@ S_pmtrans(pTHX_ OP *o, OP *expr, OP *repl)
t_cp_end = MIN(IV_MAX, t_cp + span - 1);
if (r_cp == TR_SPECIAL_HANDLING) {
- r_cp_end = TR_SPECIAL_HANDLING;
+
+ /* If unmatched lhs code points map to the final map, use that
+ * value. This being set to TR_SPECIAL_HANDLING indicates that
+ * we don't have a final map: unmatched lhs code points are
+ * simply deleted */
+ r_cp_end = (del) ? TR_SPECIAL_HANDLING : final_map;
}
else {
r_cp_end = MIN(IV_MAX, r_cp + span - 1);