summaryrefslogtreecommitdiff
path: root/dump.c
diff options
context:
space:
mode:
authorKarl Williamson <khw@cpan.org>2019-11-04 21:30:48 -0700
committerKarl Williamson <khw@cpan.org>2019-11-06 21:22:24 -0700
commitf34acfecc286f2eff2450db713da005d888a7317 (patch)
tree48c7a2dcb411f0ed37216761f759365c82c6f243 /dump.c
parent8c90d3a9c79a9471ef12dde584263fc38571cf46 (diff)
downloadperl-f34acfecc286f2eff2450db713da005d888a7317.tar.gz
Reimplement tr/// without swashes
This large commit removes the last use of swashes from core. It replaces swashes by inversion maps. This data structure is already in use for some Unicode properties, such as case changing. The inversion map data structure leads to straight forward implementation code, so I collapsed the two doop.c routines do_trans_complex_utf8() and do_trans_simple_utf8() into one. A few conditionals could be avoided in the loop if this function were split so that one version didn't have to test for, e.g., squashing, but I suspect these are in the noise in the loop, which has to deal with UTF-8 conversions. This should be faster than the previous implementation anyway. I measured the differences some releases back, and inversion maps were faster than the equivalent swash for up to 512 or 1024 different ranges. These numbers are unlikely to be exceeded in tr/// except possibly in machine-generated ones. Inversion maps are capable of handling both UTF-8 and non-UTF-8 cases, but I left in the existing non-UTF-8 implementation, which uses tables, because I suspect it is faster. This means that there is extra code, purely for runtime performance. An inversion map is always created from the input, and then if the table implementation is to be used, the table is easily derived from the map. Prior to this commit, the table implementation was used in certain edge cases involving code points above 255. Those cases are now handled by the inversion map implementation, because it would have taken extra code to detect them, and I didn't think it was worth it. That could be changed if I am wrong. Creating an inversion map for all inputs essentially normalizes them, and then the same logic is usable for all. This fixes some false negatives in the previous implementation. It also allows for detecting if the actual transliteration can be done in place. Previously, the code mostly punted on that detection for the UTF-8 case. This also allows for accurate counting of the lengths of the two sides, fixing some longstanding TODO warning tests. A new flag is created, OPpTRANS_CAN_FORCE_UTF8, when the tr/// has a below 256 character resolving to one that requires UTF-8. If this isn't set, the code knows that a non-UTF-8 input won't become UTF-8 in the process, and so can take short cuts. The bit representing this flag is the same as OPpTRANS_FROM_UTF, which is no longer used. That name is left in so that the dozen-ish modules in cpan that refer to it can still compile. AFAICT none of them actually use the flag, as well they shouldn't since it is private to the core. Inversion maps are ideally suited for tr/// implementations. An issue with them in general is that for some pathological data, they can become fragmented requiring more space than you would expect, to represent the underlying data. However, the typical tr/// would not have this issue, requiring only very short inversion maps to represent; in some cases shorter than the table implementation. Inversion maps are also easier to deparse than swashes. A deparse TODO was also fixed by this commit, and the code to deparse UTF-8 inputs is simplified. One could implement specialized data structures for specific types of inputs. For example, a common tr/// form is a single range, like tr/A-Z/a-z/. That could be implemented without a table and be quite fast. An intermediate step would be to use the inversion map implementation always when the transliteration is a single range, and then special case length=1 maps at execution time. Thanks to Nicholas Rochemagne for his help on B
Diffstat (limited to 'dump.c')
-rw-r--r--dump.c13
1 files changed, 6 insertions, 7 deletions
diff --git a/dump.c b/dump.c
index 78c151da16..f03c3f6153 100644
--- a/dump.c
+++ b/dump.c
@@ -1305,13 +1305,13 @@ S_do_op_dump_bar(pTHX_ I32 level, UV bar, PerlIO *file, const OP *o)
case OP_TRANS:
case OP_TRANSR:
- if (o->op_private & (OPpTRANS_FROM_UTF | OPpTRANS_TO_UTF)) {
- /* utf8: table stored as a swash */
+ if (o->op_private & OPpTRANS_USE_SVOP) {
+ /* utf8: table stored as an inversion map */
#ifndef USE_ITHREADS
- /* with ITHREADS, swash is stored in the pad, and the right pad
+ /* with ITHREADS, it is stored in the pad, and the right pad
* may not be active here, so skip */
S_opdump_indent(aTHX_ o, level, bar, file,
- "SWASH = 0x%" UVxf "\n",
+ "INVMAP = 0x%" UVxf "\n",
PTR2UV(MUTABLE_SV(cSVOPo->op_sv)));
#endif
}
@@ -2986,11 +2986,10 @@ Perl_op_class(pTHX_ const OP *o)
* pointer to a table of shorts used to look up translations.
* Under utf8, however, a simple table isn't practical; instead,
* the OP is an SVOP (or, under threads, a PADOP),
- * and the SV is a reference to a swash
- * (i.e., an RV pointing to an HV).
+ * and the SV is an AV.
*/
return (!custom &&
- (o->op_private & (OPpTRANS_TO_UTF|OPpTRANS_FROM_UTF))
+ (o->op_private & OPpTRANS_USE_SVOP)
)
#if defined(USE_ITHREADS)
? OPclass_PADOP : OPclass_PVOP;