Skip to content

Commit 716e15f

Browse files
committed
utf8_to_uvchr_msgs: Use different DFA tables
I'm uncertain about this commit. There are three separate DFA tables already in core. One accepts Perl extended UTF-8; one accepts only strict Unicode UTF-8; and the third accepts modified Unicode UTF-8 spelled out by them in Corrigendum Perl#9. Both the Unicode varieties reject surrogate code points and anything above U+10FFFF. C9 accepts, but the other rejects non-character code points. Without this commit, the way it works is it uses the most restrictive table for the DFA. Anything it accepts is always valid. Anything it rejects is potentially problematic, and it calls a non-inlined function to examine the input more slowly to determine if it is acceptable and/or if a warning needs to be raised. This commit examines the input flags to determine which DFA to use in this situation. The benefit is that the slower routine could be avoided for many more code points. But the vast vast majority of calls to this function aren't for any problematic code points, so the extra cost of this will very rarely be recouped. The translation from UTF-8 is critically important. We want it to be as fast as possible. I would not even consider this commit if the extra cost weren't very small. A complicating factor is that 2048 (approximately 20% of the total) Korean Hangul syllable code points are not handled by the strict table, so must be by the slower function; though they're handled at the very beginning of it. These code points are never problematic, so it is unfortunate that they have to be handled via the slower function. But still, rarely will this function be called with them. Only the strict table has this problem The way this commit works is to have a table containing pointers to the three DFA tables. The function looks at the input flags; if none are present, it uses the loosest dfa; if any restrictions are present, it adds 1 to the index to use, and it the C9 resetrictions are present, it adds an extra 1. The flags are cast to bools to get each addition. If the bool casts didn't generate conditionals, the only cost to this would be two additions and an indirection; and I would say that that cost is so tiny that this would be worth it. But I looked at godbolt, and casting to bool requires a comparison on both modern clang and gcc. That makes me unsure of the tradeoff. Another option would be to just juse two DFAs, loose and most strict. Then there would be a single conditional, and the Hanguls still would be handled by the DFA when there were no flags restricting things
1 parent c135996 commit 716e15f

File tree

1 file changed

+27
-4
lines changed

1 file changed

+27
-4
lines changed

inline.h

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2976,6 +2976,20 @@ Perl_is_utf8_fixed_width_buf_loclen_flags(const U8 * const s,
29762976
|| is_utf8_valid_partial_char_flags(*ep, s + len, flags);
29772977
}
29782978

2979+
/* There are 3 different tables driving the dfa, depending on what sort of
2980+
* restrictions are wanted.
2981+
* [0] is where all of Perl's extended UTF-8 is accepted.
2982+
* [2] is where what is acceptable is the subset of [0] that conforms to
2983+
* Unicode's requirements for free exchange between processes.
2984+
* [1] is where what is acceptable is the superset of [2] that conforms to
2985+
* Unicode Corrigendum #9, for exchange between processes that each have
2986+
* agreed not to send certain portions of [2]. */
2987+
STATIC const U8 * PL_which_utf8_dfa_tab[] = {
2988+
PL_extended_utf8_dfa_tab,
2989+
PL_c9_utf8_dfa_tab,
2990+
PL_strict_utf8_dfa_tab
2991+
};
2992+
29792993
PERL_STATIC_INLINE UV
29802994
Perl_utf8n_to_uvchr_msgs(const U8 *s,
29812995
STRLEN curlen,
@@ -3028,6 +3042,15 @@ Perl_utf8n_to_uvchr_msgs(const U8 *s,
30283042
const U8 * send = s + curlen;
30293043
s0 = s;
30303044

3045+
/* Find which dfa table to use. If no restrictions, use [0]. If any,
3046+
* use at least [1]; and use [2] for the most restrictive */
3047+
const U8 * table =
3048+
PL_which_utf8_dfa_tab[
3049+
(bool) (flags & ( UTF8_WARN_ILLEGAL_INTERCHANGE
3050+
|UTF8_DISALLOW_ILLEGAL_INTERCHANGE))
3051+
+ (bool) (flags & (UTF8_WARN_NONCHAR | UTF8_DISALLOW_NONCHAR))
3052+
];
3053+
30313054
/* This dfa is fast. If it accepts the input, it was for a
30323055
* well-formed, non-problematic code point, which can be returned
30333056
* immediately. Otherwise we call a helper function to figure out the
@@ -3043,13 +3066,13 @@ Perl_utf8n_to_uvchr_msgs(const U8 *s,
30433066
* The terminology of the dfa refers to a 'class'. The variable 'type'
30443067
* would have been named 'class' except that is a reserved word in C++
30453068
* */
3046-
PERL_UINT_FAST8_T type = PL_strict_utf8_dfa_tab[*s];
3047-
PERL_UINT_FAST8_T state = PL_strict_utf8_dfa_tab[256 + type];
3069+
PERL_UINT_FAST8_T type = table[*s];
3070+
PERL_UINT_FAST8_T state = table[256 + type];
30483071
UV uv = (0xff >> type) & NATIVE_UTF8_TO_I8(*s);
30493072

30503073
while (state > 1 && ++s < send) {
3051-
type = PL_strict_utf8_dfa_tab[*s];
3052-
state = PL_strict_utf8_dfa_tab[256 + state + type];
3074+
type = table[*s];
3075+
state = table[256 + state + type];
30533076

30543077
uv = UTF8_ACCUMULATE(uv, *s);
30553078
}

0 commit comments

Comments
 (0)